Spring 源码分析 BeanFactoryPostProcessor

前言

上一篇讲述了 SpringBoot 启动流程和相关源码分析,其中一个重要的刷新方法 AbstractApplicationContext#refresh() 是属于 Spring 的内容,也是 Spring 框架的核心方法,搞懂了这个方法,基本上可以说就完全搞懂了 Spring

想了很久不知道对于这样一个复杂的框架,从何开始讲起,那么就按照 AbstractApplicationContext#refresh() 的内部代码顺序来吧,先学习 BeanFactoryPostProcessor

本篇文章使用的版本是 SpringBoot 3.4.1 、 Spring 6.2.1

SpringBoot & Spring 架构图示概览

这里我以 SpringBoot 源码入口为起点,画了一个相关的流程图,包含了 SpringBoot、Spring 事务、Spring AOP、Spring 事件、BeanFactoryPostProcessor、BeanPostProcessor 等所有 Spring 知识,以及相关模块之间的交互联系,后续也会持续更新此图(因为我自己还没有学完),我试了下作者侧这边更新后,分享的协作链接也会实时变更,希望对大家有帮助

SpringBoot & Spring 架构图 持续更新 对于即将需要面试的同学应该会比较有帮助!

前置知识 IoC 容器

IoC 全称 Inversion of Control,叫做控制反转,它是一种设计原则,其核心思想是将对象创建、依赖管理和生命周期控制的权力从应用程序代码 反转 给容器(框架)。

原本我们需要调用某个业务方法,需要自己实例化对象,然后调用该对象某个方法,使用 Spring 之后,我们把该对象交给 Spring 管理,Spring 框架帮我们创建对象,我们只需要拿 Spring 创建好的对象即可。而我们交给 Spring 管理的对象通常就叫做 Bean

IoC 容器就是通常我们常说的 Spring 上下文ApplicationContext 的实现类,对于 SpringBoot 来说,其扩展的实现类是 AnnotationConfigServletWebServerApplicationContext

java 复制代码
public static void main(String[] args) {
   ConfigurableApplicationContext context = SpringApplication.run(SpringAiDemoApplication.class, args);
}

SpringBoot 主类的 run 方法,其返回值就是一个 IoC 容器,ApplicationContext 的子接口

前置知识 BeanDefinition 和 Bean

在开始之前,我相信大家都是已经熟悉 Spring 框架的日常用法,我们需要有一个前置知识储备,在 Spring 容器中,Bean 的创建都是由 BeanDefinition 决定的, BeanDefinition 描述了这个 Bean 的所有信息

  • Bean 的类型
  • Bean 的名字
  • 单例还是多例
  • 是否延迟加载
  • 是否允许自动装配
  • 等等

比如我们使用 @Configuration、@Bean、@Component 注解一个 Bean。刷新方法中,先解析这些注解得到了 BeanDefinition 信息,当所有配置相关的注解被解析完,得到一个

java 复制代码
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

然后 Spring 遍历这个 beanDefinitionNames 去创建 Bean

因为 HashMap 是无序的,创建 Bean 需要根据顺序创建,所以需要这个 beanDefinitionNames

BeanFactoryPostProcessor 的作用

学习源码,最有效的方式之一就是看注释,

java 复制代码
/**
 * Factory hook that allows for custom modification of an application context's
 * bean definitions, adapting the bean property values of the context's underlying
 * bean factory.
 * ......
 */
@FunctionalInterface
public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

注释说明这个类的作用相当于一个工厂回调函数(通常我们叫它 Bean 工厂后置处理器),可以调整上下文的底层Bean 工厂的 Bean 属性值。也就是在 Bean 创建前,BeanDefinition 已默认加载后,修改某些 BeanDefinition 。因为 Bean 的创建是由 BeanDefinition 决定的,所以修改 BeanDefinition,也就相当于修改即将要创建的 Bean

内置的 BeanFactoryPostProcessor

在上一篇 SpringBoot 文章中,我们提到在创建 Spring 上下文的时候,AnnotationConfigServletWebServerApplicationContext 的构造方法中,会注册一些内置的 BeanFactoryPostProcessor。源码

java 复制代码
org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors()

内置会注册以下后置处理器

