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

文章目录

综述

先回顾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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值