Spring 支持的依赖注入有 @Autowired
@Resource
@Inject
三种
@Autowired
来自 org.springframwork.beans.factory.annotation.Autowired
,装配顺序为:
- 按
type
在上下文中查找匹配的 bean - 如果有多个 bean,则按照
name
进行匹配- 如有
@Qualifier
,则按指定的 name 进行匹配 - 如没有,则按变量名进行匹配
- 如有
- 匹配不到就报错
@Autowired(required=false)
则注入失败不抛异常
@Inject
Spring 环境下和 @Autowired
相同,都依赖 AutowiredAnnotationBeanPostProcess
进行处理,但不能 (required=false)
。@Inject
由 JSR-330 定义,可以切换到谷歌的 DI 框架——Guice。
@Inject
在 Java EE 包内,SE 环境需要单独引入。
@Resource
JSR-250 定义。在 CommonAnnotationBeanPostProcessor
实现处理。同样有 name
和 type
。装配顺序:
- 如同时指定
name
和type
,从上下文找到唯一匹配 bean 进行装配,找不到抛异常 - 如指定
name
,则到上下文找id
匹配的 bean 进行装配,找不到抛异常 - 如指定
type
,则到上下文找类型匹配的唯一 bean 进行装配,找不到或找到不唯一都会抛异常 - 如果都没有指定,则默认按
byName
方式装配,找不到再按byType
进行装配
IDEA 使用 @Autowired
时很常见警告 Field injection is not recommended。
Spring 团队建议永远使用构造方法,也就是 c-args
进行依赖注入。IDEA 对这一警告的默认修改方式也是——创建一个构造器进行依赖注入。并且,跟据 Spring 团队建议,对必须的依赖,应当使用断言进行确认
Assert.notNull(svc, "svc must not be null");
为什么不能用成员依赖注入呢?
field 注入虽然简洁,但存在问题:
- 由于添加依赖过于简单(加个注释),我们很容易无意识地向一个类注入大量依赖,这违反了单一职责原理,因为我们过去通过构造器进行注入,而要是你的构造器出现大量入参,那很容易意识到自己的代码结构不对劲。打个比方——原本要数着钞票买东西的,一下子变成移动支付,点一下付钱了,就容易到了月底为账单发愁,因为我们金钱意识变薄弱了。解决方法就是——继续用构造器注入,因此对于强制依赖,Spring推荐用
c-args
注入。 - 依赖注入与容器本身耦合了,即——类唯一的正常工作方式就是通过容器反射进行实例化,这就像是集成测试一样,不像个健康的类,就像一个人原本你把饭给他就能自己吃,现在非要注射进去一样。为了让类能在容器外使用,自然还是要用
c-args
和s-args
。 - 属性注入不能用来注入
final
变量。
因此 Spring 给出建议:constructor-based
和 setter-based
DI 可以混用,
-
强制依赖就用
constructor-based
很好理解,类离开强制依赖就无法工作,这和构造方法职能相吻合,也能注入
final
变量。构造器可以保证这些变量的值不会是 null 。 -
可选、可变依赖用
setter-based
setter
值应被用于注入非必须依赖,这些依赖可以很方便地被改变或重新注入,否则会需要大量的 null 检查。