• Spring注解驱动之BeanFactoryPostProcessor原理


    概述

    我们现在来学习一下Spring里面的一些扩展原理,希望大家通过这些原理的学习,对Spring里面的运行机制,包括其内部的工作原理,能有一个非常深刻的认识,为以后学习Spring里面的其他框架会有较大的帮助。

    BeanFactoryPostProcessor的调用时机

    BeanFactoryPostProcessor其实就是BeanFactory(创建bean的工厂)的后置处理器。
    看到BeanFactoryPostProcessor会联想到BeanPostProcessor,之前说过它是bean的后置处理器,并且是在bean创建对象初始化前后进行拦截工作的。
    在这里插入图片描述
    看完接口上的描述后,我们可以指定BeanFactoryPostProcessor的调用时机。意思是在IOC容器的BeanFactory标准初始化完成之后,修改IOC容器里面的BeanFactory。
    什么是标准初始化么?后面描述是所有的bean定义已经被加载了,但是还没有bean被初始化。
    总结:BeanFactoryPostProcessor的调用时机是在BeanFactory标准化之后,我们可以定制、修改BeanFactory里面的一些内容,此时,所有的bean定义已经被加载到BeanFactory中了,但是bean的实例还没创建。

    案例实践

    首先编写一个类实现BeanFactoryPostProcessor接口。

    package com.meimeixia.ext;
    
    import java.util.Arrays;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.stereotype.Component;
    
    @Component
    public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    
    	@Override
    	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    		System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory..."); // 这个时候我们所有的bean还没被创建
    		                                                                              // 但是我们可以看一下通过Spring给我们传过来的这个beanFactory,我们能拿到什么
    		int count = beanFactory.getBeanDefinitionCount(); // 我们能拿到有几个bean定义
    		String[] names = beanFactory.getBeanDefinitionNames(); // 除此之外,我们还能拿到每一个bean定义的名字
    		System.out.println("当前BeanFactory中有" + count + "个Bean");
    		System.out.println(Arrays.asList(names));
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    注意,我们自己编写的MyBeanFactoryPostProcessor类要想让Spring知道,并且还要能被使用起来,那么它一定就得被加到容器中,为此,我们可以在其上标注一个@Component注解。
    然后创建一个配置类,例如ExtConfig,在该配置类上使用@ComponentScan注解来配置包扫描。

    package com.meimeixia.ext;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    import com.meimeixia.bean.Blue;
    
    @ComponentScan("com.meimeixia.ext")
    @Configuration
    public class ExtConfig {
    	@Bean
    	public Blue blue() {
    		return new Blue();
    	}	
    }
    
    package com.meimeixia.bean;
    
    public class Blue {
    
    	public Blue() {
    		System.out.println("blue...constructor");
    	}
    	
    	public void init() {
    		System.out.println("blue...init...");
    	}
    	
    	public void destory() {
    		System.out.println("blue...destory...");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    编写测试类

    package com.meimeixia.test;
    
    import org.junit.Test;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import com.meimeixia.ext.ExtConfig;
    
    public class IOCTest_Ext {
    	
    	@Test
    	public void test01() {
    		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ExtConfig.class);
    		// 关闭容器
    		applicationContext.close();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    接下来测试下BeanFactoryPostProcessor的调用时机。
    在这里插入图片描述在这里插入图片描述
    我们看到自己编写的BeanFactoryPostProcessor在Blue类的无参构造器创建Blue对象之前就已经工作了。我们看看到Blue组件注册到容器中的名字,只是此刻还没有创建对象。
    说明BeanFactoryPostProcessor是在所有的bean定义信息都被加载之后才调用的。

    源码分析

    在这里插入图片描述
    鼠标单击Eclipse左上角方法调用栈中的IOCTest_Ext.test01() line:12,这时程序来到了IOCTest_Ext类的test01方法中,如下图所示。
    在这里插入图片描述
    继续跟进代码,可以看到创建IOC容器时,最后还得刷新容器,如下图所示。
    在这里插入图片描述
    继续跟进代码,可以看到在刷新容器的过程中,还得执行在容器中注册的BeanFactoryPostProcessor(BeanFactory的后置处理器)的方法。
    在这里插入图片描述
    那具体是怎么来执行BeanFactoryPostProcessor的呢?我们继续跟进代码,发现又调用了一个invokeBeanFactoryPostProcessors方法,如下图所示。
    在这里插入图片描述
    在这里插入图片描述
    下面我们来仔细分析一下PostProcessorRegistrationDelegate类中的invokeBeanFactoryPostProcessors方法具体都做了哪些操作。
    在这里插入图片描述
    会发现其遍历了所有的BeanFactoryPostProcessor组件,我们自己编写的实现了BeanFactoryPostProcessor接口的MyBeanFactoryPostProcessor类肯定也属于其中,所以会被遍历到,然后便会执行其postProcessBeanFactory方法。
    在这里插入图片描述

    小结

    经过源码分析,我们可以得出这样一个结论:首先从IOC容器中找到所有类型是BeanFactoryPostProcessor的组件,然后再来执行它们其中的方法,而且是在初始化创建其他组件前面执行。
    为什么在初始化其他组件前面执行的呢,之前我们分析AOP原理是,bean的初始化是放在finishBeanFactoryInitialization(beanFactory)方法执行的。
    在这里插入图片描述

    参考

    Spring注解驱动开发第36讲——或许,这是你以前没看过的从源码角度理解BeanFactoryPostProcessor的原理

  • 相关阅读:
    SOEM 源码解析 ecx_config_find_mappings
    最长字段和问题
    Netty(二)NIO-入门
    Auto.js java布局 转 UI 布局,自己定义uii.layout()
    ArcMap影像量取面积大于CAD规划图面积
    setup里面 计算属性与监视
    解决QT中文乱码
    【openwrt学习笔记】新patch的制作和旧patch的修改
    linux 硬盘坏道检测
    修改element-UI组件样式
  • 原文地址:https://blog.csdn.net/tianzhonghaoqing/article/details/126914049