设计模式 -(11)动态代理(Dynamic Proxy)
动态代理是 静态代理 的升级,通过静态代理我们可以对某一个接口的功能进行扩展,而动态代理可以对任意接口进行扩展
如何使用JDK动态代理
如下是使用JDK动态代理对 Programmer 对象生成代理对象的过程,具体通过 java.lang.reflect.Proxy 类生成代理类
package com.example.dp._11dynamicproxy;
import com.example.dp._10staticproxy._02.Programme;
import com.example.dp._10staticproxy._02.Programmer;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProgrammerTimeHandler implements InvocationHandler {
/**
* 被代理的对象
*/
private Object target;
public ProgrammerTimeHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
long start = System.currentTimeMillis();
result = method.invoke(target,args);
System.out.println("方法耗时:" + (System.currentTimeMillis() - start));
return result;
}
public static void main(String[] args) {
// 被代理的对象
Programme target = new Programmer();
// 被代理对象功能扩展类
ProgrammerTimeHandler programmerTimeHandler = new ProgrammerTimeHandler(target);
// 生成的代理对象
Programme programmerTimeProxy =
(Programme) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), programmerTimeHandler);
programmerTimeProxy.action();
}
}
通过以上方式可以生成任意类的代理对象,在静态代理统计时长的例子中只能对 Programme 这一个类型的方法进行时长统计,并且如果要对接口中的多个方法进行同一个操作可能会产生重复代码,而动态代理通过反射方式可以对接口的任意方法进行时长统计,没有额外的代码产生
如何查看JDK生成的动态代理对象
查看源码 java.lang.reflect.Proxy.ProxyClassFactory 类 中的 apply 方法,其中下面这一段为生成代理类 .class 文件的代码:
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
sun.misc.ProxyGenerator.generateProxyClass 方法:
其中 saveGeneratedFiles 参数控制是否显示地保存 .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"));
由此可知将这个系统参数设置为 true 既可以显示地保存 .class 文件,否则.class文件只会加载到内存中,
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
再次运行,在工程目录下会生成 com/sun/proxy 文件夹,代理类 .class 文件存放于此:
通过JDK动态代理生成的 .class 文件内容
可见 代理对象 $Proxy0 实现了 Programme 接口,在 action 方法中调用了父类 Proxy 属性 InvocationHandler h 的 invoke 方法,而 这个 InvocationHandler 就是我们调用 Proxy.newProxyInstance 时传入的,由如下代码可以看出生成的代理对象实际上是调用 $Proxy0(InvocationHandler var1) 构造方法实例化了 $Proxy0
return cons.newInstance(new Object[]{h});
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.sun.proxy;
import com.example.dp._10staticproxy._02.Programme;
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 Programme {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
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 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);
}
}
public final void action() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.example.dp._10staticproxy._02.Programme").getMethod("action");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}