- 1. Spring中的三种依赖注入方式
- 1.1 Field Injection
- 1.2 Constructor Injection
- 1.1 Setter Injection
- 2. 三种依赖注入对比分析
- 2.1 可靠性
- 2.2 可维护性
- 2.3 可测试性
- 2.4 灵活性
- 2.5 循环关系的检测
- 2.6 性能表现
- 2.7 综上所述
- 3.为什么spring官方推荐构造器注入
- 3.1 Autowired注解警告Field injection is not recommended
- 3.2 Spring开发团队建议注入方式
1.Autowired注入
@Autowired注解的一大使用场景就是Field Injection。
注解原理
这种注入方式通过Java的反射机制实现,所以private的成员也可以被注入具体的对象。
引入依赖
@Autowired为Spring 框架提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired。
装配顺序:
1.按照type在上下文中查找匹配的bean,查找type为Svc的bean
2.如果有多个bean,则按照name进行匹配如果有@Qualifier注解,则按照@Qualifier指定的name进行匹配,查找name为svcA的bean如果没有,则按照变量名进行匹配查找name为svc的bean。
3.匹配不到,则报错。(@Autowired(required=false),如果设置required为false(默认为true),则注入失败时不会抛出异常)。
// 方式一:Autowired注入,@Autowired(required=false)注入失败不校验 @Autowired @Qualifier("InjectionAServiceImpl") InjectionService injectionService;
2.Resource注入
@Resource是 JSR-250 定义的注解。Spring 在 CommonAnnotationBeanPostProcessor实现了对JSR-250的注解的处理,其中就包括@Resource。
@Resource有两个重要的属性:name和type,而Spring 将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。
装配顺序:
1.如果同时指定了name和type,则从 Spring 上下文中找到唯一匹配的 bean 进行装配,找不到则抛出异常。
2.如果指定了name,则从上下文中查找名称(id)匹配的 bean 进行装配,找不到则抛出异常。
3.如果指定了type,则从上下文中找到类型匹配的唯一 bean 进行装配,找不到或是找到多个,都会抛出异常。
4.如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。
@Resource(name="InjectionAServiceImpl",type = Class) InjectionService injectionService;1.2 Constructor Injection
Constructor Injection是构造器注入,是我们日常最为推荐的一种使用方式。
将各个必需的依赖全部放在带有注解构造方法的参数中,并在构造方法中完成对应变量的初始化,这种方式,就是基于构造方法的注入。
这种注入方式很直接,通过对象构建的时候建立关系,所以这种方式对对象创建的顺序会有要求,当然Spring会为你搞定这样的先后顺序,除非你出现循环依赖,然后就会抛出异常。
注:在 Spring 4.3 及以后的版本中,如果这个类只有一个构造方法,那么这个构造方法上面也可以不写 @Autowired 注解。比如:
// 方式二:构造器注入 private final InjectionService injectionService; @Autowired public InjectionController(@Qualifier("InjectionAServiceImpl")InjectionService injectionService) { this.injectionService = injectionService; }1.1 Setter Injection
Setter Injection也会用到@Autowired注解,但使用方式与Field Injection有所不同,Field Injection是用在成员变量上,而Setter Injection的时候,是用在成员变量的Setter函数上。
注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不写的。
// 方式三:setter注入 private InjectionService injectionService; @Autowired public void setInjectionService(@Qualifier("InjectionAServiceImpl") InjectionService injectionService) { this.injectionService = injectionService; }2. 三种依赖注入对比分析 2.1 可靠性
从对象构建过程和使用过程,看对象在各阶段的使用是否可靠来评判:
由于构造函数有严格的构建顺序和不可变性,一旦构建就可用,且不会被更改。
Field Injection:不可靠
Constructor Injection:可靠
Setter Injection:不可靠
主要从更容易阅读、分析依赖关系的角度来评判:
还是由于依赖关键的明确,从构造函数中可以显现的分析出依赖关系,对于我们如何去读懂关系和维护关系更友好。
Field Injection:差
Constructor Injection:好
Setter Injection:差
当在复杂依赖关系的情况下,考察程序是否更容易编写单元测试来评判
Constructor Injection和Setter Injection的方式更容易Mock和注入对象,所以更容易实现单元测试。
Field Injection:差
Constructor Injection:好
Setter Injection:好
主要根据开发实现时候的编码灵活性来判断
由于Constructor Injection对Bean的依赖关系设计有严格的顺序要求,所以这种注入方式不太灵活。相反Field Injection和Setter Injection就非常灵活,但也因为灵活带来了局面的混乱,也是一把双刃剑
Field Injection:很灵活
Constructor Injection:不灵活
Setter Injection:很灵活
对于Bean之间是否存在循环依赖关系的检测能力:
Field Injection:不检测
Constructor Injection:自动检测
Setter Injection:不检测
不同的注入方式,对性能的影响
主要影响就是启动时间,由于Constructor Injection有严格的顺序要求,所以会拉长启动时间。
Field Injection:启动快
Constructor Injection:启动慢
Setter Injection:启动快
在使用 IDEA 进行 Spring 开发的时候,当你在字段上面使用@Autowired注解的时候,你会发现 IDEA 会有警告提示:
Field injection is not recommended Inspection info: Spring Team Recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.
翻译过来就是这个意思:
不建议使用基于 field 的注入方式。Spring 开发团队建议:在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。
将光标放到@Autowired处,使用Alt + Enter 快捷进行修改之后,代码就会变成基于 Constructor 的注入方式。如果按照 Spring 团队的建议,如果svc是必须的依赖,应该使用Assert.notNull(svc, “svc must not be null”)来确认。
3.2 Spring开发团队建议注入方式Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.
简单来说,就是强制依赖就用构造器方式可选、可变的依赖就用 setter 注入。当然你可以在同一个类中使用这两种方法。构造器注入更适合强制性的注入旨在不变性,Setter 注入更适合可变性的注入。
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. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.❞
Spring 团队提倡使用基于构造方法的注入,因为这样一方面可以将依赖注入到一个不可变的变量中 (注:final 修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service),在被调用时可以保证它们都完全准备好了。与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码异味,这个类可能承担了过多的责任。
而对于基于 setter 的注入,他们是这么说的:
❝Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later.❞
基于 setter 的注入,则只应该被用于注入非必需的依赖,同时在类中应该对这个依赖提供一个合理的默认值。如果使用 setter 注入必需的依赖,那么将会有过多的 null 检查充斥在代码中。使用 setter 注入的一个优点是,这个依赖可以很方便的被改变或者重新注入。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)