应用
@Qualifier可以用来让程序员明确指定想要指定哪个bean,那有程序员就会想问,它和@Autowired和@Resource的区别是什么?
假设有如下bean定义:
<bean id="user0" class="com.luban.entity.User">
<property name="name" value="user0000"/>
</bean>
<bean id="user1" class="com.luban.entity.User" >
<property name="name" value="user1111"/>
</bean>
<bean name="userService" class="com.luban.service.UserService" autowire="constructor"/>
user0和user1的类型都是user,
UserService的通过构造方法自动注入。
public class UserService {
private User user;
public UserService(User user11) {
this.user = user11;
}
public void test() {
System.out.println(user.getName());
}
}
main方法:
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.test();
}
现在运行main方法,会启动Spring,会报错:
Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.luban.entity.User' available: expected single matching bean but found 2: user0,user1
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:223)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1324)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1255)
at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:939)
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:842)
... 15 more
很明显,因为UserService会根据构造方法自动注入,会先根据构造方法参数类型User是找bean,找到了两个,一个user0, 一个user1,然后会根据构造方法参数名user11去进行过滤,但是过滤不出来,最终还是找到了两个,所以没法进行自动注入,所以报错。
怎么办?
要么给其中某个bean设置primary为true,表示找到多个时默认用这个。
或者给某个bean设置autowire-candidate为false,表示这个bean不作为自动注入的候选者。
还有一种就是@Qualifier, 首先,给两个bean分别定义一个qualifier:
<bean id="user0" class="com.luban.entity.User" autowire-candidate="false">
<property name="name" value="user0000"/>
<qualifier value="user00"/>
</bean>
<bean id="user1" class="com.luban.entity.User" >
<property name="name" value="user1111"/>
<qualifier value="user11"/>
</bean>
同时在UserService中使用@Qualifier注解:
public UserService(@Qualifier("user00") User user11) {
this.user = user11;
}
还需要做一件事情,因为我们是使用的XML的方式使用Spring,所以还需要添加:
<context:annotation-config/>
这样@Qualifier注解才会生效,这样配置之后UserService中的user属性就会被赋值为@Qualifier("user00")所对应的user0这个bean。
@Qualifier注解是在使用端去指定想要使用的bean,而autowire-candidate和primary是在配置端。
@Qualifier的另外一种用法
定义一个:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
修改bean的定义
<bean id="user0" class="com.luban.entity.User">
<property name="name" value="user0000"/>
<qualifier type="com.luban.annotation.Genre" value="user00"/>
</bean>
<bean id="user1" class="com.luban.entity.User" >
<property name="name" value="user1111"/>
<qualifier type="com.luban.annotation.Genre" value="user11"/>
</bean>
UserService中使用Genre注解:
public class UserService {
private User user;
public UserService(@Genre("user00") User user11) {
this.user = user11;
}
public void test() {
System.out.println(user.getName());
}
}
这种用法相当于自定义@Qualifier注解,使得可以处理更多的情况。
同样,还可以这么做,定义两个注解:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("random")
public @interface Random {
}
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("roundRobin")
public @interface RoundRobin {
}
注意,是在自定义的Qualifier注解上指定了名字,这样就是可以直接使用自定义注解,而不用指定名字了。比如先修改bean的定义:
<bean id="user0" class="com.luban.entity.User">
<property name="name" value="user0000"/>
<qualifier type="com.luban.annotation.Random"/>
</bean>
<bean id="user1" class="com.luban.entity.User" >
<property name="name" value="user1111"/>
<qualifier type="com.luban.annotation.RoundRobin"/>
</bean>
UserService中就可以这么用:
public UserService(@RoundRobin User user11) {
this.user = user11;
}