Spring refresh函数(2)——Spring Boot invokeBeanFactoryPostProcessors

目前Spring Boot已经逐渐取代原来基于xml配置的Spring,渐渐成为各大互联网或软件公司的主力框架。为了贴合实际应用,接下来主要以Spring Boot(本文使用1.5.7.RELEASE版本)为基础进行介绍。

invokeBeanFactoryPostProcessors()函数是在BeanFactory创建之后,Bean创建之前执行,作用是对已经创建的BeanFactory进行后续的操作。其中将Spring Boot中注解的Bean转换为BeanDefinition结构就在这里完成,BeanDefinition结构为之后创建Bean做准备。本文的主题便是探究Spring Boot如何注册BeanDefinition结构。

从类AbstractApplicationContext的refresh()函数中的invokeBeanFactoryPostProcessors()函数开始我们的主题。

invokeBeanFactoryPostProcessors(beanFactory);

进入到类PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors()函数。

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()函数首先会“Invoke BeanDefinitionRegistryPostProcessors first, if any.”,接口BeanDefinitionRegistryPostProcessor的实现类用于注册BeanDefinition,这里传入的是ConfigurationClassPostProcessor对象处理被@Configuration注解的类。

public static void invokeBeanFactoryPostProcessors(
      ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    //...略去部分代码...
    // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
    List<BeanDefinitionRegistryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanDefinitionRegistryPostProcessor>();
      for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
          priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
          processedBeans.add(ppName);
        }
    }
    sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
    registryPostProcessors.addAll(priorityOrderedPostProcessors);
    invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);
    //...略去部分代码...
}
图片选自我的公众号

invokeBeanDefinitionRegistryPostProcessors()函数。

invokeBeanDefinitionRegistryPostProcessors(priorityOrderedPostProcessors, registry);

invokeBeanDefinitionRegistryPostProcessors()函数中的参数ConfigurationClassPostProcessor对象用于注册BeanDefinition。

private static void invokeBeanDefinitionRegistryPostProcessors(
      Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {

    for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {
      postProcessor.postProcessBeanDefinitionRegistry(registry);
    }
}
图片选自我的公众号

执行类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);
}

进入processConfigBeanDefinitions()函数。从函数名就可以看出这个函数是用于操作被@configuration注解的类。

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    //...略去部分代码...
    // Parse each @Configuration class
    ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

    Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
    Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
    do {
      parser.parse(candidates);
      parser.validate();
      //...略去部分代码...
}

首先创建了ConfigurationClassParser对象将被用于对被@configuration注解的类进行解析使用。

// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
        this.metadataReaderFactory, this.problemReporter, this.environment,
        this.resourceLoader, this.componentScanBeanNameGenerator, registry);

具体解析过程在函数parse()中。

parser.parse(candidates);

进入类ConfigurationClassParser的parse()函数,会看到依据被@configuration注解的类的BeanDefinition的类型选择不同的parse()函数。而if-else逻辑中的parse()函数都会执行processConfigurationClass()函数。

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    this.deferredImportSelectors = new LinkedList<DeferredImportSelectorHolder>();

    for (BeanDefinitionHolder holder : configCandidates) {
      BeanDefinition bd = holder.getBeanDefinition();
      try {
        if (bd instanceof AnnotatedBeanDefinition) {
          parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
        }
        else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
          parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
        }
        else {
          parse(bd.getBeanClassName(), holder.getBeanName());
        }
      }
      catch (BeanDefinitionStoreException ex) {
        throw ex;
      }
      catch (Throwable ex) {
        throw new BeanDefinitionStoreException(
            "Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
      }
    }

    processDeferredImportSelectors();
}

protected final void parse(String className, String beanName) throws IOException {
    MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className);
    processConfigurationClass(new ConfigurationClass(reader, beanName));
}

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

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

processConfigurationClass()函数中继续调用doProcessConfigurationClass()函数。

如果变量sourceClass表示的类存在父类,doProcessConfigurationClass()函数会返回其父类,do-while循环执行。

我们程序中只有main()方法所在的类是@configuration注解的,而且无父类,当首次进入到processConfigurationClass()方法中,变量sourceClass就是我们自定义的有main()方法的入口类,do-while循环只执行一次。

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    //...略去部分代码...
    // Recursively process the configuration class and its superclass hierarchy.
    SourceClass sourceClass = asSourceClass(configClass);
    do {
      sourceClass = doProcessConfigurationClass(configClass, sourceClass);
    }
    while (sourceClass != null);

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

doProcessConfigurationClass()函数根据变量sourceClass表示的类,解析操作注解它的@ComponentScan注解、@Import注解和@ImportResource注解等。

这里看@ComponentScan注解的处理逻辑。此处存在着递归调用,递归以变量sourceClass为基础,如果扫描@ComponentScan注解描述的路径下其他类仍旧是被@configuration注解的,继续更深的扫描。doProcessConfigurationClass()->parse()->processConfigurationClass()>doProcessConfigurationClass()方法递归调用。

this.componentScanParser.parse()进行进一步处理。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
      throws IOException {
    //...略去部分代码...
    // 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
        //处理@ComponentScan注解
        Set<BeanDefinitionHolder> scannedBeanDefinitions =
            this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
        // Check the set of scanned definitions for any further config classes and parse recursively if needed
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
          if (ConfigurationClassUtils.checkConfigurationClassCandidate(
              holder.getBeanDefinition(), this.metadataReaderFactory)) {
            //这里可能出现递归
            parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
          }
        }
        //...略去部分代码...
}

进入类ComponentScanAnnotationParser中的parse()函数解析@ComponentScan注解相关的参数,例如excludeFilters,basePackages参数等。创建ClassPathBeanDefinitionScanner对象用于扫描@ComponentScan注解指定的包。

直接跳到函数最后一行scanner.doScan()。

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    //...略去部分代码...
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
        componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    //...略去部分代码...
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

进入类ClassPathBeanDefinitionScanner中的方法doScan()。

类ClassPathBeanDefinitionScanner扫描参数basePackages指定包下所有定义的Bean,转换为BeanDefinition后进行注册。参数basePackages是根据@ComponentScan注解的参数进行解析获得的,如果没有在注解中设置,默认就是被@ComponentScan注解的类所在的包。所以我们要把有main()方法的入口类放在最外层的包中,这样就会扫描到我们定义的所有包下的Bean。

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<BeanDefinitionHolder>();
    for (String basePackage : basePackages) {
      //找到包路径下用户定义的Bean
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      //循环依次注册BeanDefinition
      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);
          //注册BeanDefinition
          registerBeanDefinition(definitionHolder, this.registry);
        }
      }
    }
    return beanDefinitions;
}

doScan()方法中的findCandidateComponents()方法先找到参数basePackages路径下用户自定义的Bean,然后定义它们的BeanDefinition结构返回。

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

doScan()方法中之后的for循环依次遍历Set<BeanDefinition> BeanDefinition集合结构,方法registerBeanDefinition()对BeanDefinition进行注册。注册过程,如果是新的BeanDefinition就会直接放入到BeanFactory中的一个map结构beanDefinitionMap变量中存储,如果存在旧的BeanDefinition会进行替换。

registerBeanDefinition(definitionHolder, this.registry);

自此Spring Boot完成了用户自定义Bean的BeanDefinition结构的注册。以后将使用BeanDefinition结构对Bean进行创建。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 193,968评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,682评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,254评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,074评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,964评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,055评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,484评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,170评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,433评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,512评论 2 308
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,296评论 1 325
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,184评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,545评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,150评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,437评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,630评论 2 335

推荐阅读更多精彩内容