环境:Spring5.3.23
问题:在一个项目中,有两个Service类都实现了相同的DAO接口。请问有哪些方法可以明确指定在注入时应该使用哪个具体的DAO实现类?
要能正确的回答此问题这需要我们掌握Spring框架中提供的多种注入策略,包括@Qualifier注解、@Primary注解的使用,这2个注解是最为常见的,当然还有其它的方式来解决此问题,下面给出具体解决的几种办法:
环境准备
static interface DAO {}@Componentstatic class P1 implements DAO {}@Componentstatic class P2 implements DAO {}@Componentstatic class PersonService {@Resourceprivate List<DAO> daos ;@Overridepublic String toString() {return "PersonService [daos=" + daos + "]";}}@Configuration@ComponentScan(basePackages = {"com.pack"})static class AppConfig {}try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {context.register(AppConfig.class) ;context.refresh() ;System.out.println(context.getBean(PersonService.class)) ;}
在上面默认情况下PersonService.daos集合中将会注入2个P1, P2
PersonService [daos=[com.pack.P2@fa36558, com.pack.P1@672872e1]]
方式1:
通过@Qualifier注解,在你需要的类上添加该注解,修改代码
static class P2 implements DAO {}private List<DAO> daos ;
输出
PersonService [daos=[com.pack.main.P2@2b4bac49]]
方式2:
通过@Qualifier注解,但需要制定具体bean的名称,修改代码
("p1")static class P1 implements DAO{}("p2")static class P2 implements DAO {}("p1") // 制定注入p1private List<DAO> daos ;
输出
PersonService [daos=[com.pack.P1@59252cb6]]
原理
内部首先会查找到P1, P2 都是符合条件的,接着会依次检查他们是否符合其它条件,比如这里的daos字段上有@Qualifier注解,那么就会判断每一个符合条件的bean是否有相同的注解。
public class DefaultListableBeanFactory {protected Map<String, Object> findAutowireCandidates(...) {for (String candidate : candidateNames) {// 依次判断,isAutowireCandidate进行判断if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}// 最终在下面类中判断public class QualifierAnnotationAutowireCandidateResolver {protected boolean checkQualifier(...) {// 这里进行了注解判断}}
方式3:
通过自定义注解
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.TYPE, ElementType.FIELD})// 自定义注解上使用该注解@Qualifierstatic @interface MyLove {}@Component@MyLovestatic class P2 implements DAO {}@Resource@MyLoveprivate List<DAO> daos ;
输出
PersonService [daos=[com.pack.P2@7c9d8e2]]
方式4:
自定义注解,这种方式需要注册自定义注解
@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD, ElementType.TYPE})static @interface PackInject {}@Component@PackInjectstatic class P2 implements DAO {}@Resource@PackInjectprivate List<DAO> daos ;// 注册自定义注解,要让容器能够识别注解try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {context.registerBean(CustomAutowireConfigurer.class, bd -> {Set<Class<?>> qualifierTypes = new HashSet<>() ;qualifierTypes.add(PackInject.class) ;bd.getPropertyValues().add("customQualifierTypes", qualifierTypes) ;});}
以上方式也是可以限定注入的bean对象。
方式5:
该种方式需要自己手动获取获取,目标类实现ApplicationContextAware接口
static class PersonService implements ApplicationContextAware {private DAO dao ;public void setApplicationContext(ApplicationContext context) throws BeansException {this.dao = context.getBean("qualifierInjectMain4.P1", DAO.class) ;}}
当然你这里也可以通过实现BeanFactoryAware接口,通过BeanFactory获取。
方式6:
该种方式是根据当前环境注册合适的bean对象。
static class AppConfig {("dev")public DAO p1() {return new P1() ;}("prod")public DAO p2() {return new P2() ;}}// 生命当前环境try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) {ConfigurableEnvironment environment = context.getEnvironment() ;environment.addActiveProfile("prod") ;}
这样容器创建的就是P2对象。
方式7:
通过使用@Primary注解方式。
static class AppConfig {public DAO p1() {return new P1() ;}public DAO p2() {return new P2() ;}}
这样注入的就是P1对象。
方式8:
通过@Priority注解方式,定义优先级,值越小优先级越高
(-3)static class P1 implements DAO{}(-2)static class P2 implements DAO {}
通过以上8种方式都能正确获取你所需要的Bean对象。
以上是本篇文章的全部内容,希望对你有所帮助。
完毕!!!



