目录
1. 实例注入方式
2. @Autowired, @Resource, @Inject 三个注解的区别
3. @Autowired Field injection is not recommended
4. 总结
1. 实例注入方式
spring注入的方式有三种:
- 属性field注入
- setter方法注入
- 构造方法constructor注入
下面就分别看下这三种方式注入的区别
1.1 属性field注入
属性注入是大家最为常见也是使用最多的一种注入方式,就是在bean的变量上使用注解进行依赖注入,本质上是通过反射的方式直接注入到field。
1 |
|
这里使用@Autowired
注解注入,另外也有Resource
以及@Inject
等注解,都可以实现注入。
但是当我们使用@Autowired
注解注入时,却出现Field injection is not recommended的警告,这是为什么?且看下文。
1.2 setter方法注入
set 方法注入太过于臃肿,实际上很少使用
1 |
|
注:在 Spring 4.3 及以后的版本中,setter 上面的 @Autowired 注解是可以不写的。
1.3 构造方法注入
构造方法注入方式如下:
1 |
|
如果类只有一个构造方法,那么@Autowired
注解可以省略;如果有多个构造方法,那么需要添加上@Autowired
注解来明确指定使用哪个构造方法
1.4 属性field注入优缺点
好处:方式简洁,代码简单;
坏处:
容易违背了单一职责原则,使用这种基于field注入的方式,添加依赖是很简单的,就算类中有十几个依赖你可能都觉得没有什么问题,但是拥有太多的依赖通常意味着你的类要承担更多的责任,明显违背了单一职责原则(SRP:Single responsibility principle)
依赖注入与容器本身耦合:依赖注入框架的核心思想之一就是受容器管理的类不应该去依赖容器所使用的依赖。换句话说,这个类应该是一个简单的POJO能够被单独实例化并且能为它提供所需的依赖;这个问题具体可以表现在
- 类和依赖容器强耦合,不能在容器外使用
- 类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化
不能使用属性注入的方式构建不可变对象(final修饰的变量)
1.5 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注入更适合可变性的注入
- 基于构造方法注入
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.
- 基于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.
2. @Autowired, @Resource, @Inject 三个注解的区别
Spring 支持使用@Autowired
、@Resource
、@Inject
三个注解进行依赖注入,下面就来介绍一下这三个注解的区别。
2.1 @Autowired
@Autowired为Spring框架提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired
。
1 |
|
从Autowired注解源码上看,可以作用在:
1 | //构造函数 |
@Autowired是Spring自带的注解,通过AutowiredAnnotationBeanPostProcessor
类实现的依赖注入
下面就通过一段代码来了解它。
1 | public interface Svc { |
测试类:
1 |
|
@Qualifier(“XXX”) 中的 XX是 Bean
的名称,所以 @Autowired 和 @Qualifier 结合使用时,自动注入的策略就从 byType 转变成 byName 了
注意:使用@Qualifier 时候,如何设置的指定名称的Bean不存在,则会抛出异常,如果防止抛出异常,可以使用:
1 |
|
装配顺序:
按照
type
在上下文查看匹配的bean1
查找type为Svc的bean
如果有多个bean, 则按照
name
进行匹配
- 判断是否有
@Qualifier
注解 a. 如果有@Qualifier
注解,则按照@Qualifier
指定的name进行匹配b. 如果没有,则按照变量名进行匹配1
查找name为svcA的bean
1
查找name为svc的bean
- 匹配不到,则报错。(
@Autowired(required=false)
,如果设置required
为false
(默认为true
),则注入失败时不会抛出异常)
2.2 @Inject
@Inject
是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject
jar包,才能实现注入
@Inject
是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named
1 |
|
从@Inject
注解源码上看,可以使用在以下地方:
1 | //构造函数 |
在Spring 的环境下,@Inject
和@Autowired
是相同的 ,因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor
来处理的。
1 | public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter |
简单使用代码:
1 |
|
@Named
的作用类似 @Qualifier
!
区别:@Inject是Java EE包里的,在SE环境需要单独引入。另一个区别在于@Autowired可以设置required=false而@Inject并没有这个属性
2.3 @Resource
1 |
|
从@Resource
注解源码上看,可以使用在以下地方:
1 | //Class, interface (including annotation type), or enum declaration |
@Resource
是JSR-250定义的注解。Spring 在 CommonAnnotationBeanPostProcessor
实现了对JSR-250的注解的处理,其中就包括@Resource
。
1 | public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor |
@Resource有两个重要的属性:name
和type
,而Spring 将@Resource
注解的name属性解析为bean的名字,而type属性则解析为bean的类型。
装配顺序:
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- 如果既没有指定name,又没有指定type,则默认按照byName方式进行装配;如果没有匹配,按照byType进行装配。
3. @Autowired Field injection is not recommended
在使用IDEA进行Spring开发时,使用@Autowired
注解的时候,会发现IDEA会有警告显示:
1 | Field injection is not recommended |
大致的意思就是:
不建议使用基于 field 的注入方式。
Spring 开发团队建议:在你的Spring Bean 永远使用基于constructor 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。
比如以下代码:
1 |
|
修改为Constructor的注入方式:
1 |
|
如果按照Spring团段的建议,如果svc
是必须的依赖,应该使用Assert.notNull(svc, "svc must not be null");
来确认
4. 总结
4.1 @Autowired总结
@Autowired注解总结
可用于构造函数,成员变量以及set方法
从Spring 4.3开始,如果目标Bean只有一个构造函数,则在该构造函数上可以省略@Autowired注解;如果目标Bean有多个构造函数则不可省略
@Autowired注入方式:
按照type查找bean,如果使用@Qualifier注解声明了name,则从结果集中取出与该name相匹配的bean返回(此时可以视为通过name和type获取bean,但实质是先通过type获取所有bean,然后通过name筛选,详情见后文findAutowireCandidates()方法源码分析)
如果没有使用@Qualifier注解,且找到多个bean,则判断这些bean中是否有使用@Primary注解和@Priority注解,有就返回优先级最高的哪一个bean,没有就按照字段名称去匹配bean,匹配成功返回,不成功抛出异常。(详情见后文determineAutowireCandidate()方法源码解析)
4.2 @Resource总结
可用于成员变量以及set方法
若不指定name属性,则会把name属性值处理为字段名或set方法标识的字段名称
若指定type属性,则type属性值必须与字段类型或set方法返回值类型为父子关系(type属性值可以是子类,也可以是超类),否则会抛出异常
@Resource先按照name属性值注入,若未找到,则按type属性值注入。即默认的name或指定的name找不到 bean ,就会按 type 注入
4.3 @Inject总结
@Inject是JSR330 (Dependency Injection for Java)中的规范,需要导入javax.inject.Inject jar包 ,才能实现注入
@Inject可以作用CONSTRUCTOR、METHOD、FIELD上
@Inject是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Named;
4.4 总结
1、@Autowired是Spring自带的,@Resource是JSR250规范实现的,@Inject是JSR330规范实现的
2、@Autowired、@Inject用法基本一样,不同的是@Inject没有一个request属性
3、@Autowired、@Inject是默认按照类型匹配的,@Resource是按照名称匹配的,@Autowired默认是byType可以使用@Qualifier指定Name,@Resource默认ByName如果找不到则ByType
4、@Autowired如果需要按照名称匹配需要和@Qualifier一起使用,@Inject和@Name一起使用,@Resource则通过name进行指定
5、@Autowired可以对构造器、方法、参数、字段使用,@Resource只能对方法、字段使用, @Inject可以对构造函数、方法、字段、枚举的常量使用