Java JDK 動態代理(AOP)使用及實現原理分析
一、什麼是代理?
代理是一種常用的設計模式,其目的就是為其他物件提供一個代理以控制對某個物件的訪問。代理類負責為委託類預處理訊息,過濾訊息並轉發訊息,以及進行訊息被委託類執行後的後續處理。
代理模式UML圖:
簡單結構示意圖:
為了保持行為的一致性,代理類和委託類通常會實現相同的介面,所以在訪問者看來兩者沒有絲毫的區別。通過代理類這中間一層,能有效控制對委託類物件的直接訪問,也可以很好地隱藏和保護委託類物件,同時也為實施不同控制策略預留了空間,從而在設計上獲得了更大的靈活性。Java 動態代理機制以巧妙的方式近乎完美地實踐了代理模式的設計理念。
二、Java 動態代理類
Java動態代理類位於java.lang.reflect包下,一般主要涉及到以下兩個類:
(1)Interface InvocationHandler:該介面中僅定義了一個方法
publicobject invoke(Object obj,Method method, Object[] args)
在實際使用時,第一個引數obj一般是指代理類,method是被代理的方法,如上例中的request(),args為該方法的引數陣列。這個抽象方法在代理類中動態實現。
(2)Proxy:該類即為動態代理類,其中主要包含以下內容:
protected Proxy(InvocationHandler h):建構函式,用於給內部的h賦值。
static Class getProxyClass (ClassLoaderloader, Class[] interfaces):獲得一個代理類,其中loader是類裝載器,interfaces是真實類所擁有的全部介面的陣列。
static Object newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h):返回代理類的一個例項,返回後的代理類可以當作被代理類使用(可使用被代理類的在Subject介面中宣告過的方法)
所謂DynamicProxy是這樣一種class:它是在執行時生成的class,在生成它時你必須提供一組interface給它,然後該class就宣稱它實現了這些 interface。你當然可以把該class的例項當作這些interface中的任何一個來用。當然,這個DynamicProxy其實就是一個Proxy,它不會替你作實質性的工作,在生成它的例項時你必須提供一個handler,由它接管實際的工作。
在使用動態代理類時,我們必須實現InvocationHandler介面
通過這種方式,被代理的物件(RealSubject)可以在執行時動態改變,需要控制的介面(Subject介面)可以在執行時改變,控制的方式(DynamicSubject類)也可以動態改變,從而實現了非常靈活的動態代理關係。
動態代理步驟:
1.建立一個實現介面InvocationHandler的類,它必須實現invoke方法
2.建立被代理的類以及介面
3.通過Proxy的靜態方法
newProxyInstance(ClassLoaderloader, Class[] interfaces, InvocationHandler h)建立一個代理
4.通過代理呼叫方法
三、JDK的動態代理怎麼使用?
package jiankunking;
/**
* 需要動態代理的介面
*/
public interface Subject
{
/**
* 你好
*
* @param name
* @return
*/
public String SayHello(String name);
/**
* 再見
*
* @return
*/
public String SayGoodBye();
}
2、需要代理的實際物件
package jiankunking;
/**
* 實際物件
*/
public class RealSubject implements Subject
{
/**
* 你好
*
* @param name
* @return
*/
public String SayHello(String name)
{
return "hello " + name;
}
/**
* 再見
*
* @return
*/
public String SayGoodBye()
{
return " good bye ";
}
}
3、呼叫處理器實現類(有木有感覺這裡就是傳說中的AOP啊)
package jiankunking;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 呼叫處理器實現類
* 每次生成動態代理類物件時都需要指定一個實現了該介面的呼叫處理器物件
*/
public class InvocationHandlerImpl implements InvocationHandler
{
/**
* 這個就是我們要代理的真實物件
*/
private Object subject;
/**
* 構造方法,給我們要代理的真實物件賦初值
*
* @param subject
*/
public InvocationHandlerImpl(Object subject)
{
this.subject = subject;
}
/**
* 該方法負責集中處理動態代理類上的所有方法呼叫。
* 呼叫處理器根據這三個引數進行預處理或分派到委託類例項上反射執行
*
* @param proxy 代理類例項
* @param method 被呼叫的方法物件
* @param args 呼叫引數
* @return
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//在代理真實物件前我們可以新增一些自己的操作
System.out.println("在呼叫之前,我要乾點啥呢?");
System.out.println("Method:" + method);
//當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫
Object returnValue = method.invoke(subject, args);
//在代理真實物件後我們也可以新增一些自己的操作
System.out.println("在呼叫之後,我要乾點啥呢?");
return returnValue;
}
}
4、測試package jiankunking;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 動態代理演示
*/
public class DynamicProxyDemonstration
{
public static void main(String[] args)
{
//代理的真實物件
Subject realSubject = new RealSubject();
/**
* InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發
* 其內部通常包含指向委託類例項的引用,用於真正執行分派轉發過來的方法呼叫.
* 即:要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件來呼叫其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = realSubject.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
/**
* 該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
System.out.println("動態代理物件的型別:"+subject.getClass().getName());
String hello = subject.SayHello("jiankunking");
System.out.println(hello);
// String goodbye = subject.SayGoodBye();
// System.out.println(goodbye);
}
}
5、輸出結果如下:
演示demo下載地址:http://download.csdn.net/detail/xunzaosiyecao/9597388
四、動態代理怎麼實現的?
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
通過跟蹤提示程式碼可以看出:當代理物件呼叫真實物件的方法時,其會自動的跳轉到代理物件關聯的handler物件的invoke方法來進行呼叫。/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
* <p>{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> the given {@code loader} is {@code null} and
* the caller's class loader is not {@code null} and the
* invocation of {@link SecurityManager#checkPermission
* s.checkPermission} with
* {@code RuntimePermission("getClassLoader")} permission
* denies access;</li>
* <li> for each proxy interface, {@code intf},
* the caller's class loader is not the same as or an
* ancestor of the class loader for {@code intf} and
* invocation of {@link SecurityManager#checkPackageAccess
* s.checkPackageAccess()} denies access to {@code intf};</li>
* <li> any of the given proxy interfaces is non-public and the
* caller class is not in the same {@linkplain Package runtime package}
* as the non-public interface and the invocation of
* {@link SecurityManager#checkPermission s.checkPermission} with
* {@code ReflectPermission("newProxyInPackage.{package name}")}
* permission denies access.</li>
* </ul>
* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//檢查h 不為空,否則拋異常
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* 獲得與指定類裝載器和一組介面相關的代理類型別物件
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* 通過反射獲取建構函式物件並生成代理類例項
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//獲取代理物件的構造方法(也就是$Proxy0(InvocationHandler h))
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//生成代理類的例項並把InvocationHandlerImpl的例項傳給它的構造方法
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
真相還是沒有來到,繼續,看一下proxyClassCache/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
/**
* Look-up the value through the cache. This always evaluates the
* {@code subKeyFactory} function and optionally evaluates
* {@code valueFactory} function if there is no entry in the cache for given
* pair of (key, subKey) or the entry has already been cleared.
*
* @param key possibly null key
* @param parameter parameter used together with key to create sub-key and
* value (should not be null)
* @return the cached value (never null)
* @throws NullPointerException if {@code parameter} passed in or
* {@code sub-key} calculated by
* {@code subKeyFactory} or {@code value}
* calculated by {@code valueFactory} is null.
*/
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
//putIfAbsent這個方法在key不存在的時候加入一個值,如果key存在就不放入
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
我們可以看到它呼叫了 supplier.get(); 獲取動態代理類,其中supplier是Factory,這個類定義在WeakCach的內部。
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// try replacing us with CacheValue (this should always succeed)
if (valuesMap.replace(subKey, this, cacheValue)) {
// put also in reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
} else {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
發現重點還是木有出現,但我們可以看到它呼叫了valueFactory.apply(key, parameter)方法:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
通過看程式碼終於找到了重點://生成位元組碼
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
那麼接下來我們也使用測試一下,使用這個方法生成的位元組碼是個什麼樣子:package jiankunking;
import sun.misc.ProxyGenerator;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
* 動態代理演示
*/
public class DynamicProxyDemonstration
{
public static void main(String[] args)
{
//代理的真實物件
Subject realSubject = new RealSubject();
/**
* InvocationHandlerImpl 實現了 InvocationHandler 介面,並能實現方法呼叫從代理類到委託類的分派轉發
* 其內部通常包含指向委託類例項的引用,用於真正執行分派轉發過來的方法呼叫.
* 即:要代理哪個真實物件,就將該物件傳進去,最後是通過該真實物件來呼叫其方法
*/
InvocationHandler handler = new InvocationHandlerImpl(realSubject);
ClassLoader loader = handler.getClass().getClassLoader();
Class[] interfaces = realSubject.getClass().getInterfaces();
/**
* 該方法用於為指定類裝載器、一組介面及呼叫處理器生成動態代理類例項
*/
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
System.out.println("動態代理物件的型別:"+subject.getClass().getName());
String hello = subject.SayHello("jiankunking");
System.out.println(hello);
// 將生成的位元組碼儲存到本地,
createProxyClassFile();
}
private static void createProxyClassFile(){
String name = "ProxySubject";
byte[] data = ProxyGenerator.generateProxyClass(name,new Class[]{Subject.class});
FileOutputStream out =null;
try {
out = new FileOutputStream(name+".class");
System.out.println((new File("hello")).getAbsolutePath());
out.write(data);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=out) try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
可以看一下這裡代理物件的型別:import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import jiankunking.Subject;
public final class ProxySubject
extends Proxy
implements Subject
{
private static Method m1;
private static Method m3;
private static Method m4;
private static Method m2;
private static Method m0;
public ProxySubject(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
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);
}
}
public final String SayGoodBye()
{
try
{
return (String)this.h.invoke(this, m3, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String SayHello(String paramString)
{
try
{
return (String)this.h.invoke(this, m4, new Object[] { paramString });
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("jiankunking.Subject").getMethod("SayGoodBye", new Class[0]);
m4 = Class.forName("jiankunking.Subject").getMethod("SayHello", new Class[] { Class.forName("java.lang.String") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
這就是最終真正的代理類,它繼承自Proxy並實現了我們定義的Subject介面
也就是說:
Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
public final String SayHello(String paramString)
五、結論
到了這裡,終於解答了:
subject.SayHello("jiankunking")這句話時,為什麼會自動呼叫InvocationHandlerImpl的invoke方法?
因為JDK生成的最終真正的代理類,它繼承自Proxy並實現了我們定義的Subject介面,
在實現Subject介面方法的內部,通過反射呼叫了InvocationHandlerImpl的invoke方法。
包含生成本地class檔案的demo:
http://download.csdn.net/detail/xunzaosiyecao/9597474
https://github.com/JianKunKing/DynamicProxyDemo
通過分析程式碼可以看出Java 動態代理,具體有如下四步驟:
- 通過實現 InvocationHandler 介面建立自己的呼叫處理器;
- 通過為 Proxy 類指定 ClassLoader 物件和一組 interface 來建立動態代理類;
- 通過反射機制獲得動態代理類的建構函式,其唯一引數型別是呼叫處理器介面型別;
- 通過建構函式建立動態代理類例項,構造時呼叫處理器物件作為引數被傳入。
作者:jiankunking 出處:http://blog.csdn.net/jiankunking個人微信公眾號:
相關文章
- Java-JDK動態代理(AOP)使用及實現原理分析JavaJDK
- Java JDK 動態代理使用及實現原理分析JavaJDK
- spring aop原理 JDK動態代理和CGLIB動態代理SpringJDKCGLib
- JDK動態代理實現原理詳解(原始碼分析)JDK原始碼
- 細說JDK動態代理的實現原理JDK
- 基於JDK的動態代理原理分析JDK
- 【乾貨】JDK動態代理的實現原理以及如何手寫一個JDK動態代理JDK
- Java動態代理(AOP)Java
- Spring AOP --JDK動態代理方式SpringJDK
- CGLib動態代理原理及實現CGLib
- Java 靜態代理和動態代理的使用及原理解析Java
- 淺析Spring中AOP的實現原理——動態代理Spring
- 【Spring AOP】AOP 底層實現原理 —— 動態代理類的建立(JDK、CGlib)、工廠如何加工原始物件SpringJDKCGLib物件
- Java代理(jdk靜態代理、動態代理和cglib動態代理)JavaJDKCGLib
- 純手寫實現JDK動態代理JDK
- Java動態代理 jdk和cglib的實現比較JavaJDKCGLib
- 【深度思考】聊聊JDK動態代理原理JDK
- Java架構-Java JDK 動態代理Java架構JDK
- java動態代理原理Java
- 菜鳥學SSH(十四)——Spring容器AOP的實現原理——動態代理Spring
- java動態代理基本原理及proxy原始碼分析一Java原始碼
- Spring框架系列(12) - Spring AOP實現原理詳解之JDK代理實現Spring框架JDK
- JDK解構 - Java中的引用和動態代理的實現JDKJava
- Java使用Porxy和InvocationHandler實現動態代理Java
- AOP如何實現及實現原理
- Java動態代理(JDK和cglib)JavaJDKCGLib
- Java進階之 JDK動態代理與Cglib動態代理JavaJDKCGLib
- JDK動態代理JDK
- AOP之動態代理
- RPC核心實現原理-動態代理RPC
- FastHook——巧妙利用動態代理實現非侵入式AOPASTHook
- AOP之靜態代理VS動態代理
- 深挖JDK動態代理(二):JDK動態生成後的位元組碼分析JDK
- 動態代理及java演示Java
- JAVA_動態代理AOP切面程式設計Java程式設計
- jdk proxy invocationhandler (jdk動態代理)JDK
- 看過Jdk動態代理類長啥樣嗎?Jdk動態代理原理原始碼一本到JDK原始碼
- JDK 和 CGLib 實現動態代理和區別JDKCGLib