《Spring系列》第6章 @Configuration

前言

当我们获取一个bean的时候,是通过getBean()将对应的BeanDefinition实例化成bean对象,这个之前的文章中已经介绍了,那么接下来一个问题就是这些存储bean信息的BeanDefinition是哪里来的?

当我们在开发的时候,会通过一些常见的方式,例如@ComponentScan,@Component@Configuration@Bean这些标记需要注入IOC容器的类,那么具体是如何实现的呢?

首先需要明白,不管你是单独使用Spring框架,还是使用SpringBoot,在启动的时候都会有一个@Configuration,其实它就是一个入口,下面就会通过它为介入点进行分析

1.思路分析

那么来思考一下,Spring框架会如何加载所有BeanDefinition,如果看过很多框架源码以后不难联想到底层无非就是挨个遍历,那么具体如何实现的?

我们要想看懂Spring框架如何读取bean信息的,需要产生这样几个问题:

  1. Spring框架是如何处理@Configuration注解的?其实是把它封装成一个后置处理器
  2. 既然是后置处理器,那么对应哪个类,何时注入到BeanFactory中的
  3. 后置处理器注入以后,何时被调用?
  4. 后置处理器如何工作,都干了啥

上面这几个问题,就好比如我们看到一个新东西,会产生的疑问你是谁,你从哪里,你是干嘛的,同样的,这几个问题也是这个意思

2.ConfigurationClassPostProcessor

1)何时注册

提前透露@Configuration对应的后置处理器是ConfigurationClassPostProcessor,那么从哪里得知的呢?

之前的文章已经介绍到了,当单独使用Spring框架,创建AnnotationConfigApplicationContext的时候,会在其构造方法中创建一个AnnotatedBeanDefinitionReader,通过其名字可以看出是负责注解的Reader,当其构造方法创建的时候,又会给容器中注册很多的后置处理器,这些都是容器自身使用的,其中最重要的肯定是负责处理@Configuration的后置处理器ConfigurationClassPostProcessor,下图为Debug截图,那么接下来就是详细讲述这个类了
《Spring系列》第6章 @Configuration_第1张图片

2)类结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YItrqPmv-1679981738495)(E:\Gitee\Spring\ConfigurationClassPostProcessor.png)]

上面看到了ConfigurationClassPostProcessor的类结构,实现了很多接口,既然上面说到了它实现功能是靠后置处理器,那么肯定会实现对应的接口,从上面可以看到实现了BeanFactoryPostProcessorBeanDefinitionRegistryPostProcessor两个后置处理器

我们要看的是BeanDefinition如何注册,那么肯定关注的是BeanDefinitionRegistryPostProcessor

3.何时执行

上面介绍到了会把ConfigurationClassPostProcessor后置处理器存到DefaultListableBeanFactorybeanDefinitionMap,那么接下来就是使用了。

在之前介绍到refresh()时,其中的第5步:invokeBeanFactoryPostProcessors(),在该方法中会先执行BeanDefinitionRegistryPostProcessor后置处理器,然后在执行BeanFactoryPostProcessor ,这里就是执行点
《Spring系列》第6章 @Configuration_第2张图片

3.执行过程

a>postProcessBeanDefinitionRegistry() 入口方法

接下来进入ConfigurationClassPostProcessor类中,来一下重写的后置处理器方法postProcessBeanDefinitionRegistry()

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
   int registryId = System.identityHashCode(registry);
   if (this.registriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
   }
   if (this.factoriesPostProcessed.contains(registryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + registry);
   }
   this.registriesPostProcessed.add(registryId);

   processConfigBeanDefinitions(registry);
}

