設計模式--JDK動態代理的實現與原理解析(1)

代理模式是一種很重要很有用的設計模式。被應用到很多著名的框架中,其中Spring Aop的底層就是通過JDK動態代理和CGLib動態代理實現的。其中,JDK動態代理是其默認實現。動態代理的作用就是在不修改原目標類的前提下,對目標類方法進行增強。比如橫切一些邏輯:事務管理,日誌記錄,檢驗等。J相對於靜態代理,JDK動態代理避免了重複編寫代理類的缺點。只需要簡單的指定一組接口及目標類就可以獲得代理對象。Java在JDK1.3以後支持JDK動態代理。
相比於CGLib動態代理,JDK動態代理的特點就是:被代理的目標類必須有接口,生成的代理類實際上是實現了此接口。JDK動態代理是通過Java反射機制實現的。
JDK提供的動態代理就是通過:java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口來實現的。InvocationHandler只提供了一個方法:invoke()方法,其具體的參數意義後面代碼中進行分析。其方法實現了代理的邏輯,相當於Spring中的增強:Advice。代理邏輯類需要實現此接口。另外,通過Proxy類的newProxyInstance()方法來獲得代理類,其具體參數的意義也在後邊代碼中進行解釋。
下面通過代碼,來看一下如何實現JDK的動態代理:
被代理的接口:

package com.blog.jdkproxy;

/**
 * @Description: 模擬用戶dao
 * @Author: Jingzeng Wang
 * @Date: Created in 20:51  2017/6/26.
 */
public interface IUserDao {

    /**
     * 模擬添加用戶
     * @return 添加成功的條數
     */
    int add();
}

接口的一個簡單實現類:

package com.blog.jdkproxy;

/**
 * @Description: 用戶操作實現類
 * @Author: Jingzeng Wang
 * @Date: Created in 20:53  2017/6/26.
 */
public class UserDao implements IUserDao{
    /**
     * 模擬添加用戶
     *
     * @return 添加成功的條數
     */
    public int add() {
        System.out.println("添加用戶成功!!!");
        return 0;
    }
}

實現代理邏輯:

package com.blog.jdkproxy;

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

/**
 * @Description: 代理實現
 * @Author: Jingzeng Wang
 * @Date: Created in 20:56  2017/6/26.
 */
public class MyInvocationHandler implements InvocationHandler {

    // 被代理的目標類
    private Object target;

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    /***
     *
     * @param proxy  生成的代理對象
     * @param method 被代理的目標類的方法
     * @param args   被代理的目標類方法的參數
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //前置增強邏輯
        System.out.println("---------------------before 事務開始--------------------");
        //調用目標類方法
        Object obj = method.invoke(target, args);
        //後置增強邏輯
        System.out.println("---------------------after 事務結束---------------------");
        return obj;
    }

    /**
     * 生成代理對象
     *
     * @return 返回代理對象(調用時轉型爲目標對象的接口)
     */
    public Object getProxy() {
      //參數意義:1. 當前類加載器 2. 目標類的class對象 3. 目標類的所有接口 4. 實現InvocationHandler接口的類
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(), this);
    }
}

測試類:

package com.blog.jdkproxy;

/**
 * @Description: JDK動態代理測試類
 * @Author: Jingzeng Wang
 * @Date: Created in 21:04  2017/6/26.
 */
public class Test {

    public static void main(String[] args) {
        IUserDao userDao = new UserDao();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(userDao);
        IUserDao proxy = (IUserDao) myInvocationHandler.getProxy();
        proxy.add();

        /**
         * 相同接口的代理對象,生成的是同一個代理類class com.sun.proxy.$Proxy0   $Proxy0類名
         * 是使用了緩存  WeakCache  類實現
         * If the proxy class defined by the given loader implementing the given interfaces exists, this will simply return the cached copy;
         * otherwise, it will create the proxy class via the ProxyClassFactory
         */
        IUserDao proxy2 = (IUserDao) myInvocationHandler.getProxy();

        IUserDao userDao3 = new UserDao();
        MyInvocationHandler myInvocationHandler3 = new MyInvocationHandler(userDao3);
        IUserDao proxy3 = (IUserDao) myInvocationHandler3.getProxy();

        System.out.println(proxy.getClass());
        System.out.println(proxy2.getClass());
        System.out.println(proxy3.getClass());
    }
}

結果爲:

---------------------before 事務開始--------------------
添加用戶成功!!!
---------------------after 事務結束---------------------
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0
class com.sun.proxy.$Proxy0

從結果上,可以看到,我們可以看到,已經對目標類的add方法完成了代理操作。同時,JDK幫我們生成的代理類名字爲:class com.sun.proxy.$Proxy0。同時可以看到,如果是實現的是相同接口的代理類,生成的是同一個代理類。這是因爲,JDK爲代理類做了緩存操作。
通過上邊的代碼,就可以實現一個簡單的JDK的動態代理。但是,JDK動態代理到底是怎麼完成的,JDK的Proxy類到底爲我們做了什麼,是怎麼生成的代理類,怎樣完成的方法的調用。這些問題需要查看源碼進行分析,將在下一篇博客中進行詳細的分析。

發佈了53 篇原創文章 · 獲贊 119 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章