最近在恶啃Spring源码,就读到一个很有意思的东西BeanPostProcessor,正式的名字叫做Spring后置处理器,这个东西非常的强大,强大到可以让我们干预Bean的创建过程,写出来分享给大家。更多Spring内容进入【Spring解读系列目录】。
BeanPostProcessor
是Spring框架提供的一个扩展类点,叫做bean后置器
。通过实现BeanPostProcessor
接口,程序员就可以干预bean
实例化的过程,从而减轻beanFactory
的负担。这个接口可以设置多个,形成一个列表,然后依次执行。注意凡是实现BeanPostProcessor
接口的全部都会执行一遍。那么这个东西这么牛逼,它有什么Spring的应用实例嘛?其实我们一直用的AOP就是用这个接口在Bean实例化期间,将切面逻辑植入Bean实例中的。也正是通过BeanPostProcessor
,Spring把AOP,动态代理,IOC容器建立起了联系。
上面吹了一堆这个东西这么牛逼,就得实际看看这个东西道理怎么样。于是我们就得
假如有个配置类AppConfig,再整一个业务类IndexDao,还有一个测试类:
@Configuration
@ComponentScan("com.demo")
public class AppConfig {
}
@Repository
public class IndexDao {
public IndexDao() {
System.out.println("Constructor");
}
@PostConstruct
public void init(){
System.out.println("init");
}
public void query(){
System.out.println("query");
}
}
public class Test {
public static void main(String[] args) {
AnnotationConfigApplicationContext anno=
new AnnotationConfigApplicationContext(AppConfig.class);
IndexDao dao=anno.getBean(IndexDao.class);
dao.query();
}
}
这个例子里面注解@PostConstruct
是Spring声明周期回调的初始化方法,笔者在【Spring生命周期回调的应用】这里有详解,有兴趣的各位可以去看下。下面我们开始正片。
新建一个类继承BeanPostProcessor
接口,然后有两个实现方法,我们给实现了。因为Java 8中添加了默认的实现字符default,在最新的Spring版本中把这两个方法都加了default
默认符,因此需要用快捷键把他们调出来。先讲解一下参数,第一个参数Object bean
这里传入的就是我们要寻找的bean
,比如我在Test
里调用了IndexDao.class
,在Spring容器里就会把IndexDao
传到这个方法里做事情,只不过Spring调用对程序员来说是黑盒子不用管而已。第二个参数String beanName
,自然就是这个bean
的名字,没什么可以解释的。Spring底层存储这些bean
是类似一个map
存储解构存的,差不多就是map
这样的解构,因此这里拿到这些非常方便。我的这个例子就是当发现传入的是indexDao的时候,就执行一些事情,为了避免Spring自己的东西干扰,因此做了一个处理。
public class TestBeanPostProcessor implements BeanPostProcessor{
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){ //特指indexDao时触发
System.out.println("postProcessBeforeInitialization + IndexDao");
}
return bean; //返回该bean
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessAfterInitialization + IndexDao");
}
return bean;
}
}
上面一共两个方法,看名字一个是before
,一个是after
。顾名思义一个是初始化之前被调用的,一个就是初始化之后被调用的。那么我们运行一下Test
看看效果:
运行结果:
Constructor
postProcessBeforeInitialization + IndexDao
init
postProcessAfterInitialization + IndexDao
query
看输出确实是在初始化方法前后执行的,那么到了这里大家是否有一些对于Spring AOP
的感悟,Spring AOP
是怎么做的,就是创建一个新的代理返回出去对不对。我们也可以做啊,只要在bean
初始化之前修改返回就可以干预这个bean
的生成,比如下面的修改:
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessBeforeInitialization + IndexDao");
}
<这里是伪码>
//这里可以直接把对象重新复写成代理对象返回去,这样就干预了IndexDao的初始化,变成了IndexDaoProxy。
return Proxy.newIndexDaoProxy();
}
AOP不就是这么做的么,我们实例化一个A,但是我们拿到的是一个代理B,代理B执行了代理方法和A里面的业务方法。现在我们在实例化A的时候,对A进行加工做成B,然后把B返回出去,不就做到了Spring AOP
的过程。是不是有种刷新了认知的感觉。
这是一个小扩展,也是整BeanPostProcessor
的时候发现的。这个接口可以改变BeanPostProcessor
的顺序。怎么说呢,我们修改一下TestBeanPostProcessor
让其实现PriorityOrdered
接口,然后实现里面的getOrder()
方法。然后再复制一个TestBeanPostProcessor1
的类,修改一下输出:
@Component
public class TestBeanPostProcessor implements BeanPostProcessor , PriorityOrdered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessBeforeInitialization + IndexDao");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessAfterInitialization + IndexDao");
}
return bean;
}
@Override
public int getOrder() {
return 15; //这个值随便写的
}
}
@Component
public class TestBeanPostProcessor1 implements BeanPostProcessor, PriorityOrdered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessBeforeInitialization1 + IndexDao");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("indexDao")){
System.out.println("postProcessAfterInitialization1 + IndexDao");
}
return bean;
}
@Override
public int getOrder() {
return 11;
}
}
运行输出:
Constructor
postProcessBeforeInitialization1 + IndexDao
postProcessBeforeInitialization + IndexDao
init
postProcessAfterInitialization1 + IndexDao
postProcessAfterInitialization + IndexDao
query
对比输出结果,发现TestBeanPostProcessor1
先执行了。我们接着修改TestBeanPostProcessor1.getOrder()
方法,再执行:
TestBeanPostProcessor1里面的:
@Override
public int getOrder() {
return 100;
}
运行输出:
Constructor
postProcessBeforeInitialization + IndexDao
postProcessBeforeInitialization1 + IndexDao
init
postProcessAfterInitialization + IndexDao
postProcessAfterInitialization1 + IndexDao
query
运行输出的结果就把postProcessBeforeInitialization1
调到后面去了。其实PriorityOrdered
就是调整Bean
创建优先顺序的接口,其中getOrder()
值越小,优先权越高。
最近开始啃Spring源码,啃的越多也觉得自己的水平和小白越近,马上都快不会写代码了。但是源码干涩,难以下咽,等笔者消化消化有了新的发现再分享出来,有问题的地方欢迎读者老爷们指正,谢谢大家阅读。