java 复制代码
ConfigurationClassPostProcessor  处理所有配置注解 @Componentscan、@Import、@Bean、@Component 等
AutowiredAnnotationBeanPostProcessor  处理 @Autowired
CommonAnnotationBeanPostProcessor     处理 @PostConstruct、@Resource 等
PersistenceAnnotationBeanPostProcessor(如果用了 JPA)
EventListenerMethodProcessor    处理 @EventListener
DefaultEventListenerFactory  (不是后置处理器,不知道为什么放在这个方法里)

这里只有 ConfigurationClassPostProcessorEventListenerMethodProcessor 是工厂后置处理器 BeanFactoryPostProcessor,其他都是 Bean 的后置处理器 BeanPostProcessor,下一篇文章会介绍。在介绍 ConfigurationClassPostProcessorEventListenerMethodProcessor 的作用之前,我们先学习一个 BeanFactoryPostProcessor 的扩展接口 BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor

还是老规矩,学习一个类之前先看注释

java 复制代码
/**
 * Extension to the standard {@link BeanFactoryPostProcessor} SPI, allowing for
 * the registration of further bean definitions <i>before</i> regular
 * BeanFactoryPostProcessor detection kicks in. In particular,
 * BeanDefinitionRegistryPostProcessor may register further bean definitions
 * which in turn define BeanFactoryPostProcessor instances.

 * @see org.springframework.context.annotation.ConfigurationClassPostProcessor
 */
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

注释说 BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor 的一个扩展接口。容器刷新过程中 postProcessBeanDefinitionRegistry()postProcessBeanFactory() 之前执行,他们的区别和作用如下

和 BeanFactoryPostProcessor 的区别对比

这里用一个表格来对比 BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor 的方法

维度 postProcessBeanDefinitionRegistry postProcessBeanFactory
执行时机 更早,在所有 BeanFactoryPostProcessor 之前 稍晚,在所有 postProcessBeanDefinitionRegistry 之后
参数类型 BeanDefinitionRegistry ConfigurableListableBeanFactory
主要用途 注册、修改、移除 BeanDefinition 修改 已注册的 BeanDefinition,不能注册和移除
能力范围 注册新 Bean 的能力更强 修改现有 Bean 的属性
执行顺序 第一阶段 第二阶段

也就是说他们两都是对 BeanDefinition 做操作的,只不过时机不同

常见的 BeanFactoryPostProcessor

SpringBoot 项目中有一些内置的工厂后置处理器

  • ConfigurationClassPostProcessor 解析配置类,获取 BeanDefiniton
  • EventListenerMethodProcessor 初始化事件工厂,为了后续创建事件监听器
  • PropertySourcesPlaceholderConfigurer 处理配置类成员变量的值有属性占位符,比如有些 Bean 的某个属性值是 ${mybatis-plus.lazy-initialization} 这种表达式,就是通过这个后置处理器来解析赋值
  • MapperScannerConfigurer 如果使用了 mybatis ,扫描 @Mapper 注解的类,添加到 Spring 容器
  • CachingMetadataReaderFactoryPostProcessor 缓存 ResourceLoader 已加载的配置类,不要重复加载

下面我们详细介绍其中几个重要的工厂后置处理器的作用。

BeanFactoryPostProcessor 的执行

工厂后置处理器的执行方法是通过一个委托类 PostProcessorRegistrationDelegate 实现的

java 复制代码
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

这个方法非常有意思,作者在这里给出了一大段的注释,告诉读者不要再去 Githubissue 了,因为这个方法的代码看起来很乱、有很多重复代码,很多读者提了 issue 表明这个方法需要优化,但是作者一一拒绝,回应说这种实现是必要的,一定要确保工厂后置处理器的执行顺序。有兴趣可以查阅上述方法的源码

这里作者将 BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor 分开,并且将它们分别按照顺序排序放到不同列表中依次执行。

注意,这个阶段还没有创建 Bean ,所以要执行的 BeanFactoryPostProcessor ,都是通过从 BeanFactory 里面的 beanDefinitonNames 集合拿到的,使用 beanFactory.getBeanNamesForType() 方法

配置类加载原理 (非常非常重要)ConfigurationClassPostProcessor

这是一个非常重要的 BeanFactoryPostProcessorSpring 项目启动加载配置类几乎全都是它实现的,除了我们自己声明的 Service、Controller。 配置类本身也是 Spring Bean

