《玩转Spring》第二章 BeanPostProcessor扩展

《玩转Spring》第二章 BeanPostProcessor扩展

 

上一章,介绍了如何扩展spring类实现自动读取配置文件

这一章,我们介绍如何通过实现BeanPostProcessor接口,对容器中的Bean做一层代理,来满足我们的个性化需求。

一、基本原理

我很不想贴代码,显得太没水平。有时候自己的语言又很空洞,不得不贴代码,感觉用代码来说明一件事反而更容易些。

 

[java] view plain copy
 
 print?
  1. import org.springframework.beans.BeansException;  
  2. import org.springframework.beans.factory.config.BeanPostProcessor;  
  3.   
  4. public class BeanPostPrcessorImpl implements BeanPostProcessor {  
  5.    
  6.     // Bean 实例化之前执行该方法  
  7.     public Object postProcessBeforeInitialization(Object bean, String beanName)  
  8.            throws BeansException {  
  9.        System.out.println( beanName + "开始实例化");  
  10.        return bean;  
  11.     }  
  12.    
  13.     // Bean 实例化之后执行该方法  
  14.     public Object postProcessAfterInitialization(Object bean, String beanName)  
  15.            throws BeansException {  
  16.        System.out.println( beanName + "实例化完成");  
  17.        return bean;  
  18.     }  
  19. }  

然后将这个BeanPostProcessor接口的实现配置到spring的配置文件中就可以了:

<bean class="com.jialin.spring.BeanPostPrcessorImpl"/>

注意:

1、BeanPostProcessor的作用域是容器级的,它只和所在容器有关。

2、BeanFactory和ApplicationContext对待bean后置处理器稍有不同。ApplicationContext会自动检测在配置文件中实现了BeanPostProcessor接口的所有bean,并把它们注册为后置处理器,然后在容器创建bean的适当时候调用它。部署一个后置处理器同部署其他的bean并没有什么区别。而使用BeanFactory实现的时候,bean 后置处理器必须通过下面类似的代码显式地去注册:

[java] view plain copy
 
 print?
  1. BeanPostPrcessorImpl beanPostProcessor = new BeanPostPrcessorImpl();  
  2. Resource resource = new FileSystemResource("applicationContext.xml");  
  3. ConfigurableBeanFactory factory = new XmlBeanFactory(resource);  
  4. factory.addBeanPostProcessor(beanPostProcessor);  
  5. factory.getBean("beanName");  

二、应用

好东西总要用起来

1、利用BeanPostProcessor接口实现Bean的动态代理。

2、自动注入Logging,用于记录日志。

Logger注解

[java] view plain copy
 
 print?
  1. @Retention(RetentionPolicy.RUNTIME)  
  2. @Target( {  
  3.         ElementType.FIELD  
  4. })  
  5. public @interface Logger {  
  6. }  
  7.   
  8. package com.jialin.framework.annotation;  
  9.   
  10. import org.apache.commons.logging.Log;  
  11. import org.apache.commons.logging.LogFactory;  
  12. import org.springframework.beans.BeansException;  
  13. import org.springframework.beans.factory.BeanInitializationException;  
  14. import org.springframework.beans.factory.config.BeanPostProcessor;  
  15.   
  16. import java.lang.reflect.Field;  
  17. import java.util.ArrayList;  
  18. import java.util.Collections;  
  19. import java.util.List;  
  20.   
  21.   
  22. public class LogBeanPostProcessor implements BeanPostProcessor {  
  23.   
  24.         public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  25.             return bean;  
  26.         }  
  27.   
  28.         public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
  29.             List<Class<?>> clazzes = getAllClasses(bean);  
  30.   
  31.             for (Class<?> clazz : clazzes) {  
  32.                 initializeLog(bean, clazz);  
  33.             }  
  34.   
  35.             return bean;  
  36.         }  
  37.   
  38.         /** 
  39.          * 取得指定bean的class以及所有父类的列表, 该列表排列顺序为从父类到当前类 
  40.          * @param bean 
  41.          * @return 
  42.          */  
  43.         private List<Class<?>> getAllClasses(Object bean) {  
  44.             Class<? extends Object> clazz = bean.getClass();  
  45.             List<Class<?>> clazzes = new ArrayList<Class<?>>();  
  46.             while (clazz != null) {  
  47.                 clazzes.add(clazz);  
  48.                 clazz = clazz.getSuperclass();  
  49.             }  
  50.   
  51.             Collections.reverse(clazzes);  
  52.             return clazzes;  
  53.         }  
  54.   
  55.         /** 
  56.          * 对logger变量进行初始化 
  57.          * 
  58.          * @param bean 
  59.          * @param clazz 
  60.          */  
  61.         private void initializeLog(Object bean, Class<? extends Object> clazz) {  
  62.             Field[] fields = clazz.getDeclaredFields();  
  63.             for (Field field : fields) {  
  64.                 if (field.getAnnotation(Logger.class) == null) {  
  65.                     continue;  
  66.                 }  
  67.   
  68.                 if (!field.getType().isAssignableFrom(Log.class)) {  
  69.                     continue;  
  70.                 }  
  71.   
  72.                 field.setAccessible(true);  
  73.                 try {  
  74.                     field.set(bean, LogFactory.getLog(clazz));  
  75.                 } catch (Exception e) {  
  76.                     throw new BeanInitializationException(String  
  77.                             .format("初始化logger失败!bean=%s;field=%s", bean, field));  
  78.                 }  
  79.   
  80.             }  
  81.         }  
  82.   
  83.   
  84. }  


