使用
- 說(shuō)起動(dòng)態(tài)代理骏啰,大家都不陌生节吮,但對(duì)其原理卻一知半解。經(jīng)常遇到一個(gè)問(wèn)題判耕,java動(dòng)態(tài)代理為何只能適用接口透绩,why?你有考慮過(guò)其底層邏輯原因嗎壁熄?
- 首先看一個(gè)簡(jiǎn)單的使用
public class MyInvocationHandler implements InvocationHandler {
private Object target ;
public MyInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目標(biāo)對(duì)象的方法執(zhí)行之前簡(jiǎn)單的打印一下
System.out.println("------------------before------------------");
// 執(zhí)行目標(biāo)對(duì)象的方法
Object result = method.invoke(target, args);
// 在目標(biāo)對(duì)象的方法執(zhí)行之后簡(jiǎn)單的打印一下
System.out.println("-------------------after------------------");
return result;
}
/**
* 獲取目標(biāo)對(duì)象的代理對(duì)象
* @return 代理對(duì)象
*/
public Object getProxy() {
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
target.getClass().getInterfaces(), this);
}
}
- 以上渺贤,主要看Proxy.newProxyInstance代理方法
-
java對(duì)象創(chuàng)建過(guò)程,一般都是創(chuàng)建.java類通過(guò)javac編譯成.class文件请毛,通過(guò)類加載器創(chuàng)建對(duì)象初始化;
-
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h){
//獲得class對(duì)象 cl為 $Proxy0
Class<?> cl = getProxyClass0(loader, intfs);
//獲取構(gòu)造函數(shù)
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;
}
});
}
//根據(jù)構(gòu)造函數(shù)反射調(diào)用初始化對(duì)象瞭亮,注意里面的h為實(shí)現(xiàn)MyInvocationHandler傳入的this
return cons.newInstance(new Object[]{h});
}
- 那么動(dòng)態(tài)代理是如何創(chuàng)建代理對(duì)象呢方仿?我們分析以上代碼可知:
- newProxyInstance中g(shù)etProxyClass0()獲取代理的.class文件,這里沒(méi)有了.java源碼,而是直接生成class文件统翩,通過(guò)class創(chuàng)建對(duì)象仙蚜;
- 有class文件后調(diào)用newInstance創(chuàng)建對(duì)象,注意構(gòu)造函數(shù)中傳入?yún)?shù)h為實(shí)現(xiàn)InvocationHandler
- 進(jìn)入getProxyClass0方法厂汗,記得參數(shù)loader : 類加載器委粉, interfaces當(dāng)前需要代理的接口數(shù)據(jù)
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
return proxyClassCache.get(loader, interfaces);
}
public V get(K key, P parameter) {
//從緩存中獲取,首次肯定沒(méi)有的
......
// 創(chuàng)建類對(duì)象subKeyFactory在WeakCache初始化傳入
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);
}
//Proxy中初始化緩存WeakCache
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
- 肯定有個(gè)緩存快速查找啦娶桦,沒(méi)有就通過(guò)subKeyFactory.apply工廠類去新建啦贾节!
- 以上subKeyFactory.apply對(duì)應(yīng)的為ProxyClassFactory類中的apply方法: 生成代理類$Proxy0的class文件并返回
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");
}
/*
* 如果當(dāng)前不是接口,拋出異常衷畦,但是并未說(shuō)明我們的疑問(wèn)
*/
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 + ".";
}
/*
* 創(chuàng)建類名 $proxy + num自增加作為proxyName類名 .class
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 生成類名class的byte數(shù)組
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//native生成字節(jié)碼文件
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());
}
}
}
- 生成class文件格式步驟為:
- 首先驗(yàn)證是否時(shí)接口栗涂,這里只是驗(yàn)證代理必須是接口,至于為何這里沒(méi)有顯示哦祈争;
- 創(chuàng)建.class文件的類名為$Proxy自增的num,首次為0斤程,這個(gè)我們待會(huì)可以看到的
- 通過(guò)ProxyGenerator.generateProxyClass生成byte數(shù)組后通過(guò)調(diào)用native方法defineClass0生成class文件
- 返回的為$Proxy0類
- 還記得2中cons.newInstance(new Object[]{h}),有上面class生成可知cons即為$Proxy0調(diào)用 newInstance構(gòu)造函數(shù)傳參為 h即 Proxy.newProxyInstance()中第三個(gè)參數(shù)h實(shí)現(xiàn)InvocationHandler的對(duì)象
- super(var1)將h傳給$Proxy0父類Proxy的h,因此可知$Proxy0中所調(diào)用的super.h即為我們自己寫的實(shí)現(xiàn)InvocationHandler的對(duì)象(很多地方用的是匿名內(nèi)部類)
public final class $Proxy0 extends Proxy implements UserService {
//構(gòu)造函數(shù)中傳入的var1即為上方的h菩混,super即為Proxy
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//父類中的Proxy構(gòu)造函數(shù)h = MyInvocationHandler
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
}
- 以上分析為何動(dòng)態(tài)代理只適用于接口忿墅,看我們生成的$Proxy0 必須要extends Proxy扁藕,而由于java的單繼承原則,因此不能在繼承類了疚脐,只能實(shí)現(xiàn)接口亿柑,因此只適用與接口;
- newProxyInstance返回的為代理生成class類的代理對(duì)象 $Proxy0后亮曹,調(diào)用add方法
- $Proxy0.add() -> h為MyInvocationHandler.invoke()方法橄杨,將m3即接口方法m3 = Class.forName("test.UserService").getMethod("add"),在invoke方法中通過(guò)方式method.invoke() == m3.invoke(target , args) , result為方法返回值
public final void add() throws {
try {
//h為MyInvocationHandler 調(diào)用其invoke方法
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
- 通過(guò)以上調(diào)用了代理類中的invoke
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在目標(biāo)對(duì)象的方法執(zhí)行之前簡(jiǎn)單的打印一下
System.out.println("------------------before------------------");
// 執(zhí)行目標(biāo)對(duì)象的方法,result為方法的返回值
Object result = method.invoke(target, args);
// 在目標(biāo)對(duì)象的方法執(zhí)行之后簡(jiǎn)單的打印一下
System.out.println("-------------------after------------------");
return result;
}
- 注意:這里的invoke是被$Proxy0代理類調(diào)用的照卦,參數(shù)method為$Proxy0代理類中靜態(tài)變量式矫,在invoke中調(diào)用method.invoke即反射調(diào)用實(shí)現(xiàn)接口類的方法,以上即為代理的完善源碼分析役耕!
- 后續(xù):遇到一個(gè)有趣的問(wèn)題采转,好奇打印了一下被代理對(duì)象和代理類Proxy0重寫了toString()方法并反射調(diào)用了被代理類的toString()瞬痘,因此兩者打印的完全一致故慈!遇到不理解的,還是得多多看源碼呀框全!