SpringBoot——配置加载原理

609 阅读8分钟

一、加载逻辑

1.1)加载创世纪后置处理器

先来一个例子:

final AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(【你的配置类】.class);

注意这里的AnnotationConfigApplicationContext对象,跟进源码:

//在无参构造方法中会看到如下代码
this.reader = new AnnotatedBeanDefinitionReader(this);

AnnotatedBeanDefinitionReader:

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
   Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
   Assert.notNull(environment, "Environment must not be null");
   //把ApplicationContext对象赋值给AnnotatedBeanDefinitionReader
   this.registry = registry;
   //用户处理条件表达式计算 @Conditionl主机呃呃
   this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
   //这里干了一件惊天动地的事情,注册一堆配置的处理器,这些处理器被成为”创世纪处理器“因为这些处理器在后面的处理中起到决定性的作用
   AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

之前说过在AnnotationConfigUtils.registerAnnotationConfigProcessors中注册了一堆配置的处理器——创世纪对象。注意:这里注册的都是Bean定义、这里注册的都是Bean定义、这里注册的都是Bean定义重要的事情说三遍; 其中注册了一个“ConfigurationClassPostProcessor”这个创世纪后置处理器就是专门用来处理我们的配置类(还有其他一些用于解析注解的创世纪处理器,这个不是我们今天的重点,先放过他们),他是一个Bean工厂的后置处理器;

clipboard.png

从类图上可以看到这个类实现了BeanFactoryPostProcessor以及BeanDefinitionRegistryPostProcessor接口,为啥是BeanDefinitionRegistryPostProcessor而不是BeanFactoryPostProcessor?之前的内容讲过BeanFactoryPostProcessor可以处理和修改Bean定义,BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的加强,不仅可以处理和修改还可以注册;

1.2)配置类的注册

通过AnnotationConfigApplicationContext构造方法中的第二句代码(register(annotatedClasses)方法)通过register方法将传入的配置类进行了注册,这里还是注册的是Bean定义;

//这里的reader就是1.1中所说的AnnotatedBeanDefinitionReader对象
this.reader.register(annotatedClasses);

到了这里,解析配置的处理器与配置类都具备了,那么下一步就是该如何进行处理了;特别注意一下:这里最终通过仅仅org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean方法注册了配置类的Bean定义,跟Spring缓存、处理、解析等处理半毛钱关系没有,这是Spring生态的套路先变成Bean定义,然后再解析处理;这些Bean定义都是存放在容器BeanDefinitionMap中;

1.3)开始处理

这里就进入了Spring核心refresh方法

/*
* IOC容器刷新接口
* 这个方法是核心方法;
* 是整个Spring生态的体现;
* 体现了IOC的整个生命周期,包括Bean初始化、Bean的加载、使用、销毁等生命周期
* 此方法中包含13个核心的方法,是Spring这本九阴真经的精髓,了解了这13个方法你才是真正了解了Spring
* */
refresh();

那么解析配置类具体是在哪个方法中进行的呢,不要急,慢慢来;

/*
* 5:调用bean工厂的后置处理器
* 这里将满足条件的Bean变成Bean定义(BeanDefinition)
* 在这里才是真正的去解析类及类中的元素注解
* 例如:@Configuration、@ComponentScan、@Bean等等最后将这些Bean定义放入到BeanDefinitionMap中
* 有因必有果,这里调用了之前创建的创世纪处理器Bean定义(BeanDefinition):ConfigurationClassPostProcessor;即单独调用一次getBean
* 因为ConfigurationClassPostProcessor这个创世纪处理器对象拥有绝对的优先权,创世纪的处理器都没有被创建,那后面还玩个屁
* */
invokeBeanFactoryPostProcessors(beanFactory);

是在这里调用Bean工厂的后置处理器进行处理的,联系上面的内容,想一想,这里调用的后置处理器,是不是有且仅有创世纪的Bean定义以及我传入的配置类的Bean定义,而ConfigurationClassPostProcessor是一个Bean工厂的后置处理器接口的实现,所以是在这里对配置类进行处理;

