SpringMVC源码解读番外 --- ConfigurationClassPostProcessor对@Configuration、@Component、@Import等注解的扫描以及对应注册操作

     本篇主要梳理下这个ConfigurationClassPostProcessor(BeanFactoryPostProcessor)对@Configuration、@Component、@Import、@ImportResource、@ComponentScan这些注解的扫描,及由这些注解加载的Bean注册到BeanFactory。

 一、ConfigurationClassPostProcessor的结构

public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor,
      PriorityOrdered, ResourceLoaderAware, BeanClassLoaderAware, EnvironmentAware {

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

   void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

    我们可以看到ConfigurationClassPostProcessor的主要结构是有实现BeanDefinitionRegistryPostProcessor。那这个postProcessBeanDefinitionRegistry方法是在哪里被调用的呢?其是在AbstractApplicationContext的refresh方法中被调用的,关于这个refresh方法的具体内容我们就不再展开了,以前有梳理过,有兴趣可以看下以前的博文。

@Override
public void refresh() throws BeansException, IllegalStateException {
         ........
         // Allows post-processing of the bean factory in context subclasses.
         postProcessBeanFactory(beanFactory);
         // Invoke factory processors registered as beans in the context.
         invokeBeanFactoryPostProcessors(beanFactory);
         .......
}

二、postProcessBeanDefinitionRegistry方法

   下面我们来具体分析下ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法:

    1、postProcessBeanDefinitionRegistry方法

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    ........
   processConfigBeanDefinitions(registry);
}

    2、processConfigBeanDefinitions方法

   这个processConfigBeanDefinitions方法的处理逻辑有点多,我们分为几个部分来梳理,避免过长,看的比较乱。

      1、第一部分configCandidates(List)的添加

List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
String[] candidateNames = registry.getBeanDefinitionNames();

for (String beanName : candidateNames) {
   BeanDefinition beanDef = registry.getBeanDefinition(beanName);
   if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
         ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
 
   }
   else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
      configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
   }
}

    这里首先是将registry(所有注册的BeanDefinition)中所有的BeanDefinition循环获取。这里有一个if、else if。这里第一个if是相当于对第二个else if的是否有处理判断,如果在else if中有处理了,就不需要再进入到else if处理。

public static boolean isFullConfigurationClass(BeanDefinition beanDef) {
   return CONFIGURATION_CLASS_FULL.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}

public static boolean isLiteConfigurationClass(BeanDefinition beanDef) {
   return CONFIGURATION_CLASS_LITE.equals(beanDef.getAttribute(CONFIGURATION_CLASS_ATTRIBUTE));
}
private static final String CONFIGURATION_CLASS_FULL = "full";
private static final String CONFIGURATION_CLASS_LITE = "lite";
private static final String CONFIGURATION_CLASS_ATTRIBUTE =
      Conventions.getQualifiedAttributeName(ConfigurationClassPostProcessor.class, "configurationClass");

    可以看到这里是否已经用else if处理的标注是看这个BeanDefinition有没有设置CONFIGURATION_CLASS_ATTRIBUTE属性,如果有就表示已经处理了(会在else if中进行设置)。

    下面我们来看下else if的判断条件ConfigurationClassUtils.checkConfigurationClassCandidate:

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
   String className = beanDef.getBeanClassName();
   ...........
   AnnotationMetadata metadata;
   if (beanDef instanceof AnnotatedBeanDefinition &&
      metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();
   }
   else if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {
      Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();
      metadata = new StandardAnnotationMetadata(beanClass, true);
   }
   else {
      ....
         MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);
         metadata = metadataReader.getAnnotationMetadata();
      ........
   }
   if (isFullConfigurationCandidate(metadata)) {
      beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
   }
   else if (isLiteConfigurationCandidate(metadata)) {
      beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
   }
   else {
      return false;
   }
    ........
   return true;
}

   这一段主要是有两部分。第一部分是通过这个BeanDefinition看其哪种类型,然后获取对应的AnnotationMetadata,这个AnnotationMetadata我们在前面我在有提到个,其主要是用来描叙这个BeanDefinition对应Class有加那些注解,及这些注解的信息(关于BeanDefinition的父子结构如AbstractBeanDefinition 、AnnotatedBeanDefinition有哪些子类及对应的使用逻辑,这些前面博文也有梳理,话说现在我的影响也不是很深了,O(∩_∩)O哈哈~)。不过通过见名知意,我们可以大概知道注解相关的BeanDefiniton一般为AnnotationMetadata。例如我们的demo:

@Configuration
public class MyConfiguration
{
    @Bean
    public MyFilter getFilter()
    {
        return new MyFilter();
    }
}

                    

其的描叙就是用的AnnotationMetadata的子类。然后我们再来看下这个metadata所表示的信息

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
   @AliasFor(annotation = Component.class)
   String value() default "";
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
   String value() default "";
}

  可以看到这里jdk的元注解:Target、Retention、Documented并不会注入到metadata去描叙。其描叙的是spring自定义的注解如Component、Indexed。其各个属性所描叙的这里就不再分享了,注解就能看明白。

      获取到metadata后我们来看第二部分:isFullConfigurationCandidate、isLiteConfigurationCandidate方法的调用,这里就与前面if那个逻辑对应,看是哪种情况,再设置CONFIGURATION_CLASS_ATTRIBUTE。

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
   return metadata.isAnnotated(Configuration.class.getName());
}

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
   if (metadata.isInterface()) {
      return false;
   }
   // Any of the typical annotations found?
   for (String indicator : candidateIndicators) {
      if (metadata.isAnnotated(indicator)) {
         return true;
      }
   }
   // Finally, let's look for @Bean methods...
   try {
      return metadata.hasAnnotatedMethods(Bean.class.getName());
   }
    .........
}
private static final Set<String> candidateIndicators = new HashSet<>(4);
static {
   candidateIndicators.add(Component.class.getName());
   candidateIndicators.add(ComponentScan.class.getName());
   candidateIndicators.add(Import.class.getName());
   candidateIndicators.add(ImportResource.class.getName());
}

    可以看到,这里就是前面添加到configCandidates的入口,如果不是这两种情况,checkConfigurationClassCandidate方法就返回的false,就不会添加到configCandidates,然后configCandidates就为empty,整个processConfigBeanDefinitions方法就不会往后面处理了。

if (isFullConfigurationCandidate(metadata)) {
   beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
}
else if (isLiteConfigurationCandidate(metadata)) {
   beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
}
else {
   return false;
}

  可以看到这两个方法涉及到的注解总共有@Configuration、@Component、@ComponentScan、@Import、@ImportResource、@Bean注解。如果这个BeanDefinition所表示的类上面有除@Bean(不用于类级别标注)的其他对应注解,就返回true。

   2、configCandidates非空校验、排序,其他属性初始化

    讲完configCandidates的填充后,我们再来看下其他相关逻辑:

if (configCandidates.isEmpty()) {
   return;
}
// Sort by previously determined @Order value, if applicable
configCandidates.sort((bd1, bd2) -> {
   ..........
});
SingletonBeanRegistry sbr = null;
if (registry instanceof SingletonBeanRegistry) {
   sbr = (SingletonBeanRegistry) registry;
   if (!this.localBeanNameGeneratorSet) {
      BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
      if (generator != null) {
         this.componentScanBeanNameGenerator = generator;
         this.importBeanNameGenerator = generator;
      }
   }
}
if (this.environment == null) {
   this.environment = new StandardEnvironment();
}

  这里需要注意的是,ConfigurationClassPostProcessor有继承EnvironmentAware接口,应该一般会通过这个Aware接口去设置environment。

3、构建ConfigurationClassParser及通过其对configCandidates进行解析处理