其实我们查看源码会发现 @Service、@Controller、@Configuration 本身就是一个 @Component。查看 @Component 的注释

vbnet 复制代码
/**
 * Indicates that the annotated class is a <em>component</em>.
 *
 * <p>Such classes are considered as candidates for auto-detection
 * when using annotation-based configuration and classpath scanning.
 */

被这个注解标注的类都会被视为一个组件,在被扫描的时候会被认为是一个配置类

代码解读

ConfigurationClassPostProcessor 是一个 BeanDefinitionRegistryPostProcessor 。我们查看源码方法的调用过程 postProcessBeanDefinitionRegistry → processConfigBeanDefinitions → ConfigurationClassParser#parse → ConfigurationClassParser#processConfigurationClass → ConfigurationClassParser#doProcessConfigurationClass(arg1,arg2) → doProcessConfigurationClass(arg1,arg2,arg3)

先看 parse() 方法

java 复制代码
public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
       BeanDefinition bd = holder.getBeanDefinition();
       try {
          if (bd instanceof AnnotatedBeanDefinition annotatedBeanDef) {
             parse(annotatedBeanDef, holder.getBeanName());
          }
          else if (bd instanceof AbstractBeanDefinition abstractBeanDef && abstractBeanDef.hasBeanClass()) {
             parse(abstractBeanDef.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();
}

我们传入的入参就是 SpringBoot 主类的 BeanDefinition,这个方法根据 BeanDefinition 实际的类型走不同的重载方法,SpringBoot 项目主类的定义类型是 AnnotatedBeanDefinition 。 接下来我们看 doProcessConfigurationClass(arg1,arg2)

java 复制代码
protected void processConfigurationClass(ConfigurationClass configClass, Predicate<String> filter) {
     //......
    // 循环处理配置类,返回的 sourceClass 是父类,只要不为空就一直向上找父类循环处理
    SourceClass sourceClass = null;
    try {
       sourceClass = asSourceClass(configClass, filter);
       do {
          sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter);
       }
       while (sourceClass != null);
    }
    catch (IOException ex) {
    //...
    }

    this.configurationClasses.put(configClass, configClass);
}

这里有一个循环,解析传入的配置类,返回该配置类的父类,如果该配置类的父类不为空,就一直解析。 接下来内部最直观的解析配置类的方法 doProcessConfigurationClass(arg1,arg2,arg3)

java 复制代码
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter) {
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
       // 解析内部类。比如一个加了 @Configuration 的配置类,有一个静态内部类也加了 @Configuration
       processMemberClasses(configClass, sourceClass, filter);
    }

    // 解析 @PropertySource 注解
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
          sourceClass.getMetadata(), org.springframework.context.annotation.PropertySource.class,
          PropertySources.class, true)) {
       if (this.propertySourceRegistry != null) {
          this.propertySourceRegistry.processPropertySource(propertySource);
       }
    }

    // 解析 @ComponentScans 注解
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(),ComponentScan.class, ComponentScans.class, MergedAnnotation::isDirectlyPresent);

    if (componentScans.isEmpty()) {
       componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScan.class, ComponentScans.class, MergedAnnotation::isMetaPresent);
    }

    if (!componentScans.isEmpty()) {
       List<Condition> registerBeanConditions = collectRegisterBeanConditions(configClass);
       for (AnnotationAttributes componentScan : componentScans) {
          // 解析 @ComponentScan 注解(注意这里直接向 Spring 容器注册了 BeanDefiniton)
          //其他注解解析都是先封装成一个 ConfigurationClass 对象,后续统一注册 BeanDefinition
          Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
          // 检查 @ComponentScan 扫描到的配置类是否还有其他配置注解,例如 @Import,递归解析
          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());
             }
          }
       }
    }

    // 解析 @Import 注解(SpringBoot 自动配置类的核心实现)
    processImports(configClass, sourceClass, getImports(sourceClass), filter, true);

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

    // 解析 @Bean 注解
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
       if (methodMetadata.isAnnotated("kotlin.jvm.JvmStatic") && !methodMetadata.isStatic()) {
          continue;
       }
       configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }

    // 解析当前类实现的接口里面加了 @Bean 的 default 方法
    processInterfaces(configClass, sourceClass);

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

