JDK动态代理详解
JDK动态代理是代理模式的一种,且只能代理接口。spring也有动态代理,成为CGLib,现在主要来看一下JDK动态代理是如何实现的?
一、介绍
JDK动态代理是有JDK提供的工具类Proxy实现的,动态代理类是在运行时生成指定接口的代理类,每个代理实例(实现需要代理的接口)都有一个关联的调用处理程序对象,此对象实现了InvocationHandler,最终的业务逻辑是在InvocationHandler实现类的invoke方法上。
也即是在invoke方法上可以实现原方法中没有的业务逻辑,相当于spring aop的@Before、@After等注解。
二、样例
(1)接口
public interface ProxySource {
void test();
}
(2)实现类
public class ProxySourceImpl implements ProxySource {
@Override
public void test() {
System.out.println("原有业务逻辑");
}
}
(3)实现InvocationHandler接口和invoke方法
static class MyHandler implements InvocationHandler {
//需要代理的类
Object target;
public MyHandler(Object target) {
this.target = target;
}
/**
* @param proxy 动态代理实例
* @param method 需要执行的方法
* @param args 方法中参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do other things befor");
method.invoke(target, args);
System.out.println("do other things after");
return null;
}
}
(4)利用Proxy实现代理类
//此参数设置是为了保存生成代理类的字节码文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
ProxySource proxySource = (ProxySource) Proxy.newProxyInstance(
ProxySourceImpl.class.getClassLoader(),
ProxySourceImpl.class.getInterfaces(),
new MyHandler(new ProxySourceImpl())
);
//执行方法
proxySource.test();
(5)方法调用结果
可以看到,在原有方法执行前后都执行了其他代码。
三、源码分析(主要看Proxy.newProxyInstance方法,省略非核心代码)
(1)newProxyInstance方法
Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
{
final Class<?>[] intfs = interfaces.clone();
//获取代理接口class
Class<?> cl = getProxyClass0(loader, intfs);
//获取到class之后用反射获取构造方法,然后创建代理类实例
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});
} catch (Exception e) {
throw new InternalError(e.toString(), e);
}
}
由上述代码可知,主要是通过getProxyClass0方法获取到代理接口的class
(2)getProxyClass0方法
Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//如果已经有相应的字节码文件,则之间返回,否则通过代理类工厂创建代理类
return proxyClassCache.get(loader, interfaces);
}
而proxyClassCache又是什么东东呢?
WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
proxyClassCache是一个WeakCache对象,可知这是一个缓存对象,这个类结构是通过ConcurrentHashMap实现的,
ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
= new ConcurrentHashMap<>();
数据结构是(key,sub-key)->value
存的值也就是<ClassLoader,<interfaces,$Proxy.class>>
(3)get方法
public V get(K key, P parameter) {
Object cacheKey = CacheKey.valueOf(key, refQueue);
//根据classloader为key查看缓存中是否已有
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
//获取到weakcache种的sub-key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//根据sub-key去当前类加载器下是否有该代理接口的字节码
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
//supplier是代理类工厂实例
V value = supplier.get();
if (value != null) {
return value;
}
}
//创建代理类工厂
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
//将上述创建的代理类工厂直接赋值给supplier
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
supplier = factory;
}
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
supplier = factory;
} else {
supplier = valuesMap.get(subKey);
}
}
}
}
这个supplier.get方法点进去,核心就是ProxyClassFactory的apply方法
(4)ProxyClassFactory的apply方法
Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
for (Class<?> intf : interfaces) {
Class<?> interfaceClass = null;
try {
//通过类权限定名反射获取class
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");
}
}
long num = nextUniqueNumber.getAndIncrement();
//生成代理类的权限定名,例如com.sun.proxy.$Proxy0
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//调用本地方法生成class
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
上述代码中核心是生成字节码,即是ProxyGenerator.generateProxyClass方法。
(5)ProxyGenerator.generateProxyClass方法。
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) {
//保存生成代理类的字节码文件
}
return var4;
}
上述代码中saveGeneratedFiles点进去是这样的,也即是开头样例中的设置属性。
private static final boolean saveGeneratedFiles =
((Boolean)AccessController
.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
但主要还是ProxyGenerator的generateClassFile这个方法。
其默认给代理类生成了hashcode、equals和toString方法,也限制了代理接口和字段都不能超过65535个。
现在来看一下保存的代理类字节码文件是怎么样的(通过idea反编译后)
public final class $Proxy0 extends Proxy implements ProxySource {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
//代码省略
}
public final void test() throws {
//h是invocationhandler,所以最后是执行invoke方法
super.h.invoke(this, m3, (Object[])null);
}
public final String toString() throws {
//代码省略
}
public final int hashCode() throws {
//代码省略
}
static {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.liusy.lang.ProxySource").getMethod("test");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
}
}
可以看到,生成的$Proxy0继承了Proxy,实现了我定义的接口ProxySource,里面有四个方法,m0~m3,通过静态代码块中根据类的全限定名和方法名反射获取,而最后是执行InvocationHandler的invoke方法。
至此,JDK动态代理已经说完,希望对你有所帮助。
- asp.net web api 异常捕获
- asp.net web api 文件上传
- 使用MySQL正则表达式 __MySQL必知必会
- 史上最好用的免费翻蔷利器
- asp.net web api 接口安全与角色控制
- TensorFlow从0到1 | 第十五章 重新思考神经网络初始化
- asp.net web api 下载之断点续传
- apache2.4.x三种MPM介绍
- 没有自己的服务器如何学习生物数据分析(上篇)
- 【直播】我的基因组57:最简陋的祖源分析
- asp.net web api 使用Odata
- TensorFlow从0到1丨第十六篇 L2正则化对抗“过拟合”
- TensorFlow从0到1 | 第十七章 Step By Step上手TensorBoard
- C#要点
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法