spring boot@EnableXXXX注解编程模型分析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

@EnableXXXX编程模型

在spring boot中,@EnableXXX注解的功能通常是开启某一种功能。根据某些外部配置自动装配一些bean,来达到开启某些功能的目的。光说很抽象,要具体分析。

@Enable模型的实现方式基本有3种。一个基本的@Enable注解的模型如下。

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(XXXX.class)
    public @interface EnableDiscoveryClient {

       /** * If true, the ServiceRegistry will automatically register the local server. */
       boolean autoRegister() default true;
    }

对应XXXX.class的不同,有3种实现方式。

  • 普通配置类,里面包含@Bean方法用于实例化bean
  • ImportSelector实现类
  • ImportBeanDefinitionRegistrar实现类

上面3种类都属于@Import注解的导入对象,整个外部化配置过程围绕@Import注解进行解析,导入类。

@Import注解处理时机节点(@Confguration注解的类处理)

@Import注解的处理过程大致可以描述为:

  1. 寻找BeanFactory中所有被@Configuration注解修饰的类,包括被@Configuration派生注解修饰的类。
  2. 寻找被@Configuration注解修饰的类上的所有注解元信息(这里的搜索不仅是搜索当前注解,还会迭代往修饰注解的注解的注解上层…..一直搜索@Import,直到注解最原始的注解),获取@Import注解的导入类信息,如果没有则不处理。
  3. 根据导入类的信息,判定为

    • 普通配置类,里面包含@Bean方法用于实例化bean
    • ImportSelector实现类
    • ImportBeanDefinitionRegistrar实现类

    3种形式进行处理。

从context启动开始跟踪主线处理代码,调用链条如下。

  • org.springframework.context.support.AbstractApplicationContext#refresh
  • org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
  • org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry
  • org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions(主线代码,必看)

        public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
          //定义@Conguration注解修饰的类注册信息列表
         List configCandidates = new ArrayList<>();
         String[] candidateNames = registry.getBeanDefinitionNames();
        //检查当前context中所有的bean注册信息
         for (String beanName : candidateNames) {
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
                  ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
               if (logger.isDebugEnabled()) {
                  logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
               }
            }
             //检查class是否是@Conguration注解修饰的类,包括被“继承”@Conguration注解的注解,例如@SpringBootConguration,具体可以跟踪ConfigurationClassUtils.checkConfigurationClassCandidate实现
            else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
               configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
            }
         }
    
         // Return immediately if no @Configuration classes were found
         if (configCandidates.isEmpty()) {
            return;
         }
    
         // Sort by previously determined @Order value, if applicable
          //对配置类排序,顺序由Ordered接口决定
         configCandidates.sort((bd1, bd2) -> {
            int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
            int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
            return Integer.compare(i1, i2);
         });
    
        //......略略略
    
         // Parse each @Configuration class
          //处理每一个配置类
         ConfigurationClassParser parser = new ConfigurationClassParser(
               this.metadataReaderFactory, this.problemReporter, this.environment,
               this.resourceLoader, this.componentScanBeanNameGenerator, registry);
         Set candidates = new LinkedHashSet<>(configCandidates);
         Set alreadyParsed = new HashSet<>(configCandidates.size());
         do {
             //解析处理配置类逻辑
            parser.parse(candidates);
    
             //......略略略
         }
         while (!candidates.isEmpty());
        //......略略略
        }
  • org.springframework.context.annotation.ConfigurationClassParser#parse(java.util.Set

ImportSelector

    String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());

返回结果是所有需要导入的的类的全限定名。

对于全限定名数组,逐个进行org.springframework.context.annotation.ConfigurationClassParser#processImports,相当于循环调用processImports,把新导入的类也当做@Import导入的类处理,如果新导入的类继续导入新的类,就继续org.springframework.context.annotation.ConfigurationClassParser#processImports。直到新导入的类不是

ImportSelector

ImportBeanDefinitionRegistrar处理

