自动装配注解
- @Autowired
- @Resource
@Autowired 和 @Resource 都是Spring、SpringBoot 项目中用来进行 “依赖注入” 的注解。
他们都提供了将依赖对象注入到当前对象的功能,但二者却有众多不同,主要体现在以下5点:
- 来源不同
- 依赖查找的顺序不同
- 支持的参数不同
- 依赖注入的用法不同
- 编译器 IDEA 的提示不同
1、来源不同
@Autowired 和 @Resource 来自不同的父类,其中@Autowired 是 Spring 框架定义的注解,
而@Resource 是Java规范定义的注解。
2、依赖查找顺序不同
@Resource 先根据名称(byName)查找,如果(根据名称)查找不到,再根据类型(byType)进行查找;
@Autowired 先根据类型(byType)查找,如果存在多个(Bean)再根据指定的名称(byName)进行查找;
实验说明(@Resource):
// 定义做饭接口Cook.java,抽象方法open()、cooking()、close()
public interface Cook {
/**
* 开火
*/
String open();
/**
* 炒菜
*/
String cooking();
/**
* 关火
*/
String close();
}
/**
* service接口 实现类
* 炒西红柿
*/
@Service
public class CookTomato implements Cook {
@Override
public String open() {
return "炒西红柿前打开油烟机并开火";
}
@Override
public String cooking() {
return "炒西红柿中~";
}
@Override
public String close() {
return "炒西红柿后关闭油烟机并关火";
}
}
/**
* controller层
*/
@RestController
@RequestMapping("/cook")
public class CookController {
// 依赖注入
@Resource
private Cook cook;
@RequestMapping("/open")
public String open() {
return cook.open();
}
@RequestMapping("/cooking")
public String cooking() {
return cook.cooking();
}
@RequestMapping("/close")
public String close() {
return cook.close();
}
}
启动SpringBoot正常启动,请求这三个接口返回的都是正常数据。
如果再添加一个Cook接口的实现类,这会发生一下情况
/**
* service接口 实现类
* 炒土豆
*/
@Service
public class CookPatato implements Cook {
@Override
public String open() {
return "炒土豆丝前打开油烟机并开火";
}
@Override
public String cooking() {
return "炒土豆丝中~";
}
@Override
public String close() {
return "炒土豆丝后关闭油烟机并关火";
}
}
这个时候重启SpringBoot项目,这会启动报错。
WARN 5592 — [ restartedMain] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘CookController’: Injection of resource dependencies failed; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ‘com.janeroad.annotation.service.Cook’ available: expected single matching bean but found 2: CookTomato,CookPatato
大致意思就是:我们引入Cook但是Spring框架发现了两个实现,无法匹配到底用哪一个。
我们将代码修改一下:
@Resource(name="CookTomato")
private Cook cook;
// 或者
@Resource
@Qualifier("CookTomato")
private Cook cook;
上述代码其实都是在做同一件事,就是把注入的Cook实现类指定为西红柿实现类(CookTomato),再启动项目就能够正常启动了,接口访问也正常。
实验说明2(@Autowire)
基于上边的Controller类,同样是有两个Cook接口的实现类,将依赖注入注解改为@Autowired
/**
* controller层
*/
@RestController
@RequestMapping("/cook")
public class CookController {
// 依赖注入
@Autowire
private Cook cook;
@RequestMapping("/open")
public String open() {
return cook.open();
}
@RequestMapping("/cooking")
public String cooking() {
return cook.cooking();
}
@RequestMapping("/close")
public String close() {
return cook.close();
}
}
Description:
Field cook in com.janeroad.annotation.controller.CookController required a single bean, but 2 were found:
- cookTomato: defined in file [此处省略路径名]
- cookPatato: defined in file [此处省略路径名]
// 大致意思:注入cook字段时,找到两个依赖bean,不知道到底要注入哪一个
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
// 考虑将其中一个bean标记为@Primary,告诉Spring优先使用@primary修饰的类,
// 或者在注入时使用@Qualifier来标识应该被使用的bean
根据错误提示我们有两种解决方案:
1、将多种实现类中的其中一个类标记@Primary注解,告诉Spring优先使用那个Bean
2、在依赖注入的时候,标注一下要注入哪一个Bean
/**
* service接口 实现类
* 炒西红柿
*/
@Service
@Primary
public class CookTomato implements Cook {
@override
public String open() {
return "炒西红柿前打开油烟机并开火";
}
@override
public String cooking() {
return "炒西红柿中~";
}
@override
public String close() {
return "炒西红柿后关闭油烟机并关火";
}
}
/**
* controller层
*/
@RestController
@RequestMapping("/cook")
public class CookController {
// 依赖注入
@Autowire
@Qualifier("CookTomato")
private Cook cook;
@RequestMapping("/open")
public String open() {
return cook.open();
}
@RequestMapping("/cooking")
public String cooking() {
return cook.cooking();
}
@RequestMapping("/close")
public String close() {
return cook.close();
}
}
3、支持参数不同
@Autowired 和 @Resource 在使用的时候都可以设置参数,比如给@Resource注解设置name和type参数。
@Resource(name="userinfo", type="UserInfo.class")
private UserInfo user;
但是,二者支持的参数以及参数的数量完全不同,其中@Autowired只支持设置一个 required 参数,而@Resource 注解支持7个参数。
@Autowired 参数说明:
// @Autowired注解只支持一个参数 required
// 属性required:用于标识依赖对象是否是必须的,默认值为true,如果找不到符合的对象则会抛出异常,
// 如果设置为false,则会忽略找不到匹配对象的情况。
@Resource 参数说明:
// @Resource注解支持7个参数:
// 1.name:指定需要注入的依赖对象的名称,用于查询找匹配的Bean。默认值为空字符串
@Resource(name="myDependency")
private MyDependency dependency;
// 2.type:指定需要注入的依赖对象的类型,默认值为java.lang.Object,表示任意类型
@Resource(type="MyDependency.class")
private MyDependency dependency;
// 3.lookup:指定依赖对象的JNDI名称,用于查找匹配的资源。默认值为空字符串
@Resource(lookup = "java:/comp/env/myDependency")
private MyDependency dependen
// 4.shareable:指定注入的依赖对象是否共享。默认值为true,表示可以共享
@Resource(shareable = false)
private MyDependency dependency;
// 5.authenticationType:指定在资源查找过程中使用的身份验证类型。默认值为空字符串。
@Resource(authenticationType = "CONTAINER")
private MyDependency dependency;
4、依赖注入的支持不同
常见的依赖注入有三种:
- 属性注入
- 构造器注入
- Setter注入
@Autowired 和 @Resource 支持的依赖注入用法不用,@Autowired注解支持以上三种注入方式,@Resource只支持 属性注入 和 Setter注入 到如果使用@Resource注入构造方法时,就会提示错误。
属性注入:
@RestController
public class UserController {
// 属性注入
@Autowired
private UserService userService;
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
Setter注入:
@RestController
public class UserController {
// Setter 注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
构造方法注入:
@RestController
public class UserController {
// 构造方法注入
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
@RequestMapping("/add")
public UserInfo add(String username, String password) {
return userService.add(username, password);
}
}
5、编译器提示不同
当使用 IDEA 专业版在编写依赖注入的代码时,如果注入的是 Mapper 对象,那么使用 @Autowired 编译器会提示报错信息。虽然 IDEA 会出现报错信息,但程序是可以正常执行的。 然后,我们再将依赖注入的注解更改为 @Resource 就不会出现报错信息了。