大数跨境
0
0

【Spring】来试试这道面试题你能答上来吗?

【Spring】来试试这道面试题你能答上来吗? Spring全家桶实战案例
2024-02-29
0
导读:来试试这道面试题你能答上来吗?

环境: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 {  @Resource  private List<DAO> daos ;  @Override  public 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注解,在你需要的类上添加该注解,修改代码

@Component@Qualifierstatic class P2 implements DAO {}@Resource@Qualifierprivate List<DAO> daos ;

输出

PersonService [daos=[com.pack.main.P2@2b4bac49]]


方式2:

通过@Qualifier注解,但需要制定具体bean的名称,修改代码

@Component@Qualifier("p1")static class P1 implements DAO{}@Component@Qualifier("p2")static class P2 implements DAO {}@Resource@Qualifier("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接口

 @Componentstatic class PersonService implements ApplicationContextAware {  private DAO dao ;  @Override  public void setApplicationContext(ApplicationContext context) throws BeansException {    this.dao = context.getBean("qualifierInjectMain4.P1", DAO.class) ;  }}

当然你这里也可以通过实现BeanFactoryAware接口,通过BeanFactory获取。

方式6:

该种方式是根据当前环境注册合适的bean对象。

static class AppConfig {  @Bean  @Profile("dev")  public DAO p1() {    return new P1() ;  }  @Bean  @Profile("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 {  @Bean  @Primary  public DAO p1() {    return new P1() ;  }  @Bean  public DAO p2() {    return new P2() ;  }}

这样注入的就是P1对象。

方式8:

通过@Priority注解方式,定义优先级,值越小优先级越高

@Component@Priority(-3)static class P1 implements DAO{}@Component@Priority(-2)static class P2 implements DAO {}


通过以上8种方式都能正确获取你所需要的Bean对象。

以上是本篇文章的全部内容,希望对你有所帮助。

完毕!!!

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读285
粉丝0
内容832