1. 程式人生 > >設計模式之禪學習筆記09--代理模式(動態代理)

設計模式之禪學習筆記09--代理模式(動態代理)

1.動態代理

    動態代理還是屬於設計模式--代理模式的一種,代理類在程式執行時建立的代理方式被成為動態代理。動態代理是在實現階段不用關心代理誰,而在執行階段才指定代理哪一個物件。相對來說,自己寫代理類的方式就是靜態代理。現在有一個非常流行的名稱叫做面向橫切面程式設計,也就是AOP(Aspect Oriented Programming),其核心就是採用了動態代理機制。

 類圖:

                            

   

很簡單,兩條獨立發展的線路。動態代理實現代理的職責,業務邏輯Subject實現相關的邏輯功能,兩者之間沒有必然的相互耦合的關係。通知Advice從另一個切面切入,最終在高層模組也就是Client進行耦合,完成邏輯的封裝任務。

 2.舉個例子

     兩條線,首先建立業務邏輯這條線,建立一個抽象主題類

package com.wx.dynamicproxy.base;
/*
 抽象主題
 */
public interface Subject {
    public void doSomething(String string);
}

   建立具體的主題類,實現抽象主題介面

package com.wx.dynamicproxy.imp;
import com.wx.dynamicproxy.base.Subject;
/*
真實主題
 */
public class RealSubject implements Subject {
    /*
      業務操作
     */
    @Override
    public void doSomething(String string) {
        System.out.println("真實主題的業務操作"+string);
    }
}

 第二條線,動態代理實現代理的職責。

 建立動態代理類:DynamicProxy,這個類中使用JDK提供的Proxy類來動態建立物件,這裡也是動態代理和靜態代理的區別,靜態代理這個Proxy需要自己寫。需要傳入一下三個引數

  loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
  interfaces:目標物件實現的介面的型別(所以目標物件如果不實現介面就無法使用動態代理),使用泛型方式確認型別
  InvocationHandler h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的

package com.wx.dynamicproxy.imp;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/*
 動態代理類
 方法作為引數傳入
 */
public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h){
        //執行目標,並返回結果,
        return (T)Proxy.newProxyInstance(loader,interfaces, h);
    }
}

 代理類的三個引數中,只有InvocationHandler 需要自己寫,所以建立動態代理的動態代理的MyInvocationHandler類。實現介面InvocationHandler,這個類中持有一個被代理物件的例項target,並且所有通過動態代理實現的方法全部通過invoke方法呼叫。

package com.wx.dynamicproxy.imp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/*
   動態代理的MyInvocationHandler類,
 */
public class MyInvocationHandler implements InvocationHandler {
    //這個類中持有一個被代理物件的例項target
    private Object target=null;
    //通過建構函式傳遞一個物件
    public MyInvocationHandler(Object o)
    {
        this.target=o;
    }
    //代理方法,非常簡單,所有通過動態代理實現的方法全部通過invoke方法呼叫。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //執行被代理的方法
        return method.invoke(target,args);
    }
}

 

 測試:代理的過程是沒有問題的了

package com.wx.dynamicproxy.test;

import com.wx.dynamicproxy.base.Subject;
import com.wx.dynamicproxy.imp.DynamicProxy;
import com.wx.dynamicproxy.imp.MyInvocationHandler;
import com.wx.dynamicproxy.imp.RealSubject;
import com.wx.dynamicproxy.imp.SubjectDynamicProxy;
public class Client {
    public static void main(String[] agrs)
    {
        //定義一個主題
        Subject subject=new RealSubject();
        //定義一個Handler
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(subject);
        //定義主題的代理
        Subject proxy = DynamicProxy.newProxyInstance(subject.getClass().getClassLoader(),subject.getClass().getInterfaces(),myInvocationHandler);
        /*Subject proxyInstance = SubjectDynamicProxy.newProxyInstance(subject);*/
        proxy.doSomething("hello");
    }
}

 

 實現了一個簡單的橫切面程式設計,我們來看通知Advice,也就是我們要切入的類。

  建立一個通知介面IAdvice

package com.wx.dynamicproxy.base;
public interface IAdvice {
    //通知只有一個方法,執行即可
    public void exec();
}

  實現這個介面:

package com.wx.dynamicproxy.imp;
import com.wx.dynamicproxy.base.IAdvice;
public class BeforeAdvice implements IAdvice {
    @Override
    public void exec() {
        System.out.println("我是前置通知,我被執行了");
    }
}

 在動態代理類中把它切進去

package com.wx.dynamicproxy.imp;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/*
 動態代理類
  loader:指定當前目標物件使用類載入器,獲取載入器的方法是固定的
  interfaces:目標物件實現的介面的型別,使用泛型方式確認型別
  InvocationHandler h:事件處理,執行目標物件的方法時,會觸發事件處理器的方法,會把當前執行目標物件的方法作為引數傳入
 */
public class DynamicProxy<T> {
    public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h){
        //尋找JoinPoint連線點,AOP框架使用元資料
        if(true){
            //執行一個前置通知
            (new BeforeAdvice()).exec();
        }
        //執行目標,並返回結果,
        return (T)Proxy.newProxyInstance(loader,interfaces, h);
    }
}

 測試:

  

                          

代理模式應用得非常廣泛,大到一個系統框架、企業平臺,小到程式碼片段、事務處理,稍不留意就用到代理模式。可能該模式是大家接觸最多的模式,而且有了AOP大家寫代理就更加簡單了,有類似Spring AOP和AspectJ這樣非常優秀的工具,拿來主義可!不過,大家可以看看原始碼,特別是除錯時,只要看到類似$Proxy0這樣的結構,你就應該知道這是一個動態代理了。
友情提醒,在學習AOP框架時,弄清楚幾個名詞就成:切面(Aspect)、切入點(JoinPoint)、通知(Advice)、織入(Weave)就足夠了,理解了這幾個名詞,應用時你就可以遊刃有餘了!