我们可以继续阅读 processMemberClasses()、processImports() 等每一个具体解析注解的方法,会发现 这个解析方法的调用是一个递归的过程。先根据一个主配置类,在 SpringBoot 中就是应用启动主类。由于 @SpringBootApplication 内部组合了 @Configuration ,所以它是一个配置类,SpringBoot 会以主类为入口,从它开始扫描,然后由于 SpringBootApplication 内部组合了 @ComponentScan 注解,所以会解析主类所在的包下的所有配置类。然后由于它内部又组合了 SpringBootApplication → @EnableAutoConfiguration → @Import(AutoConfigurationImportSelector.class),所以会解析 AutoConfigurationImportSelector 获取的所有配置类。这也是 SpringBoot 自动配置的核心实现原理

SpringBoot 自动配置的核心原理

SpringBoot 文章中我们说过,这个 AutoConfigurationImportSelectorSpringBoot 自动配置的核心。从上面的解析方法 doProcessConfigurationClass(arg1,arg2,arg3) 我们知道它会解析 @Import 注解,然后我们看 processImports() 的源码

java 复制代码
for (SourceClass candidate : importCandidates) {
    if (candidate.isAssignable(ImportSelector.class)) {
       // 处理 ImportSelector 的逻辑
       Class<?> candidateClass = candidate.loadClass();
       ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class,this.environment, this.resourceLoader, this.registry);
       Predicate<String> selectorFilter = selector.getExclusionFilter();
       if (selectorFilter != null) {
          filter = filter.or(selectorFilter);
       }
       if (selector instanceof DeferredImportSelector deferredImportSelector) {
          this.deferredImportSelectorHandler.handle(configClass, deferredImportSelector);
       }
       else {
          String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
          Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames, filter);
          processImports(configClass, currentSourceClass, importSourceClasses, filter, false);
       }
    }
    else if (candidate.isAssignable(ImportBeanDefinitionRegistrar.class)) {
       // 处理 ImportBeanDefinitionRegistrar 的逻辑
       Class<?> candidateClass = candidate.loadClass();
       ImportBeanDefinitionRegistrar registrar =
             ParserStrategyUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class,
                   this.environment, this.resourceLoader, this.registry);
       configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());
    }
    else {
       // 处理普通配置类的逻辑
       this.importStack.registerImport(
             currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
       processConfigurationClass(candidate.asConfigClass(configClass), filter);
    }
}

这里对于 @Import 有三种处理逻辑,相当于一个策略模式的体现

  • @Import(XxxImportSelector.class) 执行 selectImports 获取导入的配置类
  • @Import(XxxImportBeanDefinitionRegistrar.class) 执行 registerBeanDefinitions 注册 BeanDefinition
  • @Import(XxxConfiguration.class) 普通配置类,直接解析

对于 @Import 注解中的不同类型值,会进行不同的处理逻辑,但最终都是解析配置类,无非是导入的方式不同,具体我们可以看对应接口的方法说明。

注意对于 ImportSelector 有两种处理方式,它有一个子接口 DeferredImportSelector,延迟导入器,如果导入的是此类型,那么会执行其内部类 DeferredImportSelector.Group#process

我们查看 AutoConfigurationImportSelector.AutoConfigurationGroup#process() 方法的源码,看它是怎么获取到自动配置类的

这里有一个点要注意,对于 SpringBootAutoConfigurationImportSelector 是一个延迟导入器,获取导入的配置类列表不是直接调用 AutoConfigurationImportSelector.selectImports() 而是调用内部类 AutoConfigurationGroup.process()AutoConfigurationGroup.selectImports()

java 复制代码
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
       return EMPTY_ENTRY;
    }
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    //扫描 imports 文件获取自动配置类列表
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    configurations = removeDuplicates(configurations);
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    checkExcludedClasses(configurations, exclusions);
    configurations.removeAll(exclusions);
    configurations = getConfigurationClassFilter().filter(configurations);
    fireAutoConfigurationImportEvents(configurations, exclusions);
    return new AutoConfigurationEntry(configurations, exclusions);
}
java 复制代码
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    ImportCandidates importCandidates = ImportCandidates.load(this.autoConfigurationAnnotation,getBeanClassLoader());
    List<String> configurations = importCandidates.getCandidates();
    return configurations;
}
java 复制代码
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    Assert.notNull(annotation, "'annotation' must not be null");
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    String location = String.format(LOCATION, annotation.getName());
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
       URL url = urls.nextElement();
       importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
}

