细说 @Autowried

467 阅读4分钟

@Autowired

关于 @Autowired

@Autowired 是 Spring 提供的注解,用来在 Spring 里面去实现 Bean 的依赖注入。

一说到 @Autowired,大多数人肯定会说,这个注解是根据类型来注入的。

但是经过实测,这种说法并不是很准确。@Autowired 也不仅仅是这么简单的。

本次测试使用 SpringBoot 2.2.0 版本。

(1) 当 Spring 容器中同类型的 bean 只存在一个时@Autowired 是根据类型 ByType 来注入的,被注入的对象的名字可以随意,都能注入。

eg1:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {
        return new User();
    }
}

@Service
public class TestService {
    @Autowired
    private User order; // 这里的属性名字随意,叫啥都行

    @Autowired
    private User pay; // 注入多次也都可以
}

(2) 当 Spring 容器中同类型的 bean 存在多个时@Autowire 为 根据名称 ByName 的方式注入。ByName 是将 bean 单例池中的 key(bean 的名字)与被注入的成员变量的名进行匹配,而不是与类型进行匹配。

PS:相同类型的 Bean 会在 Spring 容器中存在多个,每个 Bean 的堆内存地址都不一样,即都是不同的对象。

eg2:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {
        return new User();  // 两个类型相同的 Bean,只是名字不同
    }

    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        return new User();
    }
}

@Service
public class TestService {
    @Autowired
    private User user;  // 注入 name = user 的 bean

    @Autowired
    private User user2; // 注入 name = user2 的 bean
}

运行如上代码,Spring 启动并不报错,使用成员变量也不会报错,注入成功。

eg3:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {
        return new User();  // 两个类型相同的 Bean,只是名字不同
    }

    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        return new User();
    }
}

@Service
public class TestService {
    @Autowired
    private User user1; // 注入失败,编辑的时候,IDEA就给出报错提示了

    @Autowired
    private User order; // 注入失败,编辑的时候,IDEA就给出报错提示了
}

IDEA 给出的报错提示:

20220803172254419.png

关于 required 属性

@Autowired 有一个 required 属性,其默认值是 true,表示强制要求 bean 实例的注入,在应用启动的时候,如果 Spring 容器里面不存在对应类型的 Bean,就会报错。当设置为 false 时,Spring 启动时不会检查是否存在对应类型。

那我们将 eg3 中的 @Autowried 添加 required = false 属性后,是否能够注入成功呢?

答案是否定的,依旧无法注入。

20220803174258100.png

那如果 Spring 中不存在该类型的 Bean,可以注入成功吗?

eg4:

@Configuration
public class BeanConfiguration {
    // 没有任何 Bean
}

@Service
public class TestService {
    @Autowired(required = false)
    private User user;

    @Autowired(required = false)
    private User user2;
}

运行上面代码发现,Spring 启动时并不会报错,不过注入的属性是 null,虽然注入成功,但是也没法使用。

20220803174738459.png

关于 @Primary 注解

@Primary:当 Spring 中存在多个相同类型的 Bean 时,优先使用声明了 @Primary 的 Bean。

@Primary 和 @Bean 一起搭配使用。

eg5:

@Configuration
public class BeanConfiguration {

    @Primary              // 表明 name=user 的 bean 有最高优先级
    @Bean("user")
    public User user() {
        return new User();  // 两个类型相同的 Bean,只是名字不同
    }

    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        return new User();
    }
}

@Service
public class TestService {
    @Autowired
    private User user;  // 注入 name = user 的 bean

    @Autowired
    private User user2; // 注入 name = user2 的 bean
}

那么对于上面的代码,user 和 user2 注入的是哪一个?都是注入的 user 吧,毕竟人家使用了 @Primary 注解。

但实测发现,也确实是这样,。

20220803182442265.png

关于 @Qualifier 注解

(1) @Qualifier 可以根据 Bean 的名字来注入指定 Bean.

@Qualifier 和 @Autowired 一起搭配使用

eg6:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {    // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }

    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }
}

@Service
public class TestService {
    @Qualifier(value = "user2") // 指定注入 user2 Bean
    @Autowired
    private User user;

    @Qualifier(value = "user")  // 指定注入 user Bean
    @Autowired
    private User user2;
}

如果是上面代码的这种方式,那么是根据属性名来匹配注入呢,还是根据 @Qualifier 注解呢?

实测如下:

20220803183915076.png

看来是 @Qualifier 注解的优先级更高

(2) 延伸:@Quarifier 的第二个作用是可以作为筛选的限定符,我们在写自定义的注解时,可以在其定义上增加@Qualifier,来筛选需要的对象.

Spring 提供了将多个类型相同的 Bean 以 List 形式注入的方式。

eg7:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {    // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }

    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }
}

@Service
public class TestService {

    @Autowired
    private List<User> users = Collections.emptyList();
}

如上面代码所示,最后注入的的 users 中包含 user 和 user2 两个 Bean。

这是默认的注入,如果你只想注入其中一个 Bean,比如 users 中只想要 user2,那要怎么处理呢?

使用 @Quarifier 注解便很容易完成这个需要。eg8:

@Configuration
public class BeanConfiguration {

    @Bean("user")
    public User user() {    // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }

    @Qualifier
    @Bean("user2")
    public User user2() {   // 两个类型相同的 Bean,只是名字不同
        User user = new User();
        return user;
    }
}

@Service
public class TestService {

    @Qualifier  // 选择性注入
    @Autowired
    private List<User> users = Collections.emptyList();
}

实测结果如下:

20220804101613446.png