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

简介: 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的注册。

相关文章
|
17天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
31 0
|
20天前
|
监控 数据可视化 安全
一套成熟的Spring Cloud智慧工地平台源码,自主版权,开箱即用
这是一套基于Spring Cloud的智慧工地管理平台源码,具备自主版权,易于使用。平台运用现代技术如物联网、大数据等改进工地管理,服务包括建设各方,提供人员、车辆、视频监控等七大维度的管理。特色在于可视化管理、智能报警、移动办公和分布计算存储。功能涵盖劳务实名制管理、智能考勤、视频监控AI识别、危大工程监控、环境监测、材料管理和进度管理等,实现工地安全、高效的智慧化管理。
|
1月前
|
消息中间件 NoSQL Java
Spring Cloud项目实战Spring Cloud视频教程 含源码
Spring Cloud项目实战Spring Cloud视频教程 含源码
33 1
|
2月前
|
设计模式 Java Spring
【Spring源码】WebSocket做推送动作的底层实例是谁
我们都知道WebSocket可以主动推送消息给用户,那做推送动作的底层实例究竟是谁?我们先整体看下整个模块的组织机构。可以看到handleMessage方法定义了每个消息格式采用不同的消息处理方法,而这些方法该类并**没有实现**,而是留给了子类去实现。
25 1
【Spring源码】WebSocket做推送动作的底层实例是谁
|
2月前
|
存储 设计模式 Java
【Spring源码】Bean采用什么数据结构进行存储
我们再来看看中间新加入的阅读线索4,不知大家忘记了没。我们可以对照图片1的代码组织结构,发现这些没存储在包里的功能类都是比较杂乱的,想必是Spring觉得目前这些功能类还构不成一个包的体系,可能后面规模更大会统一集成起来管理。
29 1
【Spring源码】Bean采用什么数据结构进行存储
|
2天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
12 1
|
3天前
|
传感器 人工智能 前端开发
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
智慧校园电子班牌,坐落于班级的门口,适合于各类型学校的场景应用,班级学校日常内容更新可由班级自行管理,也可由学校统一管理。让我们一起看看,电子班牌有哪些功能呢?
43 4
JAVA语言VUE2+Spring boot+MySQL开发的智慧校园系统源码(电子班牌可人脸识别)Saas 模式
|
11天前
|
设计模式 安全 Java
【初学者慎入】Spring源码中的16种设计模式实现
以上是威哥给大家整理了16种常见的设计模式在 Spring 源码中的运用,学习 Spring 源码成为了 Java 程序员的标配,你还知道Spring 中哪些源码中运用了设计模式,欢迎留言与威哥交流。
|
15天前
|
XML 人工智能 Java
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
Spring Bean名称生成规则(含源码解析、自定义Spring Bean名称方式)
|
21天前
|
Java Maven Nacos
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
Spring Cloud Eureka 服务注册和服务发现超详细(附加--源码实现案例--及实现逻辑图)
31 0