zoukankan      html  css  js  c++  java
  • 《玩转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 河北廊坊  多云                
  • 相关阅读:
    【SW4STM32生成 hex文件的设置方法】
    【004:结构体内存地址的连续问题】
    【003:使用SW4STM32不进入中断的原因】
    [转载来之雨松:NGUI研究院之为什么打开界面太慢(十三)]
    【错误总结1:unity StartCoroutine 报 NullReferenceException 错误】
    【场景切换的多种调用方式】
    【Lua学习笔记之:Lua环境搭建 Windows 不用 visual studio】
    【KeyCode 键码】
    mysql
    numpy
  • 原文地址:https://www.cnblogs.com/handsome1013/p/7340150.html
Copyright © 2011-2022 走看看