当@Import的类是不是ImportSelector之后,如果是ImportBeanDefinitionRegistrar,那就做BeanDefinition信息注册到BeanFactory操作,具体实现在org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions实现,在这里的处理过程是。

    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
       // Candidate class is an ImportBeanDefinitionRegistrar ->
       // delegate to it to register additional bean definitions
       Class<?> candidateClass = candidate.loadClass();
       ImportBeanDefinitionRegistrar registrar =
             BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
       ParserStrategyUtils.invokeAwareMethods(
             registrar, this.environment, this.resourceLoader, this.registry);
             //将ImportBeanDefinitionRegistrar放入map缓存起来
       configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
    }
    public void addImportBeanDefinitionRegistrar(ImportBeanDefinitionRegistrar registrar, AnnotationMetadata importingClassMetadata) {
       this.importBeanDefinitionRegistrars.put(registrar, importingClassMetadata);
    }

先缓存@Import导入的ImportBeanDefinitionRegistrar信息,稍后统一调用ImportBeanDefinitionRegistrar加载注册BeanDefinition信息。

@Configurtion注解的类处理

重复上面的整个流程,处理这个被@Configuration注解标注的类。比较需要注意的是一般@Configuration注解标注的类常用@Bean方式来实例化实例。这里#Bean也会解析出一个BeanMethod信息集合,稍后跟ImportBeanDefinitionRegistrar的缓存信息一样统一调用然后注册BeanDefinition。

    // Process individual @Bean methods
    //对配置类的@Bean方法处理逻辑
    //获取所有@Bean标注的方法元信息,后续处理
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
       configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

统一调用配置类解析出来的信息注册BeanDefinition

在解析完配置类之后,实际还没有进行BeanDefinition的注册,只得到了可以用来注册BeanDefinition的“信息工具”,利用@Bean得到了BeanMethod,@Import(xxxImportBeanDefinitionRegistrar.class)得到了ImportBeanDefinitionRegistrar的实现类。最终要使用这些工具进行BeanDefinition 信息注册。

org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions中,当处理完@Configuration注解的类之后就进行ImportBeanDefinitionRegistrar的BeanDefinition注册加载。

    //处理@Configuration,递归寻找@Configuration,以及解析@Configuration里面的@Import、@Bean、@Component、@ImportResource等。
    parser.parse(candidates);
    parser.validate();

    //获取parser中解析得到的所有配置类
    Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
    configClasses.removeAll(alreadyParsed);

    // Read the model and create bean definitions based on its content
    if (this.reader == null) {
       this.reader = new ConfigurationClassBeanDefinitionReader(
             registry, this.sourceExtractor, this.resourceLoader, this.environment,
             this.importBeanNameGenerator, parser.getImportRegistry());
    }
    //根据递归找出的配置类和解析配置类得到的信息,加载BeanDefinition
    this.reader.loadBeanDefinitions(configClasses);
  • org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitions

    • org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass
        private void loadBeanDefinitionsForConfigurationClass(
              ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    
           if (trackedConditionEvaluator.shouldSkip(configClass)) {
              String beanName = configClass.getBeanName();
              if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
                 this.registry.removeBeanDefinition(beanName);
              }
              this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
              return;
           }
    
           if (configClass.isImported()) {
              registerBeanDefinitionForImportedConfigurationClass(configClass);
           }
           for (BeanMethod beanMethod : configClass.getBeanMethods()) {
               //利用@Bean的Method加载BeanDefinition
              loadBeanDefinitionsForBeanMethod(beanMethod);
           }
           loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
            //利用缓存的ImportBeanDefinitionRegistrar加载注册beandefintion
           loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
        }
    • org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsFromRegistrars(以ImportBeanDefinitionRegistrar为例跟踪)

      • org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(注册BeanDefinition信息到BeanFactory)

来源:http://ddrv.cn

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot@EnableXXXX注解编程模型分析

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