代理模式是一種很重要很有用的設計模式。被應用到很多著名的框架中,其中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類到底爲我們做了什麼,是怎麼生成的代理類,怎樣完成的方法的調用。這些問題需要查看源碼進行分析,將在下一篇博客中進行詳細的分析。