本節探討Java中的動態代理,介紹其用法和基本實現原理,利用它實現簡單的AOP框架 ...
前面兩節,我們介紹了反射和註解,利用它們,可以編寫通用靈活的程式,本節,我們來探討Java中另外一個動態特性 - 動態代理。
動態代理是一種強大的功能,它可以在運行時動態創建一個類,實現一個或多個介面,可以在不修改原有類的基礎上動態為通過該類獲取的對象添加方法、修改行為,這麼描述比較抽象,下文會具體介紹,這些特性使得它廣泛應用於各種系統程式、框架和庫中,比如Spring, Hibernate, MyBatis, Guice等。
動態代理是實現面向切麵的編程(AOP - Aspect Oriented Programming)的基礎,切麵的例子有日誌、性能監控、許可權檢查、資料庫事務等,它們在程式的很多地方都會用到,代碼都差不多,但與某個具體的業務邏輯關係也不太密切,如果在每個用到的地方都寫,代碼會很冗餘,也難以維護,AOP將這些切麵與主體邏輯相分離,代碼簡單優雅的多。
和註解類似,在大部分的應用編程中,我們不需要自己實現動態代理,而只需要按照框架和庫的文檔說明進行使用就可以了。不過,理解動態代理有助於我們更為深刻的理解這些框架和庫,也能更好的應用它們,在自己的業務需要時,也能自己實現。
理解動態代理,我們首先要瞭解靜態代理,瞭解了靜態代理後,我們再來看動態代理。動態代理有兩種實現方式,一種是Java SDK提供的,另外一種是第三方庫如cglib提供的,我們會分別介紹這兩種方式,包括其用法和基本實現原理,理解了基本概念和原理後,我們來看一個簡單的應用,實現一個極簡的AOP框架。
靜態代理
我們首先來看代理,代理是一個比較通用的詞,作為一個軟體設計模式,它在《設計模式》一書中被提出,基本概念和日常生活中的概念是類似的,代理背後一般至少有一個實際對象,代理的外部功能和實際對象一般是一樣的,用戶與代理打交道,不直接接觸實際對象,雖然外部功能和實際對象一樣,但代理有它存在的價值,比如:
- 節省成本比較高的實際對象的創建開銷,按需延遲載入,創建代理時並不真正創建實際對象,而只是保存實際對象的地址,在需要時再載入或創建
- 執行許可權檢查,代理檢查許可權後,再調用實際對象
- 屏蔽網路差異和複雜性,代理在本地,而實際對象在其他伺服器上,調用本地代理時,本地代理請求其他伺服器
代理模式的代碼結構也比較簡單,我們看個簡單的例子,代碼如下:
public class SimpleStaticProxyDemo { static interface IService { public void sayHello(); } static class RealService implements IService { @Override public void sayHello() { System.out.println("hello"); } } static class TraceProxy implements IService { private IService realService; public TraceProxy(IService realService) { this.realService = realService; } @Override public void sayHello() { System.out.println("entering sayHello"); this.realService.sayHello(); System.out.println("leaving sayHello"); } } public static void main(String[] args) { IService realService = new RealService(); IService proxyService = new TraceProxy(realService); proxyService.sayHello(); } }
代理和實際對象一般有相同的介面,在這個例子中,共同的介面是IService,實際對象是RealService,代理是TraceProxy。TraceProxy內部有一個IService的成員變數,指向實際對象,在構造方法中被初始化,對於方法sayHello的調用,它轉發給了實際對象,在調用前後輸出了一些跟蹤調試信息,程式輸出為:
entering sayHello hello leaving sayHello
我們在54節介紹過兩種設計模式,適配器和裝飾器,它們與代理模式有點類似,它們的背後都有一個別的實際對象,都是通過組合的方式指向該對象,不同之處在於,適配器是提供了一個不一樣的新介面,裝飾器是對原介面起到了"裝飾"作用,可能是增加了新介面、修改了原有的行為等,代理一般不改變介面。不過,我們並不想強調它們的差別,可以將它們看做代理的變體,統一看待。
在上面的例子中,我們想達到的目的是在實際對象的方法調用前後加一些調試語句,為了在不修改原類的情況下達到這個目的,我們在代碼中創建了一個代理類TraceProxy,它的代碼是在寫程式時固定的,所以稱為靜態代理。
輸出跟蹤調試信息是一個通用需求,可以想象,如果每個類都需要,而又不希望修改類定義,我們需要為每個類創建代理,實現所有介面,這個工作就太繁瑣了,如果再有其他的切麵需求呢,整個工作可能又要重來一遍。
這時,就需要動態代理了,主要有兩種方式實現動態代理,Java SDK和第三方庫cglib,我們先來看Java SDK。
Java SDK動態代理
用法
在靜態代理中,代理類是直接定義在代碼中的,在動態代理中,代理類是動態生成的,怎麼動態生成呢?我們用動態代理實現前面的例子:
public class SimpleJDKDynamicProxyDemo { static interface IService { public void sayHello(); } static class RealService implements IService { @Override public void sayHello() { System.out.println("hello"); } } static class SimpleInvocationHandler implements InvocationHandler { private Object realObj; public SimpleInvocationHandler(Object realObj) { this.realObj = realObj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("entering " + method.getName()); Object result = method.invoke(realObj, args); System.out.println("leaving " + method.getName()); return result; } } public static void main(String[] args) { IService realService = new RealService(); IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService)); proxyService.sayHello(); } }
代碼看起來更為複雜了,這有什麼用呢?彆著急,我們慢慢解釋。IService和RealService的定義不變,程式的輸出也沒變,但代理對象proxyService的創建方式變了,它使用java.lang.reflect包中的Proxy類的靜態方法newProxyInstance來創建代理對象,這個方法的聲明如下:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
它有三個參數:
- loader表示類載入器,下節我們會單獨探討它,例子使用和IService一樣的類載入器
- interfaces表示代理類要實現的介面列表,是一個數組,元素的類型只能是介面,不能是普通的類,例子中只有一個IService
- h的類型為InvocationHandler,它是一個介面,也定義在java.lang.reflect包中,它只定義了一個方法invoke,對代理介面所有方法的調用都會轉給該方法
newProxyInstance的返回值類型為Object,可以強制轉換為interfaces數組中的某個介面類型,這裡我們強制轉換為了IService類型,需要註意的是,它不能強制轉換為某個類類型,比如RealService,即使它實際代理的對象類型為RealService。
SimpleInvocationHandler實現了InvocationHandler,它的構造方法接受一個參數realObj表示被代理的對象,invoke方法處理所有的介面調用,它有三個參數:
- proxy表示代理對象本身,需要註意,它不是被代理的對象,這個參數一般用處不大
- method表示正在被調用的方法
- args表示方法的參數
在SimpleInvocationHandler的invoke實現中,我們調用了method的invoke方法,傳遞了實際對象realObj作為參數,達到了調用實際對象對應方法的目的,在調用任何方法前後,我們輸出了跟蹤調試語句。需要註意的是,不能將proxy作為參數傳遞給method.invoke,比如:
Object result = method.invoke(proxy, args);
上面的語句會出現死迴圈,因為proxy表示當前代理對象,這麼調用又會調用到SimpleInvocationHandler的invoke方法。
基本原理
看了上面的介紹是不是更暈了,沒關係,看下Proxy.newProxyInstance的內部就理解了。上面例子中創建proxyService的代碼可以用如下代碼代替:
Class<?> proxyCls = Proxy.getProxyClass(IService.class.getClassLoader(), new Class<?>[] { IService.class }); Constructor<?> ctor = proxyCls.getConstructor(new Class<?>[] { InvocationHandler.class }); InvocationHandler handler = new SimpleInvocationHandler(realService); IService proxyService = (IService) ctor.newInstance(handler);
分為三步:
- 通過Proxy.getProxyClass創建代理類定義,類定義會被緩存
- 獲取代理類的構造方法,構造方法有一個InvocationHandler類型的參數
- 創建InvocationHandler對象,創建代理類對象
Proxy.getProxyClass需要兩個參數,一個是ClassLoader,另一個是介面數組,它會動態生成一個類,類名以$Proxy開頭,後跟一個數字,對於上面的例子,動態生成的類定義如下所示,為簡化起見,我們忽略了異常處理的代碼:
final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } public final void sayHello() { this.h.invoke(this, m3, null); } public final String toString() { return (String) this.h.invoke(this, m2, null); } public final int hashCode() { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } static { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService") .getMethod("sayHello",new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } }
$Proxy0的父類是Proxy,它有一個構造方法,接受一個InvocationHandler類型的參數,保存為了實例變數h,h定義在父類Proxy中,它實現了介面IService,對於每個方法,如sayHello,它調用InvocationHandler的invoke方法,對於Object中的方法,如hashCode, equals和toString, $Proxy0同樣轉發給了InvocationHandler。
可以看出,這個類定義本身與被代理的對象沒有關係,與InvocationHandler的具體實現也沒有關係,而主要與介面數組有關,給定這個介面數組,它動態創建每個介面的實現代碼,實現就是轉發給InvocationHandler,與被代理對象的關係以及對它的調用由InvocationHandler的實現管理。
我們是怎麼知道$Proxy0的定義的呢?對於Oracle的JVM,可以配置java的一個屬性得到,比如:
java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true shuo.laoma.dynamic.c86.SimpleJDKDynamicProxyDemo
以上命令會把動態生成的代理類$Proxy0保存到文件$Proxy0.class中,通過一些反編譯器工具比如JD-GUI(http://jd.benow.ca/)就可以得到源碼。
理解了代理類的定義,後面的代碼就比較容易理解了,就是獲取構造方法,創建代理對象。
動態代理的優點
相比靜態代理,動態代理看起來麻煩了很多,它有什麼好處呢?使用它,可以編寫通用的代理邏輯,用於各種類型的被代理對象,而不需要為每個被代理的類型都創建一個靜態代理類。看個簡單的示例:
public class GeneralProxyDemo { static interface IServiceA { public void sayHello(); } static class ServiceAImpl implements IServiceA { @Override public void sayHello() { System.out.println("hello"); } } static interface IServiceB { public void fly(); } static class ServiceBImpl implements IServiceB { @Override public void fly() { System.out.println("flying"); } } static class SimpleInvocationHandler implements InvocationHandler { private Object realObj; public SimpleInvocationHandler(Object realObj) { this.realObj = realObj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("entering " + realObj.getClass().getSimpleName() + "::" + method.getName()); Object result = method.invoke(realObj, args); System.out.println("leaving " + realObj.getClass().getSimpleName() + "::" + method.getName()); return result; } } @SuppressWarnings("unchecked") private static <T> T getProxy(Class<T> intf, T realObj) { return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class<?>[] { intf }, new SimpleInvocationHandler(realObj)); } public static void main(String[] args) throws Exception { IServiceA a = new ServiceAImpl(); IServiceA aProxy = getProxy(IServiceA.class, a); aProxy.sayHello(); IServiceB b = new ServiceBImpl(); IServiceB bProxy = getProxy(IServiceB.class, b); bProxy.fly(); } }
在這個例子中,有兩個介面IServiceA和IServiceB,它們對應的實現類是ServiceAImpl和ServiceBImpl,雖然它們的介面和實現不同,但利用動態代理,它們可以調用同樣的方法getProxy獲取代理對象,共用同樣的代理邏輯SimpleInvocationHandler,即在每個方法調用前後輸出一條跟蹤調試語句。程式輸出為:
entering ServiceAImpl::sayHello
hello
leaving ServiceAImpl::sayHello
entering ServiceBImpl::fly
flying
leaving ServiceBImpl::fly
cglib動態代理
用法
Java SDK動態代理的局限在於,它只能為介面創建代理,返回的代理對象也只能轉換到某個介面類型,如果一個類沒有介面,或者希望代理非介面中定義的方法,那就沒有辦法了。有一個第三方的類庫cglib(https://github.com/cglib/cglib)可以做到這一點,Spring,Hibernate等都使用該類庫。我們看個簡單的例子:
public class SimpleCGLibDemo { static class RealService { public void sayHello() { System.out.println("hello"); } } static class SimpleInterceptor implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("entering " + method.getName()); Object result = proxy.invokeSuper(object, args); System.out.println("leaving " + method.getName()); return result; } } @SuppressWarnings("unchecked") private static <T> T getProxy(Class<T> cls) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(cls); enhancer.setCallback(new SimpleInterceptor()); return (T) enhancer.create(); } public static void main(String[] args) throws Exception { RealService proxyService = getProxy(RealService.class); proxyService.sayHello(); } }
RealService表示被代理的類,它沒有介面。getProxy()為一個類生成代理對象,這個代理對象可以安全的轉換為被代理類的類型,它使用了cglib的Enhancer類,Enhancer類的setSuperclass設置被代理的類,setCallback設置被代理類的public非final方法被調用時的處理類,Enhancer支持多種類型,這裡使用的類實現了MethodInterceptor介面,它與Java SDK中的InvocationHandler有點類似,方法名稱變成了intercept,多了一個MethodProxy類型的參數。
與前面的InvocationHandler不同,SimpleInterceptor中沒有被代理的對象,它通過MethodProxy的invokeSuper方法調用被代理類的方法:
Object result = proxy.invokeSuper(object, args);
註意,它不能這樣調用被代理類的方法:
Object result = method.invoke(object, args);
object是代理對象,調用這個方法還會調用到SimpleInterceptor的intercept方法,造成死迴圈。
在main方法中,我們也沒有創建被代理的對象,創建的對象直接就是代理對象。
基本實現原理
cglib的實現機制與Java SDK不同,它是通過繼承實現的,它也是動態創建了一個類,但這個類的父類是被代理的類,代理類重寫了父類的所有public非final方法,改為調用Callback中的相關方法,在上例中,調用SimpleInterceptor的intercept方法。
Java SDK代理與cglib代理比較
Java SDK代理面向的是一組介面,它為這些介面動態創建了一個實現類,介面的具體實現邏輯是通過自定義的InvocationHandler實現的,這個實現是自定義的,也就是說,其背後都不一定有真正被代理的對象,也可能多個實際對象,根據情況動態選擇。cglib代理面向的是一個具體的類,它動態創建了一個新類,繼承了該類,重寫了其方法。
從代理的角度看,Java SDK代理的是對象,需要先有一個實際對象,自定義的InvocationHandler引用該對象,然後創建一個代理類和代理對象,客戶端訪問的是代理對象,代理對象最後再調用實際對象的方法,cglib代理的是類,創建的對象只有一個。
如果目的都是為一個類的方法增強功能,Java SDK要求該類必須有介面,且只能處理介面中的方法,cglib沒有這個限制。
動態代理的應用 - AOP
利用cglib動態代理,我們實現一個極簡的AOP框架,演示AOP的基本思路和技術。
用法
我們添加一個新的註解@Aspect,其定義為:
@Retention(RUNTIME) @Target(TYPE) public @interface Aspect { Class<?>[] value(); }
它用於註解切麵類,它有一個參數,可以指定要增強的類,比如:
@Aspect({ServiceA.class,ServiceB.class}) public class ServiceLogAspect
ServiceLogAspect就是一個切麵,它負責類ServiceA和ServiceB的日誌切麵,即為這兩個類增加日誌功能。
再比如:
@Aspect({ServiceB.class}) public class ExceptionAspect
ExceptionAspect也是一個切麵,它負責類ServiceB的異常切麵。
這些切麵類與主體類怎麼協作呢?我們約定,切麵類可以聲明三個方法before/after/exception,在主體類的方法調用前/調用後/出現異常時分別調用這三個方法,這三個方法的聲明需符合如下簽名:
public static void before(Object object, Method method, Object[] args) public static void after(Object object, Method method, Object[] args, Object result) public static void exception(Object object, Method method, Object[] args, Throwable e)
object, method和args與cglib MethodInterceptor中的invoke參數一樣,after中的result表示方法執行的結果,exception中的e表示發生的異常類型。
ServiceLogAspect實現了before和after方法,加了一些日誌,其代碼為:
@Aspect({ ServiceA.class, ServiceB.class }) public class ServiceLogAspect { public static void before(Object object, Method method, Object[] args) { System.out.println("entering " + method.getDeclaringClass().getSimpleName() + "::" + method.getName() + ", args: " + Arrays.toString(args)); } public static void after(Object object, Method method, Object[] args, Object result) { System.out.println("leaving " + method.getDeclaringClass().getSimpleName() + "::" + method.getName() + ", result: " + result); } }
ExceptionAspect只實現exception方法,在異常發生時,輸出一些信息,代碼為:
@Aspect({ ServiceB.class }) public class ExceptionAspect { public static void exception(Object object, Method method, Object[] args, Throwable e) { System.err.println("exception when calling: " + method.getName() + "," + Arrays.toString(args)); } }
ServiceLogAspect的目的是在類ServiceA和ServiceB所有方法的執行前後加一些日誌,而ExceptionAspect的目的是在類ServiceB的方法執行出現異常時收到通知並輸出一些信息。它們都沒有修改類ServiceA和ServiceB本身,本身做的事是比較通用的,與ServiceA和ServiceB的具體邏輯關係也不密切,但又想改變ServiceA/ServiceB的行為,這就是AOP的思維。
只是聲明一個切麵類是不起作用的,我們需要與上節介紹的DI容器結合起來,我們實現一個新的容器CGLibContainer,它有一個方法:
public static <T> T getInstance(Class<T> cls)
通過該方法獲取ServiceA或ServiceB,它們的行為就會被改變,ServiceA和ServiceB的定義與上節一樣,這裡重覆下:
public class ServiceA { @SimpleInject ServiceB b; public void callB(){ b.action(); } } public class ServiceB { public void action(){ System.out.println("I'm B"); } }
通過CGLibContainer獲取ServiceA,會自動應用ServiceLogAspect,比如:
ServiceA a = CGLibContainer.getInstance(ServiceA.class); a.callB();
輸出為:
entering ServiceA::callB, args: [] entering ServiceB::action, args: [] I'm B leaving ServiceB::action, result: null leaving ServiceA::callB, result: null
實現原理
這是怎麼做到的呢?CGLibContainer在初始化的時候,會分析帶有@Aspect註解的類,分析出每個類的方法在調用前/調用後/出現異常時應該調用哪些方法,在創建該類的對象時,如果有需要被調用的方法,則創建一個動態代理對象,下麵我們具體來看下代碼。
為簡化起見,我們基於上節介紹的DI容器的第一個版本,即每次獲取對象時都創建一個,不支持單例。
我們定義一個枚舉InterceptPoint,表示切點(調用前/調用後/出現異常):
public static enum InterceptPoint { BEFORE, AFTER, EXCEPTION }
在CGLibContainer中定義一個靜態變數,表示每個類的每個切點的方法列表,定義如下:
static Map<Class<?>, Map<InterceptPoint, List<Method>>> interceptMethodsMap = new HashMap<>();
我們在CGLibContainer的類初始化過程中初始化該對象,方法是分析每個帶有@Aspect註解的類,這些類一般可以通過掃描所有的類得到,為簡化起見,我們將它們寫在代碼中,如下所示:
static Class<?>[] aspects = new Class<?>[] { ServiceLogAspect.class, ExceptionAspect.class };
分析這些帶@Aspect註解的類,並初始化interceptMethodsMap的代碼如下所示:
static { init(); } private static void init() { for (Class<?> cls : aspects) { Aspect aspect = cls.getAnnotation(Aspect.class); if (aspect != null) { Method before = getMethod(cls, "before", new Class<?>[] { Object.class, Method.class, Object[].class }); Method after = getMethod(cls, "after", new Class<?>[] { Object.class, Method.class, Object[].class, Object.class }); Method exception = getMethod(cls, "exception", new Class<?>[] { Object.class, Method.class, Object[].class, Throwable.class }); Class<?>[] intercepttedArr = aspect.value(); for (Class<?> interceptted : intercepttedArr) { addInterceptMethod(interceptted, InterceptPoint.BEFORE, before); addInterceptMethod(interceptted, InterceptPoint.AFTER, after); addInterceptMethod(interceptted, InterceptPoint.EXCEPTION, exception); } } } }
對每個切麵,即帶有@Aspect註解的類cls,查找其before/after/exception方法,調用方法addInterceptMethod將其加入目標類的切點方法列表中,addInterceptMethod的代碼為:
private static void addInterceptMethod(Class<?> cls, InterceptPoint point, Method method) { if (method == null) { return; } Map<InterceptPoint, List<Method>> map = interceptMethodsMap.get(cls); if (map == null) { map = new HashMap<>(); interceptMethodsMap.put(cls, map); } List<Method> methods = map.get(point); if (methods == null) { methods = new ArrayList<>(); map.put(point, methods); } methods.add(method); }
準備好了每個類的每個切點的方法列表,我們來看根據類型創建實例的代碼:
private static <T> T createInstance(Class<T> cls) throws InstantiationException, IllegalAccessException { if (!interceptMethodsMap.containsKey(cls)) { return (T) cls.newInstance(); } Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(cls); enhancer.setCallback(new AspectInterceptor()); return (T) enhancer.create(); }
如果類型cls不需要增強,則直接調用cls.newInstance(),否則使用cglib創建動態代理,callback為AspectInterceptor,其代碼為:
static class AspectInterceptor implements MethodInterceptor { @Override public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable { //執行before方法 List<Method> beforeMethods = getInterceptMethods( object.getClass().getSuperclass(), InterceptPoint.BEFORE); for (Method m : beforeMethods) { m.invoke(null, new Object[] { object, method, args }); } try { // 調用原始方法 Object result = proxy.invokeSuper(object, args); // 執行after方法 List<Method> afterMethods = getInterceptMethods( object.getClass().getSuperclass(), InterceptPoint.AFTER); for (Method m : afterMethods) { m.invoke(null, new Object[] { object, method, args, result }); } return result; } catch (Throwable e) { //執行exception方法 List<Method> exceptionMethods = getInterceptMethods( object.getClass().getSuperclass(), InterceptPoint.EXCEPTION); for (Method m : exceptionMethods) { m.invoke(null, new Object[] { object, method, args, e }); } throw e; } } }
這個代碼也容易理解,它根據原始類的實際類型查找應該執行的before/after/exception方法列表,在調用原始方法前執行before方法,執行後執行after方法,出現異常時執行exception方法,getInterceptMethods方法的代碼為:
static List<Method> getInterceptMethods(Class<?> cls, InterceptPoint point) { Map<InterceptPoint, List<Method>> map = interceptMethodsMap.get(cls); if (map == null) { return Collections.emptyList(); } List<Method> methods = map.get(point); if (methods == null) { return Collections.emptyList(); } return methods; }
這個代碼也容易理解。
CGLibContainer最終的getInstance方法就簡單了,它調用createInstance創建實例,代碼如下所示:
public static <T> T getInstance(Class<T> cls) { try { T obj = createInstance(cls); Field[] fields = cls.getDeclaredFields(); for (Field f : fields) { if (f.isAnnotationPresent(SimpleInject.class)) { if (!f.isAccessible()) { f.setAccessible(true); } Class<?> fieldCls = f.getType(); f.set(obj, getInstance(fieldCls)); } } return obj; } catch (Exception e) { throw new RuntimeException(e); } }
完整的代碼可以在github上獲取,文末有鏈接。這個AOP的實現是非常粗糙的,主要用於解釋動態代理的應用和AOP的一些基本思路和原理。
小結
本節探討了Java中的代理,從靜態代理到兩種動態代理,動態代理廣泛應用於各種系統程式、框架和庫中,用於為應用程式員提供易用的支持、實現AOP、以及其他靈活通用的功能,理解了動態代理,我們就能更好的利用這些系統程式、框架和庫,在需要的時候,也可以自己創建動態代理。
下一節,我們來進一步理解Java中的類載入過程,探討如何利用自定義的類載入器實現更為動態強大的功能。
(與其他章節一樣,本節所有代碼位於 https://github.com/swiftma/program-logic,位於包shuo.laoma.dynamic.c86下)
----------------
未完待續,查看最新文章,敬請關註微信公眾號“老馬說編程”(掃描下方二維碼),從入門到高級,深入淺出,老馬和你一起探索Java編程及電腦技術的本質。用心原創,保留所有版權。