Spring容器扩展点:后置处理器BeanPostProcessor

阳光穿透心脏的1/2处 2022-05-17 09:26 275阅读 0赞

" class="reference-link">先回顾bean生命周期的这张图,看看BeanPostProcessor调用位置 这里写图片描述

通过上图看到BeanPostProcessor(Bean后置处理器)两个方法在bean生命周期的位置,即:在Spring容器完成Bean实例化和属性设置后,并且在bean调用初始化方法之前或之后。因此BeanPostProcessor(Bean后置处理器)常用在:对bean内部的值进行修改;实现Bean的动态代理等。

可以定义一个或者多个BeanPostProcessor接口的实现,然后注册到容器中。那么该容器里管控的所有Bean在调用初始化方法之前或之后,都会调用BeanPostProcessor接口中对应的方法。
InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口。从最上面的生命周期图,我们知道它在Bean生命周期的另外三个时期提供扩展的回调接口。其使用方法与BeanPostProcessor接口类似,只时回调时机不同。

BeanPostProcessor接口有两个方法:

  1. Object postProcessBeforeInitialization(Object bean,String BeanName)throws BeansException;
  • Object postProcessAfterInitialization(Object bean,String BeanName)throws BeansException;

容器调用接口定义的方法时会将该受管Bean的实例和名字通过参数传入方法,经过处理后通过方法的返回值返回给容器。注意,不能返回null,如果返回的是null那么我们通过getBean方法将得不到目标。

BeanPostProcessor不允许标记为延迟加载。因为如果这样做,Spring容器将不会注册它们,自定义逻辑也就无法得到应用。假如你在<beans />元素的定义中使用了'default-lazy-init'属性,那就必须将每个BeanPostProcessor显示标记为'lazy-init="false"'

如果定义了多个BeanPostProcessor,可以在xml配置中通过order属性来指定执行的顺序。

简单例子

类代码:(在这个例子中,可以不需要继承接口。但为了保持和原来的程序一致,就没有删implements PlayerActionInterface了):

  1. package twm.spring.LifecycleTest;
  2. public class footballPlayer implements PlayerActionInterface{
  3. {
  4. String name;//球员名字
  5. String team;//所在球队
  6. //getter and setter......
  7. public void shoot() {
  8. System.out.println(this.getName()+"射门");
  9. }
  10. public void pass() {
  11. System.out.println(this.getName()+"边路传中");
  12. }
  13. }

注册:

  1. <bean id="cluo" class="twm.spring.LifecycleTest.footballPlayer">
  2. <property name="name" value="C.罗纳尔多"></property>
  3. </bean>

这是原来的程序。调用:

  1. public static void main(String[] args) throws Exception {
  2. ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
  3. footballPlayer smone = ctx.getBean("cluo",footballPlayer.class);
  4. smone.shoot();
  5. smone.pass();
  6. }

输出:

C.罗纳尔多射门
C.罗纳尔多边路传中

现在我们加入一个beanPostProcessor后处理器(beanPostProcessorImpl.java),修改球员名称:

  1. package twm.spring.LifecycleTest;
  2. import org.springframework.beans.BeansException;
  3. import org.springframework.beans.factory.config.BeanPostProcessor;
  4. public class beanPostProcessorImpl implements BeanPostProcessor{
  5. @Override
  6. public Object postProcessAfterInitialization(Object bean, String beanName)
  7. throws BeansException {
  8. if(bean instanceof footballPlayer){
  9. ((footballPlayer) bean).setName("Messi");
  10. }
  11. return bean;
  12. }
  13. @Override
  14. public Object postProcessBeforeInitialization(Object bean, String beanName)
  15. throws BeansException {
  16. return bean;
  17. }
  18. }

配置文件beans.xml加上:

  1. <bean class="twm.spring.LifecycleTest.beanPostProcessorImpl" />
  • 其它不变,这时再运行,输出:

Messi射门
Messi边路传中

代理使用例子

在刚才的例子基础上,有新的需求:教练团队需要在每一次调用pass(),shoot()方法时,记录调用时间,用来进行战术分析。
于是重写beanPostProcessor后处理器(beanPostProcessorImpl.java)
代码如下:

  1. public class beanPostProcessorImpl implements BeanPostProcessor{
  2. @Override
  3. public Object postProcessAfterInitialization(Object bean, String beanName)
  4. throws BeansException {
  5. /* 后处理器beanPostProcessor会对容器中所有的bean起作用,因此我们要限定一下范围。
  6. * 这个例子中,我们只处理PlayerActionInterface对象*/
  7. if(!(bean instanceof PlayerActionInterface)){
  8. return bean;
  9. }
  10. final Object finalBean=bean;
  11. Map map = new ConcurrentHashMap(100);
  12. if(map.get(beanName)!=null){
  13. return map.get(beanName);
  14. }
  15. Class[] classes=bean.getClass().getInterfaces();
  16. if(classes.length<1){
  17. //没有接口的,无法进行代理
  18. return bean;
  19. }
  20. Object proxyObj = Proxy.newProxyInstance(this.getClass().getClassLoader(),
  21. classes,
  22. new InvocationHandler() {
  23. @Override
  24. public Object invoke(Object proxy, Method method,
  25. Object[] args) throws Throwable {
  26. System.out.println("method:" + method.getName());
  27. Object result = method.invoke(finalBean, args);
  28. System.out.println("发生时间:"
  29. + (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
  30. .format(new Date()));
  31. return result;
  32. }
  33. });
  34. map.put(beanName, proxyObj);
  35. return proxyObj;
  36. }
  37. @Override
  38. public Object postProcessBeforeInitialization(Object bean, String beanName)
  39. throws BeansException {
  40. return bean;
  41. }
  42. }

业务调用代码要将以前的类声明改成接口声明PlayerActionInterface,
因此调整为:

  1. public static void main(String[] args) throws Exception {
  2. ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
  3. PlayerActionInterface smone = ctx.getBean("cluo",PlayerActionInterface.class);
  4. smone.shoot();
  5. smone.pass();
  6. }

这时再运行代码输出:

method:shoot
C.罗纳尔多射门
发生时间:2017-03-31 14:09:02
method:pass
C.罗纳尔多边路传中
发生时间:2017-03-31 14:09:02

补充:
当然也可以用beanPostProcessor实现AOP代理。

后置处理器BeanPostProcessor对上一篇提到的FactoryBean产生的bean也是有效的(双重代理)。
如果我们把刚才定义的beanPostProcessorImpl类注册到上一篇的例子中去,就会输出:

getObject
ctx.getBean(“playerfacory”):com.sun.proxy.$Proxy1
ctx.getBean(“&playerfacory”):twm.spring.LifecycleTest.PlayerFactory
-----------------------
method:shoot
method:shoot
观察进攻及防守队员跑位
method:shoot
C.罗纳尔多射门
发生时间:2017-03-31 15:22:07
无球跑动
发生时间:2017-03-31 15:22:07
method:pass
method:pass
观察进攻及防守队员跑位
method:pass
C.罗纳尔多边路传中
发生时间:2017-03-31 15:22:07
无球跑动
发生时间:2017-03-31 15:22:07

发表评论

表情:
评论列表 (有 0 条评论,275人围观)

还没有评论,来说两句吧...

相关阅读