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 大道至简 悟在天成
*/
@Component
public 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的话,默认是demoService2
public 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 大道至简 悟在天成
*/
@Component
public 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