Spring框架系列之构造方法BeanDefinition扫描原理03

  • Post author:
  • Post category:其他


上一篇幅我们讲到

ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法,这个方法扫描得到所有的BeanDefinition,那具体是怎么扫描得到所有BeanDefinition的呢?postProcessBeanDefinitionRegistry调用了一个核心方法:processConfigBeanDefinitions,代码如下:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   String[] candidateNames = registry.getBeanDefinitionNames();
  
   for (String beanName : candidateNames) {
      BeanDefinition beanDef = registry.getBeanDefinition(beanName);
      if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {
         if (logger.isDebugEnabled()) {
            logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
         }
      }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
   configCandidates.sort((bd1, bd2) -> {
      int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
      int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
      return Integer.compare(i1, i2);
   });

   // Detect any custom bean name generation strategy supplied through the enclosing application context
   SingletonBeanRegistry sbr = null;
   if (registry instanceof SingletonBeanRegistry) {
      sbr = (SingletonBeanRegistry) registry;
      if (!this.localBeanNameGeneratorSet) {
        //关键点二
         BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(
               AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);
         if (generator != null) {
            this.componentScanBeanNameGenerator = generator;
            this.importBeanNameGenerator = generator;
         }
      }
   }

   if (this.environment == null) {
      this.environment = new StandardEnvironment();
   }

   // Parse each @Configuration class
   ConfigurationClassParser parser = new ConfigurationClassParser(
         this.metadataReaderFactory, this.problemReporter, this.environment,
         this.resourceLoader, this.componentScanBeanNameGenerator, registry);

   Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   do {
     //关键点三
      parser.parse(candidates);
      parser.validate();

      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());
      }

      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);

      candidates.clear(); 
     
      if (registry.getBeanDefinitionCount() > candidateNames.length) {
         String[] newCandidateNames = registry.getBeanDefinitionNames();
         Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
         Set<String> alreadyParsedClasses = new HashSet<>();
         for (ConfigurationClass configurationClass : alreadyParsed) {
            alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
         }
         for (String candidateName : newCandidateNames) {
            if (!oldCandidateNames.contains(candidateName)) {
               BeanDefinition bd = registry.getBeanDefinition(candidateName);
               if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
                     !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                  candidates.add(new BeanDefinitionHolder(bd, candidateName));
               }
            }
         }
         candidateNames = newCandidateNames;
      }
   }
   while (!candidates.isEmpty());

   // Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
   if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
      sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
   }

   if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
      // Clear cache in externally provided MetadataReaderFactory; this is a no-op
      // for a shared cache since it'll be cleared by the ApplicationContext.
      ((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
   }
}

我们来分析下该方法的几个关键点:


关键点一

调用

ConfigurationClassUtils.checkConfigurationClassCandidate方法来检查有哪些是配置类候选者?因为只有有了配置类,Spring才直到如何进一步扫描。Spring是如何来确定哪些是配置类的呢?且看checkConfigurationClassCandidate方法的逻辑代码:

public static boolean checkConfigurationClassCandidate(
			BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
  ......
  Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());
  if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {
     beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
  }else if (config != null || isConfigurationCandidate(metadata)) {
     beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
  }else {
     return false;
  }
  ......
}

如上代码,有三种类型的类是配置类:

  • 增加了Configuration注解的类,这类配置类的BeanDefinition的CONFIGURATION_CLASS_ATTRIBUTE属性被设置为了CONFIGURATION_CLASS_FULL,还记得我们上一篇幅讲的对于CONFIGURATION_CLASS_FULL这种配置类是需要特别处理的,对,就是在这里标识的。
  • 通过isConfigurationCandidate方法检测的类,这类配置类的BeanDefinition的CONFIGURATION_CLASS_ATTRIBUTE属性被设置为了CONFIGURATION_CLASS_LITE,是不是和上面不一样。哪些是通过检测的呢?看代码:
private static final Set<String> candidateIndicators = new HashSet<>(8);

	static {
		candidateIndicators.add(Component.class.getName());
		candidateIndicators.add(ComponentScan.class.getName());
		candidateIndicators.add(Import.class.getName());
		candidateIndicators.add(ImportResource.class.getName());
	}
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
   // Do not consider an interface or an annotation...
   if (metadata.isInterface()) {
      return false;
   }
   for (String indicator : candidateIndicators) {
      if (metadata.isAnnotated(indicator)) {
         return true;
      }
   }

   // Finally, let's look for @Bean methods...
   try {
      return metadata.hasAnnotatedMethods(Bean.class.getName());
   }catch (Throwable ex) {
      if (logger.isDebugEnabled()) {
         logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
      }
      return false;
   }
}

看到了吧,加了Component、ComponentScan、Import、ImportResource注解的类,或者这个类里面有方法加了Bean注解的类都是通过检测的类。


关键点二

如果当前BeanFactory支持单例bean,则设置一个BeanNameGenerator,用来在扫描@Component和@Import某个Bean时取一个名字,这里程序员可以通过调用

applicationContext.setBeanNameGenerator方法来指定一个beanName生成器。


关键点三

parser.parse这个是核心中的核心,在”关键点一”中已经获得了需要解析的配置类,然后这里调用parse开始对配置BeanDefinition进行解析,在该方法中会遍历配置类的BeanDefinition,通过ASM这种字节码处理技术来得到资源信息,注意这里不是直接加载字节码到内存中,得到资源信息后,调用

doProcessConfigurationClass方法来进行处理,在该方法中会针对@Component、@PropertySource、@ComponentScans、@ComponentScan、@Import、@ImportResource、@Bean注解的配置类分别处理,由于篇幅原因,详细我们下一篇介绍,本篇幅中大家只要直到,这个方法就是扫描得到具体的BeanDefinition就可以了。

扫描完成后,当前这个注解类也是需要调用

this.reader.loadBeanDefinitions(configClasses)方法注册成为BeanDefinition的;

我们再来关注另外一个问题,这里为什么要用个do…while循环呢?

其实很简单,因为咱们在扫描完成后,肯定会扫描到新的配置类,所以需要循环调用parse来对新的配置类进行处理。



版权声明:本文为weixin_50903592原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。