PS:这里有个面试经常被问道的问题,调用ConfigurationClassPostProcessor创世纪后置处理器的时候,肯定是先调用带注册功能的BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法对配置类的Bean定义进行处理,然后再调用BeanFactoryPostProcessor接口的postProcessBeanFactory方法进行注册;

二、处理逻辑

/*
* 传入bean工厂和获取applicationContext中的bean工厂后置处理器(但是由于没有任何实例化过程,所以传递进来的为空)
* getBeanFactoryPostProcessors():当前Bean工厂后置处理器集合,这里并不是内置注册进入的
* 在初次调用的时候这个集合是空的;
* 这个容器用于在外部初始化AnnotationConfigApplicationContext完毕后手动调用ApplicationContext.addBeanFactoryPostProcessor()方法时才会有东西;
* 也就是自己手动加入的Bean工厂后置处理器;
* */
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

在PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors中大致分为2步; 第一步:调用所有实现了带注册功能的Bean定义后置处理器(BeanDefinitionRegistryPostProcessor); 第二步:调用所有实现了Bean工厂的后置处理器(BeanFactoryPostProcessor);

今天重点关注一下第一步的内容 进入PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法;前方高能,准备好纸巾,开始撸:

2.1)源码主干部分

第一步可以就是由创世纪的处理器ConfigurationClassPostProcessor来完成对配置的解析和加载;在所有的创世纪处理器中只有ConfigurationClassPostProcessor是实现了BeanDefinitionRegistryPostProcessor接口;