b>*processConfigBeanDefinitions() 遍历解析配置类

  1. 获取全部的beanName,并且从中解析出标注了@Configuration的bean
  2. 创建解析@Configuration类的工具类ConfigurationClassParser
  3. 接下来就是调用最核心的方法parse(),开始解析
  4. 注意这是在一个循环中,当解析完一批bean后,可能会产生新的bean,可能说着很迷糊,举个例子,在@Configuration中配置了一个@Bean,而该bean也是配置类,或者@ComponentScan扫描到了新的配置类,这些配置类在程序入口的时候并未传入,是后续扫描出来的,所以回到方法中,这里需要一个循环,来递归遍历处理,不断的扩大,直到处理完所有的配置类
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
   // <1> 获取所有的beanNames
   String[] candidateNames = registry.getBeanDefinitionNames();

   // <2> 从全部的bean中筛选需要解析的配置类
   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);
         }
      }
      // 这里会调用checkConfigurationClassCandidate()方法,其实底层就是判断是否存在@Configuration注解
      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;
   }

   // <3> 排序,后置处理器通过@Order注解可以存在顺序
   // 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();
   }

   // <4> 创建配置解析工具类
   // 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);
   // 解析完的配置类,避免出现循环@import的情况
   Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
   // 遍历解析
   do {
      StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");
      //---------------解析核心方法parse()---------------
      // 其实就是把类上面的特殊注解解析出来最终封装成BeanDefinition
      parser.parse(candidates);
      parser.validate();

      // 获取解析器中解析出的配置类ConfigurationClass:parser.getConfigurationClasses()
      Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
      configClasses.removeAll(alreadyParsed);

      // 构造一个bean定义读取器
      // 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());
      }
      // 读取ConfigurationClass获取衍生bean定义并注册到容器
      this.reader.loadBeanDefinitions(configClasses);
      alreadyParsed.addAll(configClasses);
      processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();

      // 清空候选配置类定义列表
      candidates.clear();
      // 如果容器中bean定义有新增
      // 也就是解析完这一批bean后,可能会新增一批bean,那么为啥会有新增的呢? 例如解析该@Configuration,里面可能有@Bean,这个就是新增的@Bean
      // 类似于一个递归去解析,一点点扩大范围
      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) {
            // 第1步: 筛选是否为旧的配置类
            if (!oldCandidateNames.contains(candidateName)) {
               BeanDefinition bd = registry.getBeanDefinition(candidateName);
               // 第2步: 筛选是否为标注了@Configuration的配置类 && 是否为已经解析完成的配置类
               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();
   }
}

c>parse()

调用parse()方法为入口,可以看到有很多重载方法,但是最底层都会根据bean定义生成一个ConfigurationClass对象传入processConfigurationClass方法

public void parse(Set<BeanDefinitionHolder> configCandidates) {
   for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
         // 根据bean定义的类型走不通参数的重载parse方法
         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());
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
   }

   this.deferredImportSelectorHandler.process();
}

protected final void parse(@Nullable String className, String beanName) throws IOException {
   Assert.notNull(className, "No bean class name for configuration class bean definition");
   MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
   processConfigurationClass(new ConfigurationClass(reader, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected final void parse(Class<?> clazz, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(clazz, beanName), DEFAULT_EXCLUSION_FILTER);
}

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
   processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER);
}

d>processConfigurationClass()

processConfigurationClass这个方法贴主要代码,又是一个循环,调用doProcessConfigurationClass方法,如果有返回值,递归调用,看spring的注释说递归解析配置类和他的父类,所以这代码的意思就是解析配置类,如果有父类再解析父类,如果父类有父类再一直解析下去。

protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) throws IOException {
   if (this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
      return;
   }

   // 缓存是否存在
   ConfigurationClass existingClass = this.configurationClasses.get(configClass);
   if (existingClass != null) {
      // 通过import导出的则不进行覆盖,直接返回
      if (configClass.isImported()) {
         if (existingClass.isImported()) {
            existingClass.mergeImportedBy(configClass);
         }
         // Otherwise ignore new imported config class; existing non-imported class overrides it.
         return;
      }
      else {
         // 如果是普通的bean,那么则进行替换, 因为Spring框架迭代或者扩展功能,允许覆盖旧的bean达到自定义开发
         // 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);
      }
   }

   // 递归循环解析父类
   // Recursively process the configuration class and its superclass hierarchy.
   SourceClass sourceClass = asSourceClass(configClass, filter);
   do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
   }
   while (sourceClass != null);

   // 存入缓存
   this.configurationClasses.put(configClass, configClass);
}

e>*doProcessConfigurationClass() 注解解析

这个方法很长,主要包含以下几个功能

  1. 一个配置类的成员类(配置类内嵌套定义的类)也可能适配类,先遍历这些成员配置类,调用processConfigurationClass处理它们;
  2. 处理配置类上的注解@PropertySources,@PropertySource
  3. 处理配置类上的注解@ComponentScans,@ComponentScan
  4. 处理配置类上的注解@Import
  5. 处理配置类上的注解@ImportResource
  6. 处理配置类中每个带有@Bean注解的方法
  7. 处理配置类所实现接口的缺省方法
  8. 检查父类是否需要处理,如果父类需要处理返回父类,否则返回null

