使用代理模式建立表明(representative)對象,讓代理對象控制某對象的訪問,被代理的對象能夠是遠程的對象,建立開銷大的對象或須要安全控制的對象。——[Head First 設計模式]html
簡單的講就是 :爲服務對象提供代理,經過代理控制對服務對象的訪問範圍java
思考一下,其實在咱們生活中不乏出現代理模式的場景:git
爲何須要代理呢?由於代理最大的好處就是方便,上面例子,體現代理的第一個做用:服務透明,屏蔽具體實現,用戶沒法感知真正的服務提供方;可是經過代理就能夠實現本身的需求;github
保護的理解:好比鏈家,只提供房東的房屋信息,對於房東的其餘信息,客戶無從知道,從而起到了保護做用
簡化的理解:好比滴滴,對於司機來講,只負責正真的運輸,不關心開車之外的事情,好比,找客源,講價錢等等,簡化了整個流程。編程
代理模式最典型的的應用就是AOP;講到AOP,相信你們腦海裏,都會浮現出不少關鍵字:面向切面編程、靜態代理、JDK動態代理,CGLIB;設計模式
AOP(Aspect Orient Programming),面向切面編程,用於在系統各個模塊中的業務流程中織入通用的處理邏輯,好比事務管理、操做日誌、緩存、異常處理等等。api
有關AOP的介紹後續會用心的篇章來說解,本文主要講解代理的實現。緩存
從功能的角度劃分,能夠分爲:遠程代理、虛擬代理、保護代理、智能引用代理;
按實現的角度劃分,則有靜態代理和動態代理;動態代理還能夠分爲JDK動態代理和CGLIB動態代理;安全
在瞭解具體實現以前,先詳細看一下上圖;
首先是Service提供了服務接口,ServiceImpl和Proxy都必須實現Service接口;經過實現同個接口,使得上層調用能夠像ServiceImpl同樣使用Proxy的服務;
Proxy持有Service的引用,以便後續將請求轉發給ServiceImpl;
固然除了接口,基於繼承的方式也是能夠實現代理模式,好比Cglib動態代理;
瞭解以上內容,已經大概瞭解了代理模式的具體實現。oracle
假設咱們須要提供一個提供新增用戶的服務接口給上層業務使用;
咱們首先要先制定好接口UserService,將接口 addUser(String user) 開放給上層業務;
/** * 開放代理的接口 */ public interface UserService { public void addUser(String user); }
接下來,編寫具體實現的服務類 UserServiceImpl.java
/** * 實際業務邏輯 */ public class UserServiceImpl implements UserService{ @Override public void addUser(String user) { System.out.println(String.format("add user:%s success", user)); } }
測試使用靜態代理
/** * 代理模式測試類 */ public class ProxyTest { /** * 測試靜態代理 */ @Test public void testStaticProxy(){ UserService userService = new UserServiceImpl(); StaticProxy staticProxy = new StaticProxy(userService); staticProxy.addUser("xupeng.zhang"); } }
運行上述代碼輸出結果:
begin to execute static proxy to add user......
add user:xupeng.zhang success
execute static proxy to add user end. cost 0 ms**
採用靜態代理,咱們須要在代理類StaticProxy中傳入UserService的引用;
同時,代理類須要同時實現UserService的接口;只是簡單的將請求轉發給UserServiceImpl對象;
/** * 靜態代理類 */ public class StaticProxy implements UserService{ private UserService userService; public StaticProxy(UserService userService) { this.userService = userService; } @Override public void addUser(String user) { //統計耗時 Long executeTime = System.currentTimeMillis(); System.out.println("begin to execute static proxy to add user......"); userService.addUser(user); System.out.println(String.format("execute static proxy to add user end. cost %s ms", System.currentTimeMillis() - executeTime)); } }
以上即是靜態代理類的實現方式,比較簡單;可是若是UserService新增一個接口,那麼從UserService到Proxy,都要從新實現一下,並且統計耗時的代碼邏輯每一個方法都要寫一份。
有沒有什麼辦法能夠減小這種重複性的工做呢?固然有,那就是接下來要講的動態代理;
一樣須要實現 UserService 接口,須要持有 UserService的引用;
所不一樣的是,須要JDK幫咱們動態生成代理對象;
具體作法以下:
/** * 實際執行處理類 */ public class RealInvocationHandler implements InvocationHandler{ private Object target; public RealInvocationHandler(Object target) { this.target = target; } /** * 最終代理對象會將請求轉發到invoke方法執行 * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Long executeTime = System.currentTimeMillis(); System.out.println("begin to execute method in dynamic proxy..."); Object result = method.invoke(target, args); System.out.println(String.format("execute method by dynamic proxy end, cost %s ms", System.currentTimeMillis() - executeTime)); return result; } /** * 基於反射實現動態代理對象生成 * @param <T> * @return */ public <T> T getProxy(){ return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this); } }
測試使用JDK動態代理
/** * 代理模式測試類 */ public class ProxyTest { /** * 測試JDK動態代理 */ @Test public void testJDKDynamicProxy(){ UserService userService = new UserServiceImpl(); RealInvocationHandler realInvocationHandler = new RealInvocationHandler(userService); UserService userProxy = realInvocationHandler.getProxy(); userProxy.addUser("xupeng.zhang"); } }
運行上述代碼輸出結果:
begin to execute method in dynamic proxy...
add user:xupeng.zhang success
execute method by dynamic proxy end, cost 0 ms
Process finished with exit code 0**
上述代碼中,咱們並無手寫過代理類,JDK基於反射動態幫咱們生成了代理類:
/** * 基於反射實現動態代理對象生成 * @param <T> * @return */ public <T> T getProxy(){ return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);//this即 RealInvocationHandler對象 }
若是進去newProxyInstance看源碼的話,無非就如下三句關鍵代碼:
1. Class<?> cl = getProxyClass0(loader, intfs); //獲取代理類對象 2. final Constructor<?> cons = cl.getConstructor(constructorParams);//獲取代理類的構造方法 3. return cons.newInstance(new Object[]{h});//構造方法生成代理對象並返回
其中:
loader,指定代理對象的類加載器;
intfs 即 interfaces,代理對象須要實現的接口,能夠同時指定多個接口;
h 即 RealInvocationHandler對象,方法調用的實際處理者,代理對象的方法調用都會轉發到invoke方法。
繼續查看代理類的相關信息,你會發現JDK的動態代理竟然如此奇妙:
/** * 代理模式測試類 */ public class ProxyTest { @Test public void testGetJDKProxyClassInfo(){ UserService userService = new UserServiceImpl(); RealInvocationHandler realInvocationHandler = new RealInvocationHandler(userService); UserService userProxy = realInvocationHandler.getProxy(); System.out.println(userProxy.getClass().getName()); System.out.println(userProxy.getClass().getSuperclass()); System.out.println(userProxy.getClass().getInterfaces()[0].getName()); ProxyGeneratorUtils.saveProxyClass("D:/JdkProxy.class", userProxy.getClass()); } }
運行上述代碼輸出結果:
com.sun.proxy.$Proxy2
class java.lang.reflect.Proxy
com.orig.design.proxy.UserService**
代理類的類型是com.sun.proxy.$Proxy2;且繼承自Proxy.java;
此外還實現了UserService接口;
而咱們將UserSercieProxy.class的字節碼文件反編譯會發現如下關鍵代碼:
public final void addUser(String var1) throws { try { super.h.invoke(this, m3, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
最終addUser會將請求轉發給 RealInvocationHandler的 invoke方法執行;invoke方法調用method.invoke執行服務類對應的方法;
JDK生成的代理類已經繼承了Proxy類,Java不支持多重繼承,所以沒法實現繼承式的動態代理;這個時候就能夠採用Cglib動態代理來實現;
原理上跟JDK動態代理其實大同小異:
cglib會動態生成代理類,它只關注本身的父類,經過繼承得到父類非final的接口;以後將請求轉發給MethodInterceptor的intercept方法去作實際處理;
具體作法以下:
具體實現代碼以下:
/** * cglib動態代理 * 和JDK動態代理不一樣 * 是基於繼承實現 */ public class RealInterceptor implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); /** * 具體執行的回調方法 * @param o * @param method * @param args * @param methodProxy * @return * @throws Throwable */ @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Long currentTime = System.currentTimeMillis(); System.out.println("begin to execute cglib proxy to add user"); System.out.println(String.format("method: %s", method.getName())); Object result = methodProxy.invokeSuper(o, args);//注意這裏是invokeSuper System.out.println(String.format("execute cglib proxy end, cost: %s ms", System.currentTimeMillis()-currentTime)); return result; } /** * 設置代理的父類 * 設置回調方法 * @param tClass * @param <T> * @return */ public <T> T newProxyInstance(Class<T> tClass){ enhancer.setSuperclass(tClass); enhancer.setCallback(this); return (T) enhancer.create(); } }
運行上述代碼輸出結果:
begin to execute cglib proxy to add user
method: addUser
add user:xupeng.zhang success
execute cglib proxy end, cost: 22 ms**
經過上述結果能夠看出,Cglib性能上相對於JDK動態代理稍差;一樣的代理的實現,Cglib的實現比JDK動態代理的實現要耗時;
一樣的,咱們針對代理類的相關信息進行挖掘:
/** * 代理模式測試類 */ public class ProxyTest { /** * 獲取cglib代理類信息 */ @Test public void testGetCglibProxyClassInfo() throws Exception { RealInterceptor realInterceptor = new RealInterceptor(); UserRoleService userRoleProxy = realInterceptor.newProxyInstance(UserRoleService.class); System.out.println(userRoleProxy.getClass().getName()); System.out.println(userRoleProxy.getClass().getSuperclass()); System.out.println(userRoleProxy.getClass().getInterfaces()[0].getName()); ProxyGeneratorUtils.saveCglibProxyClass("D:/CglibProxy.class",realInterceptor.getEnhancer()); } }
運行上述代碼輸出結果:
com.orig.design.proxy.UserServiceImpl$$EnhancerByCGLIB$$591ab16e
class com.orig.design.proxy.UserServiceImpl
net.sf.cglib.proxy.Factory**
Cglib生成的代理類關鍵代碼以下:
public class UserRoleService$$EnhancerByCGLIB$$8ba328ed extends UserRoleService implements Factory { .......省略部分代碼 public final void addUserRole(String var1, String var2) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (this.CGLIB$CALLBACK_0 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; } if (var10000 != null) { var10000.intercept(this, CGLIB$addUserRole$0$Method, new Object[]{var1, var2}, CGLIB$addUserRole$0$Proxy); } else { super.addUserRole(var1, var2); } } }
Cglib動態代理類直接繼承了UserRoleService,並重寫了addUserRole方法;
此外,對原有的方法作了加強處理,若是Enhancer有設置設置回調方法,則會執行對應的回調方法interceptor;
/** * 設置代理的父類 * 設置回調方法 * @param tClass * @param <T> * @return */ public <T> T newProxyInstance(Class<T> tClass){ enhancer.setSuperclass(tClass);//設置繼承的父類 enhancer.setCallback(this);//設置回調函數,入參是Callback類型 return (T) enhancer.create(); } /**回調方法**/ public interface MethodInterceptor extends Callback { Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable; }
本文相關的代碼Demo已經上傳到github上:github