本系列文章經補充和完善,已修訂整理成書《Java編程的邏輯》(馬俊昌著),由機械工業出版社華章分社出版,於2018年1月上市熱銷,讀者好評如潮!各大網店和書店有售,歡迎購買:京東自營連接 html
前面兩節,咱們介紹了反射和註解,利用它們,能夠編寫通用靈活的程序,本節,咱們來探討Java中另一個動態特性 - 動態代理。java
動態代理是一種強大的功能,它能夠在運行時動態建立一個類,實現一個或多個接口,能夠在不修改原有類的基礎上動態爲經過該類獲取的對象添加方法、修改行爲,這麼描述比較抽象,下文會具體介紹,這些特性使得它普遍應用於各類系統程序、框架和庫中,好比Spring, Hibernate, MyBatis, Guice等。git
動態代理是實現面向切面的編程(AOP - Aspect Oriented Programming)的基礎,切面的例子有日誌、性能監控、權限檢查、數據庫事務等,它們在程序的不少地方都會用到,代碼都差很少,但與某個具體的業務邏輯關係也不太密切,若是在每一個用到的地方都寫,代碼會很冗餘,也難以維護,AOP將這些切面與主體邏輯相分離,代碼簡單優雅的多。程序員
和註解相似,在大部分的應用編程中,咱們不須要本身實現動態代理,而只須要按照框架和庫的文檔說明進行使用就能夠了。不過,理解動態代理有助於咱們更爲深入的理解這些框架和庫,也能更好的應用它們,在本身的業務須要時,也能本身實現。github
理解動態代理,咱們首先要了解靜態代理,瞭解了靜態代理後,咱們再來看動態代理。動態代理有兩種實現方式,一種是Java SDK提供的,另一種是第三方庫如cglib提供的,咱們會分別介紹這兩種方式,包括其用法和基本實現原理,理解了基本概念和原理後,咱們來看一個簡單的應用,實現一個極簡的AOP框架。數據庫
咱們首先來看代理,代理是一個比較通用的詞,做爲一個軟件設計模式,它在《設計模式》一書中被提出,基本概念和平常生活中的概念是相似的,代理背後通常至少有一個實際對象,代理的外部功能和實際對象通常是同樣的,用戶與代理打交道,不直接接觸實際對象,雖然外部功能和實際對象同樣,但代理有它存在的價值,好比:編程
代理模式的代碼結構也比較簡單,咱們看個簡單的例子,代碼以下:swift
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。
在靜態代理中,代理類是直接定義在代碼中的,在動態代理中,代理類是動態生成的,怎麼動態生成呢?咱們用動態代理實現前面的例子:
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) 複製代碼
它有三個參數:
newProxyInstance的返回值類型爲Object,能夠強制轉換爲interfaces數組中的某個接口類型,這裏咱們強制轉換爲了IService類型,須要注意的是,它不能強制轉換爲某個類類型,好比RealService,即便它實際代理的對象類型爲RealService。
SimpleInvocationHandler實現了InvocationHandler,它的構造方法接受一個參數realObj表示被代理的對象,invoke方法處理全部的接口調用,它有三個參數:
在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須要兩個參數,一個是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一樣轉發給了InvocationHandler。
能夠看出,這個類定義自己與被代理的對象沒有關係,與InvocationHandler的具體實現也沒有關係,而主要與接口數組有關,給定這個接口數組,它動態建立每一個接口的實現代碼,實現就是轉發給InvocationHandler,與被代理對象的關係以及對它的調用由InvocationHandler的實現管理。
咱們是怎麼知道$Proxy0的定義的呢?對於Oracle的JVM,能夠配置java的一個屬性獲得,好比:
java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true shuo.laoma.dynamic.c86.SimpleJDKDynamicProxyDemo
複製代碼
以上命令會把動態生成的代理類Proxy0.class中,經過一些反編譯器工具好比JD-GUI就能夠獲得源碼。
理解了代理類的定義,後面的代碼就比較容易理解了,就是獲取構造方法,建立代理對象。
相比靜態代理,動態代理看起來麻煩了不少,它有什麼好處呢?使用它,能夠編寫通用的代理邏輯,用於各類類型的被代理對象,而不須要爲每一個被代理的類型都建立一個靜態代理類。看個簡單的示例:
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
複製代碼
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代理面向的是一組接口,它爲這些接口動態建立了一個實現類,接口的具體實現邏輯是經過自定義的InvocationHandler實現的,這個實現是自定義的,也就是說,其背後都不必定有真正被代理的對象,也可能多個實際對象,根據狀況動態選擇。cglib代理面向的是一個具體的類,它動態建立了一個新類,繼承了該類,重寫了其方法。
從代理的角度看,Java SDK代理的是對象,須要先有一個實際對象,自定義的InvocationHandler引用該對象,而後建立一個代理類和代理對象,客戶端訪問的是代理對象,代理對象最後再調用實際對象的方法,cglib代理的是類,建立的對象只有一個。
若是目的都是爲一個類的方法加強功能,Java SDK要求該類必須有接口,且只能處理接口中的方法,cglib沒有這個限制。
利用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中的類加載過程,探討如何利用自定義的類加載器實現更爲動態強大的功能。
(與其餘章節同樣,本節全部代碼位於 github.com/swiftma/pro…,位於包shuo.laoma.dynamic.c86下)
未完待續,查看最新文章,敬請關注微信公衆號「老馬說編程」(掃描下方二維碼),從入門到高級,深刻淺出,老馬和你一塊兒探索Java編程及計算機技術的本質。用心原創,保留全部版權。