平时在用,但可能忽略的 @Autowired 与 @Resource 的细节

315 阅读3分钟

@Autowired

基本使用

直接放在属性、或方法上即可

一些细节

1、@Autowired 是先通过 byType,再 byName

整体思路是:

1、首先通过(byType)类型以及子类查找,若在单例池中找到符合条件的一个 Bean,就将其注入。这里不管 Bean 的 id 叫什么都行,叫 abcdef 也无所谓,因为不是靠这个判断。

byType 的失效条件是:Spring 单例池中存在多个符合 byType 条件的 Bean

2、如果找到多个符合条件的 Bean,则通过 byName 来根据 Bean 的 id 进行匹配

Controller

@RestController
public class HelloController {
    @Autowired
    private UserService userService;
}

UserService 导入容器

@Configuration
@ComponentScan(basePackages = "com.gg")
public class ConfigurationA {
    // 方法名作为 Bean id
    @Bean
    public UserService userService() {
        UserService userService = new UserService();
        return userService;
    }
}

到这一步没什么问题。

但如果是下面这种情况那就会出问题了:假如配置类中有两个 UserService 的 Bean 对象

现在 Spring 单例池中有多个 UserService 类型的 Bean

HelloController 需要的类型是 UserService,根据 byName 来找,则需要 id 叫 "userService" 的 Bean

但现在配置类中并没有导入符合 HelloController 需求的组件,看看如下代码:

@Configuration
@ComponentScan(basePackages = "com.gg")
public class ConfigurationA {
    // 方法名作为 Bean id
    @Bean
    public UserService userService1() {
        UserService userService = new UserService();
        return userService;
    }
    
    @Bean
    public UserService userService2() {
        UserService userService = new UserService();
        return userService;
    }
}

到这里,会发现运行起来报一个错误,错误信息如下:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:

Error creating bean with name 'helloController': Unsatisfied dependency expressed through field 'user';

nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException:

No qualifying bean of type 'com.gg.springboot.service.UserService' available:

expected single matching bean but found 2: userService1,userService2

翻译一下最后一句话,意思是 HelloController 只允许有一个 UserService 匹配,但现在找到了两个。

故不知道用哪个合适,Spring 不能帮我们擅自做决定,所以报错!

2、byType 与 byName中间的步骤?

byType 与 byName ,他俩中间有一系列判断

执行顺序

1、首先根据 byType

2、根据 Type 从 Spring 容器里找到符合条件的多个 Bean

3、判断 autowiredCandidate(Bean 上),这个属性默认是 true,若赋值 false,则不添加到 Spring 单例池

@Bean(autowireCandidate = true)
public User user1() {
    return new User("小明");
}

4、判断是否有 @Qualifier,若有,指定根据 byName ,若找不到,直接报错

@Bean
@Qualifier("user")
public User user1() {
    return new User("小明");
}

5、判断是否标有 @Primary 的 Bean

6、判断是否标有 @Priority@Order 等带有优先级的 Bean

@Primary
@Bean
public User user1() {
    return new User("小明");
}

7、最终选取到合适的 Bean

优先级排序

@Primary > @Priority > @Order

三个注解都是:数值越小,优先级越高(因为排在了集合的前面)

3、注解添加在方法上时

3.1@Autowired 标注在方法上时,参数名作为 Bean id,且方法名随意

private User user;
​
@Autowired //方法名不是set开头,照样可以获取
public void diUser(User user) {
    this.user = user;
}

3.2而且同时可以注入多个参数

@Autowired
public void diUser(User user,User user2,User user3) { // 参数名作为 Bean id
    this.user = user;
}

@Resource

基本使用

直接放在属性、或方法上即可

一些细节

1、@Resource是先 byName,后 byType

2、注解添加在方法上时参数只能有一个

2.1、方法名随便写

2.2、方法参数只能有一个,参数多了会报错

2.3、参数名作为 Bean id

@Resource
public void mySetUser(User user,User2) {
    this.user = user;
}

重点报错信息如下

  • Caused by: java.lang.IllegalStateException:

    @Resource annotation requires a single-arg method: public void com.gg.controller.HelloController.mySetUser(com.gg.entity.User,com.gg.entity.User)

翻译一下就是 @Resource注解需要单个参数的方法

@Autowired 与 @Resource 的区别

1、@Autowired 是 Spring 家的,@Resource 是官方提供的

2、@Autowired 在方法上时可以同时注入多个参数,@Resource在方法上时只能注入单个参数

3、@Autowired 先 byType 后 byName,@Resource先 byName 后 byType(两者的 byType 失效条件都是容器中存在多个符合条件的 Bean)