// 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();
   ..........(这一部分也很重要,这里先屏蔽,下面再分析)
}
while (!candidates.isEmpty());

   这里先是通过一些成员变量去构建ConfigurationClassParser,再调用parse方法去解析处理candidates。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   this.deferredImportSelectors = new LinkedList<>();
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         if (bd instanceof AnnotatedBeanDefinition) {
            parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
         }
         else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
            parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
         }
         else {
            parse(bd.getBeanClassName(), holder.getBeanName());
         }
      }
       ..........
   }

   processDeferredImportSelectors();
}

   这里虽然是会判断holder(BeanDefinitionHolder ),这个BeanDefinitionHolder我们在前面源码梳理的时候有提到,其是用来装BeanDefinition的:

public class BeanDefinitionHolder implements BeanMetadataElement {
   private final BeanDefinition beanDefinition;
   private final String beanName;
   @Nullable
   private final String[] aliases;

  这里虽然是分情况调用parse方法,但:

protected final void parse(@Nullable String className, String beanName) throws IOException {
   MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
   processConfigurationClass(new ConfigurationClass(reader, beanName));
}
protected final void parse(Class<?> clazz, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(clazz, beanName));
}
protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

  都是大同小异,最终都是构建ConfigurationClass去调用processConfigurationClass方法,这个ConfigurationClass就先不展开了,可以知道其是用来描叙Metadata的。

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }
   ConfigurationClass existingClass = this.configurationClasses.get(configClass);
   if (existingClass != null) {
      if (configClass.isImported()) {
         if (existingClass.isImported()) {
            existingClass.mergeImportedBy(configClass);
         }
     
         return;
      }
      else {
         // Explicit bean definition found, probably replacing an import.
         // Let's remove the old one and go with the new one.
         this.configurationClasses.remove(configClass);
         this.knownSuperclasses.values().removeIf(configClass::equals);
      }
   }
   SourceClass sourceClass = asSourceClass(configClass);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
   }
   while (sourceClass != null);
   this.configurationClasses.put(configClass, configClass);
}

    这里的this.conditionEvaluator.shouldSkip就不再展开了,其是对Condition接口的处理,可以看下前面的这篇,有相关的一些梳理。可以看到先是对this.configurationClasses的get如果没有才会调用doProcessConfigurationClass方法去处理(下面有相应的put)。这先是会通过configClass构建一个SourceClass,再调用doProcessConfigurationClass方法,这里是一个do-while结构,这个是用来判断这个Class有没有父类的(先剧透,所以这个可以不关注,就按正常的没有父类这种情况来分析)。

    doProcessConfigurationClass方法

@Nullable
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
   processMemberClasses(configClass, sourceClass);
   // Process any @PropertySource annotations
   for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
         sourceClass.getMetadata(), PropertySources.class,
         org.springframework.context.annotation.PropertySource.class)) {
      ............(这里关于@PropertySources这种注解就先跳过)
   }
   // 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)) {
      ........
   }
   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), true);
   // Process any @ImportResource annotations
   AnnotationAttributes importResource =
         AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
   if (importResource != null) {
      String[] resources = importResource.getStringArray("locations");
      Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
      for (String resource : resources) {
         String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
         configClass.addImportedResource(resolvedResource, readerClass);
      }
   }
   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // Process superclass, if any
   if (sourceClass.getMetadata().hasSuperClass()) {
      String superclass = sourceClass.getMetadata().getSuperClassName();
      if (superclass != null && !superclass.startsWith("java") &&
            !this.knownSuperclasses.containsKey(superclass)) {
         this.knownSuperclasses.put(superclass, configClass);
         // Superclass found, return its annotation metadata and recurse
         return sourceClass.getSuperClass();
      }
   }
   // No superclass -> processing is complete
   return null;
}

   1、 这里的processMemberClasses方法就先跳过,因为这里也是一个父子类这种循环处理。

   2、@ComponentScans、@ComponentScan注解的扫描处理

