上篇文章《Java设计模式(13)----------代理模式》中,介绍了两种代理模式(静态代理和动态代理)的应用场景和实际应用案例。本篇文章中,对动态代理的原理做进行深入的分析。
关于动态代理,初看可能会比较费解,大概有如下几个疑问:
- 代理是怎么形成的,代理类在哪里?
TimeHandler
类是做什么用的,在哪里被调用了?- 客户端调用的时候,写的是调用
m.move();
,程序是如何执行到了TimeHandler
对象的invoke
方法中了呢?
这篇文章,主要针对如上几个疑问进行展开。
首先声明一点,案例中的TimeHandler
并不是代理类,而是代理依赖的一个类而已。真正的代理类,是JDK直接生成的字节码并进行加载的,所以对用户是不可见的。
生成代理类的代码是这个:
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
这里向newProxyInstance
传递了三个参数,分别是原始类的加载器,原始类实现的接口(Moveable
),TimeHandler
类的对象。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);
final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();
/*
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
newProxyInstance
的代码实现比较清晰,通过getProxyConstructor
方法创建了代理类,并返回了该类的构造方法。之后使用反射,生成代理类的对象并返回。
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}
在getProxyConstructor
函数中,可以看到if-else分支,这里是对单接口的情况做了代码优化。我们主要关注其代理类的生成部分,就是new ProxyBuilder(ld, clv.key()).build()
。
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build() 这种写法是lambda表达式的语法,非常精简。
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
在build方法中,从语意上可以看到代理类proxyClass
在defineProxyClass
方法中生成。之后通过反射并根据构造函数的参数,获取到代理类的构造方法并返回。
这里的参数为private static final Class<?>[] constructorParams = { InvocationHandler.class };
。所以看到这里,应该明白了TimeHandler
的作用了,就是作为代理类的构造方法的一个参数,也就是代理类依赖的对象。到这里,也就找到了文章开始处提出的问题二的答案。这里可以猜测一下,代理类中的对应接口的方法,应该是调用的TimeHandler类的invoke方法,通过控制invoke的参数,来调用不同的方法。
在defineProxyClass
方法中,最关键的代码如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
try {
Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
0, proxyClassFile.length,
loader, null);
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} 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());
}
这里先生成字节码,之后将其加载到内存中。
static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try {
int i = name.lastIndexOf('.');
Path path;
if (i > 0) {
Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Paths.get(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}
return classFile;
}
在generateProxyClass
方法中,先生成了ProxyGenerator
对象,然后调用对象的generateClassFile
生成字节码。可以看到其中有个saveGeneratedFiles
的标志位,表示是否需要保存生成的字节码文件。在generateClassFile
方法中,创建了ProxyMethod,字段表和方法表集合,并将内容按照字节码的规范写入到流中。至此代理类就生成了,文章开始处的问题一就回答完毕了
此处代码中可以根据语意看到会使用
InvocationHandler
的方法,目前对java的class文件的格式还不够了解,有机会研究一下。
刚刚说到代码中是存在打印生成的代理类的逻辑的,就是saveGeneratedFiles
标志位,在该变量定义处可以看到:
private static final boolean saveGeneratedFiles =
java.security.AccessController.doPrivileged(
new GetBooleanAction(
"jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();
表示该变量是由jdk.proxy.ProxyGenerator.saveGeneratedFiles
控制的。所以我们在代码中只需要指定这个值为true就可以了,代码如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) throws Exception{
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
Car car = new Car();
Class<?> cls = car.getClass();
InvocationHandler h = new TimeHandler(car);
Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(), h);
m.move();
System.out.println("");
m.move_back();
System.out.println("");System.out.println("");
}
}
运行之后,就可以在工程目录下找到对应的代理类的class文件啦
使用idea打开,可以看到对应的java代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
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 Moveable {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
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 move() throws Exception {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (Exception | 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 move_back() throws Exception {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (Exception | 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("Moveable").getMethod("move");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("Moveable").getMethod("move_back");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
代理类解读:
- 代理类的构造方法,接收一个
InvocationHandler
对象,调用父类Proxy的初始化方法,使用该对象为依赖h
的对象赋值。 - 以move方法为例,我们可以看到,代理类实现了
Moveable
接口,在move方法的实现中,调用了InvocationHandler
对象的invoke方法,并且其中第二个参数为m3对象。而m3对象是静态块中通过反射获取到的Moveable
接口的move
方法。所以,此处就回答的文章开始处的问题三。
到这里,动态代理的原理就非常清晰了,代理类是jdk直接以字节码的形式生成出来的,继承Proxy类,实现客户端定义的接口。因为Proxy依赖InvocationHandler
实现类,所以代理类也同样依赖。对于接口中定义的函数move,代理类中也会实现,其逻辑是调用InvocationHandler
类中的invoke方法,并将方法move方法作为invoke的一个参数。在invoke方法中,可以进行功能扩展,进而执行invoke参数中的方法,完成对原始对象的move方法的调用。不得不佩服java源码的作者们的深厚功力,能够设计出如此高扩展的结构,自己需要学习和实践的还有很多。
热门评论
道可老师,写的日记很有条理,,,,棒极了