Young87

当前位置:首页 >个人收藏

【Spring源码系列】工厂后置处理器之ConfigurationClassPostProcessor

上一篇文章对spring的工厂后置处理器做了简单的介绍,对工厂后置处理器还不了解的可以看看噢,传送门:

聊聊spring中的工厂后置处理器

今天就聊聊spring到底是如何使用工厂后置处理器的。

1.spring是怎么执行到ConfigurationClassPostProcessor的?

首先spring会在ApplicationContext的构造方法里去往beanDefinitionMap放几个spring内置的工厂后置处理器,其中最重要的就是今天要聊的ConfigurationClassPostProcessor。

if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}

在spring的refresh()方法里,会去处理所有的工厂后置处理器,方法路径:

AbstractApplicationContext—>refresh()—>invokeBeanFactoryPostProcessors(beanFactory);

上面是可以理解spring已经定义好它自己内置的工厂后置处理了,接下来就是要把这些后置处理器拿出来,搞事情了:

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

进入这个方法中,直接看最重要的一部分:

for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
   if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
      BeanDefinitionRegistryPostProcessor registryProcessor =
            (BeanDefinitionRegistryPostProcessor) postProcessor;
      /**
       * 这里执行工厂后置处理里的方法,这里当做是执行 ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()方法
       * 当这个方法执行完成后,自定义的工厂后置处理器、以及spring中所有的bean都会扫描完成
       */
      registryProcessor.postProcessBeanDefinitionRegistry(registry);
      registryProcessors.add(registryProcessor);
   }
   else {
      regularPostProcessors.add(postProcessor);
   }
}

上面的代码中有一段 registryProcessor.postProcessBeanDefinitionRegistry(registry); 这里就是去执行spring中ConfigurationClassPostProcessor中的postProcessBeanDefinitionRegistry()方法,下面直接进入该方法中,看看它到底做了些什么?

2.ConfigurationClassPostProcessor都干了些什么?

①扫描解析bean

postProcessBeanDefinitionRegistry()方法中,直接看processConfigBeanDefinitions(registry)方法,一步一步的看:

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);
      }
   }
   /**
    * 
    * 检查注解的类型:判断是否是Full全注解,即是否是@Configuration注解,如果是,会给beanDefinition设置一个full的标识
    * 如果是Lite类型,给BeanDefinition 设置一个Lite的标识
    */
   else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
   }
}

这段代码其实就是对beanDefinition加上一个标识:Full标识和Lite标识,这里记住只有加了*@Configuration*注解的类,才会加上Full标识(后面spring会为该bean生成代理对象),其他的beanDefinition则加上Lite标识。

接下来spring创建了一个的解析器,用于后面对beanDefinition进行解析:

ConfigurationClassParser parser = new ConfigurationClassParser(
      this.metadataReaderFactory, this.problemReporter, this.environment,
      this.resourceLoader, this.componentScanBeanNameGenerator, registry);

然后对所有的beanDefinition开始解析:

parser.parse(candidates);

下面我直接到最重要的一些代码,可以根据代码路径找到:

parser.parse(candidates)–>parse()–>processConfigurationClass()–>doProcessConfigurationClass()

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // Recursively process any member (nested) classes first
      /**
       * 处理内部类
       */
      processMemberClasses(configClass, sourceClass);
   }

   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
         processPropertySource(propertySource);
      }
      else {
         logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
               "]. Reason: Environment must implement ConfigurableEnvironment");
      }
   }

   // Process any @ComponentScan annotations
   Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
   if (!componentScans.isEmpty() &&
         !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      for (AnnotationAttributes componentScan : componentScans) {
         // The config class is annotated with @ComponentScan -> perform the scan immediately
         /**
          * 扫描普通类
          * 这里出所有的@Component
          */
         Set<BeanDefinitionHolder> scannedBeanDefinitions =
               this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
         // Check the set of scanned definitions for any further config classes and parse recursively if needed
         /**
          * 
          * 检查扫描出来的类是否含有Configuration
          */
         for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // Process any @Import annotations
   /**
    * 处理 @Import  @ImportSelector  @ImportBeanDefinitionRegistrar
    */
   processImports(configClass, sourceClass, getImports(sourceClass), true);

   ......
}

上面的代码主要根据spring中的基本注解,然后对加了不同的注解的bean进行相应的处理。

这里涉及spring中的基本注解主要有:@Component@ComponentScans@Configuration@Import

接下来看看spring是怎么对这些注解进行处理的?

@Component@ComponentScans

标注了这两个注解,最后扫描出来,在spring中就是普通的bean。

Set<BeanDefinitionHolder> scannedBeanDefinitions =
      this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());