好了,这里有一个 LOCATION 常量,我们看他的值是写死的

arduino 复制代码
private static final String LOCATION = "META-INF/spring/%s.imports";

这里传入的注解就是 @AutoConfiguration 的全类名,org.springframework.boot.autoconfigure.AutoConfiguration

java 复制代码
public AutoConfigurationImportSelector() {
    this(null);
}

AutoConfigurationImportSelector(Class<?> autoConfigurationAnnotation) {
    this.autoConfigurationAnnotation = (autoConfigurationAnnotation != null) ? autoConfigurationAnnotation : AutoConfiguration.class;
}

替换掉 LOCATION 的占位符之后,我们得到完整的路径地址

ini 复制代码
LOCATION = META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

然后我们再去看 MyBatisPlus、Redis、RabbitMQstarter,我们都会看到这个路径下有这样的文件

这下恍然大悟了吧,那现在让你自己定义一个 springboot-starter,还觉得困难吗?

注意 SpringBoot 3.0 以前读取的是 spring.factories ,之后改成了 imports 文件

配置类加载过程流程图

配置类递归解析流程图

下面的树形结构可能有助于进一步理解这个流程

ruby 复制代码
/**
 * Spring 配置类扫描解析的递归流程
 * 
 * 示例配置结构:
 * └── SpringBootApplication (@Configuration)  [Level 0]
  *    ├── @ComponentScan("com.example")  [Level 1]
 *     │   └── com.example.UserController (@Controller)  [Level 2]
 *     │       └── @Configuration [Level 3]
 *     └── @Bean(appBean)  [Level 1]
 *     ├── @Import(AutoConfigurationImportSelector.class)  [Level 1]
 *     │   ├── @Configuration
 *     │   ├── @Import(DataSourceConfig.class)  [Level 2]
 *     │   │   ├── @Configuration
 *     │   │   ├── @Import(RedisConfig.class)  [Level 3]
 *     │   │   │   ├── @Configuration
 *     │   │   │   └── @Import(CacheConfig.class)  [Level 4]
 *     │   │   │       ├── @Configuration
 *     │   │   │       └── @ComponentScan("com.cache")
 *     │   │   └── @Bean(dataSource)
 *     │   └── @Bean(service)
 */

只要当前正在扫描处理的配置类里面还有其他配置注解,就一直递归扫描解析,直到解析完毕为止。

特殊的 @ComponentScan

仔细观察源码会发现 @ComponentScan 注解的扫描过程中,使用 ComponentScanAnnotationParser 解析之后直接向 Spring 容器注册了 BeanDefinition 。而其他注解的扫描,例如 @Configuration、@Bean、@Import 都是将需要处理的配置类信息存储到 ConfigurationClassParser 的成员变量中。

java 复制代码
//@ComponentScan 解析器
private final ComponentScanAnnotationParser componentScanParser;
//配置类集合
private final Map<ConfigurationClass, ConfigurationClass> configurationClasses = new LinkedHashMap<>();

//延迟导入器处理器
private final DeferredImportSelectorHandler deferredImportSelectorHandler = new DeferredImportSelectorHandler();

此时还没有注册它们的 BeanDefinition

分类构建 BeanDefinition

上一段我们说了,前面的步骤解析完之后,只是把每一个配置类信息存储起来,抽象成一个 Map<ConfigurationClass, ConfigurationClass> configurationClasses,此时还没有向 Spring 容器中注册成 BeanDefiniton。我们可以查看源码,这个类里面有一些成员变量

java 复制代码
final class ConfigurationClass {
    //bean 名称
    private String beanName;
    private final Set<ConfigurationClass> importedBy = new LinkedHashSet<>(1);

    //@Bean 方法集合
    private final Set<BeanMethod> beanMethods = new LinkedHashSet<>();
    //@ImportResource 集合
    private final Map<String, Class<? extends BeanDefinitionReader>> importedResources = new LinkedHashMap<>();
    //ImportBeanDefinitionRegistrar 集合
    private final Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> importBeanDefinitionRegistrars =  new LinkedHashMap<>();
}

