一、JDK动态代理的使用
JDK
动态代理模式是基于接口,所以我们先定义一个Moveable
接口:
public interface Moveable {
void go();
}
再定义一个需要被代理的Car
类:
public class Car implements Moveable {
@Override
public void go() {
System.out.println("car is moving wu wu wu....");
}
}
现在我们遇到的问题或者需求,要给Car
类的对象在执行go()
方法时记录执行时间,Car
类的对象是不能够计算自己本身运行的时间,实现这个需求我们也可以通过继承子类来实现,例如:
public class TimeCar implements Moveable {
@Override
public void go() {
////before calc time
System.out.println("car is moving wu wu wu....");
////after calc time
}
}
但是继承这种方式不建议使用,如果需求频繁变更(要求记录日志、权限),就会造成子类过多并且可重用性不高。优先使用组合的方式,所以我们可以通过组合的方式来实现。思想就是在执行Car
类对象的go()
方法前后添加计算时间这个逻辑,通过一个代理对象来实现,JDK
的动态代理对象需要实现InvocationHandler
接口,我们添加一个计算时间的代理类:
public class CarTimeInvocationHandler implements InvocationHandler {
/**被代理对象*/
private Moveable moveable;
public CarTimeInvocationHandler(Moveable moveable) {
this.moveable = moveable;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
///before process
long start = System.currentTimeMillis();
Object o = method.invoke(moveable, args);
try {
Thread.sleep(new Random().nextInt(10_000));
} catch (InterruptedException e) {
e.printStackTrace();
}
///after process
long end = System.currentTimeMillis();
System.out.println("static proxy car run time:" + (end - start));
return o;
}
}
DynamicMain Main
类测试我们的代理是否成功。
public class DynamicMain {
public static void main(String[] args) {
Car car = new Car();
///保存生成的代理类
// System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Moveable carTime = (Moveable) Proxy.newProxyInstance(car.getClass().getClassLoader(), new Class[]{Moveable.class}, new CarTimeInvocationHandler(car));
System.out.println("=================Time=================");
carTime.go();
}
}
运行结果如下,通过结果我们可以确定时间被成功计算出来了。
=================Time=================
car is moving wu wu wu....
static proxy car run time:2874
二、原理说明
首先我们查看InvocationHandler
接口的源码,接口只定义了一个invoke
方法。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
再查看Proxy
类的源代码,截取关键部分展示如下:
public class Proxy implements java.io.Serializable {
private static final long serialVersionUID = -2222568056686623797L;
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
/**
* the invocation handler for this proxy instance.
* @serial
*/
protected InvocationHandler h;
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
///PS:获取`JDK`生成的代理类对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
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;
}
});
}
///PS:生成代理类的一对象,注入InvocationHandler对象
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
.....
}
注意其中两行就可以了,Class<?> cl = getProxyClass0(loader, intfs);
获取JDK
生成的代理类对象,return cons.newInstance(new Object[]{h});
生成代理类的一对象,并且将传入的InvocationHandler对象注入给代理类生成的对象,到这里我们就能大概知道JDK
动态代理究竟是怎么实现的了。
梳理一下流程:
-
第一步通过
public Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法返回一个由JDK
生成的实现了所有Class<?>[] interfaces
接口的代理类的一个代理对象; -
第二步生成的代理对象执行第一步传入的接口(
Moveable
)中的方法(go()
),通过查看JDK
生成的代理类,可以发现代理类维持了一个InvocationHandler
的对象,就是第一步传入的InvocationHandler
的对象,接口(Moveable
)中的方法(go()
)方法内部会执行第一步传入的InvocationHandler
的对象的invoke(...)
方法,从而实现前置后置操作;
通过在main
方法第一行设置属性System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
可以保存内存中生成的代理类到文件,生成的代理类如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import cn.missbe.model.factory.Moveable;
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 m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
/**
Proxy的newProxyInstance方法返回对象语句
return cons.newInstance(new Object[]{h});会生成当前代理类的对象,
里面的h对应的就是代理类构造方法的var1
InvocationHandler对象会被代理类维持,在调用接口方法时实际调用的是InvocationHandler 对象的invoke方法
*/
super(var1);
}
public final boolean equals(Object var1) throws {
try {
/**h就是传入到InvocationHandler 对象,调用InvocationHandler对象的invoke方法*/
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 void go() throws {
try {
super.h.invoke(this, m3, (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"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("cn.missbe.model.factory.Moveable").getMethod("go");
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
动态代理的原理,JDK
生成代理类的方式好像是通过asm
,怎么生成代理类的可以深入源码现了解,整个时序图如下图:
通过时序图可以清楚的看到整个流程,Proxy
调用newProxyInstance
方法创建代理类实例时,会通过asm
的方式(还有其它方式也可以实现)动态生成一个代理类,些代理类会维持创建对象注入的InvocationHandler
对象,InvocationHandler
对象就是需要自己实现的前后处理。当生成的代理对象调用接口的方法时,实际上执行的是生成的$Proxy0
维持的InvocationHandler
对象h
的invoke()
方法,所以具体的代理逻辑可以写到InvocationHandler
接口的具体实现类中去,可能写得有点乱,不清楚可以留言提问哈。
欢迎关注南阁公众号
转载:https://blog.csdn.net/lovequanquqn/article/details/105818091