这里面的具体代码就不写在文章里了,感兴趣的可以自己去看看,就简单的说一下它里面做的一些事情吧:

首先回去创建一个扫描器(scanner),然后拿到注解里(@ComponentScans)的包名,用scanner对包进行扫描,然后解析,最后会返回beanDefinition。

@Configuration

我们在标注了@Configuration这个注解的类里,往往会通过@Bean的方式去往spring中创建一个bean,我理解的就是该类的所有的@Bean就是在这里进行处理的。

@Import

这个注解可以说的东西就有很多了,后面我单独水一篇文章。

它可以引入三种类型的类:第一种是普通类,第二种是实现了ImportSelector接口的类,第三种是实现了ImportBeanDefinitionRegistrar接口的类。不要小看这个注解,很牛逼的,Mybatis以及SpringBoot的自动装配技术都通过@Import这个注解来实现的。

到这里,基本spring工程中的bean都扫描解析完成了,这里得到的是beanDefinition,而不是真正的bean,bean的实例化后面在说。

②执行自定义BeanDefinitionRegistryPostProcessor

经过上面的扫描解析,你自定义的BeanDefinitionRegistryPostProcessor也会出现在spring的beanDefinitionMap中,然后会去执行自定义的BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry()方法。

③执行所有的BeanFactoryPostProcessor#postProcessBeanFactory()方法

因为在spring工程中有两个顶级的工厂后置处理器接口嘛,分别是BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor

前面都是执行的BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry()方法,包括spring内置的ConfigurationClassPostProcessor和你自定义的BeanDefinitionRegistryPostProcessor

后面就是执行BeanFactoryPostProcessor#postProcessBeanFactory()方法。而spring内置的ConfigurationClassPostProcessor#postProcessBeanFactory()方法中,主要干的事情就是为添加了@Configuration的类生成代理:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
   if (EnhancedConfiguration.class.isAssignableFrom(configClass)) {
      if (logger.isDebugEnabled()) {
         logger.debug(String.format("Ignoring request to enhance %s as it has " +
               "already been enhanced. This usually indicates that more than one " +
               "ConfigurationClassPostProcessor has been registered (e.g. via " +
               "<context:annotation-config>). This is harmless, but you may " +
               "want check your configuration and remove one CCPP if possible",
               configClass.getName()));
      }
      return configClass;
   }
   //cglib 代理
   Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
   if (logger.isTraceEnabled()) {
      logger.trace(String.format("Successfully enhanced %s; enhanced class name is: %s",
            configClass.getName(), enhancedClass.getName()));
   }
   return enhancedClass;
}

到这里,基本ConfigurationClassPostProcessor的事情就干完了,下面来个总结吧。

3.总结

①先执行BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry()方法,spring内置的BeanDefinitionRegistryPostProcessor,即ConfigurationClassPostProcessor

②spring通过ConfigurationClassPostProcessor这个工厂后置处理,对整个spring工程进行扫描,解析,然后生成beanDefinition,放到beanDefinitionMap中;

③执行BeanFactoryPostProcessor#postProcessBeanFactory(),而ConfigurationClassPostProcessor为加了@Configuration的类生成了代理

④工厂后置处理器的执行顺序:

先执行spring内置的BeanDefinitionRegistryPostProcessor,即ConfigurationClassPostProcessor

再执行自定义的BeanDefinitionRegistryPostProcessor

最后执行BeanFactoryPostProcessor

这篇文章主要讲spring的ConfigurationClassPostProcessor,可能有些绕吧,其实读源码的时候更绕,只要抓住了核心,慢慢的就理解了,读源码是个枯燥的过程,也很容易放弃,我也是反反复复看了好多遍。

spring源码读完有什么好处吗?当然,spring源码读完,你在后面读SpringBoot、SpringCloud源码的时候,都会更加得心应手,就像是水到渠成一般的感觉。

今天文章里遗留了一个很重要的问题,就是@Import注解,下篇文章再单独写吧。

除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog

上一篇: 程序员应该避开的20个低级不良用户体验

下一篇: 吐血整理的Python-面试通关宝典【干货满满】

精华推荐