SpringBoot/Spring扩展点系列之使用InstantiationAwareBeanPostProcessor模拟AOP实现 - 第429篇

导读

在前面的章节,对于bean的定义,bean工厂的扩展点进行了讲解,这一节对于bean的生命周期的几个点进行讲解,这一块对于要扩展特别的重要,可以做很多的事情,会花比较大的篇幅进行详细的讲解。

SpringBoot/Spring扩展点系列:

(1)✅《SpringBoot/Spring扩展点之初出茅庐ApplicationContextInitializer》

(2)✅《SpringBoot/Spring扩展点之略有小成BeanDefinitionRegistryPostProcessor》

(3)✅《SpringBoot/Spring扩展点系列之叱咤风云BeanFactoryPostProcessor》

(4)《SpringBoot/Spring扩展点系列之使用InstantiationAwareBeanPostProcessor模拟AOP实现》

(5)待定

这一节就来看一下:

《SpringBoot/Spring扩展点系列之使用InstantiationAwareBeanPostProcessor模拟AOP实现》

一、基本概念

1.1是什么?

InstantiationAwareBeanPostProcessor是什么?

InstantiationAwareBeanPostProcessor是 BeanPostProcessor 的子接口,它添加了实例化之前的回调,以及在实例化之后但设置了显式属性或发生自动装配之前的回调。

1.2提供了什么入口?

直接看下接口:

从这里可以看出总共有5个方法。在具体介绍每个方法的作用之前,先给大家梳理一个两个概念:Spring中的实例化和初始化

(1)实例化:实例化的过程是一个创建Bean的过程,即调用Bean的构造函数,单例的Bean放入单例池中。

(2)初始化:初始化的过程是一个赋值的过程,即调用Bean的setter,设置Bean的属性,还有就是init-method。

我们先来理解一下,在java中对于一个对象的创建:

创建就是new的过程,调用构造方法,初始化就是对属性进行复制。

作为Sprig而言,每个点都可以有一个扩展的地方,构建对象之前,创建对象之后,初始化、属性的赋值,那么就有几个扩展方法:

(1)postProcessBeforeInstantiation:

实例化bean之前,相当于new这个bean之前。

耶,bean还没构建出来,那我能干啥呢,Spring不会脑壳短路了吧。存在就有存在的道理,还真的很有用,举个栗子:Mybatis的Mapper,它只有接口,没有实现类的构造方法,postProcessBeforeInstantiation就是在spring使用构造器帮你创建bean之前创建对象的,你可以创建一个代理对象返回,那么就可以实现自定义实例化对象了。

BTW:这个方法如果不需要处理的话,返回null即可,在AbstractAutowireCapableBeanFactory中的createBean方法

从这里可以看出,如果你返回了一个你自己创建的bean的话,那么之后的代码就不会执行了。

一句话:给BeanPostProcessor一个机会去返回一个代理对象,就是在流水线doCreateBean()生成对象之前, 给用户自定义返回一个对象的机会。

(2)postProcessAfterInstantiation:

实例化bean之后,相当于new这个bean之后。

上文resolveBeforeInstantiation()没有返回bean.则走流水线创建Bean

doCreateBean(beanName, mbdToUse, args)创建对象,会经过**populateBean(beanName, mbd, instanceWrapper)**方法。

populateBean(beanName, mbd, instanceWrapper)依次执行postProcessAfterInstantiation() 与postProcessPropertyValues()

这个返回返回值,是一个boolean类型,这个是干嘛呢?先看下源码,还是AbstractAutowireCapableBeanFactory中的createBean方法,进入到doCreateBean方法:

在这个往下找到一个populateBean进入:

在这里看到会获取到所有的InstantiationAwareBeanPostProcessor,如果有一个是返回false的话,那么就return回去了。这是啥意思呢?也就是说如果返回值是false,那么就不进行下面的依赖注入流程了。

(3)postProcessProperties:

bean已经实例化完成,在属性注入时阶段触发,

对于postProcessPropertyValues,这个方法在spring低版本中使用,在高版本已经过时了,使用postProcessProperties代替

(4)postProcessBeforeInitialization:

初始化bean之前。

(5)postProcessAfterInitialization:

初始化bean之后。

辅助理解:






一图胜千言:

1.3和BeanPostProcessor的区别?

BeanPostProcessor:有两个方法postProcessBeforeInitialization方法和postProcessAfterInitialization()。

在bean初始化的时候,也就是Spring激活bean的init-method方法的前后,会调用BeanPostProcessor的postProcessBeforeInitialization方法和postProcessAfterInitialization。

InstantiationAwareBeanPostProcessor:有三个方法

postProcessBeforeInstantiation(),postProcessAfterInstantiation(返回值boolean),postProcessPropertyValues()

(1)在bean实例化过程(生成实例对象)前后调用。涉及这两个方法postProcessBeforeInstantiation(),postProcessAfterInstantiation

(2)也在初始化(setter注入,init方法)的过程中的setter注入这一步之前调用。涉及这两个方法postProcessAfterInstantiation 返回值boolean,postProcessPropertyValues()

1.4使用场景

BeanPostProcessor典型的应用场景时在AOP生成代理对象的时候,AOP代理需要创建被代理类的对象,才能对对象进行代理。根据代理的特点,通过在BeanPostProcessor#postProcessAfterInitialization方法执行的时候,对被代理对象进行增强,这样就可以生成新的代理对象。

举栗子:

(1)AOP 就是基于 InstantiationAwareBeanPostProcessor实现的

(2)Autowired进行注入对象的时候,也是通过BeanPostProcessor完成。

(3)处理自定义注解:bean可以添加我们自定义的注解,自定义的注解处理方式在该类中实现,如通过注解识别一组或几组bean,在后续的业务处理中根据组bean进行逻辑。

