再谈Spring BeanPostProcessor

作者: jwfy | 来源:发表于2018-07-03 22:17 被阅读38次

    之前已经就spring中的BeanPostProcessor使用方法以及其实现细节谈论过,现在从更加宏观、抽象的角度去理解spring的bpp具体是如何工作的,现在spring自身有多少bpp,如果我们有自定义的bpp需求,应该如何实现。

    其中如下demo的代码位置 GitHub simple-spring

    目录

    再谈Spring BeanPostProcessor
    1、BeanPostProcessor 种类
    1.1、BeanPostProcessor 接口
    1.2、InstantiationAwareBeanPostProcessor 接口
    1.3、MergedBeanDefinitionPostProcessor 接口
    2、源码学习
    2.1、实例化前的提前处理
    2.2、实例化后的合并
    2.3、实例化后的数据填充
    2.4、初始化init方法
    3、Demo

    1、BeanPostProcessor 种类

    image

    如上图,是在IDEA中使用control+H命令显示出来的BeanPostProcessor的继承实现类关系图,重点介绍BeanPostProcessorInstantiationAwareBeanPostProcessorMergedBeanDefinitionPostProcessor三个接口,后面再继续结合实际的实现类,分析spring是如何操作BeanPostProcessor对象的

    阅读这里面的源码需要对Spring 源码有一定的理解,具体的可看【目录】Spring 源码学习

    1.1、BeanPostProcessor 接口

    public interface BeanPostProcessor {
        Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
        // 在调用bean的init方法(非构造方法)前调用
        // beanName 是在Spring IOC 容器的bean 名称,返回的对象需要会被直接使用
        // 切记!!!默认返回bean即可,不要无缘无故返回null,会出现各种NPE的情况
        Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
        // 在调用bean的init方法(非构造方法)前调用
    }
    

    1.2、InstantiationAwareBeanPostProcessor 接口

    public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
        Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException;
        // 在对象实例化之前被调用,传入的参数是类型以及bean的那么
        // 如果有返回非空的对象,则意味着不需要调用doCreate操作完成对象实例化等
        // 同时返回非空后会调用postProcessAfterInitialization方法
        // 注意看,不是postProcessAfterInstantiation而是postProcessAfterInitialization方法
        boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException;
         // 返回的是boolean类型,并不是和postProcessBeforeInstantiation配套使用的,在正常的实例化之后
         // 主要功能是判断是否需要完成对象填充
         // 如果返回false,则意味着不会完成对象属性的填充,例如@Resource导入的对象还是为null
        PropertyValues postProcessPropertyValues(
                PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException;
        // 提供给已经实例化的对象一种可以自定义完成对象pv属性的修改操作
        // 注意不要随意返回null,一旦返回null,不会进行对象填充、对象依赖等操作
    }
    

    1.3、MergedBeanDefinitionPostProcessor 接口

    public interface MergedBeanDefinitionPostProcessor extends BeanPostProcessor {
        void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName);
        // 在完成bean的实例化之后,填充数据(populateBean)之前,可自定义的修改beanDefinition内容
        // 名字成为merge,合并BPP对象和对于的beanDefinition的内容
        // 实际上可以完成任何想任何实现的功能
    }
    

    以上的三个接口基本上能够覆盖所有的BPP特定的功能点,从上面图也可以看出来,spring、springboot很多模块都有相应的对象实现,完成特定的功能。

    2、源码学习

    2.1、实例化前的提前处理

    直接定位到AbstractAutowireCapableBeanFactory类的createBean方法,主要介绍的是InstantiationAwareBeanPostProcessor

    try {
        Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
        if (bean != null) {
              // 如果bean有用,则会直接返回,不会再继续执行doCreateBean方法
            return bean;
        }
    }
    catch (Throwable ex) {
        throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                "BeanPostProcessor before instantiation of bean failed", ex);
        // bean创建失败,在初始化之前错误了
    }
    
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                  // 如果存在InstantiationAwareBeanPostProcessor对象
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    // 调用的是postProcessBeforeInstantiation方法
                    if (bean != null) {
                          // 如果返回的数据不为null,则调用的是postProcessAfterInitialization方法
                          // 注意别看错了,一个是实例化Instantiation,一个是初始化Initialization
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
            // 记录下是否通过提前批次的处理(如果bean不为null,则肯定是)
        }
        // 最后返回结果
        return bean;
    }
    

    其意思就是利用在实例化之前检查是否存在合适的InstantiationAwareBeanPostProcessor对象,去拦截某些需要被处理的bean提前完成bean的实例化过程,不会去调用init方法,也没有数据的填充,@Resource对象的引入等操作。

    2.2、实例化后的合并

    实例化后,仅仅是完成了对象最基础的实例化工作,还未涉及到填充数据,init方法执行等操作,主要介绍的是MergedBeanDefinitionPostProcessor

    synchronized (mbd.postProcessingLock) {
        if (!mbd.postProcessed) {
            try {
                  // 合并管理bean
                applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Post-processing of merged bean definition failed", ex);
            }
            mbd.postProcessed = true;
        }
    }
    
    protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof MergedBeanDefinitionPostProcessor) {
                  // 遍历所有的BeanPostProcessor
                  // 筛选出属于MergedBeanDefinitionPostProcessor的对象,调用postProcessMergedBeanDefinition
                MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
                bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
            }
        }
    }
    

    2.3、实例化后的数据填充

    主要的操作在populateBean方法中,涉及到的也是InstantiationAwareBeanPostProcessor

    再次强调InstantiationAwareBeanPostProcessor接口不仅仅是用在实例化前,在实例化之后也同样有用

    boolean continueWithPropertyPopulation = true;
    // 设置可以继续设置pv值的boolean对象为true
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                  // 如果是InstantiationAwareBeanPostProcessor对象
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                      // 调用postProcessAfterInstantiation
                      // 如果返回false意味着不需要填充pv操作了,直接break
                    continueWithPropertyPopulation = false;
                    break;
                }
            }
        }
    }
    
    if (!continueWithPropertyPopulation) {
         // 上文设置为false后,直接返回,完全忽略后续的applyPropertyValues操作
        return;
    }
    
    if (hasInstAwareBpps || needsDepCheck) {
        PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
        if (hasInstAwareBpps) {
            for (BeanPostProcessor bp : getBeanPostProcessors()) {
                if (bp instanceof InstantiationAwareBeanPostProcessor) {
                    InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                    // 对bean对象的pv值进行处理操作
                    if (pvs == null) {
                          // 如果返回为null,则直接返回
                          // 所以在自定义设置BPP的时候必须注意该方法的返回值
                          // 通过ide自动生成的对象,其默认返回值是null
                        return;
                    }
                }
            }
        }
        if (needsDepCheck) {
             // 检查对象的依赖问题
            checkDependencies(beanName, mbd, filteredPds, pvs);
        }
    }
    

    2.4、初始化init方法

    在spring中是可以设置对象的init方法,在对象实现之后,在initializeBean方法中,如下的代码片段

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 调用postProcessBeforeInitialization
    }
    
    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
        // 动态代理反射invoke调用初始化方法
        // 就是在这里获取到bean的init方法调用信息,invoke调用
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        // 唯一一个被调用了2次的地方的方法postProcessAfterInitialization
    }
    
    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }
    
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {
        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (result == null) {
                  // 都有个这样的操作,返回为null,则退出
                return result;
            }
        }
        return result;
    }
    

    3、Demo

    public class CustomInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
    
        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
            if (beanName.equals("student")) {
                Student student = new Student("bpp", 20);
                return student;
            }
    
            if (beanName.equals("superStudent")) {
                SuperStudent studentAndTeacher = new SuperStudent();
                studentAndTeacher.setName("zhangsan");
                return studentAndTeacher;
            }
    
            return null;
        }
    
        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
            return true;
        }
    
        public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
            //return pvs;
            return null;
        }
    
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    }
    
    public class SuperStudent {
    
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Resource
        private Teacher teacher;
    
        public void doInit() {
            System.out.println("superstudent init");
        }
    
        public void doSet() {
            if (teacher != null) {
                teacher.setAge(19);
                teacher.setName("superStudent");
                System.out.println(teacher.toString());
            } else {
                System.out.println("teacher is null");
            }
        }
    
        @Override
        public String toString() {
            return "SuperStudent{" +
                    "name='" + name + '\'' +
                    ", teacher=" + teacher +
                    '}';
        }
    }
    
    public static void runBPP() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
                new String[]{"context-bpp.xml"});
        SuperStudent student = (SuperStudent) applicationContext.getBean("superStudent");
        Teacher teacher = (Teacher) applicationContext.getBean("teacher");
        System.out.println(teacher.toString());
        System.out.println(student.toString());
        student.doSet();
        System.out.println(teacher.toString());
    }
    

    如下图几种样例的代码

    • 返回pv为null
      image

    很清楚在SuperStudent的对象是null,并没有完成填充操作,但是调用了init方法

    • 返回pv为原值,不作任何改变
      image

    完成了填充操作,而且这个SuperStudent的teacher对象是和Spring IOC 容器内的保持一致

    • 设置postProcessBeforeInstantiation
      image

    SuperStudent返回输出的对象,teacher没有值,而且也没有调用其init的方法

    相关文章

      网友评论

        本文标题:再谈Spring BeanPostProcessor

        本文链接:https://www.haomeiwen.com/subject/ahqsuftx.html