Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_3细说invokeBeanDefinitionRegistryPostProcessors

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_3细说invokeBeanDefinitionRegistryPostProcessors

20200914143739548.png

Pre

Spring5源码 - 04 invokeBeanFactoryPostProcessors 源码解读_1

Spring5源码 - 05 invokeBeanFactoryPostProcessors 源码解读_2



细说invokeBeanDefinitionRegistryPostProcessors


前两篇博文 我们过了一下这个方法的主干流程,其中有个关键的方法,我们没有细说就是这个invokeBeanDefinitionRegistryPostProcessors。


这个方法很重要,本篇博文我们就一起来剖析下。


话不多说,还是下来梳理主干流程,然后再对着源码过一遍


流程图

我们来看流程图


20201013214625313.png


源码分析

我们从头跟一下

AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);


进入到AnnotationConfigApplicationContext的构造函数中

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    //调用构造函数
    this();
    //注册我们的配置类
    register(annotatedClasses);
    //IOC容器刷新接口
    refresh();
  }


这里我们关注refresh方法,这个方法太重要了,里面非常深的调用链,我们这里先有个大概的认知

@Override
  public void refresh() throws BeansException, IllegalStateException { 
      //1:准备刷新上下文环境
      prepareRefresh();
      //2:获取告诉子类初始化Bean工厂
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      //3:对bean工厂进行填充属性
      prepareBeanFactory(beanFactory);
      try {
        // 第四:留个子类去实现该接口
        postProcessBeanFactory(beanFactory);
        // 调用我们的bean工厂的后置处理器.
        invokeBeanFactoryPostProcessors(beanFactory);
        // 调用我们bean的后置处理器
        registerBeanPostProcessors(beanFactory);
        // 初始化国际化资源处理器.
        initMessageSource();
        // 创建事件多播器
        initApplicationEventMulticaster();
        // 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomat的.
        onRefresh();
        //把我们的事件监听器注册到多播器上
        registerListeners();
        //实例化我们剩余的单实例bean.
        finishBeanFactoryInitialization(beanFactory);
        // 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
        finishRefresh();
      }
  }

我们先重点关注【invokeBeanFactoryPostProcessors(beanFactory);

跟进去,重点看 invokeBeanFactoryPostProcessors

//传入bean工厂和获取applicationContext中的bean工厂后置处理器(但是由于没有任何实例化过程,所以传递进来的为空)
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

继续,重点关注

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);


这个方法就是我们今天要研究的源码


/**
   * Invoke the given BeanDefinitionRegistryPostProcessor beans.
   */
  private static void invokeBeanDefinitionRegistryPostProcessors(
      Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {
    //获取容器中的ConfigurationClassPostProcessor的后置处理器进行bean定义的扫描
    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
  }


这里就一个,那就是 ConfigurationClassPostProcessor

仅需跟进去 ,看ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry方法

//真正的解析我们的bean定义
 processConfigBeanDefinitions(registry);


这个方法主要干的三件事儿

  1. 找到开发人员传入主配置类
  2. 创建一个配置类解析器对象
  1. 解析我们的配置类 parser.parse(candidates);
  2. 把解析出来的配置类注册到容器中 this.reader.loadBeanDefinitions(configClasses);

我们重点关注第三步 和第四步

先看第三步


解析配置类 parser.parse(candidates)


20201013220108882.png

所以重点看

parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());


跟进去

  /**
   * 真的解析我们的配置类
   * @param metadata 配置类的源信息
   * @param beanName 当前配置类的beanName
   * @throws IOException
   */
  protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    /**
     * 第一步:把我们的配置类源信息和beanName包装成一个ConfigurationClass 对象
     */
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
  }


进入

20201013220330467.png

关注

sourceClass = doProcessConfigurationClass(configClass, sourceClass);


这里面就是处理各种注解【@propertySource、@ComponentScan 、@Import、@ImportResource、@Bean】的地方,我们来看下源码

@Nullable
  protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
    // Recursively process any member (nested) classes first
    processMemberClasses(configClass, sourceClass);
    //处理我们的@propertySource注解的
    for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), PropertySources.class,
        org.springframework.context.annotation.PropertySource.class)) {
      if (this.environment instanceof ConfigurableEnvironment) {
        processPropertySource(propertySource);
      }
      else {
        logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +
            "]. Reason: Environment must implement ConfigurableEnvironment");
      }
    }
    //解析我们的 @ComponentScan 注解
    //从我们的配置类上解析处ComponentScans的对象集合属性
    Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(
        sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
    if (!componentScans.isEmpty() &&
        !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
      //循环解析 我们解析出来的AnnotationAttributes
      for (AnnotationAttributes componentScan : componentScans) {
        //把我们扫描出来的类变为bean定义的集合 真正的解析
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        //循环处理我们包扫描出来的bean定义
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
          BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
          if (bdCand == null) {
            bdCand = holder.getBeanDefinition();
          }
          //判断当前扫描出来的bean定义是不是一个配置类,若是的话 直接进行递归解析
          if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
            //递归解析
            parse(bdCand.getBeanClassName(), holder.getBeanName());
          }
        }
      }
    }
    // 处理 @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    // 处理 @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);
      }
    }
    // 处理 @Bean methods 获取到我们配置类中所有标注了@Bean的方法
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
      configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    // 处理配置类接口的
    processInterfaces(configClass, sourceClass);
    // 处理配置类的父类的
    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();
      }
    }
    // 没有父类解析完成
    return null;
  }