在Spring配置文件中,加入

     <!--配置根据注解,自动注入Log对象-->

    <bean id="logBeanPocessor" class="com.jialin.framework.annotation.LogBeanPostProcessor" lazy-init="false" />

[java] view plain copy
 
 print?
  1. //实现代理的BeanPostProcessor的实例,其中注入上文介绍的log:  
  2. import java.lang.reflect.Proxy;  
  3. import java.util.Map;  
  4. import java.util.concurrent.ConcurrentHashMap;  
  5. import org.springframework.beans.BeansException;  
  6. import org.springframework.beans.factory.config.BeanPostProcessor;  
  7. import org.apache.commons.logging.Log;  
  8.    
  9. public class ProxyBeanPostProcesser implements BeanPostProcessor {  
  10.     private Map map = new ConcurrentHashMap(100);  
  11.   
  12.    //使用logger的注解来简化定义,并自动注入  
  13.   
  14.     @Logger  
  15.     private static final Log log;  
  16.    
  17.     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {  
  18.         MyProxy proxy = new MyProxy();  
  19.    
  20.         if (bean.toString().contains("Proxy")) {  
  21.             log.info(beanName + "为代理类,不进行再次代理!");  
  22.             return bean;  
  23.         }  
  24.   
  25.   
  26.         //……  
  27.         //可以加一些其他条件,过滤掉你不想代理的bean  
  28.         //……省略部分代码  
  29.   
  30.   
  31.         if (map.get(beanName) != null) {  
  32.             log.info(beanName + "已经代理过,不进行再次代理!");  
  33.             return map.get(beanName);  
  34.         }  
  35.         proxy.setObj(bean);  
  36.         proxy.setName(beanName);  
  37.         Class[] iterClass = bean.getClass().getInterfaces();  
  38.         if (iterClass.length > 0) {  
  39.             Object proxyO = Proxy.newProxyInstance(bean.getClass().getClassLoader(), iterClass, proxy);  
  40.             map.put(beanName, proxyO);  
  41.             return proxyO;  
  42.         } else {  
  43.             log.info(beanName + "必须实现接口才能被代理。");  
  44.             return bean;  
  45.         }  
  46.     }  
  47.    
  48.     public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {  
  49.         return bean;  
  50.     }  
  51.    
  52. }  
  53.    
  54.   
  55.   
  56.   
  57. import java.lang.reflect.InvocationHandler;  
  58. import java.lang.reflect.Method;  
  59. import org.apache.commons.logging.Log;  
  60. import sun.reflect.Reflection;  
  61.    
  62. //代理类Proxy,其中注入上文介绍的log:  
  63. public class MyProxy implements InvocationHandler {  
  64.   
  65.    //使用logger的注解来简化定义,并自动注入  
  66.   
  67.     @Logger    
  68.     private static final Log  log;  
  69.    
  70.     private Object obj;  
  71.    
  72.     private String name;  
  73.    
  74.     public String getName() {  
  75.         return name;  
  76.     }  
  77.    
  78.     public void setName(String name) {  
  79.         this.name = name;  
  80.     }  
  81.    
  82.     public Object getObj() {  
  83.         return obj;  
  84.     }  
  85.    
  86.     public void setObj(Object obj) {  
  87.         this.obj = obj;  
  88.     }  
  89.    
  90.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  91.         log.info("-------------------" + "bean 名称为【" + name + "】方法为【" + method.getName() + "】-------------"  
  92.                 + obj.getClass());  
  93.         return method.invoke(obj, args);  
  94.     }  
  95.    
  96.     public void printDetail(String detail) {  
  97.         log.error(detail);  
  98.     }  
  99.    
  100. }  
  101.    

 

在Spring配置文件中,加入

<!--配置自动为Bean配置代理对象-->

<bean id="proxyBeanPocessor" class="com.jialin.framework.proxy. ProxyBeanPostProcesser " />

从上面的介绍不难看出,实现了BeanPostProcessor接口定制我们自己的Bean处理器可以在Spring容器初始化Bean的时候做我们想做的很多事。Spring首先会使用自己的处理器,然后陆续使用我们的处理器,典型的装饰者模式,我们自定义的处理器就是一个个具体的装饰者。

在这里预告一下,后续会出一个文章,教大家如何《实现一个面向服务的IOC容器,不使用任何框架,纯j2se代码》。

这篇到这,下篇继续,敬请关注!谢谢

[html] view plain copy
 
 print?
  1. 贾琳   写于 2014-6-25 河北廊坊  多云                
原文地址:https://www.cnblogs.com/handsome1013/p/7340150.html