/*
* 第一步:首先调用BeanDefinitionRegistryPostProcessor的后置处理器
* 初次进来,只有创世纪处理器Bean定义以及构造方法传入的配置类Bean定义
* 创世纪处理器:ConfigurationClassPostProcessor是带注册功能的Bean定义后置处理器(BeanDefinitionRegistryPostProcessor)实现;
* 所以这里应该是ConfigurationClassPostProcessor满足条件;
* */
//判断我们的beanFacotry实现了BeanDefinitionRegistry
if (beanFactory instanceof BeanDefinitionRegistry) {
   //强行把我们的bean工厂转为BeanDefinitionRegistry
   BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
   //保存BeanFactoryPostProcessor类型的后置
   List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
   //保存BeanDefinitionRegistryPostProcessor类型的后置处理器
   List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();

   /*
   * 循环传递进来的beanFactoryPostProcessors
   * 如果这个集合在前面初始化AnnotationConfigApplicationContext之后没有手动调用addBeanFactoryPostProcessor方法
   * 那么这个集合就是空的
   * */
   for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
      //判断我们的后置处理器是不是BeanDefinitionRegistryPostProcessor
      if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
         //进行强制转化
         BeanDefinitionRegistryPostProcessor registryProcessor =
               (BeanDefinitionRegistryPostProcessor) postProcessor;
         //调用他作为BeanDefinitionRegistryPostProcessor的处理器的后置方法
         registryProcessor.postProcessBeanDefinitionRegistry(registry);
         //添加到我们用于保存的BeanDefinitionRegistryPostProcessor的集合中
         registryProcessors.add(registryProcessor);
      }
      else {//若没有实现BeanDefinitionRegistryPostProcessor 接口,那么他就是BeanFactoryPostProcessor
         //把当前的后置处理器加入到regularPostProcessors中
         regularPostProcessors.add(postProcessor);
      }
   }

   //定义一个集合用户保存当前准备创建的BeanDefinitionRegistryPostProcessor
   List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();

   /*
   * 第1.1步:去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称
   * 这里其实就是调用创世纪处理器ConfigurationClassPostProcessor
   * */
   String[] postProcessorNames =
         beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   //循环上一步获取的BeanDefinitionRegistryPostProcessor的类型名称
   for (String ppName : postProcessorNames) {
      /*
      * 判断是否实现了PriorityOrdered接口
      * 如果实现了PriorityOrdered这个接口的会最优先调用
      * 这里体现了遵守规范的童鞋将有优先权;创世纪处理器ConfigurationClassPostProcessor就是一个遵守规范的童鞋,所以这里会优先调用
      * */
      if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
         //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去
         currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
         //同时也加入到processedBeans集合中去
         processedBeans.add(ppName);
      }
   }
   //通过PriorityOrdered中的getOrder方法返回的值,对currentRegistryProcessors集合中BeanDefinitionRegistryPostProcessor进行排序
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   //把他加入到用于保存到registryProcessors中
   registryProcessors.addAll(currentRegistryProcessors);
   /**
    * 在这里典型的BeanDefinitionRegistryPostProcessor就是ConfigurationClassPostProcessor
    * 用于进行bean定义的加载 比如我们的包扫描,@import  等等。。。。。。。。。
    * 这里调用的是带注册功能的Bean定义后置处理器(BeanDefinitionRegistryPostProcessor)中的postProcessBeanDefinitionRegistry方法
    */
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   //调用完之后,马上clea掉
   currentRegistryProcessors.clear();

   //去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称
   postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
   //循环上一步获取的BeanDefinitionRegistryPostProcessor的类型名称
   for (String ppName : postProcessorNames) {
      /*
      * 没有被处理过,且实现了Ordered接口的,上面是对实现PriorityOrdered接口的Bean定义后置处理器进行调用,这里是对实现Ordered接口的Bean定义后置处理器进行处理
      * 因此可以得出一个结论:PriorityOrdered接口的优先级比Ordered高;
      * */
      if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
         //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去
         currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
         //同时也加入到processedBeans集合中去
         processedBeans.add(ppName);
      }
   }
   //对currentRegistryProcessors集合中BeanDefinitionRegistryPostProcessor进行排序
   sortPostProcessors(currentRegistryProcessors, beanFactory);
   //把他加入到用于保存到registryProcessors中
   registryProcessors.addAll(currentRegistryProcessors);
   /*
   * 调用他的后置处理方法
   * 这里调用的是带注册功能的Bean定义后置处理器(BeanDefinitionRegistryPostProcessor)中的postProcessBeanDefinitionRegistry方法
   * */
   invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
   //调用完之后,马上clea掉
   currentRegistryProcessors.clear();

   //调用没有实现任何优先级接口的BeanDefinitionRegistryPostProcessor
   //定义一个重复处理的开关变量 默认值为true
   boolean reiterate = true;
   //第一次就可以进来
   while (reiterate) {
      //进入循环马上把开关变量给改为fasle
      reiterate = false;
      //去容器中获取BeanDefinitionRegistryPostProcessor的bean的处理器名称
      postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
      //循环上一步获取的BeanDefinitionRegistryPostProcessor的类型名称
      for (String ppName : postProcessorNames) {
         //没有被处理过的,即没有实现任何优先级接口(PriorityOrdered、Ordered)的Bean定义后置处理器
         if (!processedBeans.contains(ppName)) {
            //显示的调用getBean()的方式获取出该对象然后加入到currentRegistryProcessors集合中去
            currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
            //同时也加入到processedBeans集合中去
            processedBeans.add(ppName);
            //再次设置为true
            reiterate = true;
         }
      }
      //对currentRegistryProcessors集合中BeanDefinitionRegistryPostProcessor进行排序
      sortPostProcessors(currentRegistryProcessors, beanFactory);
      //把他加入到用于保存到registryProcessors中
      registryProcessors.addAll(currentRegistryProcessors);
      /*
       * 调用他的后置处理方法
       * 这里调用的是带注册功能的Bean定义后置处理器(BeanDefinitionRegistryPostProcessor)中的postProcessBeanDefinitionRegistry方法
       * */
      invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
      //进行clear
      currentRegistryProcessors.clear();
   }

   /*
   * 调用实现了BeanDefinitionRegistryPostProcessor的接口;这个接口实现了BeanFactoryPostProcessor
   * 这里实际调用的是BeanFactoryPostProcessor的postProcessBeanFactory方法
   * */
   invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
   //调用BeanFactoryPostProcessor
   invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
} else { //若当前的beanFacotory没有实现了BeanDefinitionRegistry 直接电泳
    //直接电泳beanFacotoryPostProcessor接口的方法进行后置处理
   invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
}
/*
* 第一步结束
* --------------------------------------------------------------
* */

这一步特别注意一下最后几行的invokeBeanFactoryPostProcessors这个方法;进入这个方法:

