什么是依赖注入
首先,依赖注入是一种设计模式,用于实现控制反转(IoC),使得一个对象(通常是一个Bean)不需要自己创建它所依赖的对象,而是通过外部容器(比如Spring容器)来注入这些依赖。这样做的好处是解耦了对象和其依赖之间的关系,提高了代码的可测试性和可维护性。
依赖注入的三种方式
在Spring框架中,依赖注入通常通过构造器注入、setter方法注入和字段注入(虽然不常见且不推荐)这三种方式实现。构造器注入和setter方法注入都是显式的方式,即Bean通过其公开的构造器或setter方法明确告诉容器它需要哪些依赖。这种方式保持了Bean的封装性,因为容器只能通过公开的接口与Bean交互,而不能直接访问Bean的私有字段。
字段注入破坏了什么原则
相比之下,字段注入(基于@Autowired等注解直接注入到字段上)破坏了封装性。因为这种方式允许容器直接访问和修改Bean的私有字段,这违反了面向对象编程的一个基本原则:封装性。封装性要求将对象的内部状态(即私有字段)隐藏起来,只通过公开的接口(如构造器、setter和getter方法)与外部交互。字段注入破坏了这一原则,因为容器现在能够直接操作Bean的内部状态,从而可能导致意外的副作用和难以维护的代码。
并且,根据SOLID设计原则来讲,一个类的设计应该符合单一职责原则,即一个类只负责一个功能。当我们基于字段注入时,随着项目的业务暴增,字段将会越来越多,我们可能就会无形之中违背了这一原则。若是采用基于构造器的注入方式,由于构造器注入的写法较为臃肿,所以就会在间接的提醒我们违背了单一职责原则,需要去重构了。
字段注入不利于单元测试
很明显,使用了Autowired注解,说明这个类依赖了Spring容器,这让我们在进行单元测试的时候必须要启动一个Spring容器才可以测试这个类,显然太麻烦,这种测试方式非常重,对于大型项目来说,往往启动一个容器就要好几分钟,这样非常耽误时间。
不过,如果使用构造器的依赖注入就不会有这种问题,或者,我们可以使用Resource注解也可以解决上述问题。
字段注入可能产生NullPointException空指针异常
对于一个bean而言,它的初始化顺序是:
静态变量或静态语句块 --> 实例变量或初始化语句块 --> 构造方法 --> @Autowired
所以在静态语句块,初始化语句块以及构造方法中使用@Autowired表明的字段都会引起空指针异常,如
@Component
class Test{
@Autowired
private Bean bean;
private final String name;
public Test(){
this.name = bean.getName();
}
}
总结
在设计Spring Bean时,应该优先使用构造器注入和setter方法注入这两种显式的方式来实现依赖注入,以保持Bean的封装性,此外还需要注意一个Bean的初始化顺序,避免NPE。所以字段注入应该尽量避免使用,除非在特殊情况下有充分的理由。