設計模式之 - 代理模式(Proxy Pattern)

代理模式:代理是一種經常使用的設計模式,其目的就是爲其餘對象提供一個代理以控制對某個對象的訪問。代理類負責爲委託類預處理消息,過濾消息並轉發消息,以及進行消息被委託類執行後的後續處理。不少能夠框架中都有用到,好比: spring的AOP的實現主要就是動態代理, mybatis的Mapper代理等。java

以下來看下代理模式的UML圖(來自百度圖片):spring

  

代理類和被代理類實現共同的接口, 其中代理類中包含一個被代理類的實例引用。代理模式能夠分爲靜態代理和動態代理,這裏主要學習下動態代理。動態代理做用能夠實現業務代理和通用邏輯代碼解耦,在不改變業務邏輯的同時,動態的給原邏輯代碼添加一些通用功能,好比打印調用日誌,權限斷定,事務處理等等。設計模式

下面用代碼實現動態代理:mybatis

1. 定義一我的的動做行爲接口app

package cn.aries.pattern.ProxyPattern;
/**
 * 人的行爲接口
 * @author aries
 */
public interface PersonAction {

    /**
     * 說話
     */
    public void personSay();
    /**
     * 跑步
     */
    public void personRunning();
    /**
     * 吃東西
     */
    public void personEating();
    
}

2. 建立人行爲的的實現類框架

package cn.aries.pattern.ProxyPattern;
public class PersonActionImpl implements PersonAction{
    @Override
    public void personSay() {
        System.out.println("人在說話...");
    }
    @Override
    public void personRunning() {
        System.out.println("人在跑步...");        
    }
    @Override
    public void personEating() {
        System.out.println("人在吃東西...");
    }
}

3. 動態代理須要一個實現了InvoketionHandler接口的類ide

package cn.aries.pattern.ProxyPattern;

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

public class ProxyPerson implements InvocationHandler{
    //被代理的實例對象
    PersonAction obj;
    private ProxyPerson(PersonAction obj){
        this.obj = obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //執行方法以前打印動做開始。
        System.out.println(method.getName() + "ation start ...");
        //使用反射執行目標方法
        method.invoke(obj, args);
        //在方法執行結束時打印動做結束。
        System.out.println(method.getName() + "ation end ...");
        return null;
    }
   //定義一個靜態方法生成代理對象
    public static Object getProxyPersonAction(PersonAction obj){
        PersonAction proxy = (PersonAction) Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new ProxyPerson(obj));
        return proxy;
    }
}

4. 客戶端代碼函數

package cn.aries.pattern.ProxyPattern;

