设计模式之 - 代理模式(Proxy Pattern)
代理模式:代理是一種常用的設計模式,其目的就是為其他對象提供一個代理以控制對某個對象的訪問。代理類負責為委托類預處理消息,過濾消息并轉發消息,以及進行消息被委托類執行后的后續處理。很多可以框架中都有用到,比如: spring的AOP的實現主要就是動態代理, mybatis的Mapper代理等。
如下來看下代理模式的UML圖(來自百度圖片):
代理類和被代理類實現共同的接口, 其中代理類中包含一個被代理類的實例引用。代理模式可以分為靜態代理和動態代理,這里主要學習下動態代理。動態代理作用可以實現業務代理和通用邏輯代碼解耦,在不改變業務邏輯的同時,動態的給原邏輯代碼添加一些通用功能,比如打印調用日志,權限判定,事務處理等等。
下面用代碼實現動態代理:
1. 定義一個人的動作行為接口
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{@Overridepublic void personSay() {System.out.println("人在說話...");}@Overridepublic void personRunning() {System.out.println("人在跑步..."); }@Overridepublic void personEating() {System.out.println("人在吃東西...");} }3. 動態代理需要一個實現了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{//被代理的實例對象 PersonAction obj;private ProxyPerson(PersonAction obj){this.obj = obj;}@Overridepublic 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.Proxy
當方法在中的是分別執行我們在目標方法執行前后添加的代碼。
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 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. 到此我了解了代理對象的生產過程,但是代理對象和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()方法的原理已經搞清楚,到此本文完結。
轉載于:https://www.cnblogs.com/qq-361807535/p/7106457.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的设计模式之 - 代理模式(Proxy Pattern)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java 格式化时间计算
- 下一篇: 使用 VSCode 编写 .NET Co