设计模式之 - 代理模式(Proxy Pattern)
代理模式:代理是一種常用的設(shè)計(jì)模式,其目的就是為其他對象提供一個(gè)代理以控制對某個(gè)對象的訪問。代理類負(fù)責(zé)為委托類預(yù)處理消息,過濾消息并轉(zhuǎn)發(fā)消息,以及進(jìn)行消息被委托類執(zhí)行后的后續(xù)處理。很多可以框架中都有用到,比如: spring的AOP的實(shí)現(xiàn)主要就是動(dòng)態(tài)代理, mybatis的Mapper代理等。
如下來看下代理模式的UML圖(來自百度圖片):
代理類和被代理類實(shí)現(xiàn)共同的接口, 其中代理類中包含一個(gè)被代理類的實(shí)例引用。代理模式可以分為靜態(tài)代理和動(dòng)態(tài)代理,這里主要學(xué)習(xí)下動(dòng)態(tài)代理。動(dòng)態(tài)代理作用可以實(shí)現(xiàn)業(yè)務(wù)代理和通用邏輯代碼解耦,在不改變業(yè)務(wù)邏輯的同時(shí),動(dòng)態(tài)的給原邏輯代碼添加一些通用功能,比如打印調(diào)用日志,權(quán)限判定,事務(wù)處理等等。
下面用代碼實(shí)現(xiàn)動(dòng)態(tài)代理:
1. 定義一個(gè)人的動(dòng)作行為接口
package cn.aries.pattern.ProxyPattern; /*** 人的行為接口* @author aries*/ public interface PersonAction {/*** 說話*/public void personSay();/*** 跑步*/public void personRunning();/*** 吃東西*/public void personEating();}2. 創(chuàng)建人行為的的實(shí)現(xiàn)類
package cn.aries.pattern.ProxyPattern; public class PersonActionImpl implements PersonAction{@Overridepublic void personSay() {System.out.println("人在說話...");}@Overridepublic void personRunning() {System.out.println("人在跑步..."); }@Overridepublic void personEating() {System.out.println("人在吃東西...");} }3. 動(dòng)態(tài)代理需要一個(gè)實(shí)現(xiàn)了InvoketionHandler接口的類
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{//被代理的實(shí)例對象 PersonAction obj;private ProxyPerson(PersonAction obj){this.obj = obj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//執(zhí)行方法之前打印動(dòng)作開始。System.out.println(method.getName() + "ation start ...");//使用反射執(zhí)行目標(biāo)方法 method.invoke(obj, args);//在方法執(zhí)行結(jié)束時(shí)打印動(dòng)作結(jié)束。System.out.println(method.getName() + "ation end ...");return null;}//定義一個(gè)靜態(tài)方法生成代理對象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 {//設(shè)置系統(tǒng)參數(shù),將生成的代理類的class文件保存到本地System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");PersonAction pa = new PersonActionImpl();//調(diào)用生成代理類的方法PersonAction proxyPa = (PersonAction) ProxyPerson.getProxyPersonAction(pa);//用代理對象調(diào)用目標(biāo)方法 proxyPa.personSay();proxyPa.personRunning();proxyPa.personEating();//打印代理對象的父類 System.out.println(proxyPa.getClass().getSuperclass());} }
執(zhí)行結(jié)果:
personSayation start ...
人在說話...
personSayation end ...
personRunningation start ...
人在跑步...
personRunningation end ...
personEatingation start ...
人在吃東西...
personEatingation end ...
class java.lang.reflect.Proxy
當(dāng)方法在中的是分別執(zhí)行我們在目標(biāo)方法執(zhí)行前后添加的代碼。
5. 代理對象是通過Proxy.newProxyInstance(...)這個(gè)方法生成的,我們進(jìn)入源代碼查看下
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.這里生成代理類的字節(jié)碼文件 */Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.
*/try {
//在這里獲取代理類的構(gòu)造函數(shù),從前面的運(yùn)行結(jié)果中可以得知,代理類是Proxy類的子類
//而constructorParams在Proxy類中是一個(gè)靜態(tài)的常量: private static final Class<?>[] constructorParams =?{ InvocationHandler.class };
//所以這里獲取的帶InvocationHandler對象為入?yún)⒌臉?gòu)造函數(shù),也就是其父類Proxy的構(gòu)造函數(shù):protected Proxy(InvocationHandler h){...}final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;
//這里調(diào)用newInstance()方法創(chuàng)建代理對象,其內(nèi)部實(shí)現(xiàn)是: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 permissionreturn 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. 到此我了解了代理對象的生產(chǎn)過程,但是代理對象和handler是什么關(guān)系呢,又是如何調(diào)用其invoke(...)方法呢,這暫時(shí)是個(gè)謎團(tuán)讓我們來看下生成的代理類的源碼,這些就都清楚了。
? 注:System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); 這個(gè)是設(shè)置系統(tǒng)參數(shù),將生產(chǎn)的代理類自己碼文件保存在本地,然后我們通過反編譯就可以獲得其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 {//這五個(gè)靜態(tài)變量前三個(gè)m0,m1,m2分別是代理類繼承的Object類的hashcode(),equals(),toString()方法//其他從m3開始是繼承的們定義的接口類的方法根據(jù)方法的多少m后面的數(shù)字遞增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 {//這里使用靜態(tài)代碼塊對通過反射對代理對象中的方法進(jìn)行實(shí)例化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方法進(jìn)行實(shí)現(xiàn)//根據(jù)類文件我們可以看到,代理類繼承了Proxy類,所以其成員變量中包含一個(gè)Handler實(shí)例對象的引用//在創(chuàng)建代理實(shí)例對象的時(shí)候,我們使用的protected Proxy(InvocationHandler h) {this.h = h;}這個(gè)構(gòu)造函數(shù)//所以下面的h就是我們傳進(jìn)去的handler對象//這里使用handler對象調(diào)用自己的invoke()方法,m3就是我們要執(zhí)行的方法,//后面的方法的參數(shù),如果有參數(shù)就傳對應(yīng)的參數(shù),沒有就傳null//此時(shí)我們明白了代理對象和handler的關(guān)系,以及如何調(diào)用到invoke()方法有了明確的認(rèn)識(shí)了。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);}}//這里原理同上,為了節(jié)省空間這里就不貼出來了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);}} }寫完后瀏覽了一下,好像沒有發(fā)現(xiàn)被代理對象的引用在代理類中出現(xiàn);然后想了下,代理類繼承了Proxy類,其中Proxy類中有我們寫的InvoketionHandler對象的是實(shí)例,而這個(gè)handler實(shí)例中就存有我們創(chuàng)建的被代理對象的實(shí)例引用,在invoke方法中,傳入的實(shí)例對象就是我們穿件的這個(gè)被代理對象;這樣就間接的持有了被代理對象的實(shí)例引用。
到此動(dòng)態(tài)代理的生成過程,以及是如何調(diào)用invoke()方法的原理已經(jīng)搞清楚,到此本文完結(jié)。
轉(zhuǎn)載于:https://www.cnblogs.com/qq-361807535/p/7106457.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的设计模式之 - 代理模式(Proxy Pattern)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 格式化时间计算
- 下一篇: 使用 VSCode 编写 .NET Co