Java動態代理講解——深入淺出
一、代理模式
要講解動態代理,就要先說代理模式,這是一種最常用的設計模式。該模式中有以下三個角色。
抽象角色:通過介面或抽象類宣告真實角色實現的業務方法,即被代理類與代理類都要實現的介面。
代理角色:實現抽象角色,是真實角色的代理,通過真實角色的業務邏輯方法來實現抽象方法,並可以附加自己的操作。
真實角色:實現抽象角色,定義真實所要實現的業務邏輯,供代理角色呼叫。
二、靜態代理與動態代理
靜態代理需要為每個被代理類(真實角色)都建立特定的代理類(代理角色),這些類需要程式設計師自己編寫,這樣會帶來極大的不便。
因此出現了動態代理,動態代理是基於JDK的反射機制。
三、動態代理的實現方式
假如我們現在要實現一個日誌增強類,在各種類呼叫各種方法前和方法執行結束後列印日誌。LogHandler實現InvocationHandler,並且需要自己實現invoke方法,該方法中需要執行日誌列印語句和原本的業務邏輯方法。
假如現在我們有一個User介面及其實現類UserImpl。public class LogHandler implements InvocationHandler{ private Object target; public LogHandler(Object target) { this.target = target; } //生成代理物件 public Object bind(){ return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("log before");//增強日誌語句 method.invoke(target, args);//呼叫被代理類的業務邏輯方法 System.out.println("log after");//增強日誌語句 return null; } }
public interface User {
void hello();
}
public class UserImpl implements User{
public void hello(){
System.out.println("hello");
}
}
要通過LogHandler類來獲得UserImpl的日誌代理類,怎麼辦呢?
public class Main { public static void main(String[] args) { UserImpl user = new UserImpl(); LogHandler logHandler = new LogHandler(user); User proxy = (User) logHandler.bind(); proxy.hello(); } }
三、實現原理
關於到底是如何生成代理類的,我們要仔細看一下bind方法中的Proxy.newProxyInstance(......)方法,這個方法裡面有一切我們想知道的玄機。我們來看一下原始碼中最核心的一些東西(一些關於安全性合法性檢查的語句就不說了)
Class<?> cl = getProxyClass0(loader, intfs);
引數裡的loader指的是被代理的類載入器,intfs指的是被代理類所實現的介面集合,getProxyClass0方法根據這兩個引數構造了一個類物件,該類繼承Proxy類(因此JDK動態代理只支援介面代理!),實現了被代理類所實現的介面。
接著進入getProxyClass0方法
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);
}
這裡可以看得很清楚,如果再之前已經生成過“擁有相同介面集合的類”的代理類,則可以直接從快取中取出該代理類物件,否則通過代理類工廠建立。在獲取到代理類之後,就要建立代理物件了,通過Java的反射機制,直接通過Contructor類的newInstance方法建立並且返回。
return cons.newInstance(new Object[]{h});
這裡new Object[]{h}是構造器引數,因為我們的代理類是繼承Proxy的,而Proxy提供了一個受保護的構造器和一個私有構造器,我們來看一下受保護的構造器。
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
其中的引數正是InvocationHandler,因此其子類代理類一定會有一個有參構造器,且有傳入InvocationHandler物件。為什麼要傳入這個引數呢?因為Proxy有個屬性就是InvocationHandler,這個屬性有什麼用呢?我們來看一下生成的代理類的程式碼(來自網路)。
public final class $Proxy0 extends Proxy implements UserManager {
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
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("cn.edu.jlu.proxy.UserManager").getMethod("addUser",
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());
}
}
public $Proxy0(InvocationHandler invocationhandler) {
super(invocationhandler);
}
@Override
public final boolean equals(Object obj) {
try {
return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))
.booleanValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final int hashCode() {
try {
return ((Integer) super.h.invoke(this, m0, null)).intValue();
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public final String toString() {
try {
return (String) super.h.invoke(this, m2, null);
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
@Override
public void addUser() {
try {
super.h.invoke(this, m3, null);
return;
} catch (Error e) {
} catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
可以看到,所有的方法都會呼叫super.h.invoke方法!因此,當我們呼叫代理類的很多方法時,其實都是指向了invoke方法。