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

mac2025-03-14  8

文章目录

综述简单例子

综述

先回顾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;Object postProcessAfterInitialization(Object bean,String BeanName)throws BeansException;

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

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

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

简单例子

类代码:

public class FootballPlayer implements PlayerActionInterface { String name;//球员名字 String team;//所在球队 @Override public void shoot() { System.out.println(this.getName()+"射门"); } @Override public void pass() { System.out.println(this.getName()+"边路传中"); } }

注册:

<bean id="footballPlayer" class="com.mvc.FootballPlayer"> <property name="name" value="C.罗纳尔多"></property> </bean>

自定义BeanPostProcessor后置处理器,修改球员名称:

import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class BeanPostProcessorImpl implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { /** * 后处理器beanPostProcessor会对容器中所有的bean起作用,因此我们要限定一下范围。 * 这个例子中,我们只处理PlayerActionInterface对象 */ if(bean instanceof PlayerActionInterface){ ((FootballPlayer) bean).setName("James"); } return bean; } }

将后置处理器加入到Spring容器中:

<bean id="beanPostProcessor" class="com.mvc.BeanPostProcessorImpl"/>

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

import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class BeanPostProcessorImpl implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { /** * 后处理器beanPostProcessor会对容器中所有的bean起作用,因此我们要限定一下范围。 * 这个例子中,我们只处理PlayerActionInterface对象 */ if(bean instanceof PlayerActionInterface){ ((FootballPlayer) bean).setName("James"); } 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; } }

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

@Test public void test() throws Exception{ ApplicationContext ctx= new ClassPathXmlApplicationContext("applicationContext.xml"); PlayerActionInterface bean = ctx.getBean("footballPlayer", PlayerActionInterface.class); bean.pass(); }

运行结果:

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

最新回复(0)