(4)打印日志:将每个bean的初始化情况打印出来;打印初始化时间等

二、扩展实现方式

方式一:使用@Configuration + @Bean 方式初始化

方式二:使用@ComponentScan + @Component方式初始化

接下来通过方式二来更深入的理解一下这个类的几个方法。

2.1构建一个应用

这里使用了上一节构建的应用,你可以接着或者重新构建一个新的Spring Boot项目。

2.2 实现接口InstantiationAwareBeanPostProcessor

创建一个类,实现接口InstantiationAwareBeanPostProcessor:

package com.kfit.config;import org.springframework.beans.BeansException;import org.springframework.beans.PropertyValues;import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;import org.springframework.stereotype.Component;/** * * * @author 悟纤「公众号SpringBoot」 * @date 2022-06-06 * @slogan 大道至简 悟在天成 */@Componentpublic class MyInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor{    /**     * 实例化bean之前,相当于new这个bean之前     *     *     * 当调用postProcessBeforeInstantiation返回对象时,就可以直接返回对象了,就不会走到AbstractAutowireCapableBeanFactory的doCreateBean方法     * @param beanClass     * @param beanName     * @return     * @throws BeansException     */    @Override    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {        System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessBeforeInstantiation,beanName="+beanName);        return null;    }    /**     * 实例化bean之后,相当于new这个bean之后     *     * 如果返回值是false,那么就不进行下面的依赖注入流程了     * @param bean     * @param beanName     * @return     * @throws BeansException     */    @Override    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {        System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessAfterInstantiation");        return true;    }    /**     * bean已经实例化完成,在属性注入时阶段触发,     * Instantiation(实例化)     * @param pvs     * @param bean     * @param beanName     * @return     * @throws BeansException     */    @Override    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) throws BeansException {        System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessProperties");        return null;    }//    /**//     * 这个方法在spring低版本中使用,在高版本已经过时了,使用postProcessProperties代替//     * deprecated as of 5.1, in favor of {@link #postProcessProperties(PropertyValues, Object, String)}//     * @param pvs//     * @param pds//     * @param bean//     * @param beanName//     * @return//     * @throws BeansException//     *///    @Override//    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {//        return null;//    }    /**     * 初始化bean之前,相当于把bean注入spring上下文之前     * Initialization(初始化)     * @param bean     * @param beanName     * @return     * @throws BeansException     */    @Override    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {        System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessBeforeInitialization");        return bean;    }    /**     * 初始化bean之后,相当于把bean注入spring上下文之后     * @param bean     * @param beanName     * @return     * @throws BeansException     */    @Override    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {        System.out.println("MyInstantiationAwareBeanPostProcessor.postProcessAfterInitialization");        return bean;    }}

2.3 启动测试

运行项目,看下效果:

从这里可以看出Spring每对一个bean进行操作,就会进入到这个扩展点。

通过对于beanName=demoService1可以清晰的看到了整个执行过程:

postProcessBeforeInstantiation->执行类的构造方法->postProcessAfterInstantiation->postProcessProperties->postProcessBeforeInitialization->注解了@PostConstruct的初始化方法(init-metod)-> postProcessAfterInitialization

三、简单模拟一个 AOP

通过实践来体验下InstantiationAwareBeanPostProcessor 后置处理器的功能,利用它生成一个代理类,简单模拟一个 AOP:有一个DemoService2的类,这个类的要使用动态代理进行方法的切入,另外又要交给Spring处理。

3.1 创建DemoService2

首先创建一个DemoService2的类,并且使用@Service直接交给Spring进行管理:

package com.kfit.demo.service;import org.springframework.stereotype.Service;/** * *  利用InstantiationAwareBeanPostProcessor生成一个代理类 * * @author 悟纤「公众号SpringBoot」 * @date 2022-06-06 * @slogan 大道至简 悟在天成 */@Service//不配置beanName的话,默认是demoService2public class DemoService2 {    public int add(int a,int b){        return a+b;    }}

此时可以直接使用DemoService2进行方法的调用了,如果我们要在计算一下方法的耗时,在修改源码的情况下,那么就是使用AOP技术了,接下来我们来模拟一下Spring的AOP的实现。

3.2 自定义bean的创建

这里的话,就需要自定义bean的创建过程,需要使用到扩展入口postProcessBeforeInstantiation:

package com.kfit.config;import com.kfit.demo.service.DemoService2;import com.kfit.demo.service.DemoService2Proxy;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;import org.springframework.cglib.proxy.Enhancer;import org.springframework.stereotype.Component;/** * * @author 悟纤「公众号SpringBoot」 * @date 2022-06-06 * @slogan 大道至简 悟在天成 */@Componentpublic class AOPInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {    @Override    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {        if(beanClass == DemoService2.class){            //使用cglib实现动态代理AOP            Enhancer enhancer = new Enhancer();            enhancer.setSuperclass(beanClass);            enhancer.setCallback(new DemoService2Proxy());            DemoService2 demoService2 = (DemoService2)enhancer.create();            return demoService2;        }        return null;    }}

到这里就可以运行看下酷酷的效果了:

看下DemoService2实例:

很明显,上面使用了InstantiationAwareBeanPostProcessor 后返回了代理对象 DemoService2Proxy,而不是DemoService2。

通过这个InstantiationAwareBeanPostProcessor 的实践,我们也清楚了 AOP 的实现基础了,自己解读 AOP 源码时,也就相对轻松了。

另外如果只是使用到了postProcessBeforeInstantiation方法,那么实现接口BeanPostProcessor是一个更好的选择。



购买完整视频,请前往:http://www.mark-to-win.com/TeacherV2.html?id=287