当所有配置类解析完成之后,得到一个全量集合 Map<ConfigurationClass, ConfigurationClass> ,然后在 ConfigurationClassPostProcessor#processConfigBeanDefinitions() 中调用 ConfigurationClassBeanDefinitionReader.loadBeanDefinitions() 这一步执行完毕后,整个应用所有的配置都构建 BeanDefinition 加载完毕

EventListenerMethodProcessor

它是一个解析事件监听的后置处理器

java 复制代码
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    this.beanFactory = beanFactory;
    this.originalEvaluationContext.setBeanResolver(new BeanFactoryResolver(this.beanFactory));

    Map<String, EventListenerFactory> beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    List<EventListenerFactory> factories = new ArrayList<>(beans.values());
    AnnotationAwareOrderComparator.sort(factories);
    this.eventListenerFactories = factories;
}

我们可以看到这个后置处理方法中,主要是对 eventListenerFactories 赋值,然后后续等所有单例 Bean 实例化完成后,会调用它的生命周期方法 EventListenerMethodProcessor#afterSingletonsInstantiated(),会用这个工厂创建事件监听器,添加到 ApplicationContext 中,Spring 事件机制 就是把所有事件监听器添加到 ApplicationContextapplicationListeners 字段中,调用 ApplicationEventPublisher#publishEvent(ApplicationEvent) 的时候从这里找到符合的监听器批量执行,相当于广播一个消息。

java 复制代码
//org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType)
@Override
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
    ResolvableType type = (eventType != null ? eventType : ResolvableType.forInstance(event));
    Executor executor = getTaskExecutor();
    for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
       if (executor != null && listener.supportsAsyncExecution()) {
          try {
          //异步事件
             executor.execute(() -> invokeListener(listener, event));
          }
          catch (RejectedExecutionException ex) {
             // Probably on shutdown -> invoke listener locally instead
             invokeListener(listener, event);
          }
       }
       else {
          invokeListener(listener, event);
       }
    }
}

MapperScannerConfigurer

如果我们使用 Mybatis 框架的话,就会有这个工厂后置处理器

注册来源

上面我们知道 SpringBoot 启动后会先执行 ConfigurationClassPostProcessor 加载自动配置类,于是加载到了 MybatisPlusAutoConfiguration,它有一个静态内部类

java 复制代码
@org.springframework.context.annotation.Configuration(proxyBeanMethods = false)
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {

    @Override
    public void afterPropertiesSet() {
        logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }
}

这里 @Import 导入了 AutoConfiguredMapperScannerRegistrar 类,再查看AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions 注册方法中注册了 MapperScannerConfigurerBeanDefiniton

作用

我们查看 MapperScannerConfigurer#postProcessBeanDefinitionRegistry,源码很简单

java 复制代码
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  if (this.processPropertyPlaceHolders) {
   //解析属性值中的占位符
    processPropertyPlaceHolders();
  }
  
  //实例化 `ClassPathBeanDefinitionScanner` 的实现类扫描类路径下的 @Mapper 注解
  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }
  if (StringUtils.hasText(defaultScope)) {
    scanner.setDefaultScope(defaultScope);
  }
  scanner.registerFilters();
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

就是把类路径下的 @Mapper 注解的类扫描出来注册为 BeanDefiniton

自我扩展

参考 MapperScannerConfigurer 假设我们后续需要自己制作一个组件,读取 @Xxx 标注的类注册为 BeanDefiniton 交给 Spring ,是不是就很简单了~

CachingMetadataReaderFactoryPostProcessor

这个类是 SpringBoot 提供的一个重要的缓存元信息的工厂后置处理器,它是通过 ApplicationContextInitializer 添加到 ApplicationContext 中的

java 复制代码
class SharedMetadataReaderFactoryContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered, BeanRegistrationExcludeFilter {
    public static final String BEAN_NAME = "org.springframework.boot.autoconfigure.internalCachingMetadataReaderFactory";
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
       if (AotDetector.useGeneratedArtifacts()) {
          return;
       }
       BeanFactoryPostProcessor postProcessor = new CachingMetadataReaderFactoryPostProcessor(applicationContext);
       applicationContext.addBeanFactoryPostProcessor(postProcessor);
    }
}

