一、引言
Spring框架对Java开发的重要性不言而喻,其核心特性就是IOC(Inversion of Control, 控制反转)和AOP(面向切面编程),平时使用最多的就是其中的IOC,我们通过将组件交由Spring的IOC容器管理,将对象的依赖关系由Spring控制,避免硬编码所造成的过度程序耦合。
二、Spring三种注入方式
1、Field注入
@Controller
public class AppleController {
@Autowired
// @Resource
private AppleService appleService;
}
此种注解方式,应用最广泛:
(1). 注入简单,只需在字段上添加@Autowired或@Resource;
(2). 减少大量冗余代码,美观;
(3). 新增Field时不需要过多代码修改;
Spring 注释 @Autowired 和@Resource 的区别
2、构造器或者叫构造函数注入
@Controller
public class AppleController {
private final AppleService appService;
private final AppleService appService1;
@Autowired
public AppleController (AppleService appService,AppleService appService1) {
this.appService= appService;
this.appService1= appService1;
}
}
或
@Controller
public class AppleController {
private final AppleService appService;
// 当只有一个参数时可不写@Autowired
public AppleController (AppleService appService) {
this.appService= appService;
}
}
Spring4.x推荐的注入方式即构造函数注入。对比Field注入:
(1). 新增Field修改麻烦
(2). 当Field多余5个时不符合构造方法的基本规范,显得笨重、臃肿;
3、Setter注入
@Controller
public class AppleController {
private AppleService appService;
//使用方式上同,略
@Autowired
public void setAppleService(AppleService appService) {
this.appService= appService;
}
}
Spring3.x推荐的注入方式,但并没有被广泛应用,当初推荐的理由:
(1). 解决了构造器注入的笨重;
(2). 可以让类在之后重新配置或者重新注入。
三、为什么Spring4.x推荐构造函数注入?
依赖不可变:加入了final来约束修饰Field,这条是很显然的;
依赖不可为空:在实例化的时候会检查构造函数参数是否为空,如果为空(未找到改类型的实例对象)则会抛出异常。
单一职责:当使用构造函数注入时,如果参数过多,你会发现当前类的职责过大,需要进行拆分。而使用Field注入时,你并不会意识到此问题。
更利于单元测试:按照其他两种方式注入,当单元测试时需要初始化整个spring的环境,而采用构造方法注入时,只需要初始化需要的类即可,即可以直接实例化需要的类。
避免IOC容器以外环境调用时潜在的NPE(空指针异常)。
避免循环依赖。
保证返回客户端(调用)的代码的时候是完全初始化的状态。
四、注意
Field注入方式
//承接上面field注入的代码,假如客户端代码使用下面的调用(或者再Junit测试中使用)
//这里只是模拟一下,正常来说我们只会暴露接口给客户端,不会暴露实现。
AppleController appleController = new AppleController ();
appleController.eat(); // -> NullPointerException
如果使用Field注入,缺点显而易见,对于IOC容器以外的环境,除了使用反射来提供它需要的依赖之外,无法复用该实现类。而且将一直是个潜在的隐患,因为你不调用将一直无法发现NPE的存在。
还值得一提另外一点是:使用Field注入可能会导致循环依赖,即A里面注入B,B里面又注入A:
public class A {
@Autowired
private B b;
}
public class B {
@Autowired
private A a;
}
如果使用构造器注入,在spring项目启动的时候,就会抛出:BeanCurrentlyInCreationException:Requested bean is currently in creation: Is there an unresolvable circular reference?从而提醒你避免循环依赖,如果是Field注入的话,启动的时候不会报错,在使用那个bean的时候才会报错。