@Autowired 和 @Resource 都是 Spring/Spring Boot 项目中,用来进行依赖注入的注解。它们都提供了将依赖对象注入到当前对象的功能,但二者却有众多不同, 其主要区别主要体现在以下 5 点:
- 来源不同;
- 依赖查找的顺序不同;
- 支持的参数不同;
- 依赖注入的支持不同;
- 编译器 IDEA 的提示不同。
1.来源不同
@Autowired 和 @Resource 来自不同的“父类”,其中 @Autowired 是 Spring 定义的注解,而 @Resource 是 Java 定义的注解,它来自于 JSR-250(Java 250 规范提案)。
小知识:JSR 是 Java Specification Requests 的缩写,意思是“Java 规范提案”。任何人都可以提交 JSR 给 Java 官方,但只有最终确定的 JSR,才会以 JSR-XXX 的格式发布,如 JSR-250,而被发布的 JSR 就可以看作是 Java 语言的规范或标准。
这里我们要注意, @Autowired跟Spring框架强耦合了, 如果换成其他框架,@Autowired就没作用了
。而@Resource
是JSR-250提供的,它是Java标准,绝大部分框架都支持
2.依赖查找顺序不同
依赖注入的功能,是通过先在 Spring IoC 容器中查找对象,再将对象注入引入到当前类中。而查找有分为两种实现:按名称(byName)查找或按类型(byType)查找,其中 @Autowired 和 @Resource 都是既使用了名称查找又使用了类型查找,但二者进行查找的顺序却截然相反。
2.1 @Autowired 查找顺序
@Autowired 是先根据类型(byType)查找,如果存在多个 Bean 再根据名称(byName)进行查找,这个名称通常就是类名(首字母小写)。就比如说下面代码中的 smsService
就是我这里所说的名称
举个例子,SmsService
接口有两个实现类: SmsServiceImpl1
和 SmsServiceImpl2
,且它们都已经被 Spring 容器所管理。
这个时候我们还是建议通过 @Qualifier
注解来显示指定名称而不是依赖变量的名称。
@Autowired 的具体查找流程如下:
关于以上流程,可以通过查看 Spring 源码中的 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues 实现分析得出,源码执行流程如下图所示:
2.2 @Resource 查找顺序
@Resource 是先根据名称查找,如果(根据名称)查找不到,再根据类型进行查找,它的具体流程如下图所示:
关于以上流程可以在 Spring 源码的 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessPropertyValues 中分析得出。虽然 @Resource 是 JSR-250 定义的,但是由 Spring 提供了具体实现,它的源码实现如下:
2.3 查找顺序小结
由上面的分析可以得出:
- @Autowired 先根据类型(byType)查找,如果存在多个(Bean)再根据名称(byName)进行查找;
- @Resource 先根据名称(byName)查找,如果(根据名称)查找不到,再根据类型(byType)进行查找。
为了更好的理解,我们来看一下同一接口
有多个实现类
,是如何注入的
接口:IAnimal
public Interface IAnimal{
......
}
实现类:DogImpl ,实现了IAnimal接口。
@Service("dogImpl") //只写@Service 默认 value 为首字母小写 daoImpl 和 @Component 效果一样
public class DaoImpl impliments IAnimal{
...
}
业务类:AnimalController
public class AnimalController {
@Autowired
private IAnimal animal;
......
}
假如有一个“动物”的接口 IAnimal, DogImpl类实现了接口 IAnimal, 且该接口只有 DogImpl这一个实现类,那么在引用实现类的时候,我们使用的是实现类的接口(像上面程序展示的那样)。Spring会按 byType的方式寻找接口的实现类,将其注入
假如有另一个实现类 CatImpl 也实现了接口 IAnimal, 这时候@Autowired去引用, 在同时存在两个实现类的情况下,会出现什么情况呢?
答:会报错。 这是由于 @Autowired 的特性决定的: @Autowired 的注入方式是 byType 注入, 当要注入的类型在容器中存在多个时,Spring是不知道要引入哪个实现类的,所以会报错。
@Service("catImpl")
public class CatImpl impliments IAnimal{
...
}
这张情况我们可以使用byName的方式
@Resource
默认是按照 byName
的方式注入的, 如果通过 byName 的方式匹配不到
,再按 byType 的方式去匹配
。所以上面的引用可以替换为:
public class AnimalController {
@Resource(name="dogImpl") //实现类1中 @Service注解中标定的名称,不写默认是下面的名字
private IAnimal dogImpl;
@Resource(name="catImpl") //实现类2中 @Service注解中标定的名称
private IAnimal catImpl;
}
@Qualifier
注解也是 byName
的方式,但是与@Resource 有区别,@Qualifier 使用的是 类名
。
public class AnimalController {
@Qualifier("DaoImpl") //实现类1的类名。注意区分与@Resource(name="dogImpl") 的区别。
private IAnimal dogImpl;
......
}
或者 @AutoWired + @Qualifier
public class AnimalController {
@Autowired
@Qualifier("dogImpl")
private IAnimal animal;
......
}
3.支持的参数不同
@Autowired 和 @Resource 在使用时都可以设置参数,比如给 @Resource 注解设置 name 和 type 参数,实现代码如下:
但二者支持的参数以及参数的个数完全不同,其中 @Autowired 只支持设置一个 required 的参数,表示是否开启自动注入
,默认是true。
而 @Resource 支持 7 个参数,支持的参数如下图所示:
其中, @Resource
有两个比较重要且日常开发常用的属性:name
(名称)、type
(类型)。
如果仅指定 name
属性则注入方式为byName
,如果仅指定type
属性则注入方式为byType
,如果同时指定name
和type
属性(不建议这么做)则注入方式为byType
+byName
。
4.依赖注入的支持不同
@Autowired 和 @Resource 支持依赖注入的用法不同,常见依赖注入有以下 3 种实现: 1. 属性注入 2. 构造方法注入 3. Setter 注入
这 3 种实现注入的实现代码如下。
4.1 属性注入
4.2 构造方法注入
4.3 Setter 注入
其中,@Autowired 支持属性注入、构造方法注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入,当使用 @Resource 实现构造方法注入时就会提示以下错误:
5.编译器提示不同
当使用 IDEA 专业版在编写依赖注入的代码时,如果注入的是 Mapper 对象,那么使用 @Autowired 编译器会提示报错信息,报错内容如下图所示:
虽然 IDEA 会出现报错信息,但程序是可以正常执行的。 然后,我们再将依赖注入的注解更改为 @Resource 就不会出现报错信息了,具体实现如下:
总结
@Autowired 和 @Resource 都是用来实现依赖注入的注解(在 Spring/Spring Boot 项目中),但二者却有着 5 点不同:
- 来源不同:@Autowired 来自 Spring 框架,而 @Resource 来自于(Java)JSR-250;
- 依赖查找的顺序不同:@Autowired 先根据类型再根据名称查询,而 @Resource 先根据名称再根据类型查询;
- 支持的参数不同:@Autowired 只支持设置 1 个参数,而 @Resource 支持设置 7 个参数;
- 依赖注入的用法支持不同:@Autowired 既支持构造方法注入,又支持属性注入和 Setter 注入,而 @Resource 只支持属性注入和 Setter 注入;
- 编译器 IDEA 的提示不同:当注入 Mapper 对象时,使用 @Autowired 注解编译器会提示错误,而使用 @Resource 注解则不会提示错误。