本文共 5413 字,大约阅读时间需要 18 分钟。
最近在恶啃Spring源码,就读到一个很有意思的东西BeanPostProcessor,正式的名字叫做Spring后置处理器,这个东西非常的强大,强大到可以让我们干预Bean的创建过程,写出来分享给大家。更多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 { }
@Repositorypublic 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声明周期回调的初始化方法,笔者在这里有详解,有兴趣的各位可以去看下。下面我们开始正片。
新建一个类继承BeanPostProcessor
接口,然后有两个实现方法,我们给实现了。因为Java 8中添加了默认的实现字符default,在最新的Spring版本中把这两个方法都加了default
默认符,因此需要用快捷键把他们调出来。先讲解一下参数,第一个参数Object bean
这里传入的就是我们要寻找的bean
,比如我在Test
里调用了IndexDao.class
,在Spring容器里就会把IndexDao
传到这个方法里做事情,只不过Spring调用对程序员来说是黑盒子不用管而已。第二个参数String beanName
,自然就是这个bean
的名字,没什么可以解释的。Spring底层存储这些bean
是类似一个map
存储解构存的,差不多就是map<BeanName,BeanDefinitation>
这样的解构,因此这里拿到这些非常方便。我的这个例子就是当发现传入的是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
看看效果:
运行结果:ConstructorpostProcessBeforeInitialization + IndexDaoinitpostProcessAfterInitialization + IndexDaoquery
看输出确实是在初始化方法前后执行的,那么到了这里大家是否有一些对于Spring AOP
的感悟,Spring AOP
是怎么做的,就是创建一个新的代理返回出去对不对。我们也可以做啊,只要在bean
初始化之前修改返回就可以干预这个bean
的生成,比如下面的修改:
@Overridepublic 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
的类,修改一下输出:
@Componentpublic 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; //这个值随便写的 }}
@Componentpublic 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; }}运行输出:ConstructorpostProcessBeforeInitialization1 + IndexDaopostProcessBeforeInitialization + IndexDaoinitpostProcessAfterInitialization1 + IndexDaopostProcessAfterInitialization + IndexDaoquery
对比输出结果,发现TestBeanPostProcessor1
先执行了。我们接着修改TestBeanPostProcessor1.getOrder()
方法,再执行:
TestBeanPostProcessor1里面的:@Overridepublic int getOrder() { return 100;}运行输出:ConstructorpostProcessBeforeInitialization + IndexDaopostProcessBeforeInitialization1 + IndexDaoinitpostProcessAfterInitialization + IndexDaopostProcessAfterInitialization1 + IndexDaoquery
运行输出的结果就把postProcessBeforeInitialization1
调到后面去了。其实PriorityOrdered
就是调整Bean
创建优先顺序的接口,其中getOrder()
值越小,优先权越高。
最近开始啃Spring源码,啃的越多也觉得自己的水平和小白越近,马上都快不会写代码了。但是源码干涩,难以下咽,等笔者消化消化有了新的发现再分享出来,有问题的地方欢迎读者老爷们指正,谢谢大家阅读。
转载地址:http://vtmsi.baihongyu.com/