java动态代理 $Proxy0源码分析
动态代理简介
- 本来是对被代理对象的函数的直接调用,现在却通过一个代理对象间接调用被代理对象的函数,在间接调用时,就可以增加我们想实现的功能(指
InvocationHandler
子类对象的invoke
方法中的实现,可以随意添加我们想执行的代码)。 - 代理对象和被代理对象看起来是一样的,因为它们都实现了同一个接口。这一点可以通过反编译字节码来看。
- 一般情况是,先把java文件编译成class文件,然后类加载器加载这个class字节码文件,从而让JVM可以生成这个类的对象。但动态代理是,在内存中生成这些字节码,然后再正常地加载字节码,生成对象,但是你却从头到尾都感受不到这个类的存在。
- 动态代理只能针对interface。
示例代码
InvocationHandler只是一个只有一个函数的接口,这还意味着Proxy.newProxyInstance
的第三个参数可以使用lambda表达式。
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
有一个接口coder,它有两个函数code和debug。javaCoder类实现了接口coder。
import java.lang.reflect.*;
interface coder{
void code();
void debug(String whichBug);
}
class javaCoder implements coder{
public void code(){
System.out.println("im coding java");
}
public void debug(String whichBug){
System.out.println("im debuging java"+whichBug);
}
}
class DynamicProxyHandler implements InvocationHandler {
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("**** proxy: " + proxy.getClass() +
", method: " + method + ", args: " + args);
if(args != null)
for(Object arg : args)
System.out.println(" " + arg);
return method.invoke(proxied, args);
}
}
class SimpleDynamicProxy {
public static void consumer(coder who) {
who.code();
who.debug("空指针异常");
System.out.println(who.toString());
System.out.println(who.hashCode());
System.out.println(who.equals(new Object()));
}
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
javaCoder javaer = new javaCoder();
consumer(javaer);
System.out.println();
// Insert a proxy and call again:
coder proxy = (coder)Proxy.newProxyInstance(
coder.class.getClassLoader(),
new Class[]{ coder.class },
new DynamicProxyHandler(javaer));
consumer(proxy);
}
}/*output:
im coding java
im debuging java空指针异常
javaCoder@45ee12a7
1173230247
false
**** proxy: class $Proxy0, method: public abstract void coder.code(), args: null
im coding java
**** proxy: class $Proxy0, method: public abstract void coder.debug(java.lang.String), args: [Ljava.lang.Object;@266474c2
空指针异常
im debuging java空指针异常
**** proxy: class $Proxy0, method: public java.lang.String java.lang.Object.toString(), args: null
javaCoder@45ee12a7
**** proxy: class $Proxy0, method: public native int java.lang.Object.hashCode(), args: null
1173230247
**** proxy: class $Proxy0, method: public boolean java.lang.Object.equals(java.lang.Object), args: [Ljava.lang.Object;@6f94fa3e
java.lang.Object@5e481248
false
*/
从用法上来看,InvocationHandler
子类对象的invoke
方法是动态代理调用函数的最后过程,这是我们可以自由设计的地方,比如可以根据Method
对象的名字来得到proxy想要调用的方法。我们只管把InvocationHandler
子类对象传给Proxy.newProxyInstance
作为第三个参数。
如果javaCoder实现了多个接口,同时你也想代理到这些接口的所有方法,那么你应该如此改写:
coder proxy = (coder)Proxy.newProxyInstance(
javaer.getClass().getClassLoader(),//此时应该获取与这个类相关的类加载器,而不是针对于某个接口
javaer.getClass().getInterfaces() ,//获得这个类实现过的所有接口
new DynamicProxyHandler(javaer));
自动生成的$Proxy0源码
在调用Proxy.newProxyInstance
之前,加一句System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
,就会在项目的根目录生成一个$Proxy0.class
,本来这是字节码文件不能直接查看的,但IDEA会自动帮我们反编译class文件。
下面是$Proxy0源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
final class $Proxy0 extends Proxy implements coder {
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 code() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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 debug(String var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
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("coder").getMethod("code");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("coder").getMethod("debug", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
- 从类定义
final class $Proxy0 extends Proxy implements coder
可以看出,java的动态代理只能代理接口interface。因为它已经继承了Proxy
类,那么可变部分只有implements coder
了,如果同时代理了两个接口,那么这里implements后面会跟两个。 - 从此类的静态变量和静态初始化块可以看出,它们保存了coder接口的方法和Object的三个方法对应的Method对象。顺便能看出,
getMethod
方法有时有第二个参数,是因为第一个参数对应的方法有形参。 InvocationHandler
子类对象通过构造函数里的super(var1);
传给了父类的成员变量h
,每次到了需要真正调用时则执行super.h.invoke()
。