動態(tài)代理
1. 什么是動態(tài)代理
在上一章節(jié)中糠赦,我們講的是代理其實都是靜態(tài)代理,動態(tài)代理是在運行階段動態(tài)的創(chuàng)建代理并且動態(tài)的處理對所代理方法的調用慨畸。在動態(tài)代理上所做的所有調用都會被重定向到單一的調用處理器中滨达。在現(xiàn)在很流行的Spring中有一個AOP(面向切面)的其中核心實現(xiàn)技術就是動態(tài)代理的技術。
2. 為什么要用動態(tài)代理
動態(tài)代理的優(yōu)勢在于可以很方便的對代理類的函數(shù)進行統(tǒng)一的處理赡盘,而不用修改每個代理類中的方法。例如我們想計算出每一個方法的執(zhí)行時間缰揪,如果使用靜態(tài)代理的話陨享,那么就需要在每一個代理類中進行更改,但是如果使用了動態(tài)代理可以對類的所有方法進行統(tǒng)一的管理钝腺。一處添加抛姑,所有方法適用。
3. 動態(tài)代理的簡單實現(xiàn)
3.1 靜態(tài)代理的實現(xiàn)
我們先看一下靜態(tài)代理的是如何實現(xiàn)的艳狐,關于靜態(tài)代理詳細的解釋可以看不學無數(shù)——Java代理模式定硝,這里只貼出關于靜態(tài)代理的一些代碼。
Homeowner
接口如下:
interface Homeowner{
public void LeaseHouse(Home home);
}
RealHomeowner
類如下
class RealHomeowner implements Homeowner{
@Override
public void LeaseHouse(Home home) {
System.out.println("房價是: "+ home.getPrice());
System.out.println("房子顏色是: "+ home.getColor());
System.out.println("房子出租成功");
}
}
代理類HomeProxy
的實現(xiàn)
class HomeProxy implements Homeowner{
private Homeowner homeowner;
public HomeProxy(Homeowner homeowner){
this.homeowner = homeowner;
}
@Override
public void LeaseHouse(Home home) {
System.out.println("中介干預");
homeowner.LeaseHouse(home);
System.out.println("中介干預完成");
}
}
在main方法中使用
public static void main(String[] args) {
Home home = new Home("red",1000);
RealHomeowner realHomeowner = new RealHomeowner();
Homeowner homeowner = new HomeProxy(realHomeowner);
homeowner.LeaseHouse(home);
}
打印的信息如下:
中介干預
房價是: 1000
房子顏色是: red
房子出租成功
中介干預完成
3.2 動態(tài)代理的實現(xiàn)
在動態(tài)代理中是不需要代理類的毫目,就是不需要上面靜態(tài)代理中的HomeProxy
類蔬啡,通過實現(xiàn)了InvocationHandler
類,所有方法都由該'Handler'來處理了镀虐,意思就是所有被代理的方法都由InvocationHandler
接管實際的處理任務箱蟆。那么看實際的例子
DynamicPro
類
class DynamicPro implements InvocationHandler{
//真實被代理的實例對象
private Object object;
public DynamicPro(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("中介干預");
Object result = method.invoke(object,args);
System.out.println("中介干預完成");
return result;
}
}
在主方法如下的調用
public static void main(String[] args) {
Home home = new Home("red",1000);
//創(chuàng)建一個被代理的實例對象
RealHomeowner realHomeowner = new RealHomeowner();
//創(chuàng)建一個與被代理對象相關的InvocationHandler
DynamicPro dynamicPro = new DynamicPro(realHomeowner);
//創(chuàng)建一個類加載器
ClassLoader classLoader = realHomeowner.getClass().getClassLoader();
//被代理類的接口數(shù)組,里面的每一個方法都會執(zhí)行InvocationHandler中的invoke方法
Class<?>[] proxInterface = realHomeowner.getClass().getInterfaces();
Homeowner homeowner = (Homeowner) Proxy.newProxyInstance(classLoader,proxInterface,dynamicPro);
homeowner.LeaseHouse(home);
}
打印如下
中介干預
房價是: 1000
房子顏色是: red
房子出租成功
中介干預完成
4. 動態(tài)代理和靜態(tài)代理的區(qū)別
上面是關于動態(tài)代理的類圖,我們可以和靜態(tài)代理的類圖進行對比一下
可以看到在動態(tài)代理中不需要了實際的代理角色類刮便,因為實際的代理角色在動態(tài)代理中時動態(tài)生成的空猜。在動態(tài)代理中增加了InvocationHandler
接口類,這個接口中只有一個方法,就是invoke()
方法辈毯。我們可以實現(xiàn)InvocationHandler
類然后在invoke()
方法中對調用實際方法時的前置或者后置處理久信。
5. 動態(tài)代理原理簡單分析
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
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 {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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;
}
});
}
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);
}
}
通過上面的代碼的Debug發(fā)現(xiàn)最后是通過下面代碼返回一個對象的
//返回構造器生成的實例對象
return cons.newInstance(new Object[]{h});
然后發(fā)現(xiàn)cons是從下面的代碼獲得的
//獲得此代理類的構造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
cl是從下面的代碼中獲得的
//查找或生成指定的代理類
Class<?> cl = getProxyClass0(loader, intfs);
而intfs是從下面的代碼獲得
//克隆一個接口類
final Class<?>[] intfs = interfaces.clone();
隨后想進去看getProxyClass0
生成的代理類是什么,但是發(fā)現(xiàn)進不去漓摩。后來查資料知道它由于是動態(tài)生成的裙士,類是緩存在java虛擬機中的,可以通過下面的方法將類打印出來管毙。
動態(tài)代理類的字節(jié)碼在程序運行時由Java反射機制動態(tài)生成
public static void main(String[] args) {
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Homeowner.class.getInterfaces());
String path = "/Users/hupengfei/git/Test/src/main/java/Practice/Day06/Homeowner.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
fos.write(classFile);
fos.flush();
System.out.println("代理類class文件寫入成功");
} catch (Exception e) {
System.out.println("寫文件錯誤");
}
}
對生成的class文件進行反編譯腿椎,在Idea中能直接查看
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import Practice.Day06.Home;
import Practice.Day06.Homeowner;
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 Homeowner {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void LeaseHouse(Home var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final Homeowner getProxy() throws {
try {
return (Homeowner)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("Practice.Day06.Homeowner").getMethod("LeaseHouse", Class.forName("Practice.Day06.Home"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("Practice.Day06.Homeowner").getMethod("getProxy");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
首先我們可以先看生成的此類的構造函數(shù)
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
-----調用了父類的構造函數(shù),而它的父類是Proxy類夭咬,父類的構造函數(shù)如下
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
--在父類中的h的定義如下
protected InvocationHandler h;
此時我們就知道為什么我們的動態(tài)代理都會執(zhí)行傳入的InvocationHandler
中的invoke()
方法了
在下面的靜態(tài)代碼塊中我們發(fā)現(xiàn)了LeaseHouse()
方法
m4 = Class.forName("Practice.Day06.Homeowner").getMethod("LeaseHouse", Class.forName("Practice.Day06.Home"));
然后在上面會發(fā)現(xiàn)有我們的方法
public final void LeaseHouse(Home var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
此時我們再回想一下在InvocationHandler
類中的invoke()
方法中傳入的參數(shù)有Method
方法了啃炸,這樣就可以將外部對于被代理對象的調用都轉化為調用invoke()
方法,再由invoke()
方法中調用被代理對象的方法卓舵。
6. 參考文章
- https://www.cnblogs.com/gonjan-blog/p/6685611.html
- 設計模式之禪
- Java編程思想