// 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
      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
      for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
         if (ConfigurationClassUtils.checkConfigurationClassCandidate(
               holder.getBeanDefinition(), this.metadataReaderFactory)) {
            parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
         }
      }
   }
}

     这里是如果当前关于Class描叙的Metadata中有ComponentScans注解或者ComponentScans注解,就遍历解析,通过this.componentScanParser.parse方法:

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
   ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
         componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

   Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");
   boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);
   scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :
         BeanUtils.instantiateClass(generatorClass));
   ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");
   if (scopedProxyMode != ScopedProxyMode.DEFAULT) {
      scanner.setScopedProxyMode(scopedProxyMode);
   }
   else {
      Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");
      scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));
   }
   scanner.setResourcePattern(componentScan.getString("resourcePattern"));
   for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addIncludeFilter(typeFilter);
      }
   }
   for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {
      for (TypeFilter typeFilter : typeFiltersFor(filter)) {
         scanner.addExcludeFilter(typeFilter);
      }
   }
   boolean lazyInit = componentScan.getBoolean("lazyInit");
   if (lazyInit) {
      scanner.getBeanDefinitionDefaults().setLazyInit(true);
   }

   Set<String> basePackages = new LinkedHashSet<>();
   String[] basePackagesArray = componentScan.getStringArray("basePackages");
   for (String pkg : basePackagesArray) {
      String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),
            ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
      basePackages.addAll(Arrays.asList(tokenized));
   }
   for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
   }
   if (basePackages.isEmpty()) {
      basePackages.add(ClassUtils.getPackageName(declaringClass));
   }
   scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {
      @Override
      protected boolean matchClassName(String className) {
         return declaringClass.equals(className);
      }
   });
   return scanner.doScan(StringUtils.toStringArray(basePackages));
}
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

  可以看到这里是对componentScan注解属性的处理,然后将这些信息设置到scanner(ClassPathBeanDefinitionScanner)中去,这里需要了解@ComponentScan的基本使用,可以搜索下其他的博文介绍。,这里先注意下TypeFilter接口关于exclude、include的设置。然后通过scanner扫描这些包名。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

    这里是Scanner扫描器的相关逻辑了,这里我们就梳理下其主要内容。

      这里就是通过findCandidateComponents(下面再分析)方法去扫描分析basePackage包,获取其下的BeanDefinition,再遍历,再通过checkCandidate方法(这个方法主要是判断其有没有在当前的registry中,然后不在,就调用registerBeanDefinition方法注入到registry中)。

   下面我们来看这里主要的方法findCandidateComponents

     其一般会调到scanCandidateComponents方法:

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      for (Resource resource : resources) {
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     candidates.add(sbd);
                  }
           ......
         }
      }
   }
     ........
   return candidates;
}

  这里首先是获取该包下所有的资源Resource,即使是没有加对应的注解的也会获取到。然后可读判断,通过isCandidateComponent去判断,看符不符合:

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
   for (TypeFilter tf : this.excludeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return false;
      }
   }
   for (TypeFilter tf : this.includeFilters) {
      if (tf.match(metadataReader, getMetadataReaderFactory())) {
         return isConditionMatch(metadataReader);
      }
   }
   return false;
}

   可以看到这里,就是就是对前面注入的TypeFilter,当然springMVC容器默认会注入一些。可以看到如果exclude、include都不满足,最终是返回false不注入。

  同时这里我们需要再回到前面关于scanner的创建,我们这里的scanner是ClassPathBeanDefinitionScanner

ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
      componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
      Environment environment, @Nullable ResourceLoader resourceLoader) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   this.registry = registry;
   if (useDefaultFilters) {
      registerDefaultFilters();
   }
   setEnvironment(environment);
   setResourceLoader(resourceLoader);
}
public @interface ComponentScan {
   ..........
   boolean useDefaultFilters() default true;
   ............
protected void registerDefaultFilters() {
   this.includeFilters.add(new AnnotationTypeFilter(Component.class));
   ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
   try {
      this.includeFilters.add(new AnnotationTypeFilter(
            ((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
        .........
}
public AnnotationTypeFilter(Class<? extends Annotation> annotationType) {
   this(annotationType, true, false);
}
public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) {
   super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces);
   this.annotationType = annotationType;
   this.considerMetaAnnotations = considerMetaAnnotations;
}

   可以看到这里默认会注入一些includeFilters,通过构建AnnotationTypeFilter,这里我们比较熟悉的是@Component,说到这个@Component,我们知道还有@Repository,、@Service、@Controller。但其实这些@Repository,、@Service其是对@Component注解的拓展,可以用@Component完全代替@Repository,、@Service:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {
   @AliasFor(annotation = Component.class)
   String value() default "";
}
.......
@Component
public @interface Service {
   @AliasFor(annotation = Component.class)
   String value() default "";
}
........
@Component
public @interface Repository {
   @AliasFor(annotation = Component.class)
   String value() default "";
}

之所以由@Component拓展这些注解,就是用来体现分层的。标注其他的在源码级别等于就是标注的@Component,然后在这里通过添加的默认扫描@Component的AnnotationTypeFilter去注入这些Bean。

public abstract class AbstractTypeHierarchyTraversingFilter implements TypeFilter {
    .......
   @Override
   public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
         throws IOException {
      if (matchSelf(metadataReader)) {
         return true;
      }
    }
@Override
protected boolean matchSelf(MetadataReader metadataReader) {
   AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
   return metadata.hasAnnotation(this.annotationType.getName()) ||
         (this.considerMetaAnnotations && metadata.hasMetaAnnotation(this.annotationType.getName()));
}

    这里如果有注解@Component,这个isCandidateComponent就返回true,去去构建这个Resouce的BeanDefinition,当然,你也可以去实现自己的TypeFilter(同时SpringMVC自己也有注入其他的TypeFilter)。

   isCandidateComponent方法调用构建BeanDefinition后,再调用 isCandidateComponent,这次的入参是BeanDefinition:

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
   AnnotationMetadata metadata = beanDefinition.getMetadata();
   return (metadata.isIndependent() && (metadata.isConcrete() ||
         (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

    这一步也通过,就正式将这个BeanDefinition添加到candidates中等待注入。

   现在我们再回到最初的doProcessConfigurationClass方法,我们当前将的还是这个逻辑:

 2、@ComponentScans、@ComponentScan注解的扫描处理

   这里的调用有点深入了:

for (AnnotationAttributes componentScan : componentScans) {
   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
   for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
      if (ConfigurationClassUtils.checkConfigurationClassCandidate(
            holder.getBeanDefinition(), this.metadataReaderFactory)) {
         parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
      }
   }
}

   前面这一部分就是获取scannedBeanDefinitions。可以看到这里会再调用ConfigurationClassUtils.checkConfigurationClassCandidate方法判断,通过再调用parse方法,到这里就有一个循环了,与前面"这里先是通过一些成员变量去构建ConfigurationClassParser,在调用去parse方法去解析处理candidates" 这里,整个就有形成一个循环。因为这里你可能通过@ComponentScan注入的BeanDefinition会再有@Configuration、@ComponentScan这些注解,所以会再扫描注入。

   3、@Import注解的处理

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
      throws IOException {
   if (visited.add(sourceClass)) {
      ..........
      imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
   }
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
      .........
   else {
      this.importStack.push(configClass);
      try {
         for (SourceClass candidate : importCandidates) {
            if (candidate.isAssignable(ImportSelector.class)) {
               Class<?> candidateClass = candidate.loadClass();
               ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
               ParserStrategyUtils.invokeAwareMethods(
                     selector, this.environment, this.resourceLoader, this.registry);
               if (this.deferredImportSelectors != null && selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectors.add(
                        new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
               }
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                  processImports(configClass, currentSourceClass, importSourceClasses, false);
               }
            }
            else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
               Class<?> candidateClass = candidate.loadClass();
               ImportBeanDefinitionRegistrar registrar =
                     BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
               ParserStrategyUtils.invokeAwareMethods(
                     registrar, this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
               this.importStack.registerImport(
                     currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass));
            }
         }
      }
     ...........
}
public @interface Import {
   /**
    * {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
    * or regular component classes to import.
    */
   Class<?>[] value();
}

   可以看到@Import注解的,注入如ImportSelector、ImportBeanDefinitionRegistrar(这个用法可以搜索其他博文),同时这里会形成两个循环调用,如果是ImportSelector,会有一个这个方法本身的循环调用processImports,如果是一般的,就会调用processConfigurationClass,这里就又回到前面的方法循环了:

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
     ............
   SourceClass sourceClass = asSourceClass(configClass);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
   }
   while (sourceClass != null);
   this.configurationClasses.put(configClass, configClass);
}

   同时这里需要注意this.configurationClasses.put的操作。因为这里最初我梳理这个源码的时候有个疑问,这些@Import、@ComponentScan等注解主要是为了注入其他的我们自定义要使用的Bean的,关于@Import这里整个逻辑没有体现这个点,例如我们通过@Import注入一个普通的类,例如Student,并且这个Student上面并没有加@Component等注解,这个Student是怎样注入的呢,答案就是在个configurationClasses中。

    4、@ImportResource、@Bean的注入

// Process any @ImportResource annotations
AnnotationAttributes importResource =
      AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
if (importResource != null) {
   String[] resources = importResource.getStringArray("locations");
   Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
   for (String resource : resources) {
      String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
      configClass.addImportedResource(resolvedResource, readerClass);
   }
}
// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}
public void addImportedResource(String importedResource, Class<? extends BeanDefinitionReader> readerClass) {
   this.importedResources.put(importedResource, readerClass);
}
private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
   AnnotationMetadata original = sourceClass.getMetadata();
   Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
     ............
   return beanMethods;
}

     上面这些获取的信息都是设置在configClass(ConfigurationClass)中。

     下面的关于processInterfaces、以及SuperClass就不展开了。到这里整个doProcessConfigurationClass方法就梳理完成了。然后这些信息都已经通过configClass保存到了configurationClasses中了

private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();

   然后在回到parse方法,这里最后会处理@Import注入的DeferredImportSelector,通过processDeferredImportSelectors方法。

   我们再回到最开始的地方,processConfigBeanDefinitions方法:

Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
do {
   parser.parse(candidates);
   parser.validate();
   Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
   ............
   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());

   这里就是通过parser.parse(candidates)方法将这些信息填充到parser(ConfigurationClassParser)的属性,例如前面的configurationClasses,然后通过parser.getConfigurationClasses()获取构建configClasses,再通过

this.reader.loadBeanDefinitions(configClasses);
reader(ConfigurationClassBeanDefinitionReader)去注入这些Bean:
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
   TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
   for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
   }
}
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()) {
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }
   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