所以doProcessConfigurationClass()这一步的重点工作就是扫描某个配置类的注解,根据注解功能找到指定扫描范围的类,递归解析(调用parse或processConfigurationClass方法)

重点这个递归,有了它就可以实现一个bean定义解析出多个ConfigurationClass对象

这里有个特例,@ComponentScan扫描的类会被直接注册到spring容器

@Nullable
protected final SourceClass doProcessConfigurationClass(
      ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)
      throws IOException {

   if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
      // 1.内部类  这一步看看有没有内部类
      // Recursively process any member (nested) classes first
      processMemberClasses(configClass, sourceClass, filter);
   }

   // 2.@PropertySource 这一步解析@PropertySource注解,更改配置文件位置时会使用,一般使用默认位置,不咋更改
   // 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");
      }
   }

   // 3.@ComponentScan 这一步就很重要了,解析@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) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
               bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
               parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
         }
      }
   }

   // 4.@Import 解析@Import注解
   // Process any @Import annotations
   processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

   // 5.@ImportResource解析@ImportResource解析
   // 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);
      }
   }

   // 6.@Bean 解析带有@Bean注解的方法,加入到configClass的beanMethods属性中
   // Process individual @Bean methods
   Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
   for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
   }

   // 6.接口@Bean 解析实现的接口中带有@Bean注解的默认方法,加入到configClass的beanMethods属性中
   // Process default methods on interfaces
   processInterfaces(configClass, sourceClass);

   // 7.父类 如果有父类返回父类,以继续解析
   // 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()也会解析当前类的内部类,如果是配置类,则会递归调用processConfigurationClass()去解析这个内部类

private void processMemberClasses(ConfigurationClass configClass, SourceClass sourceClass,
      Predicate<String> filter) throws IOException {

   // 1.获取所有内部类, 注意这里是内部类,而非外部类
   Collection<SourceClass> memberClasses = sourceClass.getMemberClasses();
   if (!memberClasses.isEmpty()) {
      // 2.如果是配置类,加入候选
      List<SourceClass> candidates = new ArrayList<>(memberClasses.size());
      for (SourceClass memberClass : memberClasses) {
         // 这里会判断类是否包含那些注入容器的注解
         if (ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata()) &&
               !memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())) {
            candidates.add(memberClass);
         }
      }
      // 3.排序
      OrderComparator.sort(candidates);
      // 4.遍历候选类
      for (SourceClass candidate : candidates) {
         if (this.importStack.contains(configClass)) {
            this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
         }
         else {
            this.importStack.push(configClass);
            try {
               // ------------递归调用,去解析该内部类------------
               processConfigurationClass(candidate.asConfigClass(configClass), filter);
            }
            finally {
               this.importStack.pop();
            }
         }
      }
   }
}

2) 解析@PropertySource

可以通过此注解来引入配置属性

private void processPropertySource(AnnotationAttributes propertySource) throws IOException {
   String name = propertySource.getString("name");
   if (!StringUtils.hasLength(name)) {
      name = null;
   }
   String encoding = propertySource.getString("encoding");
   if (!StringUtils.hasLength(encoding)) {
      encoding = null;
   }
   String[] locations = propertySource.getStringArray("value");
   Assert.isTrue(locations.length > 0, "At least one @PropertySource(value) location is required");
   boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");

   Class<? extends PropertySourceFactory> factoryClass = propertySource.getClass("factory");
   PropertySourceFactory factory = (factoryClass == PropertySourceFactory.class ?
         DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));

   for (String location : locations) {
      try {
         String resolvedLocation = this.environment.resolveRequiredPlaceholders(location);
         Resource resource = this.resourceLoader.getResource(resolvedLocation);
         addPropertySource(factory.createPropertySource(name, new EncodedResource(resource, encoding)));
      }
      catch (IllegalArgumentException | FileNotFoundException | UnknownHostException | SocketException ex) {
         // Placeholders not resolvable or resource not found when trying to open it
         if (ignoreResourceNotFound) {
            if (logger.isInfoEnabled()) {
               logger.info("Properties location [" + location + "] not resolvable: " + ex.getMessage());
            }
         }
         else {
            throw ex;
         }
      }
   }
}

3) 解析@ComponentScan

// Process any @ComponentScan annotations
// 1.获取 @ComponentScan 中的注解属性 
Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
      sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