private static void invokeBeanFactoryPostProcessors(
      Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
   /*
   * 这里是调用Bean工厂的后置处理器,注意这里不是带注册功能的
   * */
   for (BeanFactoryPostProcessor postProcessor : postProcessors) {
      /*
      * 下面又是风轻云淡的一句调用,其实暗藏玄机;
      * 这里必定会会出现一个最初期加载的创世纪处理器——ConfigurationClassPostProcessor:这个方法里真的做了一件惊天动地的事情;
      * */
      postProcessor.postProcessBeanFactory(beanFactory);
   }
}
/**
 * Prepare the Configuration classes for servicing bean requests at runtime
 * by replacing them with CGLIB-enhanced subclasses.
 */
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   int factoryId = System.identityHashCode(beanFactory);
   if (this.factoriesPostProcessed.contains(factoryId)) {
      throw new IllegalStateException(
            "postProcessBeanFactory already called on this post-processor against " + beanFactory);
   }
   this.factoriesPostProcessed.add(factoryId);
   if (!this.registriesPostProcessed.contains(factoryId)) {
      // BeanDefinitionRegistryPostProcessor hook apparently not supported...
      // Simply call processConfigurationClasses lazily at this point then.
      processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
   }
   /*
   * 这里特别注意,就是在下面这一段调用中使用Cglib对配置类进行了代理;回想一下我们的配置类中是否会存在@Configuration、@Bean等这样使用注解标注的方法;
   * 这里又有一个常见面试问题;如果我们调用了配置类中使用@Bean注解标注的方法,那么在后面又进行了10次方法的调用,这时此方法中的处理实际上只会被
   * 调用1次,因为被代理了,除了第一次调用之外的所有调用都是从缓存池中进行获取的;这里也是满足了面向Bean编程的思想,既然已经有了为何还要重复去处理呢;
   * 特别注意只有@Configuration注解标注的类在生成Bean定义(BeanDefinition)的时候类型才会为Full;在深入的源码中可以看到只有Bean定义(Beandefinition)
   * 类型为Full的时候才会被代理
   * */
   enhanceConfigurationClasses(beanFactory);
   beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

2.2)配置解析执行流程

第一步其实就是不断的调用Bean工厂的后置处理器,其调用过程简单来说就是下面这种顺序;

clipboard.png

回顾第一章我们说过,在启动的时候会创建一个创世纪的处理器ConfigurationClassPostProcessor,这个处理器即实现了BeanDefinitionRegistryPostProcessor也实现了BeanFactoryPostProcessor,因此在这个流程中会在第一步及第四步进行调用; 第一步调用的是带注册功能的后置处理器中的方法,因此在这一步会去处理类中@Bean等元素;将元素变成Bean定义; 第四步调用的是不带注册功能的后置处理器中的方法;

看一个示例代码,下面是一个我们自定义的带注册功能的Bean工厂后置处理器的实现:

@Component
public class TestConfig implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        
    }
    
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

假设目前在我的代码中自己实现了一个BeanDefinitionRegistryPostProcessor那么将会在第三步和第四步进行调用,这里也就是充分说明了Spring是因为其生态而强大,可以在特定的地方进行回调,给予开发人员充分的发挥空间;

(PS:这里有个比较常见的面试题,我们自己定义的BeanDefinitionRegistryPostProcessor是在什么时候被加载的呢?结合上面所讲,在第一步的时候有且仅有一个创世纪的处理器ConfigurationClassPostProcessor存在,这个处理器是专门用于解析配置类的,我们自定义的所有配置都是通过这个创世纪后置处理器来完成加载的,因此我们自定义的后置处理器只会存在于第一步之后;PS:想自虐的童鞋,可以跟一下第一步的代码深入一下);

clipboard.png

这一步只有一个创世纪的后置处理器ConfigurationClassPostProcessor,所以看看这个后置处理器的实现方法:

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);
   //真正的解析我们的bean定义,这个方法太深了,当时跟这个方法的时候,膀胱和大肠都要憋炸了,才找到;有兴趣挑战的童鞋可以尝试一下;
   processConfigBeanDefinitions(registry);
}

processConfigBeanDefinitions方法是解析注入的处理,调用链非常深,这里提一嘴:Spring中看到以do开头的方法要特别留意,按Spring的规范这些do开头的方法都是实实在在做事情的方法;