2.1.2 构造器注入
构造器注入的形式也很简单,就是通过类的构造函数来完成对象的注入,示例代码如代码清单2-5所示。
代码清单2-5 构造器注入示例代码
public class ClientService { private HealthRecordService healthRecordService; @Autowired public ClientService(HealthRecordService healthRecordService) { this.healthRecordService = healthRecordService; } public void recordUserHealthData() { healthRecordService.recordUserHealthData(); } }
可以看到构造器注入能解决对象外部可见性的问题,因为HealthRecordService是通过ClientService构造函数进行注入的,所以势必可以脱离ClientService而独立存在。
我们进一步引用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关键词来修饰所依赖的对象,而依赖不为空是指所传入的依赖对象肯定是一个实例对象,从而避免出现空指针异常。
同时,基于构造器注入,我们也来讨论前面介绍的ClassA和ClassB之间的循环依赖关系,实现方式如代码清单2-6所示。
代码清单2-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; } }
一旦采用构造器注入,在Spring项目启动的时候,就会抛出一个循环依赖异常,从而提醒你避免使用循环依赖。
通过上述分析,我们可以看到字段注入的三大问题都可以通过使用构造器注入的方式来解决。但是,构造器注入的显著问题就是当构造函数中存在较多依赖对象时,大量的构造器参数会让代码冗长。这时候就可以引入Setter方法注入。