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

来源:互联网 发布:网络赢钱游戏 编辑:程序博客网 时间:2024/04/28 16:38

本文转自点击打开链接



综述

先回顾bean生命周期的这张图,看看BeanPostProcessor调用位置
这里写图片描述

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

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

BeanPostProcessor接口有两个方法:

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

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

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

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

简单例子

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

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

注册:

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

这是原来的程序。调用:

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

输出:

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

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

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

配置文件beans.xml加上:

<bean class="twm.spring.LifecycleTest.beanPostProcessorImpl" />
  • 1

其它不变,这时再运行,输出:

Messi射门
Messi边路传中

代理使用例子

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

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

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

public static void main(String[] args) throws Exception {    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");    PlayerActionInterface smone = ctx.getBean("cluo",PlayerActionInterface.class);    smone.shoot();    smone.pass();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 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

但是有个超出预料的,就是每一个方法调用都多出了一个:“发生时间:2017-03-31 15:22:07”。这怎么回事呢?
后面有空再研究。


阅读全文
0 0
原创粉丝点击