配置类注册到容器中 this.reader.loadBeanDefinitions(configClasses)

第三步完成了,我们来看下这一步Spring是如和把配置类注册到容器中的

  public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    //注册我们的配置类到容器中
    for (ConfigurationClass configClass : configurationModel) {
      loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
  }

那自然就是

loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);


20201013221148153.png

很清晰哈

一个个的看下吧

注册@Import的bean

  private void registerBeanDefinitionForImportedConfigurationClass(ConfigurationClass configClass) {
    //获取我们的配置类的源信息
    AnnotationMetadata metadata = configClass.getMetadata();
    //构建为我们的bean定义
    AnnotatedGenericBeanDefinition configBeanDef = new AnnotatedGenericBeanDefinition(metadata);
    //设置他的scope
    ScopeMetadata scopeMetadata = scopeMetadataResolver.resolveScopeMetadata(configBeanDef);
    configBeanDef.setScope(scopeMetadata.getScopeName());
    //获取bean的名称
    String configBeanName = this.importBeanNameGenerator.generateBeanName(configBeanDef, this.registry);
    //处理我们的JRS250组件的
    AnnotationConfigUtils.processCommonDefinitionAnnotations(configBeanDef, metadata);
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(configBeanDef, configBeanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //注册我们的bean定义到我们的容器中
    this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.getBeanDefinition());
    configClass.setBeanName(configBeanName);
    if (logger.isDebugEnabled()) {
      logger.debug("Registered bean definition for imported class '" + configBeanName + "'");
    }
  }


重点就是

this.registry.registerBeanDefinition(definitionHolder.getBeanName(), definitionHolder.


注册@Bean的bean

2020101322160190.png


处理@Bean可以跟的各种属性


注册@ImportResources的bean

private void loadBeanDefinitionsFromImportedResources(
            Map<String, Class<? extends BeanDefinitionReader>> importedResources) {
    // reader实例缓存
    Map<Class<?>, BeanDefinitionReader> readerInstanceCache = new HashMap<>();
    // 循环处理<配置文件路径-reader>
    importedResources.forEach((resource, readerClass) -> {
        // 如果注解配置的Reader是默认的(我们一般其实也不改)
        if (BeanDefinitionReader.class == readerClass) {
            if (StringUtils.endsWithIgnoreCase(resource, ".groovy")) {
                // 如果文件名是.groovy结尾,则使用GroovyBeanDefinitionReader
                // 说实话我也第一次知道还可以用groovy脚本来做spring的配置文件
                // 后面我去看了一下BeanDefinitionReader这个接口的实现类,发现还一个
                // PropertiesBeanDefinitionReader,感兴趣的同学可以去研究一下
                readerClass = GroovyBeanDefinitionReader.class;
            }
            else {
                // 默认情况下我们使用XmlBeanDefinitionReader  
                readerClass = XmlBeanDefinitionReader.class;
            }
        }
        // 先从缓存拿
        BeanDefinitionReader reader = readerInstanceCache.get(readerClass);
        if (reader == null) {
            try {
                // 拿不到就新建一个,配置的reader类必须有一个只有BeanDefinitionRegistry参数的构造器
                reader = readerClass.getConstructor(BeanDefinitionRegistry.class).newInstance(this.registry);
                // Delegate the current ResourceLoader to it if possible
                if (reader instanceof AbstractBeanDefinitionReader) {
                    AbstractBeanDefinitionReader abdr = ((AbstractBeanDefinitionReader) reader);
                    abdr.setResourceLoader(this.resourceLoader);
                    abdr.setEnvironment(this.environment);
                }
                readerInstanceCache.put(readerClass, reader);
            }
            catch (Throwable ex) {
                throw new IllegalStateException(
                    "Could not instantiate BeanDefinitionReader class [" + readerClass.getName() + "]");
            }
        }
        // 使用reader从文件加载bean
        reader.loadBeanDefinitions(resource);
    });
}


默认情况下 是创建一个XmlBeanDefinitionReader来解析加载我们的配置文件中定义的bean的。


注册ImportBeanDefinition的bean

  private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) {
    registrars.forEach((registrar, metadata) ->
        registrar.registerBeanDefinitions(metadata, this.registry));
  }


很直观了哈 ,循环直接调用ImportBeanDefinitionRegistrar.registerBeanDefinitions方法进行beanDefinition的注册。

相关文章
|
2月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
96 2
|
2月前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
102 5
|
2天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
57 2
|
2月前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
73 9
|
4月前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
359 24
|
4月前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
237 24
|
3月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
196 5
|
3月前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
|
3月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
155 9
下一篇
开通oss服务