   这里注入这些BeanDefinition后,会candidates.clear(),在获取一次registry.getBeanDefinitionNames(),这次排除以前的,再检索这次新注入的BeanDefinition,因为我们可能例如通过注入的XML中读取到了新的Bean,这些Bean再次出发解析:例如我们前面没有展开的解析的:

private void loadBeanDefinitionsFromImportedResources(
      Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
   Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
   for (Map.Entry<String, Class<? extends BeanDefinitionReader>> entry : importedResources.entrySet()) {
      String resource = entry.getKey();
      Class<? extends BeanDefinitionReader> readerClass = entry.getValue();
      if (BeanDefinitionReader.class == readerClass) {
         if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
            // When clearly asking for Groovy, that's what they'll get...
            readerClass = GroovyBeanDefinitionReader.class;
         }
         else {
            // Primarily ".xml" files but for any other extension as well
            readerClass = XmlBeanDefinitionReader.class;
         }
      }
     .............
      // TODO SPR-6310: qualify relative path locations as done in AbstractContextLoader.modifyLocations
      reader.loadBeanDefinitions(resource);
   }
}

   这里可能创建XmlBeanDefinitionReader去解析注入的XML。如果能读到新的BeanDefinition,再通过ConfigurationClassUtils.checkConfigurationClassCandidate方法判断,通过再添加到candidates(前面clear了),然后再出发while的循环。

   至此,关于这个ConfigurationClassPostProcessor的主要逻辑就梳理完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值