先简短的回顾下jdk动态代理用法
1.定义一个基础的接口
public interface Service {
void print();
}
2.简单的实现一下接口
public class MyService implements Service {
@Override
public void print() {
System.out.println("this is print");
}
}
3.实现jdk的InvocationHandler接口
public class MyHandler implements InvocationHandler {
private Service service;
public MyHandler(Service service){
this.service = service;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("this is before!");
Object result = method.invoke(service, args);
System.out.println("this is after!");
return result;
}
}
4.调用Proxy类实现动态增强
public static void main(String[] args) {
Service service = new MyService();
Service proxyInstance = (Service) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
proxyInstance.print();
}
如果不出意外的话,控制台会打印出如下信息
this is before!
this is print
this is after!
这说明我们写的方法,得到了增强!
JDK动态代理的原理
1.jdk动态代理的实质是什么?
不知道大家有没有想过,这行代码
Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
返回的究竟是个什么东西?
带着疑问,我们先使用反射打印一下类名试试。
System.out.println(proxyInstance.getClass().getName());
得到的结果为
com.sun.proxy.$Proxy0
很显然这个类并不是我们创建的。所以到这儿就应该想到了,动态代理实质,就是使用字节码技术,重新生成了一个新类,来达到增强的效果。
那么增强的新类到底是个怎样的类呢?我们来挖掘一下动态代理的源码。
2.jdk动态代理源码分析
分析源码的时候我一般都是根据方法的参数和返回值,大致推敲一下方法的功能,这样可以快速找到关键方法。所以后面的代码都是被我精简过的,大家可以对比着源码阅读。
首先进入newProxyInstance方法,在去除掉业务不相干代码后如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
}
- 第一步通过getProxyClass0方法,获得了一个class对象cl
- 第二步获得了cl对象的构造函数,这个构造函数的参数是一个 InvocationHandler类型
- 第三步通过我们传入的InvocationHandler接口实现类h构造了cl对象的实例。
也就是新类就算这个cl,弄清除cl是啥,动态代理的原理我们就基本弄懂了。所以跟着这个目标,进入getProxyClass0方法。
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
return proxyClassCache.get(loader, interfaces);
}
除去校验的方法外,只剩下一行代码。所以只能在进去看看。在进入之前,先看看proxyClassCache是啥。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
public WeakCache(BiFunction<K, P, ?> subKeyFactory,
BiFunction<K, P, V> valueFactory) {
this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
this.valueFactory = Objects.requireNonNull(valueFactory);
}
现在在进入proxyClassCache的get方法里,进入get方法咋一看代码有点多,其实上面都是缓存相关处理(缓存的话我建议等看完主流程再回头看,那样更有助于理解缓存),我们先跳过,直接看关键代码
while (true) {
if (supplier != null) {
V value = supplier.get();
if (value != null) {
return value;
}
}
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
}
}
关键代码经简化后如上,这样就能清晰的看出supplier就是new Factory(key, parameter, subKey, valuesMap)的实例。所以进入supplier的get方法。
public synchronized V get() { // serialize access
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
return value;
}
}
进入get方法后,关键代码就一句valueFactory.apply(key, parameter),而这个valueFactory就是上面在创建WeakCache时设置的ProxyClassFactory。所以进入ProxyClassFactory的apply方法。
在apply方法中,终于看到了创建代理类的关键方法
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
其中proxyClassFile就是新类的字节码,而defineClass0方法,就是加载这个新类。对于字节码如何构造在这儿我就不深究了。感兴趣的可以自己研究。我更关心新类的结构。所以既然找到了新类的生成方法,我们就将他打印到文件中瞧一瞧。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
"ProxyService", new Class[]{Service.class}, Modifier.FINAL);
OutputStream outputStream = new FileOutputStream(new File("d:/JdkProxy.class"));
outputStream.write(proxyClassFile);
在d盘下找到这个文件,直接用idea打开。
final class ProxyService extends Proxy implements Service {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public ProxyService(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 print() throws {
try {
super.h.invoke(this, m3, (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 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("study.demo.jdk_proxy.Service").getMethod("print");
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动态代理的本质心里就应该有底了。所以后面就不用再说下去了吧。
下一篇,分析一下cglib动态代理的本质