• Springboot启动流程分析(三):刷新IOC容器之执行beanFactory后置处理器原理


    目录

    1 准备刷新容器prepareRefresh

    1.1 设置属性,打印日志

    1.2 根据容器中environment初始化servlet参数

    1.3 校验environment参数

    1.4 初始化监听器和监听事件

    2 获取beanFactory

    3 准备beanFactory参数

    4 设置servlet相关beanFactory参数

    5 执行beanFactory后置处理器

    5.1 获取beanFactory后置处理器

    5.2 执行beanFactory后置处理器方法 

    5.2.1 第一次执行BeanDefinitionRegistry的后置处理器

    5.2.2 第二次执行BeanDefinitionRegistry的后置处理器

    5.2.3 第三次执行BeanDefinitionRegistry的后置处理器

    5.2.4 第四次执行BeanDefinitionRegistry的后置处理器

    5.2.5 第一次执行BeanFactory的后置处理器

    5.2.6 第二次执行BeanFactory的后置处理器

    5.2.7 上半部分代码总结

    5.2.8 第三次执行BeanFactory的后置处理器

    5.2.9 第四次执行BeanFactory的后置处理器

    5.2.10 第五次执行BeanFactory的后置处理器

    5.2.11 清理beanFatcory缓存

    6 总结


    本章最重要的方法就是关于beanFactory的后置处理器执行原理,具体为类PostProcessorRegistrationDelegate中的方法invokeBeanFactoryPostProcessors,初看上去,完全不知所云,但是通过详细的代码解析,我们可以通过它对Spring框架有更深的理解。

    如果没有耐心的同学,可以直接去看最后一节的总结内容,也能有一个大致的了解。

    下面正式开始:

    在SpringApplication的run方法中,执行完成

    createApplicationContext、prepareContext

    分别完成IOC容器实例化和environment环境准备以后,我们正式进入容器刷新阶段,也就是refreshContext方法:

    1. context = this.createApplicationContext();
    2. ......
    3. this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    4. this.refreshContext(context);

     而refreshContext方法最终会进入AbstractApplicationContext类中的refresh方法。这里要说明,其实refreshContext这个方法最终调用的是其参数context的refresh方法,也就是IOC容器自身的刷新方法。通过下面代码可以很清晰的看出来:

    1. protected void refresh(ApplicationContext applicationContext) {
    2. Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    3. ((AbstractApplicationContext)applicationContext).refresh();
    4. }

    清楚了这一点,对我们后文的一些方法属性理解会更有帮助。下面我们进入真正的刷新方法: 

    1. public void refresh() throws BeansException, IllegalStateException {
    2. synchronized(this.startupShutdownMonitor) {
    3. //准备刷新容器
    4. this.prepareRefresh();
    5. //获取bean工厂
    6. ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
    7. //准备bean工厂
    8. this.prepareBeanFactory(beanFactory);
    9. try {
    10. //准备bean工厂后置处理器
    11. this.postProcessBeanFactory(beanFactory);
    12. //执行bean工厂后置处理器
    13. this.invokeBeanFactoryPostProcessors(beanFactory);

     通过同步方法块进入后,我们来看一下每一步都做了什么。

    1 准备刷新容器prepareRefresh

    为了方便理解,我们实行分步、分段讲解。

    1.1 设置属性,打印日志

    第一步,会设置一些当前实例,其实也就是IOC容器,它本身是以

    AnnotationConfigServletWebServerApplicationContext

    的形式实例化,在SpringApplication中被强转为其父类AbstractApplicationContext。所以在实例化的过程中,其实父类的属性也会被初始化。所以我们在这里可以继续设置其基本属性。

    之后如果当前配置文件设置的日志级别是debug及以下,会在控制台打印日志,代表当前容器进入刷新阶段,具体代码如下:

    1. this.startupDate = System.currentTimeMillis();
    2. this.closed.set(false);
    3. this.active.set(true);
    4. if (this.logger.isDebugEnabled()) {
    5. if (this.logger.isTraceEnabled()) {
    6. this.logger.trace("Refreshing " + this);
    7. } else {
    8. this.logger.debug("Refreshing " + this.getDisplayName());
    9. }
    10. }

    1.2 根据容器中environment初始化servlet参数

    this.initPropertySources()

    这一句代码就是初始化environment的部分参数属性,主要与servlet相关。

    在IOC容器中参数environment初始化的过程中,会设置2个标准的servlet参数:

    servletContextInitParams、servletConfigInitParams

    其初始化代码示例为:

    1. protected void customizePropertySources(MutablePropertySources propertySources) {
    2. propertySources.addLast(new StubPropertySource("servletConfigInitParams"));
    3. propertySources.addLast(new StubPropertySource("servletContextInitParams"));

    而在我们这一步的初始化过程中, 也是先获取到IOC容器的environment参数,之后执行其servlet专用的初始化方法。如果传入的servlet参数有值,则对上面的初始化参数值进行替换。

    但是根据这一步中initPropertySources方法的具体实现,其传入的servlet参数均为空,所以我们认为这一步保持environment自己的初始值不变:

    1. protected void initPropertySources() {
    2. ConfigurableEnvironment env = this.getEnvironment();
    3. if (env instanceof ConfigurableWebEnvironment) {
    4. ((ConfigurableWebEnvironment)env).initPropertySources(this.servletContext, (ServletConfig)null);
    5. }

    1.3 校验environment参数

    this.getEnvironment().validateRequiredProperties();

    主要作用就是校验environment中key-value形式的参数配置中,有些必输参数的value是否为空,如果为空会抛出运行时异常。

    1.4 初始化监听器和监听事件

    在前文执行prepareContext方法的过程中,在加载初始化器和prepareContext最后一步执行监听器方法的时候,会分别把SpringApplication实例化过程中通过spring.factories配置文件加载和代码直接加载的监听器都添加到IOC自己的监听器中,也就是其参数

    private final Set> applicationListeners

    在这里,会把监听器和监听事件进一步处理:

    1. if (this.earlyApplicationListeners == null) {
    2. this.earlyApplicationListeners = new LinkedHashSet(this.applicationListeners);
    3. } else {
    4. this.applicationListeners.clear();
    5. this.applicationListeners.addAll(this.earlyApplicationListeners);
    6. }
    7. this.earlyApplicationEvents = new LinkedHashSet();

    2 获取beanFactory

    ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

    其实质是调用GenericApplicationContext中的方法执行了两步操作:

    第一步,通过原子类AtomicBoolean中的cas方法,控制IOC容器只能刷新一次,否则就抛出异常。

    1. protected final void refreshBeanFactory() throws IllegalStateException {
    2. //第一步
    3. if (!this.refreshed.compareAndSet(false, true)) {
    4. throw new IllegalStateException("GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    5. } else {
    6. //第二步
    7. this.beanFactory.setSerializationId(this.getId());
    8. }
    9. }

    第二步,GenericApplicationContext就是我们实例化的IOC容器的父类,所以其beanFactory就是前面在IOC容器实例化过程中新建的bean工厂对象。

    在这里,拿到当前beanFactory后,设置其序列id:serializationId,为当前容器名称,也就是yml文件中配置的spring.application.name参数,如果没有配置取默认值application。

    之后把serializationId和通过弱引用构建的当前beanFactory通过key-value的形式放入bean工厂类的一个map属性serializableFactories中:

    1. public void setSerializationId(@Nullable String serializationId) {
    2. if (serializationId != null) {
    3. serializableFactories.put(serializationId, new WeakReference(this));
    4. ......
    5. this.serializationId = serializationId;
    6. }

    3 准备beanFactory参数

    this.prepareBeanFactory(beanFactory);

     主要是设置一些beanFactory的属性,例如当前默认的类加载器、参数解析器、资源编辑相关属性、忽略的依赖类型、能够解析的依赖类型,还有注册了environment相关的实例到IOC容器中,例如beanName为environment、systemProperties、systemEnvironment及其实例。

    1. protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    2. beanFactory.setBeanClassLoader(this.getClassLoader());
    3. ......
    4. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    5. ......
    6. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    7. ......
    8. if (!beanFactory.containsLocalBean("environment")) {
    9. beanFactory.registerSingleton("environment", this.getEnvironment());
    10. }

    总的来说就是一些beanFacotry的set或者Map属性的put方法。

    4 设置servlet相关beanFactory参数

    this.postProcessBeanFactory(beanFactory)

    这其实是一个抽象方法,可以在子类中有不同的实现,可以灵活以多种方式的实现,来为beanFactory设置一些属性。具体在当前的启动方式,则是通过属性设置的方式,来把servlet的一些基本属性request、session通过scope属性的方式,设置到beanFactory的Map形式的 scopes参数中。

    同时也会设置一些servlet相关的bean的后置处理器、忽略的依赖类型、可以解析的依赖类型,以List或者Map的形式储存到beanFactory的对应属性中:

    1. protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    2. beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
    3. beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    4. this.registerWebApplicationScopes();
    5. }

    可以理解,这一步依然是一些准备工作,但是它和上面的准备工作不同,不是通用的,需要根据不同的子类来实现。

    5 执行beanFactory后置处理器

    this.invokeBeanFactoryPostProcessors(beanFactory);

     这一步,真正开始执行,在环境准备阶段加载的beanFactory后置处理器。

    核心功能便是下面这一个:

    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());

    5.1 获取beanFactory后置处理器

    在执行之前,首先会通过getBeanFactoryPostProcessors方法,获取容器中已有的bean的后置处理器(其实就是beanFactory的一个List属性),那么这些后置处理器哪来的呢?

    举个例子,在前面准备容器刷新阶段的方法中,当监听器ConfigFileApplicationListener执行到ApplicationPreparedEvent这一步的时候,会直接把当前监听器中的实现了BeanFactoryPostProcessor的内部类加载到IOC容器中:

    1. private void onApplicationPreparedEvent(ApplicationEvent event) {
    2. this.logger.switchTo(ConfigFileApplicationListener.class);
    3. this.addPostProcessors(((ApplicationPreparedEvent)event).getApplicationContext());
    4. }
    5. ......
    6. protected void addPostProcessors(ConfigurableApplicationContext context) {
    7. context.addBeanFactoryPostProcessor(new ConfigFileApplicationListener.PropertySourceOrderingPostProcessor(context));
    8. }

    这一步,获取到的beanFactory后置处理器如下:

    [org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor]

    可以看到,传入的beanFactory后置处理器一共有3个,分别是在环境准备阶段,初始化器加载的Initializer,和监听器加载的Listener。 

    5.2 执行beanFactory后置处理器方法 

    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());

    下面我们介绍具体的invokeBeanFactoryPostProcessors方法,由于这个方法非常长,先大概介绍下。

    先说明一下,上文传入的beanFactory的后置处理器参数其实包含两部分:bean定义信息注册器BeanDefinitionRegistry的后置处理器,和真正的beanFactory的后置处理器。

    看方法名,说的是执行beanFactory的后置处理器,其实在这之前,还需要先执行bean定义信息注册器BeanDefinitionRegistry的后置处理器方法,而且会执行多次。

    刚看到代码,如果不仔细看,会觉得很莫名其妙,感觉就是同样的代码执行了3次,但是如果仔细比较,还是能比较出不同的。

    具体的不同,主要是在收集用户执行的BeanDefinitionRegistry的后置处理器和beanFactory的后置处理器的时候。

    以BeanDefinitionRegistry的后置处理器为例,一边执行,一边会往beanFactory中加入新的信息,这样下一次需要执行的后置处理器会变多,因为归根结底,后置处理器还是需要从beanFactory中去加载。

    同时也会过滤掉已经执行过的后置处理器,避免重复执行。

    具体的表现形式为,例如BeanDefinitionRegistry的后置处理器在执行到某一阶段,会借助环境准备阶段加载到IOC容器中的著启动类(main方法类),以启动类为基准,通过其全类名获取到其所在的包名,然后扫描包下所有的类,构建一个项目中所有类的集合。

    从其中筛选合适的后置处理器,供下一步的BeanDefinitionRegistry的后置处理器调用。

    总而言之,就是每一次BeanDefinitionRegistry的后置处理器的调用都是为其下一次调用做准备。

    而所有的BeanDefinitionRegistry的后置处理器的调用,又都是为beanFactory的后置处理器调用做准备。

    而每一次的beanFactory的后置处理器调用调用,也是为下一次做准备。并不是无意义的代码重复。

    以上两点,就是执行这段方法的主线,要把握主线,才不至于被代码绕到云山雾罩。

    下面我们详细解读代码,为了便于理解,依旧分段解析。

    5.2.1 第一次执行BeanDefinitionRegistry的后置处理器

    先介绍2个重要参数:beanFactory后置处理器集合regularPostProcessors和BeanDefinitionRegistry的后置处理器集合registryProcessors。

    根据上一节,传入的三个beanFactory的后置处理器,需要进行分类,如果是BeanDefinitionRegistryPostProcessor类型的BeanDefinitionRegistry后置处理器,先执行此处理器的默认方法postProcessBeanDefinitionRegistry(),执行完成后把处理器加入集合registryProcessors中。

    如果beanFactory后置处理器的类型不是BeanDefinitionRegistryPostProcessor,则认为是普通的beanFactory后置处理器,加入集合regularPostProcessors:

    1. BeanFactoryPostProcessor postProcessor = (BeanFactoryPostProcessor)var6.next();
    2. if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
    3. BeanDefinitionRegistryPostProcessor registryProcessor = (BeanDefinitionRegistryPostProcessor)postProcessor;
    4. registryProcessor.postProcessBeanDefinitionRegistry(registry);
    5. registryProcessors.add(registryProcessor);
    6. } else {
    7. regularPostProcessors.add(postProcessor);
    8. }

    这一段的重点就是BeanDefinitionRegistry后置处理器执行postProcessBeanDefinitionRegistry()方法做了什么?

    下面分别介绍:

    CachingMetadataReaderFactoryPostProcessor作用

    属于SharedMetadataReaderFactoryContextInitializer的静态内部类,其执行后置处理器特定方法的作用有两个。

    首先,在IOC容器中,注册了一个特殊的beanName的bean的定义信息。

    其次,对IOC容器中已有的一个特殊beanName为internalConfigurationAnnotationProcessor(简写)的bean的定义信息进行了增强。

    和IOC容器实例化过程中,调用的AnnotationConfigUtils方法有点类似。

    这些初始化定义的beanName基本都以internal开头,代表IOC容器内具体有特殊意义的bean。

    1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    2. this.register(registry);
    3. this.configureConfigurationClassPostProcessor(registry);
    4. }

    ConfigurationWarningsPostProcessor作用

    属于ConfigurationWarningsApplicationContextInitializer中的静态final类,也是SpringApplication类实例化过程中加载的初始化器initializers,在执行其initializer方法加载进来的beanFactory后置处理器。

    在其特定的处理器执行方法中,这个作用只有一个,那就是打印告警信息。

    1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    2. ConfigurationWarningsApplicationContextInitializer.Check[] var2 = this.checks;
    3. int var3 = var2.length;
    4. for(int var4 = 0; var4 < var3; ++var4) {
    5. ConfigurationWarningsApplicationContextInitializer.Check check = var2[var4];
    6. String message = check.getWarning(registry);
    7. if (StringUtils.hasLength(message)) {
    8. this.warn(message);
    9. ......

    打印告警信息的步骤,主要分为两步:

    第一步,通过注入IOC容器中主启动类的注解信息,获取需要扫描的包名。

    第二步,检查包名是否有问题,主要确认包名不包含org和org.springframework,因为这是Spring框架专用的包名。

    1. public String getWarning(BeanDefinitionRegistry registry) {
    2. Set scannedPackages = this.getComponentScanningPackages(registry);
    3. List problematicPackages = this.getProblematicPackages(scannedPackages);
    4. return problematicPackages.isEmpty() ? null : "Your ApplicationContext is unlikely to start due to a @ComponentScan of " + StringUtils.collectionToDelimitedString(problematicPackages, ", ") + ".";
    5. }

    其具体实现为:

    第一步,因为目前容器中加载的bean定义信息不多,主要就是以internal开头的IOC容器内部的一些bean定义信息和启动类BookstoreApplication的bean定义信息。

    首先,筛选出bean的定义信息是注解类型——AnnotatedBeanDefinition的,只有主启动类满足要求,这样就获取了主启动类的所有bean定义信息。

    之后,把主启动类的注解信息进行层层解析,得到复合注解和元注解的集合,从中筛选出主启动类上标记为@ComponentScan的所有信息,如果没有标记@ComponentScan注解,取拆解后集合中@ComponentScan的默认属性。

    在获取@ComponentScan的过程中,其实和在前文容器环境准备阶段使用load方法时,我们选择使用注解还是xml来解析启动类的方法会有关联,在前文由于我们是注解方式启动,所以会用isComponent来校验主启动类是否有@Component注解。

    前文我们介绍了,如何通过解析启动类的复合注解,其实在解析完成后,会以key-value的形式,把启动类的注解信息,存放在key为"Packages annotation filter: java.lang.,org.springframework.lang."的Map缓存中,其实就是AnnotationTypeMappings中一个属性为final的静态Map,其名称为standardRepeatablesCache。

    那么在完成isComponent校验后,还会执行一个register方法,会把一个key为"No annotation filtering",value为启动类所有复合注解信息,注意还没有解析,放在具有同样属性的缓存noRepeatablesCache中。

    那么到了当前获取@ComponentScan的过程,就可以通过key"No annotation filtering",直接从缓存获取启动类复合注解,不过还需要重复isComponent校验的部分方法,就是通过AnnotationTypeMappings的实例化方法来解析复合注解,得到复合注解和元注解的集合返回。

    之后通过反射获取@ComponentScan注解的所有信息,就可以获取到启动类配置了哪些需要扫描的包,注意这里如果没有配置@ComponentScan注解,返回默认值,即启动类的包名,通过getComponentScanningPackages方法调用如下方法:

    1. private void addComponentScanningPackages(Set packages, AnnotationMetadata metadata) {
    2. //获取ComponentScan注解所有属性
    3. AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true));
    4. if (attributes != null) {
    5. this.addPackages(packages, attributes.getStringArray("value"));
    6. this.addPackages(packages, attributes.getStringArray("basePackages"));
    7. this.addClasses(packages, attributes.getStringArray("basePackageClasses"));
    8. if (packages.isEmpty()) {
    9. packages.add(ClassUtils.getPackageName(metadata.getClassName()));
    10. ......

    第二步,根据第一步获取的包名,校验是否满足包名不包含org和org.springframework的规范。

    5.2.2 第二次执行BeanDefinitionRegistry的后置处理器

    1. currentRegistryProcessors = new ArrayList();
    2. postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    3. String[] var16 = postProcessorNames;
    4. var9 = postProcessorNames.length;
    5. int var10;
    6. String ppName;
    7. for(var10 = 0; var10 < var9; ++var10) {
    8. ppName = var16[var10];
    9. if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    10. currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    11. processedBeans.add(ppName);
    12. }
    13. }
    14. sortPostProcessors(currentRegistryProcessors, beanFactory);
    15. registryProcessors.addAll(currentRegistryProcessors);
    16. invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    17. currentRegistryProcessors.clear();

    有个大概印象就行,重要步骤,下面会一步步解释: 

    postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);

    postProcessorNames是在方法进入后初始化的一个String数组,其实根据当前方法名称getBeanNamesForType就可以看出来,就是从beanFactory中根据给定的参数类型获取beanName的集合。

    这里beanName需要满足的条件就是其对应的class信息要是属于BeanDefinitionRegistryPostProcessor这个接口类型的,也就是beanName对应的class信息要实现这个接口,即可满足要求。

    知道了原理,就知道该怎么做了,那如果我们自己做会怎么做呢?

    其实最简单的可以从beanFactory的beanDefinitionMap属性中,获取所有的value,也就是bean的定义信息——RootBeanDefinition,可以从RootBeanDefinition中获取beanName对应的class信息,如果class是实现了BeanDefinitionRegistryPostProcessor接口的,就直接返回。

    通过比对,我们最终会获取到postProcessorNames的值为:[org.springframework.context.annotation.internalConfigurationAnnotationProcessor]。

    因为在beanDefinitionMap映射中,它对应的class为ConfigurationClassPostProcessor,实现了接口BeanDefinitionRegistryPostProcessor。

    Spring实现

    上面我们说的只是一个大概的做法,那么Spring框架是如何实现这个方法的呢?

    第一步:

    Spring会根据getBeanNamesForType的参数设定,进入指定的方法,先获取到容器中所有的beanName的注册信息,然后遍历处理。

    1. private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
    2. List result = new ArrayList();
    3. Iterator var5 = this.beanDefinitionNames.iterator();

    第二步:

    在遍历过程中,首先会对bean的定义信息进行合并,可能同一个bean在多处进行了信息定义,会放入一个ConcurrentHashMap中,需要的时候去获取信息然后合并。

    RootBeanDefinition mbd = this.getMergedLocalBeanDefinition(beanName);

    第三步:

    完成bean信息合并后,会继续判断这个beanName对应的class是否属于FactoryBean接口类型,也就是是否实现了这个接口。

    boolean isFactoryBean = this.isFactoryBean(beanName, mbd);

    注意FactoryBean和beanFactory完全不一样,前者重点是一个bean,后者的重点是一个工厂Factory。FactoryBean的主要作用就是对bean中的实例进行定制化的管理,beanFactory则在于标准化的生产bean实例,二者是相辅相成的。

    由于还在容器的初始化阶段,容器内bean的实例极少,容器中定义的信息基本还是关于beanFactory的,所以beanName对应的class不属于FactoryBean类型

    第四步:

    根据beanName,bean定义信息,还有BeanDefinitionRegistryPostProcessor类型,判断是否满足要求。首先会从bean定义信息RootBeanDefinition中,根据beanName信息获取其对应的class类型,再判断其是否继承了接口BeanDefinitionRegistryPostProcessor。

    matchFound = this.isTypeMatch(beanName, type, allowFactoryBeanInit);

    满足要求,则返回beanName到集合postProcessorNames中,如果不满足,继续遍历获取下一个beanName。

    1. if (matchFound) {
    2. result.add(beanName);
    3. }

    Spring的实现方式和我们预想的大差不差,但是作为框架,它做了很多扩展、兼容方面的考虑,所以真正的方法还是会比较复杂。

    在获取到postProcessorNames后,会对其执行遍历,如果其中beanName对应的class实现了PriorityOrdered接口,则会把beanName对应的class进行实例化:

    1. for(var10 = 0; var10 < var9; ++var10) {
    2. ppName = var16[var10];
    3. if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    4. currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    5. processedBeans.add(ppName);
    6. }
    7. }

    这里,会把beanFactory生成的实例加入到currentRegistryProcessors集合中,为下一步执行beanDefinitionRegistry后置处理器做准备。

    beanName加入processedBeans集合,为下文防止重复执行做准备。

    在这里beanFactory.getBean方法也是很重要,但是不是这一章的重点,我们后面会重点讲到。

    但是我们可以说一下这个方法的作用,大概就是看这个beanName是否被实例化了,如果实例化过且需要获取单例实例,从缓存获取,否则生成新的单例实例。

    下面继续执行:

    1. sortPostProcessors(currentRegistryProcessors, beanFactory);
    2. registryProcessors.addAll(currentRegistryProcessors);

    对currentRegistryProcessors进行排序,然后加入registryProcessors集合中,registryProcessors是用于后面执行beanFactory的后置处理器。

    下面再进入到一个重要方法:

    invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

    执行currentRegistryProcessors中,也就是实例ConfigurationClassPostProcessor的beanDefinitionRegistry后置处理器的指定方法:

    1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    2. int registryId = System.identityHashCode(registry);
    3. ......
    4. } else {
    5. this.registriesPostProcessed.add(registryId);
    6. this.processConfigBeanDefinitions(registry);
    7. }
    8. }

    这里主要做了两件事,把registryId加入registriesPostProcessed集合,执行processConfigBeanDefinitions方法。

    重点在第二步其主要作用是根据主启动类的全类名,获取到当前项目中所有需要注册到IOC容器中的类,以及主启动类的注解@EnableAutoConfiguration,来加载一系列Spring框架提供的默认类,来注册到IOC容器中

    关于这一点,会开专门的章节详细讲解,我们需要知道的就是,它加载了一系列bean定义信息到容器后,为下面继续执行的方法做了准备。

    5.2.3 第三次执行BeanDefinitionRegistry的后置处理器

    1. postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    2. var16 = postProcessorNames;
    3. var9 = postProcessorNames.length;
    4. for(var10 = 0; var10 < var9; ++var10) {
    5. ppName = var16[var10];
    6. //和第二次执行BeanDefinitionRegistry后置处理器唯一不同,需要实现Ordered类
    7. if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
    8. currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    9. processedBeans.add(ppName);
    10. }
    11. }
    12. sortPostProcessors(currentRegistryProcessors, beanFactory);
    13. registryProcessors.addAll(currentRegistryProcessors);
    14. invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    15. currentRegistryProcessors.clear();

    有了前一次执行BeanDefinitionRegistry后置处理器方法讲解的铺垫,这一次执行就比较好理解了。

    首先会从容器中获取bean的类型为BeanDefinitionRegistryPostProcessor.class的beanName,然后遍历这些beanName,进入遍历循环后,通过processedBeans集合判断当前beanName的后置处理器是否已被执行过,只有未被执行过且实现了Ordered接口的后置处理器才会被加入currentRegistryProcessors集合中。

    注意currentRegistryProcessors在上一步已被清空。

    同时这个满足条件的处理器的beanName也会被加入processedBeans集合,防止下次重复执行。

    之后就是排序,再把新获取到的BeanDefinitionRegistry后置处理器集合currentRegistryProcessors加入到registryProcessors,为beanFactory的后置处理器执行做准备。

    之后便是最重要的,第三次执行BeanDefinitionRegistry后置处理器指定方法。

    通过第二次的执行BeanDefinitionRegistry后置处理器——ConfigurationClassPostProcessor类中的方法,我们在IOC容器中注册了当前工程中,主启动类下所有的自定义class文件,以及Spring框架自带的默认的自动装配类。

    所以这一次需要执行的BeanDefinitionRegistry后置处理器,理论上会比上一次多,又由于processedBeans集合的过滤作用,只会执行新增的BeanDefinitionRegistry后置处理器的指定方法。

    所以关键便在于新增了哪些后置处理器,可以先看下这次加载的postProcessorNames:

    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration$TokenKeyEndpointRegistrar

    可以看到,由于internalConfigurationAnnotationProcessor(IOC容器实例化过程中,注册的一批以internal开头的内部beanName)是ConfigurationClassPostProcessor类的beanName,已经被执行过。

    新增了AuthorizationServerEndpointsConfiguration中的静态内部类TokenKeyEndpointRegistrar,但是其只是实现了BeanDefinitionRegistryPostProcessor接口,并不满足同时还要实现Ordered接口的条件。

    所以这次的需要执行的currentRegistryProcessors集合为空,跳过。

    5.2.4 第四次执行BeanDefinitionRegistry的后置处理器

    1. boolean reiterate = true;
    2. while(reiterate) {
    3. reiterate = false;
    4. postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    5. String[] var19 = postProcessorNames;
    6. var10 = postProcessorNames.length;
    7. for(int var26 = 0; var26 < var10; ++var26) {
    8. String ppName = var19[var26];
    9. if (!processedBeans.contains(ppName)) {
    10. currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
    11. processedBeans.add(ppName);
    12. reiterate = true;
    13. }
    14. }
    15. sortPostProcessors(currentRegistryProcessors, beanFactory);
    16. registryProcessors.addAll(currentRegistryProcessors);
    17. invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
    18. currentRegistryProcessors.clear();
    19. }

    可以看到,其实除了第一次执行BeanDefinitionRegistry后置处理器,是边判断边执行,后三次执行BeanDefinitionRegistry后置处理器的方法很类似,只不过第二次执行的的需要继承PriorityOrdered接口,第三次需要继承Ordered接口。

    等到第四次的时候,由于又while循环的存在,只要是BeanDefinitionRegistry后置处理器,且没有被执行过,就可以一直执行下去。

    了解了这些,其实我们就可以完全自定义我们需要的任何BeanDefinitionRegistry后置处理器,同时还能指定它的执行顺序。

    其实Springboot的所有设计思想,都藏在它的源码里,所以说学习Spring最好的方法,还是看源码。

    由于第四次只是新增了一个TokenKeyEndpointRegistrar的BeanDefinitionRegistry后置处理器实现类,所以当前while循环只会执行一次,其执行方法为:

    1. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    2. this.registry = registry;
    3. }

    简单来说,就是把IOC容器注入到了TokenKeyEndpointRegistrar这个类中,供后续处理。

    5.2.5 第一次执行BeanFactory的后置处理器

    这这一部分代码的最后,我们终于可以执行BeanFactory的后置处理器了:

    1. invokeBeanFactoryPostProcessors((Collection)registryProcessors, (ConfigurableListableBeanFactory)beanFactory);
    2. invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

    可以看到,其实是同样的方法,就是参数不同,也就是说指向的beanFactory后置处理器有优先级之分,先执行registry类型的,再执行regular类型的。

    先看看registtry类型的后置处理器有哪些:

    [org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$CachingMetadataReaderFactoryPostProcessor, org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer$ConfigurationWarningsPostProcessor, org.springframework.context.annotation.ConfigurationClassPostProcessor, org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration$TokenKeyEndpointRegistrar]

    前两个beanFactory后置处理器的方法为空,所以重要的在第三、四个后置处理器的方法:

    ConfigurationClassPostProcessor作用

    1. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    2. //获取容器id
    3. int factoryId = System.identityHashCode(beanFactory);
    4. if (this.factoriesPostProcessed.contains(factoryId)) {
    5. //如果容器id加入过集合factoriesPostProcessed报错
    6. throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);
    7. } else {
    8. //只有没有被加入过集合factoriesPostProcessed的容器id,才能加入集合
    9. this.factoriesPostProcessed.add(factoryId);
    10. if (!this.registriesPostProcessed.contains(factoryId)) {
    11. //如果没有被加入过BeanDefinitionRegistry后置处理器,需要重新扫描当前工程主启动类下所有class文件,及Spring的默认加载class文件,并把bean信息注册到beanFactory中
    12. this.processConfigBeanDefinitions((BeanDefinitionRegistry)beanFactory);
    13. }
    14. //使用cglib代理增强beanFactory,可以使用类似AOP的拦截器
    15. this.enhanceConfigurationClasses(beanFactory);
    16. //添加一个新的beanFactory后置处理器
    17. beanFactory.addBeanPostProcessor(new ConfigurationClassPostProcessor.ImportAwareBeanPostProcessor(beanFactory));
    18. }
    19. }

     可以看到其主要作用就是在方法enhanceConfigurationClasses中,使用cglib代理增强了beanFactory,和添加了一个beanFactory的后置处理器。

    如果仔细追踪cglib代理增强,可以看到其实是对beanFactory中,满足一定条件的bean进行增强,满足什么条件呢?如果bean定义信息中的如下属性值为“full”,则放入一个新的map中,在后面进行集中增强:

    org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass

    且不能加有@Configuration注解,也不能是一个静态方法:

    1. if ("full".equals(configClassAttr)) {
    2. if (!(beanDef instanceof AbstractBeanDefinition)) {
    3. throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" + beanName + "' since it is not stored in an AbstractBeanDefinition subclass");
    4. }
    5. if (this.logger.isInfoEnabled() && beanFactory.containsSingleton(beanName)) {
    6. this.logger.info("Cannot enhance @Configuration bean definition '" + beanName + "' since its singleton instance has been created too early. The typical cause is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor return type: Consider declaring such methods as 'static'.");
    7. }
    8. configBeanDefs.put(beanName, (AbstractBeanDefinition)beanDef);
    9. }

    之后,便是循环遍历map,来对满足条件的bean进行增强:

    1. if (!configBeanDefs.isEmpty()) {
    2. ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
    3. Iterator var14 = configBeanDefs.entrySet().iterator();
    4. while(var14.hasNext()) {
    5. Entry entry = (Entry)var14.next();
    6. AbstractBeanDefinition beanDef = (AbstractBeanDefinition)entry.getValue();
    7. beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
    8. Class configClass = beanDef.getBeanClass();
    9. Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);

     TokenKeyEndpointRegistrar作用

    主要是利用传入的beanFactory参数,来获取JwtAccessTokenConverter的继承类,并把获取到的类的定义信息注册到IOC容器中。

    注意这里的registry是在前文中,TokenKeyEndpointRegistrar的BeanDefinitionRegistry后置处理器方法中被注入的。

    1. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    2. String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, JwtAccessTokenConverter.class, false, false);
    3. if (names.length > 0) {
    4. BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
    5. builder.addConstructorArgReference(names[0]);
    6. this.registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
    7. }
    8. }

    5.2.6 第二次执行BeanFactory的后置处理器

    invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

    执行regularPostProcessors集合中的beanFactory后置处理器。

    此时regularPostProcessors集合数据为:

    org.springframework.boot.context.config.ConfigFileApplicationListener$PropertySourceOrderingPostProcessor

     PropertySourceOrderingPostProcessor作用

    主要是对容器中environment参数中存在的defaultProperties属性值进行重排序,排到最后。

    1. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    2. this.reorderSources(this.context.getEnvironment());
    3. }
    4. private void reorderSources(ConfigurableEnvironment environment) {
    5. PropertySource defaultProperties = environment.getPropertySources().remove("defaultProperties");
    6. if (defaultProperties != null) {
    7. environment.getPropertySources().addLast(defaultProperties);
    8. }
    9. }

    5.2.7 上半部分代码总结

    1. public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
    2. Set processedBeans = new HashSet();
    3. ArrayList regularPostProcessors;
    4. ArrayList registryProcessors;
    5. int var9;
    6. ArrayList currentRegistryProcessors;
    7. String[] postProcessorNames;
    8. if (beanFactory instanceof BeanDefinitionRegistry) {
    9. //5.2节前面所有流程
    10. .......
    11. } else {
    12. invokeBeanFactoryPostProcessors((Collection)beanFactoryPostProcessors, (ConfigurableListableBeanFactory)beanFactory);
    13. }

    可以看到,在类PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors方法中,我们在前面讲到的所有5.2节中的内容,都是if条件满足后执行的方法。

    也就是说,前面方法执行的基础就是当前的beanFactory是BeanDefinitionRegistry类型的,才会先执行BeanDefinitionRegistry类型的后置处理器,再执行beanFactory的后置处理器。

    否则,会直接执行所有的beanFactory的后置处理器方法。

    5.2.8 第三次执行BeanFactory的后置处理器

    1. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    2. regularPostProcessors = new ArrayList();
    3. registryProcessors = new ArrayList();
    4. currentRegistryProcessors = new ArrayList();
    5. postProcessorNames = postProcessorNames;
    6. int var20 = postProcessorNames.length;
    7. String ppName;
    8. for(var9 = 0; var9 < var20; ++var9) {
    9. ppName = postProcessorNames[var9];
    10. if (!processedBeans.contains(ppName)) {
    11. if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    12. regularPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    13. } else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    14. registryProcessors.add(ppName);
    15. } else {
    16. currentRegistryProcessors.add(ppName);
    17. }
    18. }
    19. }
    20. sortPostProcessors(regularPostProcessors, beanFactory);
    21. invokeBeanFactoryPostProcessors((Collection)regularPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

    执行完所有的BeanDefinitionRegistry类型的后置处理器方法,再执行了两次beanFactory的后置处理器方法。

    我们终于进入invokeBeanFactoryPostProcessors这个方法的下半部分,又要开始执行beanFactory的后置处理器方法。

    注意这里的beanFactory的后置处理器获取方式和前面的获取方式是不同的:

    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);

    这里是按类型BeanFactoryPostProcessor类型去获取的,而前面的beanFactory的后置处理器是按BeanDefinitionRegistryPostProcessor类型去获取的。

    这也是为什么前面的beanFactory的后置处理器BeanDefinitionRegistry类型的后置处理器有的都归属一个类,前面先执行了BeanDefinitionRegistry类型的方法,为下一次执行beanFactory的后置处理器做了准备。

    设计的也是很巧妙。

    进入到这里呢,真正执行第三次beanFactory的后置处理器的前面,也和前面的BeanDefinitionRegistry后置处理器一样,区分成了三类:PriorityOrdered、Ordered、current,按优先级做了分类。那显然它们也是依次按顺序执行了。

    在这里我们可以看到满足PriorityOrdered条件的后置处理器没有,略过这一段。

    5.2.9 第四次执行BeanFactory的后置处理器

    1. List orderedPostProcessors = new ArrayList(registryProcessors.size());
    2. Iterator var21 = registryProcessors.iterator();
    3. while(var21.hasNext()) {
    4. String postProcessorName = (String)var21.next();
    5. orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    6. }
    7. sortPostProcessors(orderedPostProcessors, beanFactory);
    8. invokeBeanFactoryPostProcessors((Collection)orderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

    执行继承了Ordered接口的后置处理器方法,由于本项目使用的是JPA持久层框架,这一步会把JPA相关的类注册到容器中,获取到的后置处理器为:

    1. org.springframework.data.jpa.repository.support.EntityManagerBeanDefinitionRegistrarPostProcessor
    2. org.springframework.boot.context.properties.ConfigurationPropertiesBeanDefinitionValidator

    其后置处理器方法作用分别为:

    EntityManagerBeanDefinitionRegistrarPostProcessor作用

    通过JPA框架中指定的工具类BeanDefinitionUtils,获取预先定义好的一系列类信息,结合传入的IOC容器的factoryBean信息,依次构建EntityManagerFactoryBeanDefinition对象,生成bean的定义信息,最后注册到IOC容器中。

    这样在后续大规模的生成bean实例后,可以直接从容器中获取JPA的相关信息,此时注册的bean有:

    1. static {
    2. List> types = new ArrayList();
    3. types.add(EntityManagerFactory.class);
    4. types.add(AbstractEntityManagerFactoryBean.class);
    5. if (ClassUtils.isPresent("org.springframework.jndi.JndiObjectFactoryBean", ClassUtils.getDefaultClassLoader())) {
    6. types.add(JndiObjectFactoryBean.class);
    7. }
    8. EMF_TYPES = Collections.unmodifiableList(types);
    9. }

    ConfigurationPropertiesBeanDefinitionValidator作用

    主要做yml配置方面的校验:

    1. private void validate(ConfigurableListableBeanFactory beanFactory, String beanName) {
    2. try {
    3. Class beanClass = beanFactory.getType(beanName, false);
    4. if (beanClass != null && BindMethod.forType(beanClass) == BindMethod.VALUE_OBJECT) {
    5. throw new BeanCreationException(beanName, "@EnableConfigurationProperties or @ConfigurationPropertiesScan must be used to add @ConstructorBinding type " + beanClass.getName());
    6. }

    5.2.10 第五次执行BeanFactory的后置处理器

    1. List nonOrderedPostProcessors = new ArrayList(currentRegistryProcessors.size());
    2. Iterator var24 = currentRegistryProcessors.iterator();
    3. while(var24.hasNext()) {
    4. ppName = (String)var24.next();
    5. nonOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
    6. }
    7. invokeBeanFactoryPostProcessors((Collection)nonOrderedPostProcessors, (ConfigurableListableBeanFactory)beanFactory);

    执行优先级最低的后置处理器方法:

    [org.springframework.context.event.EventListenerMethodProcessor, org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor, org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor]

    EventListenerMethodProcessor作用

    主要是从beanFactory中,获取EventListenerFactory的实现类,进行排序后,在放入当前类的集合eventListenerFactories中,为后续创建监听器做准备。注意这里的实现类应该是Spring利用默认加载路径自动装配进去的:

    1. public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    2. this.beanFactory = beanFactory;
    3. Map beans = beanFactory.getBeansOfType(EventListenerFactory.class, false, false);
    4. List factories = new ArrayList(beans.values());
    5. AnnotationAwareOrderComparator.sort(factories);
    6. this.eventListenerFactories = factories;
    7. }

    RsaKeyConversionServicePostProcessor作用

    用于ssl公钥私钥的解析,一般来说,知道就行。

    PreserveErrorControllerTargetClassPostProcessor作用

    给beanFactory中ErrorController实现类的属性preserveTargetClass,设置固定值。

    5.2.11 清理beanFatcory缓存

    beanFactory.clearMetadataCache();

    主要就是设置一些beanFactory的属性,和清空一些map信息。

    6 总结

    本章的重点在beanFactory的后置处理器执行原理,其实所谓的原理就是分别执行了4次BeanDefinitionRegistry后置处理器的指定方法,和5次beanFactory后置处理器指定方法。

    通过上文的源码解析,可以看到大致来说就是把所有的后置处理器分成了三类:PriorityOrdered最高优先级、Ordered高优先级和普通优先级,依次执行,如果需要自定义后置处理器,且要指定其执行顺序,实现对应的优先级接口即可。

    其中有几个需要注意的点:包括BeanDefinitionRegistry后置处理器第一次的执行和第二次执行,以及如何设置beanFactory后置处理器优先级高于PriorityOrdered。

    BeanDefinitionRegistry后置处理器第一次的执行

    可以认为主要是做一些准备工作,为后续需要用到的BeanDefinitionRegistry后置处理器ConfigurationClassPostProcessor设置属性,以及校验当前工程包名是否合法。

    BeanDefinitionRegistry后置处理器第二次的执行

    这里也是最重要的一步,其执行了ConfigurationClassPostProcessor处理器的指定方法,把当前工程中符合条件的类信息、Spring框架默认的类信息,全部注册进了IOC容器。为下一步执行做好了必须的信息准备。

    设置beanFactory后置处理器优先级高于PriorityOrdered

    通过源码可以知道,如果一个类同时实现了BeanDefinitionRegistryPostProcessor接口和BeanFactoryPostProcessor接口,它是可以在BeanFactoryPostProcessor的PriorityOrdered优先级之前执行的,这对我们了解其执行时机有重要意义。

  • 相关阅读:
    特产店自助下单寄件教程
    类与对象(二)----对象详解
    linux 文件系统命令
    关于k8s集群中连接数不够,内存溢出的20道高级面试题
    ctfshow-web入门-php特性(web109-web115)
    Cmake的安装与使用
    上线后出现问题,被自己坑了...
    Nest.js 入门基础
    Python转换文件夹中的图片格式
    清水模板和混水模板的区别是什么?
  • 原文地址:https://blog.csdn.net/bigbearxyz/article/details/126599078