1. 程式人生 > >《玩轉Spring》第二章 BeanPostProcessor擴充套件

《玩轉Spring》第二章 BeanPostProcessor擴充套件

這一章,我們介紹如何通過實現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 河北廊坊  多雲