【Spring】IoC&DI详解-2. DI详解

下面我们就来详细介绍下DI(Dependency Injection),中文翻译就是依赖注入,它是一种设计模式,用来实现IoC编程思想,它的核心思想就是将对象之间的依赖关系进行解耦合。通过依赖注入,可以为类的属性、构造方法、setter方法中提供所依赖的对象和值,是一种可重用、低耦合的思想
关于依赖注入,Spring提供了三种方式:

  1. 属性注入(Field Injection)
  2. 构造方法注入(Constructor Injection)
  3. setter注入(Setter Injection)

2.1 属性注入

Controller层代码如下:

@Controller // 将bean存储到Spring容器中
public class TestController {

    @Autowired
    private TestService testService; // 属性注入

    public void testDI() {
        // 调用属性方法
        testService.testService();
    }
}

此时在启动类中获取bean实例并调用testDI方法

@SpringBootApplication
public class IocDiDemoApplication {

    public static void main(String[] args) {
        // 1. 获取IoC容器
        ApplicationContext context = SpringApplication.run(IocDiDemoApplication.class, args);
        // 2. 获取TestController的bean对象
        TestController bean = (TestController) context.getBean("testController");
        // 3. 调用testDI方法
        bean.testDI();
    }
}

image.png
此时程序正常运行!说明Spring容器通过@Autowired注解已经自动使用 属性注入 的方式给TestService进行赋值了

2.2 构造方法注入

2.2.1 构造注入示例

此时如果我们不使用属性注入的方式,而是使用构造方法进行注入!代码示例如下:

@Controller // 将bean存储到Spring容器中
public class TestController {
    private TestService testService;

    public TestController(TestService testService) {
        this.testService = testService;
    }

    public void testDI() {
        // 调用属性方法
        testService.testService();
    }


    public void testController() {
        System.out.println("test controller");
    }
}

再次执行启动类代码,发现程序正常运行!

2.2.2 多个构造方法

此时我们将代码进行修改,提供多个构造方法:

@Controller // 将bean存储到Spring容器中
public class TestController {

    private TestService testService;

    private TestMapper testMapper;

    public TestController(TestService testService) {
        this.testService = testService;
    }

    public TestController(TestService testService, TestMapper testMapper) {
        this.testService = testService;
    }

    public void testDI() {
        // 调用属性方法
        testService.testService();
    }
}

image.png
此时就会发现程序报错了!

这是因为如果提供了多个构造器,那么Spring容器无法确定使用哪个构造器,因此默认会注入到无参构造器中,如果想要自己指定构造器,可以在构造方法上加上@Autowired注解

解决方式如下:

@Controller // 将bean存储到Spring容器中
public class TestController {
    
    private TestService testService;

    private TestMapper testMapper;

    @Autowired
    public TestController(TestService testService) {
        this.testService = testService;
    }

    public TestController(TestService testService, TestMapper testMapper) {
        this.testService = testService;
    }

    public void testDI() {
        // 调用属性方法
        testService.testService();
    }
}

2.3 setter注入

我们还可以提供setter方法注入类所需要的依赖对象

@Controller // 将bean存储到Spring容器中
public class TestController {

    private TestService testService;

    @Autowired
    public void setTestService(TestService testService) {
        this.testService = testService;
    }

    public void testDI() {
        // 调用属性方法
        testService.testService();
    }
}

启动程序后,正常打印结果,说明setter方法同样可以进行依赖注入

2.4 三种注入方式优缺点分析

  • 属性注入:
    • 优点:比较简单,使用方便
    • 缺点:只能在IoC容器中进行使用;有可能出现NPE(空指针异常);而且不能注入Final修饰的属性
  • 构造注入:
    • 优点:可以注入Final修饰的属性;注入的对象不会被改变较为安全;依赖对象在使用之前一定会被完全初始化;通用性好(是JDK支持的)
    • 缺点:如果需要注入多个对象,代码比较冗长
  • setter注入:
    • 优点:方便在类实例创建完后,重新赋值或者注入
    • 缺点:不能注入Final修饰的属性;对象有可能被外部类访问调用,不安全

2.5 @Autowired存在的问题

下面来演示@Autowired注解存在的问题!
Config/BeanConfig.java

@Configuration
public class BeanConfig {

    @Bean
    public LocalDateTime localDateTime() {
        return LocalDateTime.now();
    }

    @Bean
    LocalDateTime localDateTime2() {
        return LocalDateTime.of(2024, 4, 1, 12, 0, 0);
    }

    @Bean
    LocalDateTime localDateTime3() {
        return LocalDateTime.of(2024, 5, 1, 8, 0, 0);
    }
}

我们将三个相同类型的LocalDateTime对象交由Spring容器进行管理!此时在TestConrtoller中尝试使用@Autowired注解就会发现编译失败!
image.png
这是因为@Autowired注解按照bean的类型进行自动装配,此时Spring容器内部有三个同一类型的bean,因此无法进行注入,解决方式有如下三种:

  1. 使用@Primary注解,告知Spring容器在获取该类型bean时优先选择该对象

image.png

  1. 使用@Autowired + @Qualifier搭配使用,配置value属性为bean的名称

image.png

  1. 使用@Resource注解声明bean的名称

image.png

常见面试题:@Autowired和@Resource注解的区别

  • @Autowired是Spring框架提供的注解,而@Resource是JDK提供的注解
  • @Autowired默认按照类型进行依赖注入,而@Resource是按照名称进行注入,相比较于@Autowired,@Resource提供了更多的注解参数设置
上一篇:TCP网络三次握手(链接请求)和四次挥手(断链请求),FIN报文和RST报文


下一篇:UDS升级入门,手把手教你———MCU相关驱动功能实现