使用注解的方式注入bean实例,在两年前的开发中,还经常看到@Resource注解,这个注解是基于JSR250标准的,现在基本很少看到使用了,取而代之的是@Autowired注解,也是官方推荐的。随着spring boot的出现,很多开发小伙伴喜欢通过config配置类加载一些bean,在加载这些bean会依赖到其他的一些bean实例,这个时候又慢慢的开始有比较多的使用@Qualifier注解和@Autowired配合使用,但是从版本来看@Qualifier注解早在spring2.5版本就已经存在了。当然注入的注解并不止这两种,@Primary@Inject都会说到。在使用过程中不同的注解对bean注入有着不同的优先级,下面就一起来了解一下。

代码部分

实体类Monkey,用于被注入的一个bean。

public class Monkey {

    public int age = 10;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Monkey(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Monkey{" +"age=" + age + '}';
    }
}

配置加载类InjectionConfig

@Configuration
@ComponentScan(basePackages = {"com.zdydoit.core.injection"})
public class InjectionConfig {

    @Bean
    public Monkey monkey() {
        return new Monkey(20);
    }

    @Bean("monkey2")
    public Monkey monkey2() {
        return new Monkey(30);
    }
}

依赖Monkey对象的InjectionService类。

@Service
public class InjectionService {

    @Autowired
    private Monkey monkey;

    public void print() {
        System.out.println("==>" + monkey);
    }
}

最后就是测试类InjectionTest啦。

public class InjectionTest {

    @Test
    public void test() {
        ApplicationContext app = new AnnotationConfigApplicationContext(InjectionConfig.class);
        InjectionService service = app.getBean(InjectionService.class);
        service.print();
    }
}

代码部分就是如上,后面来开始依次来说这些注解的优先级。

@Autowried

只用@Autowired注解的时候,这个就很简单了,首先会去IOC容器中找beanName为monkey的bean,如果找到直接注入,如果找不到就会报错。运行一下测试结果。

==>Monkey{age=20}

很好理解,不多做解释。

@Qualifier

这个注解需要注意的是,注解类里面只有一个字段name,这个name如果不给定一个值得话,会报错的,错误里面就是说这个name值为空,而且@AutoWired注解的required字段为true。也就是这个Monkey注入是必须的,但是找不到对应的bean。

因此在这里使用的时候要注意给@Qualifier的name字段要赋值。并且要将@Autowird注解的required字段设置为false,防止没有对应的bean导致报错。使用需谨慎。

这里的使用方式就是将原来注入的方式改为如下:

@Autowired(required=false)
@Qualifier("monkey2")
private Monkey monkey;

得到的结果如下:

==>Monkey{age=30}

可以看的出来加上这个注解以后,不会去找beanName为monkey的实例,而是monkey2,这里还是要注意加上这个注解后,只会去找monkey2,没有备选。

@Primary

这个注解不是用在注入的时候,而是用在创建bean的使用,需要在创建的bean的方法上加上这个注解。在congfig配置类中新加入下面这段代码:

@Bean
@Primary
public Monkey monkey3(){
    return new Monkey(40);
}

注入bean的代码将@Qualifier注解去掉,如下:

@Autowired
private Monkey monkey;

看一下运行的结果:

==>Monkey{age=40}

这里可以知道,当存在同一类型的bean,其中有一个bean创建的时候加上了@Primary注解后,在注入bean的时候,会优先选择这个被@Primary修饰的bean。

@Resource、@Inject

这两个注解分别是基于JSR250标准和JSR330标准的,都可以用于注入bean实例,这里不做多的解释。但是@Inject注解在使用的需要引入javax.inject依赖如下:

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

每个注解的基本使用都已经说过了,但是在多个注解组合使用的时候会出现什么情况呢,下面来说一个例子(只说一个,因为组合的方式有点多,都说没有太大的意义,后面会做总结)。

@Autowired、@Qualifier、@Primary注解同时存在

需要将配置类中的代码改成如下:

@Bean
    public Monkey monkey() {
        return new Monkey(20);
    }
    @Bean("monkey2")
    public Monkey monkey2() {
        return new Monkey(30);
    }
    @Bean
    @Primary
    public Monkey monkey3(){
        return new Monkey(40);
    }

将注入的代码改为如下:

@Qualifier("monkey2")
@Autowired(required = false)
private Monkey monkey;

运行的结果是如下:

==>Monkey{age=30}

这个结果可以看出来@Qualifier的优先级很高,@Primary注解也暗淡失色了。其实从日常生活中也可以很容易的理解,条件越苛刻,往往就会优先级更高一点。

总结

这个注解有很多的组合方式,感兴趣的可以去尝试一下,下面做一个总结啦。

  • 当单独使用@Autowired首先会根据注入bean的名字去IOC容器中找,如果找不到,会根据当前注入类的类型去找,当找到一个的时候,很简单,直接注入,如果有多个,那就会出现报错。因为他也不知选谁。
  • @Autowired@Qualifier(name="demo")组合使用的时候,只会到IOC容器中找beanName为demo的bean,找不到就注入null,如果这个时候@Autowired的required字段为true那么悲催了,报错。
  • @Autowired@Qualifier(name="demo")@Primary同时被使用的时候,@Qualifier(name="demo")注解优先级最高,还是会去beanName为demo的bean,找不到就注入null。
  • @Autowired@Primary同时使用,会优先注入@Primary修饰的bean。
  • @Resource@Qaulifier同时使用,@Qaulifier不会起作用,依然采用默认的方式,按照注入的bean的名字从IOC容器中获取,获取不到就按照注入类的类型去找,找到一个就注入,找到多个就报错。
  • @Inject@Qaulifier配合使用与@Autowired@Qaulifier是相同的。
  • @Inject@Primary同时使用,会优先注入@Primary修饰的bean。
  • @Resource@Primary同时使用,@Primary不会起到作用,采用@Resource默认的方式注入。从这一点和上面@Resource@Qaulifier配合使用上,可以看出来@Resource真的是油盐不进。

主要的匹配的规则上面都列出来,如果你想到更多新的组合,可以尝试一下(欢迎留言,说出你的组合)。

还有要再说一下@Inject注解,他是需要引入第三方包javax.inject,可以知道不依赖于spring,如果需要自定义容器的话,可以使用@Inject注解实现注入。另外上面的Inject注解的各种组合方式和@Autowired的各种组合方式,出现的结果都是一样的,也可以看出@Inject的兼容性。