JAVA 動態代理原理和實現
在 Java 中動態代理和代理都很常見,幾乎是所有主流框架都用到過的知識。在面試中也是經常被提到的話題,於是便總結了本文。
Java動態代理的基本原理為:被代理對象需要實現某個接口(這是前提),代理對象會攔截對被代理對象的方法調用,在其中可以全然拋棄被代理對象的方法實現而完成另外的功能,也可以在被代理對象方法調用的前後增加一些額外的功能。
動態代理可以為其他對象提供一個代理以控制對某個對象的訪問。
代理類負責為委托類預處理消息,過濾消息並轉發消息,以及進行消息被委托類執行後的後續處理。
JDK 動態代理
動態代理的核心其實就是代理對象的生成,即 Proxy.newProxyInstance(classLoader, proxyInterface, handler)。
讓我們進入newProxyInstance方法觀摩下,核心代碼其實就三行。
這個方法需要三個參數:
- ClassLoader,用於加載代理類的 Loader 類,通常這個 Loader 和被代理的類是同一個 Loader 類。
- Interfaces,是要被代理的那些那些接口。
- InvocationHandler,就是用於執行除了被代理接口中方法之外的用戶自定義的操作,他也是用戶需要代理的最終目的。用戶調用目標方法都被代理到 InvocationHandler 類中定義的唯一方法 invoke 中。
1 2 3 4 5 6 |
//獲取代理類
Class cl = getProxyClass(loader, interfaces); //獲取帶有InvocationHandler參數的構造方法
Constructor cons = cl.getConstructor(constructorParams);
//把handler傳入構造方法生成實例
return (Object) cons.newInstance( new Object[] { h });
|
一個典型的動態代理創建對象過程可分為以下四個步驟:
1、通過實現InvocationHandler接口創建調用處理器
1 |
IvocationHandler handler = new InvocationHandlerImpl(...); |
2、通過為Proxy類指定ClassLoader對象和一組interface創建動態代理類
1 |
Class clazz = Proxy.getProxyClass(classLoader, new Class[]{...});
|
3、通過反射機制獲取動態代理類的構造函數,其參數類型是調用處理器接口類型
1 |
Constructor constructor = clazz.getConstructor( new Class[]{InvocationHandler. class });
|
4、通過構造函數創建代理類實例,此時需將調用處理器對象作為參數被傳入
1 |
Interface Proxy = (Interface)constructor.newInstance( new Object[] (handler));
|
為了簡化對象創建過程,Proxy類中的newProxyInstance方法封裝了2~4,只需兩步即可完成代理對象的創建。
1 2 |
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject. class .getClassLoader(),
new Class[]{Subject. class }, new InvocationHandlerImpl (real));
|
生成的proxySubject繼承Proxy類實現Subject接口。實現的Subject的方法實際是調用處理器的invoke方法,而invoke方法利用反射調用的是被代理對象的方法(Object result=method.invoke(proxied,args));
重點Proxy.newProxyInstance,源碼分析,會在其他文檔中單獨總結記錄。類Proxy的getProxyClass方法調用ProxyGenerator的 generateProxyClass方法產生ProxySubject.class的二進制數據。
創建代理對象時序圖
獲取代理類
getProxyClass(loader, interfaces)方法用於獲取代理類,它主要做了三件事情:
在當前類加載器的緩存裏搜索是否有代理類,沒有則生成代理類並緩存在本地JVM裏。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
// 緩存的key使用接口名稱生成的List
Object key = Arrays.asList(interfaceNames);
synchronized (cache) {
do {
Object value = cache.get(key);
// 緩存裏保存了代理類的引用
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null ) {
// 代理類已經存在則返回
return proxyClass;
} else if (value == pendingGenerationMarker) {
// 如果代理類正在產生,則等待
try {
cache.wait();
} catch (InterruptedException e) {
}
continue ;
} else {
//沒有代理類,則標記代理準備生成
cache.put(key, pendingGenerationMarker);
break ;
}
} while ( true );
}
|
生成並加載代理類
代理類的生成主要是以下這兩行代碼:
1 2 3 4 |
//生成代理類的字節碼文件並保存到硬盤中(默認不保存到硬盤)
proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
//使用類加載器將字節碼加載到內存中
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0 , proxyClassFile.length);
|
代理類的生成過程
ProxyGenerator.generateProxyClass()方法屬於sun.misc包下,Oracle並沒有提供源代碼,但是我們可以使用
JD-GUI這樣的反編譯軟件打開jre\lib\rt.jar來一探究竟,以下是其核心代碼的分析。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
//添加接口中定義的方法,此時方法體為空
for ( int i = 0 ; i < this .interfaces.length; i++) {
localObject1 = this .interfaces[i].getMethods();
for ( int k = 0 ; k < localObject1.length; k++) {
addProxyMethod(localObject1[k], this .interfaces[i]);
}
}
//添加一個帶有InvocationHandler的構造方法
MethodInfo localMethodInfo = new MethodInfo( "<init>" , "(Ljava/lang/reflect/InvocationHandler;)V" , 1 );
//循環生成方法體代碼(省略)
//方法體裏生成調用InvocationHandler的invoke方法代碼。(此處有所省略)
this .cp.getInterfaceMethodRef( "InvocationHandler" , "invoke" , "Object; Method; Object;" )
//將生成的字節碼,寫入硬盤,前面有個if判斷,默認情況下不保存到硬盤。
localFileOutputStream = new FileOutputStream(ProxyGenerator.access$ 000 ( this .val$name) + ".class" );
localFileOutputStream.write( this .val$classFile);
|
生成的代理類源碼
那麽通過以上分析,我們可以推出動態代理為我們生成了一個這樣的代理類。把方法doSomeThing的方法體修改為調用LogInvocationHandler的invoke方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public class ProxyBusiness extends Proxy implements IBusiness, IBusiness2 {
private LogInvocationHandler h;
@Override
public void doSomeThing2() {
try {
Method m = (h.target).getClass().getMethod( "doSomeThing2" , null );
h.invoke( this , m, null );
} catch (Throwable e) {
// 異常處理(略)
}
}
@Override
public boolean doSomeThing() {
try {
Method m = (h.target).getClass().getMethod( "doSomeThing" , null );
return (Boolean) h.invoke( this , m, null );
} catch (Throwable e) {
// 異常處理(略)
}
return false ;
}
public ProxyBusiness(LogInvocationHandler h) {
this .h = h;
}
|
測試代理的代碼如下:
1 2 3 4 5 6 7 |
//測試
public static void main(String[] args) {
//構建AOP的Advice
LogInvocationHandler handler = new LogInvocationHandler( new Business());
new ProxyBusiness(handler).doSomeThing();
new ProxyBusiness(handler).doSomeThing2();
}
|
下面看一個自定義代理的實現。
被代理類接口
1 2 3 |
public interface Subject {
public void doSomething();
}
|
被代理類
1 2 3 4 5 6 |
//目標對象
public class RealSubject implements Subject{
public void doSomething() {
System.out.println( "call doSomething()" );
}
}
|
調用處理器(切面)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class ProxyHandler implements InvocationHandler {
private Object proxied;
public ProxyHandler( Object proxied ) {
this .proxied = proxied;
}
public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable {
//在轉調具體目標對象之前,可以執行一些功能處理
System.out.println( "doSomething before" );
//轉調具體目標對象的方法
return method.invoke( proxied, args);
//在轉調具體目標對象之後,可以執行一些功能處理
System.out.println( "doSomething after" );
}
}
|
測試我們的代理實現
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class DynamicProxy {
public static void main( String args[] ) {
RealSubject real = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject. class .getClassLoader(),
new Class[]{Subject. class }, new ProxyHandler(real));
proxySubject.doSomething();
//write proxySubject class binary data to file
createProxyClassFile();
}
public static void createProxyClassFile() {
String name = "ProxySubject" ;
byte [] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject. class } );
try {
FileOutputStream out = new FileOutputStream( name + ".class" );
out.write( data );
out.close();
} catch ( Exception e ) {
e.printStackTrace();
}
}
}
|
運行結果:
1 2 3 |
doSomething before
call doSomething()
doSomething after
|
Proxy 接口
Proxy 的主要靜態變量
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// 映射表:用於維護類裝載器對象到其對應的代理類緩存
private static Map loaderToCache = new WeakHashMap();
// 標記:用於標記一個動態代理類正在被創建中
private static Object pendingGenerationMarker = new Object();
// 同步表:記錄已經被創建的動態代理類類型,主要被方法 isProxyClass 進行相關的判斷
private static Map proxyClasses = Collections.synchronizedMap( new WeakHashMap());
// 關聯的調用處理器引用
protected InvocationHandler h;
Proxy的構造方法
// 由於 Proxy 內部從不直接調用構造函數,所以 private 類型意味著禁止任何調用
private Proxy() {}
// 由於 Proxy 內部從不直接調用構造函數,所以 protected 意味著只有子類可以調用
protected Proxy(InvocationHandler h) { this .h = h;}
|
ProxySubject 源碼
創建的代理類 ProxySubject.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
import java.lang.reflect.*;
public final class ProxySubject extends Proxy implements Subject{
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
public ProxySubject(InvocationHandler invocationhandler){
super (invocationhandler);
}
public final boolean equals(Object obj){
try {
return ((Boolean) super .h.invoke( this , m1, new Object[] {
obj
})).booleanValue();
} catch (Error _ex) {
} catch (Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer) super .h.invoke( this , m0, null )).intValue();
} catch (Error _ex) {
} catch (Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}
/*關鍵部分*/
public final void doSomething() {
try {
// Proxy類中protected InvocationHandler h;關聯的調用處理器引用
super .h.invoke( this , m3, null );
return ;
} catch (Error _ex) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String) super .h.invoke( this , m2, null );
} catch (Error _ex) {
} catch (Throwable throwable){
throw new UndeclaredThrowableException(throwable);
}
}
static {
try {
m1 = Class.forName( "java.lang.Object" ).getMethod( "equals" , new Class[] {
Class.forName( "java.lang.Object" )
});
m0 = Class.forName( "java.lang.Object" ).getMethod( "hashCode" , new Class[ 0 ]);
m3 = Class.forName( "Subject" ).getMethod( "doSomething" , new Class[ 0 ]);
m2 = Class.forName( "java.lang.Object" ).getMethod( "toString" , new Class[ 0 ]);
} catch (NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
} catch (ClassNotFoundException classnotfoundexception){
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
|
CGLib 動態代理
動態字節碼生成。使用動態字節碼生成技術實現AOP原理是在運行期間目標字節碼加載後,生成目標類的子類,將切面邏輯加入到子類中,所以使用Cglib實現AOP不需要基於接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
public static void main(String[] args) {
byteCodeGe();
}
public static void byteCodeGe() {
//創建一個織入器
Enhancer enhancer = new Enhancer();
//設置父類
enhancer.setSuperclass(Business. class );
//設置需要織入的邏輯
enhancer.setCallback( new LogIntercept());
//使用織入器創建子類
IBusiness2 newBusiness = (IBusiness2) enhancer.create();
newBusiness.doSomeThing2();
}
/**
* 記錄日誌
*/
public static class LogIntercept implements MethodInterceptor {
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//執行原有邏輯,註意這裏是invokeSuper
Object rev = proxy.invokeSuper(target, args);
//執行織入的日誌
if (method.getName().equals( "doSomeThing2" )) {
System.out.println( "記錄日誌" );
}
return rev;
}
}
|
轉自:https://www.xttblog.com/?p=2732
JAVA 動態代理原理和實現