上一章,介绍了如何扩展spring类实现自动读取配置文件。
这一章,我们介绍如何通过实现BeanPostProcessor接口,对容器中的Bean做一层代理,来满足我们的个性化需求。
一、基本原理
我很不想贴代码,显得太没水平。有时候自己的语言又很空洞,不得不贴代码,感觉用代码来说明一件事反而更容易些。
import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; public class BeanPostPrcessorImpl implements BeanPostProcessor { // Bean 实例化之前执行该方法 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println( beanName + "开始实例化"); return bean; } // Bean 实例化之后执行该方法 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println( beanName + "实例化完成"); return bean; } }
然后将这个BeanPostProcessor接口的实现配置到Spring的配置文件中就可以了:
<bean class="com.jialin.spring.BeanPostPrcessorImpl"/>
注意:
1、BeanPostProcessor的作用域是容器级的,它只和所在容器有关。
2、BeanFactory和ApplicationContext对待bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它。部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过下面类似的代码显式地去注册:
BeanPostPrcessorImpl beanPostProcessor = new BeanPostPrcessorImpl(); Resource resource = new FileSystemResource("applicationContext.xml"); ConfigurableBeanFactory factory = new XmlBeanFactory(resource); factory.addBeanPostProcessor(beanPostProcessor); factory.getBean("beanName");
二、应用
好东西总要用起来
1、利用BeanPostProcessor接口实现Bean的动态代理。
2、自动注入Logging,用于记录日志。
Logger注解
@Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.FIELD }) public @interface Logger { } package com.jialin.framework.annotation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanInitializationException; import org.springframework.beans.factory.config.BeanPostProcessor; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; public class LogBeanPostProcessor implements BeanPostProcessor { public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { List<Class<?>> clazzes = getAllClasses(bean); for (Class<?> clazz : clazzes) { initializeLog(bean, clazz); } return bean; } /** * 取得指定bean的class以及所有父类的列表, 该列表排列顺序为从父类到当前类 * @param bean * @return */ private List<Class<?>> getAllClasses(Object bean) { Class<? extends Object> clazz = bean.getClass(); List<Class<?>> clazzes = new ArrayList<Class<?>>(); while (clazz != null) { clazzes.add(clazz); clazz = clazz.getSuperclass(); } Collections.reverse(clazzes); return clazzes; } /** * 对logger变量进行初始化 * * @param bean * @param clazz */ private void initializeLog(Object bean, Class<? extends Object> clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (field.getAnnotation(Logger.class) == null) { continue; } if (!field.getType().isAssignableFrom(Log.class)) { continue; } field.setAccessible(true); try { field.set(bean, LogFactory.getLog(clazz)); } catch (Exception e) { throw new BeanInitializationException(String .format("初始化logger失败!bean=%s;field=%s", bean, field)); } } } }
在Spring配置文件中,加入
<!--配置根据注解,自动注入Log对象-->
<bean id="logBeanPocessor" class="com.jialin.framework.annotation.LogBeanPostProcessor" lazy-init="false" />
//实现代理的BeanPostProcessor的实例,其中注入上文介绍的log: import java.lang.reflect.Proxy; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.apache.commons.logging.Log; public class ProxyBeanPostProcesser implements BeanPostProcessor { private Map map = new ConcurrentHashMap(100); //使用logger的注解来简化定义,并自动注入 @Logger private static final Log log; public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { MyProxy proxy = new MyProxy(); if (bean.toString().contains("Proxy")) { log.info(beanName + "为代理类,不进行再次代理!"); return bean; } //…… //可以加一些其他条件,过滤掉你不想代理的bean //……省略部分代码 if (map.get(beanName) != null) { log.info(beanName + "已经代理过,不进行再次代理!"); return map.get(beanName); } proxy.setObj(bean); proxy.setName(beanName); Class[] iterClass = bean.getClass().getInterfaces(); if (iterClass.length > 0) { Object proxyO = Proxy.newProxyInstance(bean.getClass().getClassLoader(), iterClass, proxy); map.put(beanName, proxyO); return proxyO; } else { log.info(beanName + "必须实现接口才能被代理。"); return bean; } } public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { return bean; } } import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import org.apache.commons.logging.Log; import sun.reflect.Reflection; //代理类Proxy,其中注入上文介绍的log: public class MyProxy implements InvocationHandler { //使用logger的注解来简化定义,并自动注入 @Logger private static final Log log; private Object obj; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { log.info("-------------------" + "bean 名称为【" + name + "】方法为【" + method.getName() + "】-------------" + obj.getClass()); return method.invoke(obj, args); } public void printDetail(String detail) { log.error(detail); } }
在Spring配置文件中,加入
<!--配置自动为Bean配置代理对象-->
<bean id="proxyBeanPocessor" class="com.jialin.framework.proxy. ProxyBeanPostProcesser " />
从上面的介绍不难看出,实现了BeanPostProcessor接口定制我们自己的Bean处理器可以在Spring容器初始化Bean的时候做我们想做的很多事。Spring首先会使用自己的处理器,然后陆续使用我们的处理器,典型的装饰者模式,我们自定义的处理器就是一个个具体的装饰者。
在这里预告一下,后续会出一个文章,教大家如何《实现一个面向服务的IOC容器,不使用任何框架,纯j2se代码》。
这篇到这,下篇继续,敬请关注!谢谢
贾琳 写于 2014-6-25 河北廊坊 多云
《玩转Spring》第二章 BeanPostProcessor扩展