public class App {
    public static void main(String[] args) throws Exception {
        //設置系統參數,將生成的代理類的class文件保存到本地
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        PersonAction pa = new PersonActionImpl();
        //調用生成代理類的方法
        PersonAction proxyPa = (PersonAction) ProxyPerson.getProxyPersonAction(pa);  
     //用代理對象調用目標方法 proxyPa.personSay(); proxyPa.personRunning(); proxyPa.personEating();
//打印代理對象的父類 System.out.println(proxyPa.getClass().getSuperclass()); } }

執行結果:學習

personSayation start ...
人在說話...
personSayation end ...
personRunningation start ...
人在跑步...
personRunningation end ...
personEatingation start ...
人在吃東西...
personEatingation end ...
class java.lang.reflect.Proxyui

當方法在中的是分別執行咱們在目標方法執行先後添加的代碼。

5. 代理對象是經過Proxy.newProxyInstance(...)這個方法生成的,咱們進入源代碼查看下

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{
        if (h == null) {
            throw new NullPointerException();
        }
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        /*
         * Look up or generate the designated proxy class.
      這裏生成代理類的字節碼文件
*/ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler.
*/
try {
      //在這裏獲取代理類的構造函數,從前面的運行結果中能夠得知,代理類是Proxy類的子類
      //而constructorParams在Proxy類中是一個靜態的常量: private static final Class<?>[] constructorParams = { InvocationHandler.class };
      //因此這裏獲取的帶InvocationHandler對象爲入參的構造函數,也就是其父類Proxy的構造函數:protected Proxy(InvocationHandler h){...}
final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h;
        //這裏調用newInstance()方法建立代理對象,其內部實現是:return cons.newInstance(new Object[] {h} );使用反射經過含參(hanlder)生成代理對象。
       //其中h賦值給了其父類Proxy類的成員變量: protected InvocationHandler h;
       //最終在這裏生成代理對象並返回
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) { // create proxy instance with doPrivilege as the proxy class may // implement non-public interfaces that requires a special permission return AccessController.doPrivileged(new PrivilegedAction<Object>() { public Object run() { return newInstance(cons, ih); } }); } else { return newInstance(cons, ih); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString()); } }

 6. 到此我瞭解了代理對象的生產過程,可是代理對象和handler是什麼關係呢,又是如何調用其invoke(...)方法呢,這暫時是個謎團讓咱們來看下生成的代理類的源碼,這些就都清楚了。

  注:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 這個是設置系統參數,將生產的代理類本身碼文件保存在本地,而後咱們經過反編譯就能夠得到其Java代碼。

package com.sun.proxy;

import cn.aries.pattern.ProxyPattern.PersonAction;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements PersonAction {
    //這五個靜態變量前三個m0,m1,m2分別是代理類繼承的Object類的hashcode(),equals(),toString()方法 //其餘從m3開始是繼承的們定義的接口類的方法根據方法的多少m後面的數字遞增
    private static Method m1;
    private static Method m3;
    private static Method m5;
    private static Method m0;
    private static Method m4;
    private static Method m2;
    
    static {
        try {
            //這裏使用靜態代碼塊對經過反射對代理對象中的方法進行實例化
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
            m3 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personEating", new Class[0]);
            m5 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personRunning", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m4 = Class.forName("cn.aries.pattern.ProxyPattern.PersonAction").getMethod("personSay", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
    
    public $Proxy0(InvocationHandler paramInvocationHandler)throws{
        super(paramInvocationHandler);
    }

    //這裏是對咱們定義的personEating方法進行實現 //根據類文件咱們能夠看到,代理類繼承了Proxy類,因此其成員變量中包含一個Handler實例對象的引用 //在建立代理實例對象的時候,咱們使用的protected Proxy(InvocationHandler h) {this.h = h;}這個構造函數 //因此下面的h就是咱們傳進去的handler對象 //這裏使用handler對象調用本身的invoke()方法,m3就是咱們要執行的方法, //後面的方法的參數,若是有參數就傳對應的參數,沒有就傳null //此時咱們明白了代理對象和handler的關係,以及如何調用到invoke()方法有了明確的認識了。
    public final void personEating()throws{
        try
        {
          this.h.invoke(this, m3, null);
          return;
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
    //這裏原理同上,爲了節省空間這裏就不貼出來了
    public final void personSay(){...}
    public final void personRunning(){...}

    public final int hashCode()throws{
        try{
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
    
    public final String toString()throws {
        try{
          return (String)this.h.invoke(this, m2, null);
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable)
        {
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
    
    public final boolean equals(Object paramObject)throws{
        try{
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }catch (Error|RuntimeException localError){
          throw localError;
        }catch (Throwable localThrowable){
          throw new UndeclaredThrowableException(localThrowable);
        }
    }
}

寫完後瀏覽了一下,好像沒有發現被代理對象的引用在代理類中出現;而後想了下,代理類繼承了Proxy類,其中Proxy類中有咱們寫的InvoketionHandler對象的是實例,而這個handler實例中就存有咱們建立的被代理對象的實例引用,在invoke方法中,傳入的實例對象就是咱們穿件的這個被代理對象;這樣就間接的持有了被代理對象的實例引用。

到此動態代理的生成過程,以及是如何調用invoke()方法的原理已經搞清楚,到此本文完結。

相關文章
相關標籤/搜索