我们可以查看 CachingMetadataReaderFactoryPostProcessor#postProcessBeanDefinitionRegistry 后置处理方法源码,

java 复制代码
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    register(registry);
    configureConfigurationClassPostProcessor(registry);
}

先注册了一个 SharedMetadataReaderFactoryBeanBeanDefiniton ,实际上最后就是向容器中注入 ConcurrentReferenceCachingMetadataReaderFactory,然后第二行代码很关键,给 ConfigurationClassPostProcessor 这个工厂后置处理器的属性 metadataReaderFactory 赋值为刚刚创建的 ConcurrentReferenceCachingMetadataReaderFactory

我们再去看原生的 ConfigurationClassPostProcessor.metadataReaderFactory 默认值

java 复制代码
private MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();

在判断一个类是不是配置类的时候会用到它,默认的实现是每次都会用资源加载器读取一遍这个类,

java 复制代码
@Override
public MetadataReader getMetadataReader(String className) throws IOException {
    try {
       String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX;
       //重新读取文件
       Resource resource = this.resourceLoader.getResource(resourcePath);
       return getMetadataReader(resource);
    }
}

ConcurrentReferenceCachingMetadataReaderFactory 的实现是,第一次读取之后放入缓存,后续每次获取会先从缓存中查询

java 复制代码
@Override
public MetadataReader getMetadataReader(String className) throws IOException {
    //读取缓存
    MetadataReader metadataReader = this.classNameCache.get(className);
    if (metadataReader == null) {
       metadataReader = super.getMetadataReader(className);
       this.classNameCache.put(className, metadataReader);
    }
    return metadataReader;
}

它的作用就是提高程序性能,避免重复读取类的元数据信息

BeanFactoryPostProcessor 与 BeanPostProcessor 对比

这两个名字很像,这里列出它们的区别,下一篇文章会详细介绍 BeanPostProcessor

对比维度 BeanFactoryPostProcessor BeanPostProcessor
处理时机 Bean 定义加载之后,Bean 实例化之前 Bean 实例化之后,初始化回调前后
作用对象 Bean 的定义元数据(BeanDefinition) Bean 实例对象
主要功能 修改、注册或移除 BeanDefinition 修改或包装 Bean 实例
执行阶段 postProcessBeanFactory() postProcessBeforeInitialization() postProcessAfterInitialization()
触发次数 每个容器仅执行一次 每个 Bean 实例都会执行
Spring 核心阶段 BeanFactory 准备阶段 Bean 生命周期阶段
典型应用场景 1. 修改 Bean 的属性值(占位符解析) 2. 动态注册 BeanDefinition 3. 根据条件移除 Bean 1. 代理增强(AOP) 2. 属性注入后的处理 3. 自定义初始化逻辑
实现示例 PropertySourcesPlaceholderConfigurer ConfigurationClassPostProcessor AutowiredAnnotationBeanPostProcessor CommonAnnotationBeanPostProcessor

结语

篇幅原因,这里无法列出所有的工厂后置处理器的具体作用和实现,不过 SpringBoot 启动过程中,Spring 容器刷新过程中调用的 BeanFactoryPostProcessor 相对于 BeanPostProcessor 要少的多,但是两者都很重要,有兴趣可以逐一查看源码。

如果这篇文章对你有帮助,记得点赞加关注!你的支持就是我继续创作的动力!

相关推荐
万岳科技程序员小金5 小时前
多商户商城系统源码 + APP/小程序开发:技术架构与应用解
程序员·开源·源码·多商户商城系统源码·多商户商城小程序·多商户商城app开发·多商户商城平台开发
南极企鹅5 小时前
springBoot项目有几个端口
java·spring boot·后端
忧郁的Mr.Li6 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
冬奇Lab7 小时前
Android 15 ServiceManager与Binder服务注册深度解析
android·源码·源码阅读
暮色妖娆丶7 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
Coder_Boy_7 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring
雨中飘荡的记忆7 小时前
Spring Batch实战
java·spring
爱学英语的程序员7 小时前
面试官:你了解过哪些数据库?
java·数据库·spring boot·sql·mysql·mybatis
Java新手村7 小时前
基于 Vue 3 + Spring Boot 3 的 AI 面试辅助系统:实时语音识别 + 大模型智能回答
vue.js·人工智能·spring boot