在我们的后端工程中,面临着一个很严峻的问题:代码的体量太大了!那么我们如果不能很好的处理各个方法之间的关系,就会对后期的代码维护和更新造成很大的问题,例如可能只是一次对方法的简单更改,却要改动一系列调用这个方法的相关代码,而我们今天要学习的控制反转和依赖注入,就很好的为我们解决了这个问题,因此我们今天来详细介绍一下控制反转和依赖注入
目录
前言:
控制反转(IOC):
注入依赖(DI):
示例:
总结:
在介绍控制反转和依赖注入之前,我们要先知道什么是内聚性和耦合性:
内聚性:软件中各个功能模块内部之间的功能联系
耦合性:衡量软件中各个层/模块之间的依赖,关联的程度
而我们自己在写项目的时候,要遵循高内聚,低耦合的基本逻辑,翻译一下就是让每一个模块负责的功能尽可能单一,让每个功能之间的练习尽可能减少。
控制反转(Inversion of Control,IoC)是一种软件设计原则,用于解耦组件之间的关系和提高代码的可维护性和可测试性。
在传统的编程模式中,组件通常会自己创建和管理其所依赖的对象。这导致了高度的耦合性,使得组件难以独立测试和维护。而控制反转通过将组件的控制权转移到外部容器或框架中,改变了这种关系。
在控制反转中,组件不再负责创建和管理依赖对象,而是声明它们所需的依赖关系。外部容器或框架会负责创建和注入正确的依赖对象。这样,组件只需关注自身的逻辑实现,而不需要关心依赖对象的创建和生命周期管理。
控制反转的核心思想是通过外部容器来实现组件之间的解耦,提高代码的可测试性和可维护性。它可以使用依赖注入来实现,即将依赖对象作为参数传递给组件,或通过属性或方法注入的方式提供依赖对象。
总结来说:以前我们创建对象都是直接在方法当中,而现在我们把创建对象的权利交给了IOC容器,而方法如果需要使用对象,就在IOC容器里面寻找自己需要的对象来使用,而我们通过容器这种方式,避免了方法与方法之间的直接联系,减少了各个方法之间的耦合性。
而我们把IOC容器中的这些对象叫做Bean对象,而我们想要把一个对象交给IOC容器进行管理,我们只需要在对应的类上加上如下注解:(图片来自黑马程序员)
而我们在一个类上添加了这些注解后,这个类创建的权力就交给了OIC容器,完成了与需要使用这个类的方法的解耦
声明bean的时候,我们可以使用value来指定bean的名字,如果没有指定,默认为类名首字母小写
使用以上四个注解都可以声明bean,但是在spring boot集成web开发中,声明控制器bean只能用@Controller
此外不是只要在类前面加上这四个注解,该类创建的对象就会变为bean对象,这是因为如果想要生效,还需要被@ComponentScan扫描
@ComponentScan注解虽然没有显示配置,但实际上已经包含在了启动类声明注解@SpiringBootApplication中,扫描的默认范围是启动类所在包以及其子包
因此如果我们把注解了bean的类放到了与启动类所在包平级的包里面,那么是无法扫描到的
我们可以自己在@SpiringBootApplication中找到这个注解
注入依赖(Dependency Injection,DI)是控制反转(Inversion of Control,IoC)的一种实现方式,它用于将一个组件所依赖的对象(依赖)从外部容器注入到组件中。
在注入依赖的过程中,组件不再负责创建和管理所需的依赖对象,而是将依赖对象作为参数、属性或方法的方式传递给组件。这样,组件只需声明自己所需的依赖,而不需要关心依赖对象的具体创建和生命周期管理。
注入依赖有助于解耦组件之间的关系,提高代码的灵活性、可测试性和可维护性。它允许我们通过配置或编程方式在不修改组件代码的情况下,改变组件的依赖对象,使得系统更易于扩展和升级。
常见的注入依赖方式包括:
通过注入依赖,我们可以实现组件之间的松耦合,提高代码的可测试性和可维护性。同时,注入依赖也是实现控制反转的一种方式,将组件的控制权转移到外部容器或框架,达到解耦和灵活管理的目的
在 Spring 框架中,有多个注解用于实现依赖注入,以下是一些常用的注解:
@Autowired
:用于自动装配 Bean 的依赖关系。当容器中存在与目标类型匹配的 Bean 时,会将其注入到目标对象中。如果存在多个匹配的 Bean,则可以结合其他注解使用,或使用限定符(Qualifier)来指定具体的 Bean。
@Qualifier
:用于注入特定名称或限定符的 Bean。结合 @Autowired
使用时,可以解决存在多个匹配的 Bean 的问题。可以将 @Qualifier
标注在具体的属性或构造函数参数上,并指定要注入的 Bean 的名称或限定符。
@Resource
:类似于 @Autowired
,也用于自动装配 Bean 的依赖关系。它可以根据 Bean 的名称或类型进行注入。如果指定了名称,则会注入与该名称匹配的 Bean;如果没有指定名称,则会根据类型进行注入。
@Value
:用于注入简单类型的值或表达式。可以将属性值直接注入到 Bean 中,并支持 SpEL 表达式。
当存在多个相同类型的 Bean 时,可以采取以下解决方法:
使用 @Qualifier
注解结合 @Autowired
:在注入的地方使用 @Autowired
注解,同时结合 @Qualifier
注解指定需要注入的具体 Bean 的名称或限定符。
@Qualifier("bean的名称")
@AutoWired
使用 @Primary
:在多个相同类型的 Bean 中,使用 @Primary
注解标注一个 Bean,表示它是首选的 Bean,会优先被注入。
使用 @Resource
注解:@Resource
注解可以根据名称进行注入。可以将 @Resource
注解标注在需要注入的地方,通过指定 Bean 的名称进行注入。
@Resource(name="bean的名称")
@AutoWired
使用 XML 或 Java 配置文件进行手动配置:在配置文件中手动指定要注入的具体 Bean 的名称或限定符。
需要注意的是,更多的时候,建议在容器中避免存在多个相同类型的 Bean,以便减少歧义和复杂性。可以通过修改 Bean 的名称、使用限定符等方式来确保唯一性。
首先,我们定义一个接口和一些实现类:
// 定义接口
public interface MessageService {
String getMessage();
}
// 实现类1
@Component
public class HelloWorldService implements MessageService {
@Override
public String getMessage() {
return "Hello, World!";
}
}
// 实现类2
@Component
public class GreetingService implements MessageService {
@Override
public String getMessage() {
return "Greetings!";
}
}
接下来,我们创建一个类来使用这些实现类:
@Component
public class MessagePrinter {
private final MessageService messageService;
@Autowired
public MessagePrinter(MessageService messageService) {
this.messageService = messageService;
}
public void printMessage() {
System.out.println(messageService.getMessage());
}
}
在上面的例子中,MessagePrinter
类通过构造函数进行依赖注入,它依赖于一个MessageService
对象。注解@Autowired
告诉Spring容器,它需要为这个构造函数注入一个MessageService
的实例。
最后,我们可以创建一个启动类来运行这个示例:
@SpringBootApplication
public class MyApp {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(MyApp.class, args);
// 从容器中获取MessagePrinter实例并调用printMessage方法
MessagePrinter printer = context.getBean(MessagePrinter.class);
printer.printMessage();
}
}
在上述示例中,Spring Boot会自动扫描并实例化被注解@Component
修饰的类,并将它们纳入容器管理。通过@Autowired
注解,Spring Boot能够自动将依赖注入到需要的地方。
控制反转(IoC)和依赖注入(DI)是Spring Boot中的核心特性,它们使得组件的创建和管理更加灵活和可控。通过使用注解和容器,我们可以将对象的创建和依赖关系的管理交给Spring Boot来完成,从而提高应用程序的可维护性和可扩展性。因此我们要完全熟练的掌握控制反转和依赖注入的具体应用。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!