戳蓝字「TopCoder」关注我们哦!
关于JDK动态代理,想必小伙伴都知道,它只能代理接口!但是有的小伙伴可能会有疑问,为什么它只能代理接口,不能代理类呢?这里借助某位技术大佬的话--”源码面前,了无秘密“,下面咱们就一起看下JDK动态代理源码实现,最后再探讨下JDK动态代理为什么不能代理类,OK,话不多说,Let's go~
本文分为两部分来分析JDK动态代理,首先看下JDK动态代理使用,然后分析动态代理源码实现及探讨下其为什么不能代理类。 (ps:已经熟悉JDK动态代理基础概念及使用的小伙伴可以跳过第一部分)
Java在JDK1.3后引入的动态代理机制,使我们可以在运行期动态的创建代理类。使用JDK动态代理需要有四个角色:被代理的类,被代理类的接口,织入器,和InvocationHandler,而织入器使用接口(通过生成字节码机制)生成一个代理类,然后在这个代理类中织入代码(被代理的类是AOP里所说的目标,InvocationHandler是切面)。
动态代理是在运行期间通过接口生成代理类的,与静态代理相比更加灵活,但是也有一定的限制,第一是代理对象必须实现一个接口,否则会报异常,因为人家原理就是根据接口来生成代理对象的。第二是有性能问题,因为是通过反射来实现调用的,所以比正常的直接调用来得慢,并且通过生成类文件也会多消耗部分方法区空间,可能引起Full GC。
public static interface Hello {
void hi(String msg);
}
public static class HelloImpl implements Hello {
@Override
public void hi(String msg) {
System.out.println("hello " + msg);
}
}
/**
* 代理类
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class HelloProxy implements InvocationHandler {
private Object proxied = null;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("hello proxy");
return method.invoke(proxied, args);
}
}
public static void main(String[] args) {
Hello hello = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), new Class[]{Hello.class}, new HelloProxy(new HelloImpl()));
System.out.println(hello.getClass()); // class com.sun.proxy.$Proxy0
hello.hi("world");
}
通过调用Proxy静态方法 Proxy.newProxyInstance()
可以创建动态代理,这个方法需要得到一个类加载器,一个你希望该代理实现的接口列表(不是类或抽象类),以及InvocationHandler的一个实现类。动态代理可以将所有调用重定向到调用处理器,因此通常会调用处理器的构造器传递一个”实际”对象的引用,从而将调用处理器在执行中介任务时,将请求转发。
上述示例代码我们看到了 method.invoke
,这不是反射么,动态代理和反射到底是个什么关系 ?动态代理底层是基于反射来实现的,这么说也不算错但稍微有些不全面。以上述代码为例,首先是写一个InvocationHandler实现类并调用Proxy.newProxyInstance生成代理类,然后在InvocationHandler实现类中通过反射调用对应的代理方法。最后看下上述代码的调用栈信息:在默认情况下,方法的反射调用为委派实现,委派给本地实现来进行方法调用。在调用超过 15 次之后,委派实现便会将委派对象切换至动态实现。这个动态的字节码是在Java运行过程中通过ASM自动生成的,它将直接使用 invoke 指令来调用目标方法。由于本文重点是讨论动态代理,所以针对反射就不再详细讨论了。
以上述示例代码为例,默认情况下在运行时程序自动生成proxy class,如果我们想看下自动生成proxy clas到底长什么样该如何办呢?可以通过参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
(或者执行 System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
)来保存成对应的class文件,比如示例代码中生成的class文件如下(在IDEA中查看源码):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import com.youzan.owl.goingmerry.util.ProxyMain.Hello;
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 Hello {
private static Method m1;
private static Method m3;
private static Method m2;
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 hi(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 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 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"));
m3 = Class.forName("com.youzan.owl.goingmerry.util.ProxyMain$Hello").getMethod("hi", Class.forName("java.lang.String"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
下面开始分析JDK动态代理生成proxy class流程源码,以上述示例代码为例,从 Proxy.newProxyInstance()
方法开始,由于篇幅有限,以下代码分析只贴出主要流程代码。首先加载被代理类的接口的class,然后通过执行方法 java.lang.reflect.Proxy.ProxyClassFactory#apply
创建对应proxy class:
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
/*
* 判断不是接口则抛异常,说明了JDK动态代理只能对接口代理
* 不过到这里还不能说明为什么不能对类进行代理
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
//...
}
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
// public static final String PROXY_PACKAGE = "com.sun.proxy";
// 默认生成的proxy class 包路径
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
* 自动生成一个proxy class,序号+1
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num; // proxyClassNamePrefix = "$Proxy";
/*
* Generate the specified proxy class.
* 开始生成动态proxy class,这里使用了ProxyGenerator类
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
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());
}
}
关于 ProxyGenerator.generateProxyClass()
,感兴趣的同学可以自行阅读对应代码,其内部主要就是按照固定“ 模板
”按照字节码格式生成对应class字节码,大致就是实现要代理接口的所有public方法及固定的方法(hashCode、equals和toString),然后调用InvocationHandler的invoke方法,也就是走到示例代码中的 HelloProxy
类( class HelloProxy implements InvocationHandler
)。
其实我们还可以在程序中直接调用 ProxyGenerator.generateProxyClass()
来生成动态代理类,如下所示:
String name = "$Proxy00";
byte[] data = ProxyGenerator.generateProxyClass("com.sun.proxy." + name, new Class[] { Hello.class } );
FileOutputStream out = new FileOutputStream("/tmp/" + name + ".class" );
out.write(data);
out.close();
这里可以对比下$Proxy00.class和JDK动态代理生成的$Proxy0.class文件,二者除了类名之外,代码都是相同的。
最后来看下JDK动态代理为什么不能代理类,对照着生成的$Proxy0.class文件,如下所示一起来分析下:
public final class $Proxy0 extends Proxy implements Hello {
private static Method m1;
private static Method m3;
private static Method m2;
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 hi(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);
}
}
类Proxy的作用简单来说就是保存下用于自定义的InvocationHandler,便于在方法代理时执行自定义InvocationHandler的逻辑。由于$Proxy0已经继承了类Proxy,所以不能再extends一个类了,所以只能implements一个接口了。
参考资料:
https://mp.weixin.qq.com/s/58CkiW1ZjqiwLWctJbBVFg
https://www.cnblogs.com/flyoung2008/archive/2013/08/11/3251148.html