if (!componentScans.isEmpty() &&
      !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
   // 2.遍历
   for (AnnotationAttributes componentScan : componentScans) {
      // The config class is annotated with @ComponentScan -> perform the scan immediately
      // 3.扫描
      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
      // 4.如果扫描出来的是配置类,递归进行解析
      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());
         }
      }
   }
}

a> parse() 解析注解属性

该方法就是负责解析@ComponentScan注解的各种属性,借助的是新的工具类componentScanParser

下面的代码大体一看,很简单,就是挨个解析属性,但是在其中隐藏了一点,basePackages属性如果为空,则会把当前类的路径存进去,简单点说就是自动扫描配置类同级和子包下的bean

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, 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 includeFilterAttributes : componentScan.getAnnotationArray("includeFilters")) {
      List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(includeFilterAttributes, this.environment,
            this.resourceLoader, this.registry);
      for (TypeFilter typeFilter : typeFilters) {
         scanner.addIncludeFilter(typeFilter);
      }
   }
   for (AnnotationAttributes excludeFilterAttributes : componentScan.getAnnotationArray("excludeFilters")) {
      List<TypeFilter> typeFilters = TypeFilterUtils.createTypeFiltersFor(excludeFilterAttributes, this.environment,
         this.resourceLoader, this.registry);
      for (TypeFilter typeFilter : typeFilters) {
         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);
      Collections.addAll(basePackages, 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));
}

b> doScan() 扫描

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) {
      // 该方法负责扫描包下的所有类, 匹配存在@Comonent注解的类
      // 具体底层的方法就不写了
      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;
}

4) 解析Import

// Process any @Import annotations
processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

a> getImports() 获取引入的类

该方法在上面的参数中直接调用,目的就是获取@Import引入的所有类

private Set<SourceClass> getImports(SourceClass sourceClass) throws IOException {
   Set<SourceClass> imports = new LinkedHashSet<>();
   Set<SourceClass> visited = new LinkedHashSet<>();
   collectImports(sourceClass, imports, visited);
   return imports;
}

private void collectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)
      throws IOException {

   // 避免重复获取
   if (visited.add(sourceClass)) {
      for (SourceClass annotation : sourceClass.getAnnotations()) {
         String annName = annotation.getMetadata().getClassName();
         if (!annName.equals(Import.class.getName())) {
            // 递归获取
            collectImports(annotation, imports, visited);
         }
      }
      // 获取Import引入的类
      imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value"));
   }
}

b> 注入方法

在介绍下一章节之前,首先回忆一下通过@Import注入bean的方式,有4种,之前已经单独介绍了,这里重复一下:

  1. Import直接导入类,直接就是一个普通的bean
  2. 导入的接口实现了ImportSelector
  3. 导入的接口实现了ImportBeanDefinitionRegistrar
  4. 导入的接口实现了DeferredImportSelector

下一小章,主要就是负责依次解析这些方式

c> processImports() 递归解析引入类

循环所有@Import引入的类

  1. 如果实现了ImportSelector接口(多类导入),直接实例化这个类,获取引入的多个类,递归解析
  2. 如果实现了ImportBeanDefinitionRegistrar接口(导入的bean定义注册器),直接实例化这个类,并加入到当前解析配置类的importBeanDefinitionRegistrars属性中,以便后续调用其registerBeanDefinitions方法
  3. 如果导入的是普通类,直接递归解析这个类
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
      Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

   if (importCandidates.isEmpty()) {
      return;
   }

   if (checkForCircularImports && isChainedImportOnStack(configClass)) {
      this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
   }
   else {
      this.importStack.push(configClass);
      try {
         // 遍历所有的导入类
         for (SourceClass candidate : importCandidates) {
            // 方式2:解析实现了ImportSelector接口
            if (candidate.isAssignable(ImportSelector.class)) {
               // Candidate class is an ImportSelector -> delegate to it to determine imports
               Class<?> candidateClass = candidate.loadClass();
               ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,
                     this.environment, this.resourceLoader, this.registry);
               // 方式4:解析实现了DeferredImportSelector接口
               if (selector instanceof DeferredImportSelector) {
                  this.deferredImportSelectorHandler.handle(configClass, (DeferredImportSelector) selector);
               }
               else {
                  String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
                  Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
                  processImports(configClass, currentSourceClass, importSourceClasses, false);
               }
            }
            // 方式3:解析实现了ImportBeanDefinitionRegistrar接口
            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 =
                     ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                           this.environment, this.resourceLoader, this.registry);
               configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
            }
            else {
               // 方式1:不满足上面集中,将其当作配置类处理
               // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
               // process it as an @Configuration class
               this.importStack.registerImport(currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
               processConfigurationClass(candidate.asConfigClass(configClass));
            }
         }
      }
      catch (BeanDefinitionStoreException ex) {
         throw ex;
      }
      catch (Throwable ex) {
         throw new BeanDefinitionStoreException(
               "Failed to process import candidates for configuration class [" +
               configClass.getMetadata().getClassName() + "]", ex);
      }
      finally {
         this.importStack.pop();
      }
   }
}

