参考文章:http://vojtechruzicka.com/field-dependency-injection-considered-harmful/
首先对于还不熟悉@Autowired的同学当然也包括我先去看看它到底有什么作用。—自动装配
大体意思就是就是说用了它就可以省去类里面的set/get方法,也不用在xml文件中要注入的类中设置属性,它会根据@Autowired注释自动完成。
言归正传,那为什么spring team不推荐使用Filed injection呢?
注入类型
有三种主要的方式注入依赖到你的类中。构造器注入、Setter(方法)和Field injection。下面让我们来比较一下这三种方式。
构造器注入
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
this.dependencyA = dependencyA;
this.dependencyB = dependencyB;
this.dependencyC = dependencyC;
}
Setter注入
private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;
@Autowired
public void setDependencyA(DependencyA dependencyA) {
this.dependencyA = dependencyA;
}
@Autowired
public void setDependencyB(DependencyB dependencyB) {
this.dependencyB = dependencyB;
}
@Autowired
public void setDependencyC(DependencyC dependencyC) {
this.dependencyC = dependencyC;
}
Field 注入
@Autowired
private DependencyA dependencyA;
@Autowired
private DependencyB dependencyB;
@Autowired
private DependencyC dependencyC;
问题是什么
正如你所见,Field注入看起来非常好,够简洁,并且也没有样板式的代码(为了实现通用和简单的任务而重复的代码)。代码通俗易懂。你的类可以专注于业务而不被依赖注入所污染。你只需要把@Autowired扔到变量之上就好了,不需要特殊的构造器或者set方法,依赖注入容器会提供你所需的依赖。Java本身就非常繁重,所以要尽可能的让你的代码简洁,对吗?
单一职责侵入原则
添加依赖是很简单的,可能过于简单了。添加六个、十个甚至一堆依赖一点也没有困难。当你使用构造器方式注入,到了某个特定的点,构造器中的参数变得太多以至于很明显地发现something is wrong。拥有太多的依赖通常意味着你的类要承担更多的责任。明显违背了单一职责原则(SRP:Single responsibility principle)。
依赖隐藏
使用依赖注入容器意味着类不再对依赖负责,获取依赖的职责从类中抽离出来,依赖容器会帮你装配。当类不再为依赖负责,它应该更明确的使用公有的接口方法或构造器,使用这种方式能很清晰的了解类需要什么,也能明确它是可选的(setter注入)还是强制的(构造器注入)。
依赖注入容器耦合
依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说,这个类应该是一个简单的POJO(Plain Ordinary Java Object)能够被单独实例化并且你也能为它提供它所需的依赖。只有这样,你才能在单元测试中实例化这个类而不必去启动依赖注入容器,实现测试分离(启动容器更多是集成测试)。
然而,当使用变量直接注入时,没有一种方式能直接地实例化这个类并且满足其所有的依赖。这就意味着:
1.有一种方式(调用默认构造器)来创建对象就是使用new关键字,但是当这个对象缺少一些必要的依赖,调用的时候就会出现空指针异常。
举个栗子:
public class DependencyA {
public void a(){
System.out.println("dependencyA");
}
}
public class POJO {
@Autowired
private DependencyA dependencyA;
public void execute(){
dependencyA.a();
}
}
public class Test {
public static void main(String[] args) {
POJO pojo = new POJO();
pojo.execute();
}
}
当你执行execute()方法时就会报空指针异常,是因为DependencyA没有被创建,使用这种方式(@Autowired)也不会强制你去创建类所需的依赖,所以当使用者调用的方法的时候就可能会出现空指针异常。
2.像这样的类没办法在容器之外被重用,也不能期望反射提供其所需的依赖。
不变性
不像构造器注入,Field注入不能有效地用来指定依赖
构造器注入 VS Setter注入
Setter
Setter应该被用来注入可变的依赖。当没有提供依赖时,这个类也应该能够运转。当实例化对象后,这些依赖也能随时改变。其实也视情况而变,有时,一个不变的对象是理想状态。有时,最好是能在运行期间改变对象的属性。
构造器
构造器对注入强制性的的依赖是好的。对象需要这些依赖才能正常运转,通过构造器提供这些依赖就能保证对象初始化后就能被使用。使用构造器注入的一个可能的影响就是循环依赖。