我們在使用spring這類框架的時候,基於動態代理的使用,比如AOP,會使得開發更加靈活,那麼在字節碼的層面動態代理是什麼樣子的呢,生成出來的代理類結構是什麼,本次我們首先寫一個動態代理的例子,然後得到生成的動態代理類。
定義接口:
public interface SubJect {
void request();
}
定義實現類:
public class RealSubJect implements SubJect {
@Override
public void request() {
System.out.println("method calling");
}
}
定義動態代理類:
public class DynamicSubject implements InvocationHandler {
private Object sub;
public DynamicSubject(Object obj){
this.sub = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("befor method call");
method.invoke(sub,args);
System.out.println("after method call");
return null;
}
}
定義客戶端:
public class Client {
public static void main(String[] args) {
RealSubJect realSubJect = new RealSubJect();
InvocationHandler invocationHandler = new DynamicSubject(realSubJect);
SubJect subJect = (SubJect) Proxy.newProxyInstance(realSubJect.getClass().getClassLoader(),realSubJect.getClass().getInterfaces(),invocationHandler);
subJect.request();
System.out.println(subJect.getClass());//打印動態代理類的class
System.out.println(subJect.getClass().getSuperclass()); //打印父類
}
}
運行客戶端得到結果:
befor method call
method calling
after method call
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy
那麼這個com.sun.proxy.$Proxy0是怎麼出來的呢,這個需要進入到Proxy.newProxyInstance()裏邊看一下他的邏輯:
Proxy.newProxyInstance()
-->getProxyClass0()
-->proxyClassCache.get()[通過ProxyClassFactory獲取]
-->WeakCache.Factory.get()
-->valueFactory.apply(key, parameter)
-->Proxy.apply()
-->byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
-->byte[] proxyClassFile = ProxyGenerator.generateClassFile();
ProxyGenerator是 sun.misc包裏邊的,我們得到的代碼是ide反編譯的結果,我們貼出來,var4是生成出來的字節數組,然後
saveGeneratedFiles是一個開關,如果爲true就會把class文件輸出到磁盤
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();//返回最終的字節數組
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
saveGeneratedFiles的定義:
private static final boolean saveGeneratedFiles =
((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
ok 我們設置一下sun.misc.ProxyGenerator.saveGeneratedFiles就可以得到class文件。下邊這行代碼放在Client main方法的第一行,提前設置。
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
運行Client,然後在我們的工程裏邊就會出現一個目錄
com.sun.proxy.$Proxy0
這個就是動態代理類:
package com.sun.proxy;
import com.twodragonlake.jvm.bytecode.SubJect;
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 SubJect {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
/**
構造方法
這個構造方法在Proxy類的newProxyInstance方法裏邊,有一個地方是獲取代理類的構造器:
Class<?> cl = getProxyClass0(loader, intfs); //代理類
final Constructor<?> cons = cl.getConstructor(constructorParams);//獲取代理類的構造器
return cons.newInstance(new Object[]{h});//實例化代理類;
h是InvocationHandler,InvocationHandler是我們定義的DynamicSubject的接口,這個時候就會調用當前的這個構造方法
動態代理類的父類Proxy持有InvocationHandler的引用,完成這個引用的賦值。
*/
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
/**
調用Proxy的InvocationHandler的invoke方法,其實就是調用DynamicSubject的invoke方法,
因爲DynamicSubject實現了InvocationHandler,m1是Object類的equals方法,new Object[]{var1}是equals的方法參數
如果DynamicSubject重新了equals方法就會轉發到DynamicSubject的equals方法,否則就是調用Object的equals方法
*/
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
/**
和equals方法道理了一樣
*/
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 void request() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
/**
和equals方法道理了一樣
*/
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
//類加載的時候,將Object類的是個方法拿出來
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.twodragonlake.jvm.bytecode.SubJect").getMethod("request");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
除了equals、toString、hashCode的其他的Object的方法,都不會得到代理類的轉發,原來是什麼樣子的,代理後也還是什麼樣子的,不會發生變化。
好,到目前爲止我們分析了動態代理類的源碼,那回到client的代碼:
SubJect subJect = (SubJect) Proxy.newProxyInstance(realSubJect.getClass().getClassLoader(),realSubJect.getClass().getInterfaces(),invocationHandler);
subJect返回的是一個動態代理類,這個類的名字是Proxy0的父類是Proxy,父類持有InvocationHandler的引用,也就是持有DynamicSubject的引用,同時Proxy0裏邊生成了DynamicSubject裏邊所聲明的方法,並且轉發到了DynamicSubject裏邊。當我們調用subJect.request();
的時候就是調用了Proxy0將方法的調用通過父類Proxy持有的InvocationHandler的引用,即【super.h.invoke(this, m0, (Object[])null))】進行了轉發,轉發到DynamicSubject的invoke方法。所以說DynamicSubject的invoke方法的第一個參數是proxy,接下里就會打印:
befor method call
method calling
after method call
動態代理的優勢:
代理對象可以在沒有真實對象不存在的情況下提前生成代理對象,代理對象可以代理多種真實對象,而且jdk的動態代理是面向接口的。
cglib:
面向繼承的代理,子類可以重寫父類的實現,同時代理類可以調用父類的方法(jdk動態代理沒有這個優勢,因爲jdk動態代理是面向接口的)