美文网首页
自定义 AutowireCandidateResolver

自定义 AutowireCandidateResolver

作者: 蓝笔头 | 来源:发表于2021-07-09 14:47 被阅读0次

    (一)AutowireCandidateResolver 流程剖析

    AutowireCandidateResolver:用于确定特定 BeanDefinition 是否有资格作为特定依赖项的自动装配候选者的策略接口。

    接口定义:

    public interface AutowireCandidateResolver {
        // 确定给定的 BeanDefinition 是否有资格作为给定依赖项的自动装配候选者 
        default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
            return bdHolder.getBeanDefinition().isAutowireCandidate();
        }
    
        //  指定的依赖是否是必要的
        //  仅在没有候选者 bean 或者存在多个候选者 bean 的时候有用
        default boolean isRequired(DependencyDescriptor descriptor) {
            return descriptor.isRequired();
        }
    }
    

    源码解读:

    package org.springframework.beans.factory.support;
    
    public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
            implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    ...
        public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
            ...
            try {
                // 1. 查找符合条件的所有的 Bean
                // 条件:isAutowireCandidate() 返回 true
                Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
                if (matchingBeans.isEmpty()) {
                    // 2. 没有符合条件的 Bean
                    if (isRequired(descriptor)) {
                        // 2.1 isRequired()==true,需要进行依赖注入。抛出异常
                        raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                    }
                    // 2.2 isRequired()==false,不需要进行依赖注入。则返回 null
                    return null;
                }
    
                String autowiredBeanName;
                Object instanceCandidate;
    
                if (matchingBeans.size() > 1) {
                    // 3. 从多个候选者中选取优先级最高的一个
                    // 查找是否有候选者被 @Primary 注解
                    // or 查找 @Priority 最大的一个
                    autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                    if (autowiredBeanName == null) {
                        // 4. 没有找到
                        if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                            // 4.1 isRequired()==true,需要进行依赖注入
                            // 4.2 !indicatesMultipleBeans(type),表示不是一个集合(Array、Collection、Map)
                            // 抛出异常
                            return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                        }
                        else {
                            // 4.3 否则,则返回 null
                            return null;
                        }
                    }
                    
                    // 5. 返回找到的候选者实例
                    instanceCandidate = matchingBeans.get(autowiredBeanName);
                }
                else {
                    // We have exactly one match.
                    // 6. 只有一个候选者,返回即可。
                }
                return result;
            }
        }
    }
    

    1)定义一个接口,和两个实现此接口的 Spring Bean

    public interface MyService {
        void print();
    }
    
    @Slf4j
    @Component
    public class AService implements MyService {
        @Override
        public void print() {
            log.info("I am the AService");
        }
    }
    
    @Slf4j
    @Component
    public class BService implements MyService {
        @Override
        public void print() {
            log.info("I am the BService");
        }
    }
    

    2)在另一个 Spring Bean 中注入此接口的 Bean

    @Service
    @RequiredArgsConstructor
    public class DemoService {
        private final MyService aService;
    
        public void print() {
            aService.print();
        }
    }
    

    3)启动服务,打印如下报错信息。

    ***************************
    APPLICATION FAILED TO START
    ***************************
    
    Description:
    
    Parameter 0 of constructor in com.example.springdemo.service.DemoService required a single bean, but 2 were found:
        - AService: defined in file [F:\tmp\spring-demo\target\classes\com\example\springdemo\service\AService.class]
        - BService: defined in file [F:\tmp\spring-demo\target\classes\com\example\springdemo\service\BService.class]
    
    
    Action:
    
    Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
    

    4)给 BService 添加 @Primary 注解。

    @Slf4j
    @Component
    @Primary
    public class BService implements MyService
    

    5)调用 DemoService beanprint() 方法。

    @Slf4j
    @SpringBootApplication
    public class SpringDemoApplication {
    
        public static void main(String[] args) {
            ConfigurableApplicationContext context = SpringApplication.run(SpringDemoApplication.class, args);
            context.getBean(DemoService.class).print();
        }
    }
    

    控制台输出:

    2021-07-09 14:23:29.198  INFO 8608 --- [           main] com.example.springdemo.service.BService  : I am the BService
    

    (二)自定义 ApplicationContextInitializer

    接口定义:

    // 用于在 refresh() 方法调用之前初始化 Spring ConfigurableApplicationContext 的回调接口。
    // 通常在需要对应用程序上下文进行一些编程初始化的 Web 应用程序中使用。
    @FunctionalInterface
    public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    
        /**
         * Initialize the given application context.
         * @param applicationContext the application to configure
         */
        void initialize(C applicationContext);
    
    }
    

    1)实现 ApplicationContextInitializer

    package com.example.springdemo.registry;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.ApplicationContextInitializer;
    import org.springframework.context.ConfigurableApplicationContext;
    
    @Slf4j
    public class MyApplicationContextInitializer implements ApplicationContextInitializer {
         @Override
         public void initialize(ConfigurableApplicationContext applicationContext) {
             log.info("-----MyApplicationContextInitializer initialize-----");
         }
     }
    

    2)在 META-INF/spring.factories 中配置

    org.springframework.context.ApplicationContextInitializer=com.example.springdemo.registry.MyApplicationContextInitializer
    

    3)启动项目,控制台打印如下内容:

    2021-07-08 19:45:14.426  INFO 17968 --- [           main] c.e.s.r.MyApplicationContextInitializer  : -----MyApplicationContextInitializer initialize-----
    

    (三)自定义 AutowireCandidateResolver

    1)自定义一个 ContextAnnotationAutowireCandidateResolver

    @Slf4j
    public class MyContextAnnotationAutowireCandidateResolver extends ContextAnnotationAutowireCandidateResolver {
        @Override
        public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
            boolean match = super.isAutowireCandidate(bdHolder, descriptor);
    
            String beanClassName = bdHolder.getBeanDefinition().getBeanClassName();
            if (beanClassName != null && beanClassName.startsWith("com.example.springdemo")) {
                log.info("");
                log.info("[isAutowireCandidate] [descriptor] dependencyName:{}, dependencyType:{}", descriptor.getDependencyName(), descriptor.getDependencyType());
                log.info("[isAutowireCandidate] [bdHolder] beanName:{}, beanClassName:{}", bdHolder.getBeanName(), beanClassName);
                log.info("[isAutowireCandidate] match:{}", match);
                log.info("");
            }
    
            return match;
        }
    
        @Override
        public boolean isRequired(DependencyDescriptor descriptor) {
            boolean required = super.isRequired(descriptor);
    
            if (descriptor.getDependencyType().getName().startsWith("com.example.springdemo")) {
                log.info("");
                log.info("[isRequired] [descriptor] dependencyName:{}, dependencyType:{}", descriptor.getDependencyName(), descriptor.getDependencyType());
                log.info("[isRequired] required:{}", required);
                log.info("");
            }
    
            return required;
        }
    }
    

    2)通过在定义一个 ApplicationContextInitializerMyContextAnnotationAutowireCandidateResolver 配置为 BeanFactoryAutowireCandidateResolver

    @Slf4j
    public class MyApplicationContextInitializer implements ApplicationContextInitializer {
         @Override
         public void initialize(ConfigurableApplicationContext applicationContext) {
             log.info("-----MyApplicationContextInitializer initialize-----");
             if (applicationContext.getBeanFactory() instanceof DefaultListableBeanFactory) {
                 DefaultListableBeanFactory bf = (DefaultListableBeanFactory) applicationContext.getBeanFactory();
                 bf.setAutowireCandidateResolver(new MyContextAnnotationAutowireCandidateResolver());
             } else {
                 throw new IllegalStateException("错误的 BeanFactory");
             }
         }
     }
    

    3)启动服务,控制台打印如下内容:

    2021-07-09 14:37:12.315  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:12.315  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] [descriptor] dependencyName:aService, dependencyType:interface com.example.springdemo.service.MyService
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] [bdHolder] beanName:AService, beanClassName:com.example.springdemo.service.AService
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] match:true
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] [descriptor] dependencyName:aService, dependencyType:interface com.example.springdemo.service.MyService
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] [bdHolder] beanName:BService, beanClassName:com.example.springdemo.service.BService
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isAutowireCandidate] match:true
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:12.316  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:12.317  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isRequired] [descriptor] dependencyName:aService, dependencyType:interface com.example.springdemo.service.MyService
    2021-07-09 14:37:12.317  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isRequired] required:true
    2021-07-09 14:37:12.317  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:24.370  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : 
    2021-07-09 14:37:24.371  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isRequired] [descriptor] dependencyName:aService, dependencyType:interface com.example.springdemo.service.MyService
    2021-07-09 14:37:24.371  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver : [isRequired] required:true
    2021-07-09 14:37:24.372  INFO 17004 --- [           main] ntextAnnotationAutowireCandidateResolver :
    

    参考

    相关文章

      网友评论

          本文标题:自定义 AutowireCandidateResolver

          本文链接:https://www.haomeiwen.com/subject/ccitpltx.html