1 概述
之前分享过一篇博客是代理模式的 =》 一篇文章带你搞懂代理模式,一直没有更新JDK动态代理的底层实现,现补上…
JDK动态代理(JDK Dynamic Proxy)是Java语言提供的一种代理模式实现。它允许在运行时动态地创建代理类和代理对象,来代替原始对象进行方法的调用。在使用JDK动态代理时,主要依靠Java提供的两个关键类
java.lang.reflect.Proxy
:JDK提供的代理对象生成工具类java.lang.reflect.InvocationHandler
:代理对象的回调处理器,是一个函数式接口:接收三个参数,返回一个结果
2 调试准备
- 自定义代理对象生成器:用来记录方法执行耗时
package com.lin.tesia.proxy.jdk;
import java.lang.reflect.Proxy;
/**
* 适用于实现了单个接口的实现类生成代理类,多个接口的话返回指定接口类型S,忽略其他接口
*
* @param <S> 接口
* @param <T> 实现类
* @author linxh
* @date 2023/5/15 22:46
*/
public class ProxyGenerator<S, T extends S> {
private final T target;
public ProxyGenerator(T target) {
this.target = target;
}
@SuppressWarnings("unchecked")
public S getProxy() {
Object proxyInstance = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy, method, args) -> {
long start = System.currentTimeMillis();
Object result = method.invoke(target, args);
long end = System.currentTimeMillis();
System.out.println(method.getName() + "执行耗时:\t" + (end - start));
return result;
});
return (S) proxyInstance;
}
}
- 接口和实现类:非public接口、有默认实现方法
package com.lin.tesia.proxy.jdk;
interface CalculatorService {
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
default int mole(int x, int y) {
return x % y;
}
}
package com.lin.tesia.proxy.jdk;
import java.util.concurrent.TimeUnit;
/**
* @author linxh
* @date 2023/5/15 22:43
*/
public class CalculatorServiceImpl implements CalculatorService {
@Override
public int add(int x, int y) {
try {
TimeUnit.MILLISECONDS.sleep(1234);
} catch (InterruptedException e) {
e.printStackTrace();
}
return x + y;
}
@Override
public int sub(int x, int y) {
return x - y;
}
@Override
public int mul(int x, int y) {
return x * y;
}
@Override
public int div(int x, int y) {
return x / y;
}
}
- main
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
ProxyGenerator<CalculatorService, CalculatorServiceImpl> proxyGenerator = new ProxyGenerator<>(new CalculatorServiceImpl());
CalculatorService proxy = proxyGenerator.getProxy();
System.out.println();
System.out.println(proxy.add(3, 4));
System.out.println();
System.out.println(proxy.sub(6, 4));
System.out.println();
System.out.println(proxy.mole(9, 5));
}
- 结果
add执行耗时: 1235
7
sub执行耗时: 0
2
mole执行耗时: 0
4
3 动态代理生成的类结构
上面测试类中通过sun.misc.ProxyGenerator.saveGeneratedFiles
参数开启了JDK生成的代理对象保存在本地磁盘中,生成的类文件结构如下图
- 包名和被代理对象的父接口包名一致(因为该接口是非public的)
- 类名为$Proxy0,并继承自Proxy和实现了需要被代理的接口
- 继承自Proxy,所以和Proxy类一样具备了一个有参构造函数,该参数类型为InvocationHandler,则这个参数是上面我们通过lambda表达式定义的方法耗时记录回调处理器对象
- 具备了很多个Method对象的成员变量,具体的方法签名是在static静态代码块中在类加载的初始化阶段指定
动态代理生成及回调流程如下:
- 当调用getProxy方法获取到的proxy虽然也是CalculatorService类型,但其实这是一个代理对象
- 当调用proxy的方法如mole方法的时候,会把当前代理对象、原始对象的mole方法对象、参数列表回到到自定义实现的InvocationHandler中,则上面自定义的方法耗时记录回调器
- 当回调到自定义的处理器方法中,会执行method.invoke方法(反射的方法应该都很熟了),这时运行时的具体对象就是指定的CalculatorServiceImpl对象,所以CalculatorServiceImpl的modle就是这样被拦截到的,整体流程大致是这样
4 Proxy源码分析
Proxy的源码关键在于newProxyInstance方法,该方法是创建代理对象的静态方法,涉及到代理类字节码文件的生成(这里不做说明),只对关键点说明加以解析说明
- newProxyInstance
/**
* ClassLoader loader:类加载器
* Class<?>[] interfaces:所有的父接口
* InvocationHandler h:回调处理器
*/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// 核心方法
Class<?> cl = getProxyClass0(loader, intfs);
// 拿到了Class对象后,后面的操作就是通过反射创建cl对应的运行时的代理对象而已,不做说明
}
- getProxyClass0:该方法是直接在缓存中通过类加载和接口列表获取代理对象,那这个proxyClassCache是什么?查看到proxyClassCache是Proxy类的一个类变量,在初始化这个对象的时候指定了两个参数:KeyFactory,和ProxyClassFactory
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
return proxyClassCache.get(loader, interfaces);
}
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
- KeyFactory:KeyFactory主要用来说明当前代理对象的父类接口是有几个,一般来说只有一个顶级接口的情况是最多的
private static final class KeyFactory
implements BiFunction<ClassLoader, Class<?>[], Object>
{
@Override
public Object apply(ClassLoader classLoader, Class<?>[] interfaces) {
switch (interfaces.length) {
case 1: return new Key1(interfaces[0]); // the most frequent
case 2: return new Key2(interfaces[0], interfaces[1]);
case 0: return key0;
default: return new KeyX(interfaces);
}
}
}
- ProxyClassFactory:这个对象是生成代理对象的核心,该对象和KeyFactory一样是一个BigFunction,接收两个入参(类加载器和接口列表)返回一个结果(代理对象对应的class对象),在proxyClassCache缓存中获取不到结果的时候就是回调这个方法创建,具体回调这个方法在WeakCache的内部类Factory的get方法中(这里不做说明,只需要知道这里的apply方法做了什么即可,不用管缓存的处理)
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 生成的代理对象的前缀,和下面的原子类型组合在一起,所以看到上面生成的类名是$Proxy0、$Proxy1...
private static final String proxyClassNamePrefix = "$Proxy";
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
// 通过IdentityHashMap来处理重复的接口,因为这里的的接口少且重复率基本没有所有使用IdentityHashMap而不是HashMap
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) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
// 校验是否是接口类型
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
// 校验接口是否重复
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
// 再次遍历所有接口,确定要生成代理对象的包名,如果接口是public的话则使用默认包名:ReflectUtil.PROXY_PACKAGE(com.sun.proxy),如果非public如上面的接口,则使用同路径包名
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) {
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 把要生成的代理对象校验都通过,并确认了包名、类名,则可以生成代理对象了,返回字节码文件回去(具体的字节码生成不作说明,我也没看,没有意义),但是在generateProxyClass方法中有一个参数是saveGeneratedFiles,这个就是我们在上面指定开启的sun.misc.ProxyGenerator.saveGeneratedFiles参数,如果开启了就会对生成的字节码文件保存一份到本地磁盘中
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}