1. 程式人生 > >Proxy和InvocationHandler實現自己的AOP

Proxy和InvocationHandler實現自己的AOP

一、

spring Aop實際上也是通過動態代理機制進行面向切面程式設計的,在java裡可以通過實現InvocationHandler介面建立自己的動態代理類,然後通過Proxy建立動態的代理類的物件。

動態代理其實就是java.lang.reflect.Proxy類動態的根據指定的所有介面生成一個class byte,該class會繼承Proxy類,並實現所有你指定的介面(您在引數中傳入的介面陣列);然後再利用指定的classloader將 class byte載入進系統,最後生成這樣一個類的物件,並初始化該物件的一些值,如invocationHandler,以即所有的介面對應的Method成員。 初始化之後將物件返回給呼叫的客戶端。這樣客戶端拿到的就是一個實現你所有的介面的Proxy物件

首先InvocationHandler介面中只有一個方法

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy:  指代我們所代理的那個真實物件
method:  指代的是我們所要呼叫真實物件的某個方法的Method物件
args:  指代的是呼叫真實物件某個方法時接受的引數

Proxy中動態建立例項的方法

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws
IllegalArgumentException

這個方法的作用就是得到一個動態的代理物件,其接收三個引數,我們來看看這三個引數所代表的含義:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

loader:  一個ClassLoader物件,定義了由哪個ClassLoader物件來對生成的代理物件進行載入

interfaces:  一個Interface
物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面(多型),這樣我就能呼叫這組介面中的方法了 h:  一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件上

二、

好了現在簡單的模擬一下aop
首先新建一個介面

public interface Subject {

    public void rent();

    public void hello(String str);
}

對應的實現類

public class SubjectImpl implements Subject{

    @Override
    public void rent()
    {
        System.out.println("I want to rent my house");
    }

    @Override
    public void hello(String str)
    {
        System.out.println("hello: " + str);
    }

}
public class DynamicProxy implements InvocationHandler
{
    // 要代理的物件
    private Object target;

    // 將構造方法禁用掉,不讓外部通過new來得到DynamicProxy物件
    private DynamicProxy()
    {
    };

    /**
     * 返回一個動態的代理物件
     * 
     * @param object
     * @return
     */
    public static Object newInstance(Object object)
    {
        DynamicProxy proxy = new DynamicProxy();
        proxy.target = object;
        //    通過Proxy的newProxyInstance方法來得到一個代理物件
        Object result = Proxy.newProxyInstance(object.getClass()
                .getClassLoader(), object.getClass().getInterfaces(), proxy);
        return result;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable
    {
//        //    只有方法名為add和delete時候才引入日誌
//        if(method.getName().equals("add") || method.getName().equals("delete"))
//        {
//            Logger.logInfo("動態代理類");
//        }

        // 根據LogAnnotation來判斷,如果被標註了註解,則輸出日誌
        if(method.isAnnotationPresent(LogAnnotation.class))
        {
            LogAnnotation log = method.getAnnotation(LogAnnotation.class);
            Logger.logInfo(log.value());
        }

        Object object = method.invoke(target, args);
        return object;
    }
}

然後是spring的配置

<!-- 如果要對static方法進行注入,可以通過factory-method屬性來制定方法名字,並通過建構函式的方式傳入引數 -->
        <bean id="userDAOProxy" class="com.xiaoluo.proxy.DynamicProxy" factory-method="newInstance">
            <constructor-arg ref="userDAO"/>
        </bean>

因為我們的DynamicProxy類的物件以及代理物件是通過static方法來進行注入的,因此我們如果要對其進行注入的話,需要通過 factory-method 這個屬性來給我們的靜態方法進行屬性注入,通過 來講引數傳遞進去,這樣我們的userDAOProxy就是一個代理物件了

三、

關於InvocationHandler介面的invoke方法,其實這個類就是最終Proxy呼叫的固定介面方法。Proxy不管客戶端的業務方法是怎麼實現的。當客戶端呼叫Proxy時,它只

會呼叫InvocationHandler的invoke介面,所以我們的真正實現的方法就必須在invoke方法中去呼叫。關係如下:

BusinessProcessorImpl bpimpl = new BusinessProcessorImpl();
BusinessProcessorHandler handler = new BusinessProcessorHandler(bpimpl);

BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(....);

bp.processBusiness()-->invocationHandler.invoke()-->bpimpl.processBusiness();

Proxy.newProxyInstance方法會做如下幾件事:

1,根據傳入的第二個引數interfaces動態生成一個類,實現interfaces中的介面,該例中即BusinessProcessor介面的processBusiness方法。並且繼承了Proxy類,重寫了hashcode,toString,equals等三個方法。具體實現可參看 ProxyGenerator.generateProxyClass(…); 該例中生成了$Proxy0類

2,通過傳入的第一個引數classloder將剛生成的類載入到jvm中。即將$Proxy0類load

3,利用第三個引數,呼叫 Proxy0 Proxy0(InvocationHandler)建構函式 建立$Proxy0的物件,並且用interfaces引數遍歷其所有介面的方法,並生成Method物件初始化物件的幾個Method成員變數

4,將$Proxy0的例項返回給客戶端。

現在好了。我們再看客戶端怎麼調就清楚了。

1,客戶端拿到的是 Proxy0 Proxy0繼承了BusinessProcessor,因此轉化為BusinessProcessor沒任何問題。

BusinessProcessor bp = (BusinessProcessor)Proxy.newProxyInstance(….);

2,bp.processBusiness();

實際上呼叫的是 Proxy0.processBusiness(); Proxy0.processBusiness()的實現就是通過InvocationHandler去呼叫invoke方法啦!

注:文章以自己記錄為主,詳細的可以去下面地方看

http://www.cnblogs.com/xiaoluo501395377/p/3384821.html