5) 解析@ImportResource

如果项目使用的XML来注册Bean,那么可以使用该注解来导入xml文件到配置类,然后就会解析当前xml文件

这里就略了,因为更多的是使用注解了,而这里是为了兼容

6) 解析内部@Bean

看这几行源码,大体意思就是获取到@Bean标注的方法,封装成MethodMetadata,存入beanMethods

// Process individual @Bean methods
Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
for (MethodMetadata methodMetadata : beanMethods) {
   configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
}

retrieveBeanMethodMetadata()

获取全部的MethodMetadata

private Set<MethodMetadata> retrieveBeanMethodMetadata(SourceClass sourceClass) {
   AnnotationMetadata original = sourceClass.getMetadata();
   // 获取到全部标注了@Bean注解的方法
   Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());
   if (beanMethods.size() > 1 && original instanceof StandardAnnotationMetadata) {
      // Try reading the class file via ASM for deterministic declaration order...
      // Unfortunately, the JVM's standard reflection returns methods in arbitrary
      // order, even between different runs of the same application on the same JVM.
      try {
         AnnotationMetadata asm =
               this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
         Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());
         if (asmMethods.size() >= beanMethods.size()) {
            Set<MethodMetadata> selectedMethods = new LinkedHashSet<>(asmMethods.size());
            for (MethodMetadata asmMethod : asmMethods) {
               for (MethodMetadata beanMethod : beanMethods) {
                  if (beanMethod.getMethodName().equals(asmMethod.getMethodName())) {
                     selectedMethods.add(beanMethod);
                     break;
                  }
               }
            }
            if (selectedMethods.size() == beanMethods.size()) {
               // All reflection-detected methods found in ASM method set -> proceed
               beanMethods = selectedMethods;
            }
         }
      }
      catch (IOException ex) {
         logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);
         // No worries, let's continue with the reflection metadata we started with...
      }
   }
   return beanMethods;
}

解析

上面看到会把@Bean存入到beanMethods中,那么何时会开始解析,这里需要回到processConfigBeanDefinitions()
《Spring系列》第6章 @Configuration_第3张图片

这里继续方法向下走,看到底层方法,里面就是各种解析,然后如果@Bean没有设置名称,就把方法名称当作BeanName存入
《Spring系列》第6章 @Configuration_第4张图片

f>loadBeanDefinitions()

这里继续回到b> processConfigBeanDefinitions()中,会先调用c> parse()执行各种解析,其中有一部分不是直接一步到位解析完成,而是先解析一半在后续处理,例如我们常用的@Bean,是先存入beanMethods,那么何时统一处理这些,就是在该方法中,该方法逻辑就是挨个处理而已

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()) {
      // 注册@Import的类
      registerBeanDefinitionForImportedConfigurationClass(configClass);
   }
   for (BeanMethod beanMethod : configClass.getBeanMethods()) {
      // ----------加载@Bean----------
      loadBeanDefinitionsForBeanMethod(beanMethod);
   }

   // 加载@ImportResource
   loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
   // 加载@Import引入的实现ImportBeanDefinitionRegistrar接口的类
   loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

igClass.getBeanName();
if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
this.registry.removeBeanDefinition(beanName);
}
this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
return;
}

if (configClass.isImported()) {
// 注册@Import的类
registerBeanDefinitionForImportedConfigurationClass(configClass);
}
for (BeanMethod beanMethod : configClass.getBeanMethods()) {
// ----------加载@Bean----------
loadBeanDefinitionsForBeanMethod(beanMethod);
}

// 加载@ImportResource
loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
// 加载@Import引入的实现ImportBeanDefinitionRegistrar接口的类
loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

你可能感兴趣的:(Spring,spring,java)