解耦利器:带你快速了解控制反转以及依赖注入

前言:

        在我们的后端工程中,面临着一个很严峻的问题:代码的体量太大了!那么我们如果不能很好的处理各个方法之间的关系,就会对后期的代码维护和更新造成很大的问题,例如可能只是一次对方法的简单更改,却要改动一系列调用这个方法的相关代码,而我们今天要学习的控制反转和依赖注入,就很好的为我们解决了这个问题,因此我们今天来详细介绍一下控制反转和依赖注入

解耦利器:带你快速了解控制反转以及依赖注入_第1张图片

目录

前言:

控制反转(IOC):

注入依赖(DI):

示例:

总结:


 

 

 在介绍控制反转和依赖注入之前,我们要先知道什么是内聚性和耦合性:

内聚性:软件中各个功能模块内部之间的功能联系

耦合性:衡量软件中各个层/模块之间的依赖,关联的程度

而我们自己在写项目的时候,要遵循高内聚,低耦合的基本逻辑,翻译一下就是让每一个模块负责的功能尽可能单一,让每个功能之间的练习尽可能减少

控制反转(IOC):

控制反转(Inversion of Control,IoC)是一种软件设计原则,用于解耦组件之间的关系和提高代码的可维护性和可测试性。

在传统的编程模式中,组件通常会自己创建和管理其所依赖的对象。这导致了高度的耦合性,使得组件难以独立测试和维护。而控制反转通过将组件的控制权转移到外部容器或框架中,改变了这种关系。

在控制反转中,组件不再负责创建和管理依赖对象,而是声明它们所需的依赖关系。外部容器或框架会负责创建和注入正确的依赖对象。这样,组件只需关注自身的逻辑实现,而不需要关心依赖对象的创建和生命周期管理。

控制反转的核心思想是通过外部容器来实现组件之间的解耦,提高代码的可测试性和可维护性。它可以使用依赖注入来实现,即将依赖对象作为参数传递给组件,或通过属性或方法注入的方式提供依赖对象。

总结来说:以前我们创建对象都是直接在方法当中,而现在我们把创建对象的权利交给了IOC容器,而方法如果需要使用对象,就在IOC容器里面寻找自己需要的对象来使用,而我们通过容器这种方式,避免了方法与方法之间的直接联系,减少了各个方法之间的耦合性。

 而我们把IOC容器中的这些对象叫做Bean对象,而我们想要把一个对象交给IOC容器进行管理,我们只需要在对应的类上加上如下注解:(图片来自黑马程序员)

解耦利器:带你快速了解控制反转以及依赖注入_第2张图片

而我们在一个类上添加了这些注解后,这个类创建的权力就交给了OIC容器,完成了与需要使用这个类的方法的解耦

声明bean的时候,我们可以使用value来指定bean的名字,如果没有指定,默认为类名首字母小写

使用以上四个注解都可以声明bean,但是在spring boot集成web开发中,声明控制器bean只能用@Controller

此外不是只要在类前面加上这四个注解,该类创建的对象就会变为bean对象,这是因为如果想要生效,还需要被@ComponentScan扫描

@ComponentScan注解虽然没有显示配置,但实际上已经包含在了启动类声明注解@SpiringBootApplication中,扫描的默认范围是启动类所在包以及其子包

因此如果我们把注解了bean的类放到了与启动类所在包平级的包里面,那么是无法扫描到的

我们可以自己在@SpiringBootApplication中找到这个注解

解耦利器:带你快速了解控制反转以及依赖注入_第3张图片

注入依赖(DI):

注入依赖(Dependency Injection,DI)是控制反转(Inversion of Control,IoC)的一种实现方式,它用于将一个组件所依赖的对象(依赖)从外部容器注入到组件中。

在注入依赖的过程中,组件不再负责创建和管理所需的依赖对象,而是将依赖对象作为参数、属性或方法的方式传递给组件。这样,组件只需声明自己所需的依赖,而不需要关心依赖对象的具体创建和生命周期管理。

注入依赖有助于解耦组件之间的关系,提高代码的灵活性、可测试性和可维护性。它允许我们通过配置或编程方式在不修改组件代码的情况下,改变组件的依赖对象,使得系统更易于扩展和升级。

常见的注入依赖方式包括:

  1. 构造函数注入(Constructor Injection):依赖通过组件的构造函数参数传递进来。
  2. 属性注入(Property Injection):依赖通过组件的属性(或字段)进行注入。
  3. 方法注入(Method Injection):依赖通过组件的方法参数传递进来。

通过注入依赖,我们可以实现组件之间的松耦合,提高代码的可测试性和可维护性。同时,注入依赖也是实现控制反转的一种方式,将组件的控制权转移到外部容器或框架,达到解耦和灵活管理的目的

在 Spring 框架中,有多个注解用于实现依赖注入,以下是一些常用的注解:

  1. @Autowired:用于自动装配 Bean 的依赖关系。当容器中存在与目标类型匹配的 Bean 时,会将其注入到目标对象中。如果存在多个匹配的 Bean,则可以结合其他注解使用,或使用限定符(Qualifier)来指定具体的 Bean。

  2. @Qualifier:用于注入特定名称或限定符的 Bean。结合 @Autowired 使用时,可以解决存在多个匹配的 Bean 的问题。可以将 @Qualifier 标注在具体的属性或构造函数参数上,并指定要注入的 Bean 的名称或限定符。

  3. @Resource:类似于 @Autowired,也用于自动装配 Bean 的依赖关系。它可以根据 Bean 的名称或类型进行注入。如果指定了名称,则会注入与该名称匹配的 Bean;如果没有指定名称,则会根据类型进行注入。

  4. @Value:用于注入简单类型的值或表达式。可以将属性值直接注入到 Bean 中,并支持 SpEL 表达式。

当存在多个相同类型的 Bean 时,可以采取以下解决方法:

  1. 使用 @Qualifier 注解结合 @Autowired:在注入的地方使用 @Autowired 注解,同时结合 @Qualifier 注解指定需要注入的具体 Bean 的名称或限定符。

    @Qualifier("bean的名称")
    @AutoWired
    
  2. 使用 @Primary:在多个相同类型的 Bean 中,使用 @Primary 注解标注一个 Bean,表示它是首选的 Bean,会优先被注入。

  3. 使用 @Resource 注解:@Resource 注解可以根据名称进行注入。可以将 @Resource 注解标注在需要注入的地方,通过指定 Bean 的名称进行注入。

    @Resource(name="bean的名称")
    @AutoWired
    
  4. 使用 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来完成,从而提高应用程序的可维护性和可扩展性。因此我们要完全熟练的掌握控制反转和依赖注入的具体应用。

如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!

69e9169c980f43e0aad31ff9ada88a9c.png

 

你可能感兴趣的:(java,开发语言,spring,boot,spring,spring)