-
@Autowired
,@Resource
,@Inject
三个注解的区别 -
当你在使用
@Autowired
时,是否有出现过Field injection is not recommended
的警告?你知道这是为什么吗? -
Spring 依赖注入有哪几种方式?官方是怎么建议使用的呢?
@Autowired
, @Resource
, @Inject
三个注解的区别
@Autowired
,
@Resource
,
@Inject
三个注解进行依赖注入。下面来介绍一下这三个注解有什么区别。
@Autowired
@Autowired
为Spring 框架提供的注解,需要导入包
org.springframework.beans.factory.annotation.Autowired
。
public interface Svc {
void sayHello();
}
@Service
public class SvcA implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service A");
}
}
@Service
public class SvcB implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service B");
}
}
@Service
public class SvcC implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service C");
}
}
@SpringBootTest
public class SimpleTest {
@Autowired
// @Qualifier("svcA")
Svc svc;
@Test
void rc() {
Assertions.assertNotNull(svc);
svc.sayHello();
}
}
type
在上下文中查找匹配的bean,
查找type为Svc的bean
name
进行匹配
-
如果有
@Qualifier
注解,则按照
@Qualifier
指定的
name
进行匹配,
查找name为svcA的bean
-
如果没有,则按照变量名进行匹配,
查找name为svcA的bean
@Autowired(required=false)
,如果设置
required
为
false
(默认为
true
),则注入失败时不会抛出异常)
@Inject
@Inject
和@Autowired
是相同的,因为它们的依赖注入都是使用AutowiredAnnotationBeanPostProcessor
来处理的。
@Inject
是 JSR-330 定义的规范,如果使用这种方式,切换到Guice
也是可以的。
Guice 是 google 开源的轻量级 DI 框架
@Inject
是Java EE包里的,在SE环境需要单独引入。另一个区别在于
@Autowired
可以设置
required=false
而
@Inject
并没有这个属性。
@Resource
@Resource
是JSR-250定义的注解。Spring 在
CommonAnnotationBeanPostProcessor
实现了对
JSR-250
的注解的处理,其中就包括
@Resource
。
@Resource
有两个重要的属性:
name
和
type
,而Spring 将
@Resource
注解的
name
属性解析为bean的名字,而
type
属性则解析为bean的类型。
-
如果同时指定了
name
和
type
,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
-
如果指定了
name
,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
-
如果指定了
type
,则从上下文中找到类型匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
-
如果既没有指定
name
,又没有指定
type
,则默认按照
byName
方式进行装配;如果没有匹配,按照
byType
h进行装配。
IDEA 提示 Field injection is not recommended
使用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 的方式进行依赖注入。对于必须的依赖,永远使用断言来确认。
@Service
public class HelpService {
@Autowired
@Qualifier("svcB")
private Svc svc;
public void sayHello() {
svc.sayHello();
}
}
public interface Svc {
void sayHello();
}
@Service
public class SvcB implements Svc {
@Override
public void sayHello() {
System.out.println("hello, this is service B");
}
}
@Autowired
处,使用
Alt + Enter
快捷进行修改之后,代码就会变成基于Constructor的注入方式,修改之后:
@Service
public class HelpService {
private final Svc svc;
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
// Assert.notNull(svc, "svc must not be null");
this.svc = svc;
}
public void sayHello() {
svc.sayHello();
}
}
svc
是必须的依赖,应该使用
Assert.notNull(svc, "svc must not be null")
来确认。
-
基于 field 注入(属性注入)
-
基于 setter 注入
-
基于 constructor 注入(构造器注入)
1. 基于 field 注入
@Autowired
private Svc svc;
2. 基于 setter 方法注入
setXXX()
方法以及在方法上面使用注解,来完成依赖注入。比如:
private Helper helper;
@Autowired
public void setHelper(Helper helper) {
this.helper = helper;
}
注:在
Spring 4.3
及以后的版本中,setter 上面的
@Autowired
注解是可以不写的。
3. 基于 constructor 注入
private final Svc svc;
@Autowired
public HelpService(@Qualifier("svcB") Svc svc) {
this.svc = svc;
}
在
Spring 4.3
及以后的版本中,如果这个类只有一个构造方法,那么这个构造方法上面也可以不写
@Autowired
注解。
基于 field 注入的好处
@Autowired
扔到变量之上就好了,不需要特殊的构造器或者set方法,依赖注入容器会提供你所需的依赖。
基于 field 注入的坏处
成也萧何败也萧何
单一职责原则
(SRP:Single responsibility principle)。
这个问题在我司的项目代码真的很常见。
-
你的类和依赖容器强耦合,不能在容器外使用
-
你的类不能绕过反射(例如单元测试的时候)进行实例化,必须通过依赖容器才能实例化,这更像是集成测试
final
修饰的变量)
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.
final
修饰的变量),另一方面也可以保证这些变量的值不会是 null。此外,经过构造方法完成依赖注入的组件 (注:比如各个 service
),在被调用时可以保证它们都完全准备好了。与此同时,从代码质量的角度来看,一个巨大的构造方法通常代表着出现了代码异味,这个类可能承担了过多的责任。
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.
小结
juejin.cn/post/6844904056230690824
GitHub 上有什么好玩的项目?
SpringSecurity + JWT 实现单点登录
本文分享自微信公众号 - Java后端(web_resource)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
相关文章
暂无评论...