三、Spring依赖注入类型

本文详细介绍了Spring框架中的三种依赖注入类型:字段注入、构造器注入和setter方法注入。字段注入虽易用但可能导致外部可见性问题和循环依赖;构造器注入更推荐,确保组件不可变且避免循环依赖,但参数过多时代码冗长;setter方法注入提供按需注入的选择,适用于部分依赖。
摘要由CSDN通过智能技术生成

一、spring 概述
二、XML配置spring容器
三、Spring依赖注入类型


Spring依赖注入类型

Spring为开发人员提供了三种不同的依赖注入类型,分别是字段注入、构造器注入和setter方法注入。现在,假设我们有一个HealthRecordService接口以及它的实现类,如代码清单1-1所示。

代码清单1-1
public interface HealthRecordService {
    public void recordUserHealthData();
}

public class HealthRecordServiceImpl implements HealthRecordService {
    @Override
    public void recordUserHealthData () {
        System.out.println("HealthRecordService has been called.");
    }
}

基于HealthRecordServiceImpl实现类,我们该如何在Spring中完成该类的注入呢?三种方法各有什么优缺点以及如何选择?

1.1 字段注入

想要在一个类中通过字段的形式注入某个对象,就可以使用字段注入,示例代码如代码清单1-2所示。

代码清单1-2
public class ClientService {
    @Autowired
    private HealthRecordService healthRecordService;
    public void recordUserHealthData() {
        healthRecordService.recordUserHealthData();
    }
}

从代码片段1-2中可以看到,通过@Autowired注解类中定义的字段,就可以实现注入,简单方便并且代码的可读性也很高。虽然,字段注入是三种注入方式中最常用,也是最容易使用的一种,但同时,我们也最应该避免使用字段注入。为什么不推荐使用呢?

  • 原因一:对象的外部可见性。如代码清单1-2中定义了私有字段HealthRecordService healthRecordService,在Ioc容器中可以正常使用,但是当我们脱离容器单独使用时,如代码清单1-3,很明显会抛出NullPointerException空指针异常,原因是无法在ClientService的外部实例化HealthRecordService对象。采用字段注入,类与容器的耦合度过高,我们无法脱离容器来使用目标对象。同时,如果我们编写测试用例来验证ClientService类的正确性,那么想要使用HealthRecordService对象时,就只能通过反射的方式获得,这种做法不符合JavaBean开发规范的,而且可能导致空指针异常。

    代码清单1-3
    ClientService clientService = new ClientService();
    clientService.recordUserHealthData();
    
  • 原因二:可能导致潜在的循环依赖。如代码清单1-4ClassAClassB相互注入,形成循环依赖。但是上述代码在Spring中是合法的,容器启动时也不会报任何错误,而只有在使用到某个具体的ClassAClassB时才会报错。

    代码清单1-4
    public class ClassA {
        @Autowired
        private ClassB classB;
    }
    public class ClassB {
        @Autowired
        private ClassA classA;
    }
    
  • 原因三:无法设置需要注入的对象为final,也无法注入那些不可变对象。这是因为,字段只有在类实例化时才会实例化。

1.2 构造器注入

Spring官方并不推荐开发人员使用字段注入这种方式,而是推荐构造器注入。构造器注入是通过类的构造器来完成对象的注入,示例代码如代码清单1-5所示。因为HealthRecordService是通过ClientService构造函数进行注入的,所以势必可以脱离ClientService而独立存在,从而解决对象外部可见性的问题。

代码清单1-5
public class ClientService {
    private HealthRecordService healthRecordService;
    @Autowired
    public ClientService(HealthRecordService healthRecordService) {
        this.healthRecordService = healthRecordService;
    }
    public void recordUserHealthData() {
        healthRecordService.recordUserHealthData();
    }
}

Spring官方文档解释构造器注入的特性:The Spring team generally advocates constructor injection as it enables one to implement application components as immutable objects and to ensure that required dependencies are not null. Furthermore constructor-injected components are always returned to client (calling) code in a fully initialized state.

构造器注入能够保证注入的组件不可变,并且确保需要的依赖不为空。组件不可变就意味着我们可以使用final关键词来修饰所依赖的对象,而依赖不为空是指所传入的依赖对象肯定是一个实例对象,从而避免出现空指针异常。

构造器在遇到循环依赖时,如代码清单1-5所示,一旦采用构造器注入,在Spring项目启动的时候,就会抛出一个循环依赖异常,从而提醒你避免使用循环依赖。通过上述分析可知,字段注入的三大问题都可以通过使用构造器注入的方式来解决。但是,构造器注入的显著问题就是当构造函数中存在较多依赖对象时,大量的构造器参数会让代码冗长。这时候就可以引入setter方法注入。

代码清单1-6
public class ClassA {
    private ClassB classB;
    @Autowired
    public ClassA(ClassB classB) {
        this.classB = classB;
    }
}
public class ClassB {
    private ClassA classA;
    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }
}

1.3 setter 方法注入

setter方法注入如代码清单1-7所示,setter方法注入需要使用@Autowired注解字段setter方法,和构造器注入看上去有点类似,但它比构造函数更具可读性,因为我们可以把多个依赖对象分别通过setter方法逐一进行注入。不像构造器注入,一次强制注入全部字段,setter方法注入可以有选择地注入一部分依赖对象,换句话说,setter方法注入可以实现按需注入,实现只在需要时注入依赖。

代码清单1-7
public class ClientService {
    private HealthRecordService healthRecordService;
    @Autowired
    public void setHealthRecordService(HealthRecordService healthRecordService) {
        this.healthRecordService = healthRecordService;
    }
    public void recordUserHealthData() {
        healthRecordService.recordUserHealthData();
    }
}

关于循环依赖,构造器注入可以提示开发者不能这么做,但是setter方法注入可以完美解决该问题。如代码清单1-8,在ClassAClassB的作用域都是Singleton时,代码可以正确执行。

代码清单1-8
public class ClassA {
    private ClassB classB;
    @Autowired
    public void setClassB(ClassB classB) {
        this.classB = classB;
    }
}
public class ClassB {
    private ClassA classA;
    @Autowired
    public void setClassA(ClassA classA) {
        this.classA = classA;
    }
}

对比构造器注入,setter方法注入可以对依赖对象进行多次重复注入,而构造器注入中无法实现。

构造器注入适合于强制对象注入;setter 注入适合于可选对象注入;字段注入是应该被避免使用的,因为对象无法脱离容器而独立运行

  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值