jdk动态代理: 从源码,到字节码,到自己手写动态代理
Posted 小豆的奇思妙想
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jdk动态代理: 从源码,到字节码,到自己手写动态代理相关的知识,希望对你有一定的参考价值。
注意
本篇文章,讲解非常详细.已经到了字节码层面.
内容和篇幅较多.请有耐心,并按照源码依依查看.
反编译查看: 动态代理生成的字节码
分析: 所有的方法处理,都转发给InvocationHandler
源码分析: Proxy.newProxyInstance()
作用: 安全校验,Class反射构造器创建对象
获取所有接口
获取动态对象的Class
反射获取构造器
通过构造器,创建对象
核心: Class cl = getProxyClass0(loader,infs)
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
// lzy分析: null判断
Objects.requireNonNull(h);
// lzy分析: 获取所有需要动态代理的接口
final Class<?>[] intfs = interfaces.clone();
// lzy分析: 系统安全校验器
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// lzy分析: 安全校验
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
// lzy分析(核心): 生成代理类的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
// lzy分析: 安全校验
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// lzy分析: 反射获取构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
// lzy分析: 判断构造方法权限修饰符.如果没有权限,强制访问.
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
// lzy分析: 通过构造方法创建对象.
// h是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);
}
}
源码分析: Proxy.getProxyClass0(loader,infs)
// lzy分析: 接口个数限制
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
// lzy分析: 通过类加载器loader 和 所有代理接口interfaces,从缓存中获取
return proxyClassCache.get(loader, interfaces);
源码分析: WeakCache.get()
作用: 获取工厂,利用工厂创建代理对象
类加载获取,已经加载的接口
所有接口进行Hash唯一标识
准备创建工厂
通过工厂创建代理对象
重试机制: 工厂存在问题,循环中将创建新工厂
核心: V value = suppile.get()
public V get(K key, P parameter) {
// lzy分析: null指针判断
Objects.requireNonNull(parameter);
// lzy分析: 将进入ReferenceQueue队列中数据,删除掉
expungeStaleEntries();
// lzy分析: 类加载器 和 引用队列,存储到WeakReference引用中
Object cacheKey = CacheKey.valueOf(key, refQueue);
// lazily install the 2nd level valuesMap for the particular cacheKey
// lzy分析: 通过类加载器,获取已加载过的接口和接口工厂
// key: 类加载器的WeakReference封装
// value:
// key: 接口元数据
// value: 接口工厂(作用: 创建动态代理对象)
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
// lzy分析: 初始化操作,如果是第一次.那么创建一个空的Map
if (valuesMap == null) {
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {
valuesMap = oldValuesMap;
}
}
// create subKey and retrieve the possible Supplier<V> stored by that
// subKey from valuesMap
// lzy分析: 将所有接口进行hash唯一标识. 确保对应着一个工厂.
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
// lzy分析: 通过接口元数据对象,尝试性获取工厂
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
// lzy分析: 第一次是没有工厂的.
// 这一步在第一次会跳过 , 下面代码将创建工厂
// 通常情况下,执行这一步.是在第二次循环.
// 因为第一次循环,是在创建工厂
// 只有到了第二次循环,工厂才被创建好.
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
// 利用工厂进行创建
V value = supplier.get();
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
// 创建一个工厂
if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
// 将工厂放入缓存中
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
// 已经创建好了,为第二次循环作准备.
// 第二次循环,可以利用工厂进行创建
supplier = factory;
}
// else retry with winning supplier
// lzy分析: 这是工厂的重试机制
// 触发条件: V value = supplier.get();
// 通过工厂创建对象,是null值
} else {
// lzy分析: 由于旧工厂存在问题,无法成功创建对
// 那么使用新工厂来代替旧工厂.
// 意味着: 将进入下一轮循环.用新工厂,创建对象.
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
源码细节分析: CacheKey.valueOf(key,refQueue)
作用:弱引用,GC触发时一定会被回收.但WeakReference对象本身,将进入到ReferenceQueue中
private static final class CacheKey<K> extends WeakReference<K> {
static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {
return key == null
// null key means we can't weakly reference it,
// so we use a NULL_KEY singleton as cache key
? NULL_KEY
// non-null key requires wrapping with a WeakReference
: new CacheKey<>(key, refQueue);
}
}
源码细节分析: WeakCache.expungeStaleEntries()
作用: 每次创建动态代理时,都会先清理一遍因GC触发而保留下来的Reference中引用.
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {
cacheKey.expungeFrom(map, reverseMap);
}
}
源码细节分析: Proxy.KeyFactory.apply()
作用: 将所有接口进行hash值.确保唯一标识,对应着一个工厂
位置: subkeyFactory.apply(key,parameter)
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);
}
}
}
源码分析: WeakCache.get()
作用: 中间层,创建好的对象. 实行WeakReference弱引用
核心: valueFactory.apply(key,paramteter)
public synchronized V get() { // serialize access
// re-check
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// something changed while we were waiting:
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);
// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);
// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
源码分析:Proxy.ProxyClassFactory.apply()
作用: 创建代理对象准备工作. 检验接口,代理类名称
核心: ProxyGenerator.generateProxyClass()
核心: defineClass0()
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
// lzy分析: 对所有接口进行校验
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
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");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
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;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
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) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
// lzy分析:采用并发安全的操作,动态代理的名称自增1
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
// lzy分析: 生成动态代理类的字节码文件.即$Proxy.class
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// lzy分析: 字节码文件,加载到JVM虚拟机内存中.
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
源码分析: Proxy.defineClass0()
作用: 将字节码文件,加载到内存中
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
这里是一个本地方法,不做分析.
如果我们想达到相同效果,可以自定义一个类加载器.如下:
public class MyClassLoader extends ClassLoader {
private byte[] bytes;
public MyClassLoader(byte[] bytes){
this.bytes = bytes;
}
public Class<?> findClass(String name) throws ClassNotFoundException {
return defineClass(name,bytes,0,bytes.length);
}
}
源码分析: ProxyGenerator.generateProxyClass()
核心: var3.generateClassFile()
// lzy分析: 动态代理创建者对象
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// lzy分析: 利用创建者对象,开始创建
final byte[] var4 = var3.generateClassFile();
// lzy分析: 将生成的字节码二进制流,保存到本地文件
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);
}
}
});
}
理论探讨
接下来工作,我们将生成动态代理的字节码文件.
即,$Proxy.class.我们将到操作字节码的层面.
问题: 怎么样操作字节码?
通常方式有两种.第一种是ASM框架,第二种方式是javassist框架.
ASM框架是指令层面的框架,使用技术要求高,必须懂得JVM指令和字节码.运行速度更快.
javassist框架是高度封装的框架,使用门槛低,不需懂JVM.运行速度偏慢.
jdk中动态代理是如何实现的呢?
它既没有采用ASM框架,也没有采用javassist框架.
它简单粗暴按照JVM字节码规范,强行用字节流进行写入.
当做一个文本文件一样,先写入一个4字节的魔术号,再写入一个2字节的
大版本号,2字节的小版本号,依次按照特定规范写入.整个过程,非常原始和底层.
没有进行任何过多的二次封装.
字节码规范: 强制按照先后顺序,并且指定大小
1. 魔术号(4字节)
2. 大版本号(2字节)
3. 小版本号(2字节)
4. 常量池大小(2字节)
5. 常量池
6. 访问修饰符(2字节)
7. 当前类(2字节)
8. 父类(2字节)
9. 接口个数(2字节)
10.接口
11. 字段个数(2字节)
12. 字段
13. 方法个数(2字节)
14. 方法
15. 属性个数(2字节)
16. 属性
源码分析: ProxyGenerator.generateClassFile()
作用: 生成字节码文件.本质是,写入一个字节流
核心流程:
// 作用: 生成字节码文件
generateClassFile(): 建立IO流,向其中写入字节
第一部分: 添加三个固定方法的动态代理
hashCode(),equals(),toString()
第二部分: 获取所有的接口
循环遍历: 将接口所有的方法,建立动态代理
核心: this.addProxyMethod(var8, var4);
注意: 这里仅仅是解析数据,将数据进行封装.没有开始真正的生成字节码.
第三部分: 检查所有代理方法的返回类型
checkReturnTypes()
第四部分:
1. 添加构造方法 this.methods.add(this.generateConstructor());
2. 生成代理方法的字节码(核心): 利用第二部分解析的元素
// 生成字段的字节码
// 作用: 每个动态代理的方法,都需要一个Method的反射类引用进行指向
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
// 生成方法的字节码
this.methods.add(var16.generateMethod());
第五部分: 写入类的字节码
1. 魔术(4个字节)
注意: 这里的魔术不是0xCAFEBABY,动态代理生成的有些特殊.
var14.writeInt(-889275714);
2. 大版本号(2字节)
var14.writeShort(0);
3. 小版本号(2字节)
var14.writeShort(49);
4. 常量池: 向常量池中的每一项,写入数据
// 作用: 创建常量池
// 第一步: 魔术号 + 大版本号 + 小版本号 + 常量池大小
// 第二步: 写入每一项的数据. 每个项的组成: 1. 标识 2.数据
this.cp.write(var14); //注意: 常量池的解析早就完成,这一步已经到了写入字节码
5. 类的权限修饰符
var14.writeShort(this.accessFlags);
6. 当前类
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
7. 父类
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
8. 接口大小
var14.writeShort(this.interfaces.length);
9. 生成接口相关信息
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
10. 字段大小
var14.writeShort(this.fields.size());
11. 写入字段相关信息
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
12. 方法大小
var14.writeShort(this.methods.size());
13. 写入方法相关信息
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
14. 写入空指令
var14.writeShort(0)
private byte[] generateClassFile() {
// 第一部分: 添加方法的元数据信息. 只是准备阶段.还没有开始生成字节码
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
// 第二部分: 检查所有代理方法的返回类型
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
// 第三部分: 生成构造方法的字节码
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
// 第三部分: 生成字段的字节码
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
// 第三部分: 生成方法的字节码
this.methods.add(var16.generateMethod());
}
}
// 第四部分: 生成静态初始化的字节码
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
源码细节分析: ProxyGenerator.addProxyMethod()
作用: 解析方法的元数据.
注意: 这一步没有产生字节码,只是准备工作.准备数据阶段.
private void addProxyMethod(Method var1, Class<?> var2) {
String var3 = var1.getName();
Class[] var4 = var1.getParameterTypes();
Class var5 = var1.getReturnType();
Class[] var6 = var1.getExceptionTypes();
String var7 = var3 + getParameterDescriptors(var4);
Object var8 = (List)this.proxyMethods.get(var7);
if (var8 != null) {
Iterator var9 = ((List)var8).iterator();
while(var9.hasNext()) {
ProxyGenerator.ProxyMethod var10 = (ProxyGenerator.ProxyMethod)var9.next();
if (var5 == var10.returnType) {
ArrayList var11 = new ArrayList();
collectCompatibleTypes(var6, var10.exceptionTypes, var11);
collectCompatibleTypes(var10.exceptionTypes, var6, var11);
var10.exceptionTypes = new Class[var11.size()];
var10.exceptionTypes = (Class[])var11.toArray(var10.exceptionTypes);
return;
}
}
} else {
// lzy分析: 初始化
// key: 方法名+方法描述符
// value: ArrayList
var8 = new ArrayList(3);
this.proxyMethods.put(var7, var8);
}
// 数据准备: 创建动态代理的核心信息.将依照这些元数据信息,产生代理类的方法信息.
((List)var8).add(new ProxyGenerator.ProxyMethod(var3, var4, var5, var6, var2));
}
源码细节分析:ProxyGenerator.generateConstructor()
作用: 构造方法的字节码生成
注意: 我们将仔细分析,MethodInfo的构造方法(在下面)
// 创建方法信息类: 指定方法名 和 描述符 和 访问修饰符
ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
// 准备一个IO流: 此流最终写入到字节码文件中
DataOutputStream var2 = new DataOutputStream(var1.code);
// 获取局部变量表中下标0位置的数据,即this
this.code_aload(0, var2);
// 获取局部变量表下标1位置的数据,即InvocationHandler
this.code_aload(1, var2);
// 调用超类构造器 0xb7 invokespecial
var2.writeByte(183);
// 获取常量池中方法的下标. 作用: 调用父类Proxy中的有参构造方法
var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
// 方法返回值void 0xb1 return
var2.writeByte(177);
// 方法的操作数栈最大深度
var1.maxStack = 10;
// 局部变量表大小
var1.maxLocals = 2;
// 异常表
var1.declaredExceptions = new short[0];
return var1;
源码细节分析: ProxyGenerator.MethodInfo()
作用: 将方法相关信息. 生成字节码写入IO流中. 写入到常量池中
public MethodInfo(String var2, String var3, int var4) {
// 方法名称
this.name = var2;
// 方法描述符
this.descriptor = var3;
// 方法的权限修饰符
this.accessFlags = var4;
// 尝试将这些信息放入常量池
ProxyGenerator.this.cp.getUtf8(var2);
ProxyGenerator.this.cp.getUtf8(var3);
ProxyGenerator.this.cp.getUtf8("Code");
ProxyGenerator.this.cp.getUtf8("Exceptions");
}
源码细节分析:ProxyGenerator.ConstantPool.getUtf8()
原理: 尝试从常量池中获取. 如果没有,就保存到常量池中,并建立缓存.
public short getUtf8(String var1) {
if (var1 == null) {
throw new NullPointerException();
} else {
return this.getValue(var1);
}
}
调用关系: getValue()
private short getValue(Object var1) {
Short var2 = (Short)this.map.get(var1);
if (var2 != null) {
return var2;
} else if (this.readOnly) {
throw new InternalError("late constant pool addition: " + var1);
} else {
short var3 = this.addEntry(new ProxyGenerator.ConstantPool.ValueEntry(var1));
this.map.put(var1, new Short(var3));
return var3;
}
}
调用关系: addEntry()
private short addEntry(ProxyGenerator.ConstantPool.Entry var1) {
this.pool.add(var1);
if (this.pool.size() >= 65535) {
throw new IllegalArgumentException("constant pool size limit exceeded");
} else {
return (short)this.pool.size();
}
}
分析: 常量池类
private static class ConstantPool {
// 封装的功能类: 常量池中数据最终写入文件中.
// Enrty是封装了具有IO流功能的常量池项.
// 包含了: 写入的细节
private List<ProxyGenerator.ConstantPool.Entry> pool;
// key: 字符串标识
// value: 在常量池中的下标
private Map<Object, Short> map
}
继承体系分析: 两个子类
class ValueEntry extends ProxyGenerator.ConstantPool.Entry
class IndirectEntry extends ProxyGenerator.ConstantPool.Entry
两个类的职责不同:
ValueEntry处理基本数据类型
IndirectEntry处理其他的特殊类型.方法字段等等.
分析: ValueEntry
第一部分: 标识
第二部分: 真实数据
private static class ValueEntry extends ProxyGenerator.ConstantPool.Entry {
private Object value;
public void write(DataOutputStream var1) throws IOException {
if (this.value instanceof String) {
var1.writeByte(1);
var1.writeUTF((String)this.value);
} else if (this.value instanceof Integer) {
var1.writeByte(3);
var1.writeInt((Integer)this.value);
} else if (this.value instanceof Float) {
var1.writeByte(4);
var1.writeFloat((Float)this.value);
} else if (this.value instanceof Long) {
var1.writeByte(5);
var1.writeLong((Long)this.value);
} else {
if (!(this.value instanceof Double)) {
throw new InternalError("bogus value entry: " + this.value);
}
var1.writeDouble(6.0D);
var1.writeDouble((Double)this.value);
}
}
}
分析:IndirectEntry
第一部分: 标识符
第二部分: 真实数据
第三部分: 真实数据
private static class IndirectEntry extends ProxyGenerator.ConstantPool.Entry {
private int tag;
private short index0;
private short index1;
public void write(DataOutputStream var1) throws IOException {
var1.writeByte(this.tag);
var1.writeShort(this.index0);
if (this.tag == 9 || this.tag == 10 || this.tag == 11 || this.tag == 12) {
var1.writeShort(this.index1);
}
}
}
:
CONSTANT_Filedref_info 9
CONSTANT_Methodref_info 10
CONSTANT_InterfaceMethodref_info 11
CONSTANT_NameAndType_info 12
为什么要这样设计?
因为这四类数据,他的值有两部分.一部分,无法表示完整.
两部分
u2 name_index;
u2 descriptor_index;
方法或字段的名称索引 :
描述符的索引 :
源码分析:ProxyGenerator.ProxyMethod.generateMethod()
动态代理: 生成源码 return (String)super.h.invoke(this, A.m2, null);
Java代码: super.h
字节码操作:
// 180的实际含义: JVM操作指令getField:0xB4 转十进制180
var9.writeByte(180);
// 获取字段: Field java/lang/reflect/Proxy.h:Ljava/lang/reflect/InvocationHandler
// 在常量池中的索引,最终返回18
var9.writeShort(ProxyGenerator.this.cp.getFieldRef("java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;"));
java代码: A.m2
// 178的实际含义: JVM操作指令getStatic:0xB2 转十进制178
var9.writeByte(178);
// 获取字段: A.m2 Ljava/lang/reflect/Method;
// 在常量池中的索引
var9.writeShort(ProxyGenerator.this.cp.getFieldRef(ProxyGenerator.dotToSlash(ProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
分析源码:
if (this.parameterTypes.length > 0) {
// 将数据大小,压入到操作数栈中
ProxyGenerator.this.code_ipush(this.parameterTypes.length, var9);
// 数据创建指令.anewarray
var9.writeByte(189);
// 获取常量池中java/lang/Object的索引,写入到操作数栈中
var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/Object"));
//
for(int var10 = 0; var10 < this.parameterTypes.length; ++var10) {
// 复制栈顶数据.即Object数组
// 作用: 指令是一次性的.每次复制,都需要从局部变量表中获取Object数组的引用
var9.writeByte(89);
// 将常量压入到操作数栈中(每次循环增加一个): 0 , 1 ,2 , 3.....
// 作用: Object数组初始化,从0开始
ProxyGenerator.this.code_ipush(var10, var9);
// 类型兼容转换方法
// 作用: 因为数组作用是方法的参数列表,但是参数类型肯定不一定是Object
// 需要通过类型判断,存储正确的类型到Object数组中
// this.parameterTypes[var10] 获取一个Class类型. 判断参数类型的依据
// var3[var10]真实的数据. 按情况,对数据类型作出一些调整.
// var9字节码输出流.
// 例如: Integer,Byte,Short,Char,Boolean 在JVM层面都当做int来处理
// 我们需要将引用数据类型的Integer,拆箱成int类型
this.codeWrapArgument(this.parameterTypes[var10], var3[var10], var9);
// 十进制83 转换 十六进制0x53. JVM指令aastore
// 作用: 将引用数据类型存储到局部变量表中
var9.writeByte(83);
}
} else {
var9.writeByte(1);
}
解析:
判断方法调用时,需要多少个形式参数.
如果大于0,需要参数.
//
否则.传递null
// 十进制1 转成 16进制0x01. JVM指令集中:
var9.writeByte(1);
Java源码:
// 十进制185 转 十六进制0xb9
// 作用: invokeInteger指令 调用接口
var9.writeByte(185);
// 获取接口在常量池中的索引
// 为调用指令作准备
var9.writeShort(ProxyGenerator.this.cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
Java源码:
// 将1压入操作数栈
var9.writeByte(4);
// nop无操作指令
// 不清楚,为什么这里填写一个这样指令???
var9.writeByte(0);
// 如果是void,无需返回值,也无需类型转换
if (this.returnType == Void.TYPE) {
// 87 --> 0x57 --> pop指令: 弹出栈的数据
var9.writeByte(87);
// 177 --> 0xb1 --> return指令.结束方法
var9.writeByte(177);
} else {
// 检查类型转换
this.codeUnwrapReturnValue(this.returnType, var9);
}
注意:
异常部分,不作分析.涉及到方法的异常表.
源码:
private ProxyGenerator.MethodInfo generateMethod() throws IOException {
String var1 = ProxyGenerator.getMethodDescriptor(this.parameterTypes, this.returnType);
ProxyGenerator.MethodInfo var2 = ProxyGenerator.this.new MethodInfo(this.methodName, var1, 17);
int[] var3 = new int[this.parameterTypes.length];
int var4 = 1;
for(int var5 = 0; var5 < var3.length; ++var5) {
var3[var5] = var4;
var4 += ProxyGenerator.getWordsPerType(this.parameterTypes[var5]);
}
byte var7 = 0;
DataOutputStream var9 = new DataOutputStream(var2.code);
ProxyGenerator.this.code_aload(0, var9);
var9.writeByte(180);
var9.writeShort(ProxyGenerator.this.cp.getFieldRef("java/lang/reflect/Proxy", "h", "Ljava/lang/reflect/InvocationHandler;"));
ProxyGenerator.this.code_aload(0, var9);
var9.writeByte(178);
var9.writeShort(ProxyGenerator.this.cp.getFieldRef(ProxyGenerator.dotToSlash(ProxyGenerator.this.className), this.methodFieldName, "Ljava/lang/reflect/Method;"));
if (this.parameterTypes.length > 0) {
ProxyGenerator.this.code_ipush(this.parameterTypes.length, var9);
var9.writeByte(189);
var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/Object"));
for(int var10 = 0; var10 < this.parameterTypes.length; ++var10) {
var9.writeByte(89);
ProxyGenerator.this.code_ipush(var10, var9);
this.codeWrapArgument(this.parameterTypes[var10], var3[var10], var9);
var9.writeByte(83);
}
} else {
var9.writeByte(1);
}
var9.writeByte(185);
var9.writeShort(ProxyGenerator.this.cp.getInterfaceMethodRef("java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"));
var9.writeByte(4);
var9.writeByte(0);
if (this.returnType == Void.TYPE) {
var9.writeByte(87);
var9.writeByte(177);
} else {
this.codeUnwrapReturnValue(this.returnType, var9);
}
short var6;
short var8 = var6 = (short)var2.code.size();
List var13 = ProxyGenerator.computeUniqueCatchList(this.exceptionTypes);
if (var13.size() > 0) {
Iterator var11 = var13.iterator();
while(var11.hasNext()) {
Class var12 = (Class)var11.next();
var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(var12.getName()))));
}
var9.writeByte(191);
var6 = (short)var2.code.size();
var2.exceptionTable.add(new ProxyGenerator.ExceptionTableEntry(var7, var8, var6, ProxyGenerator.this.cp.getClass("java/lang/Throwable")));
ProxyGenerator.this.code_astore(var4, var9);
var9.writeByte(187);
var9.writeShort(ProxyGenerator.this.cp.getClass("java/lang/reflect/UndeclaredThrowableException"));
var9.writeByte(89);
ProxyGenerator.this.code_aload(var4, var9);
var9.writeByte(183);
var9.writeShort(ProxyGenerator.this.cp.getMethodRef("java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V"));
var9.writeByte(191);
}
if (var2.code.size() > 65535) {
throw new IllegalArgumentException("code size limit exceeded");
} else {
var2.maxStack = 10;
var2.maxLocals = (short)(var4 + 1);
var2.declaredExceptions = new short[this.exceptionTypes.length];
for(int var14 = 0; var14 < this.exceptionTypes.length; ++var14) {
var2.declaredExceptions[var14] = ProxyGenerator.this.cp.getClass(ProxyGenerator.dotToSlash(this.exceptionTypes[var14].getName()));
}
return var2;
}
}
理论探讨
jdk最开始技术并不成熟,没有出现字节码框架这种东西.
导致动态代理源码,在字节码层面显得十分复杂.
接下来,我们将使用javassist字节码框架,来实现动态代理.
自定义类加载器
public class MyClassLoader extends ClassLoader {
private byte[] bytes;
public MyClassLoader(byte[] bytes){
this.bytes = bytes;
}
public Class<?> findClass(String name) throws ClassNotFoundException {
return defineClass(name,bytes,0,bytes.length);
}
}
手写动态代理
依赖:
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.27.0-GA</version>
</dependency>
public class MyProxy {
private final static String name ="$Proxy";
private static int i = 0;
protected static InvocationHandler h;
public MyProxy(InvocationHandler handler) {
h = handler;
}
public static Object newProxyInstance(Class[] cls, InvocationHandler handler) throws NotFoundException, CannotCompileException, IOException {
// 代理类名
String proxyName = name+(i++);
// 处理器
h = handler;
// 常量池中添加字节码
ClassPool pool = ClassPool.getDefault();
// 新建一个字节码
CtClass ctClass = pool.makeClass(proxyName);
// 设置父类为当前类
ctClass.setSuperclass(pool.get("d1.proxy.MyProxy"));
// 接口
for (Class c : cls) {
// 实现接口
ctClass.addInterface(pool.get("java.lang.Comparable"));
// 添加成员属性: Method,保存方法的反射
Method[] declaredMethods = c.getDeclaredMethods();
for (int i1 = 0; i1 < declaredMethods.length; i1++) {
String code = String.format("private static java.lang.reflect.Method %smethod%d = Class.forName(\"%s\").getDeclaredMethods()[%d];" , c.getSimpleName(), i1, c.getName(), i1);
CtField m0 = CtField.make(code, ctClass);
ctClass.addField(m0);
// 重写方法
CtClass[] paramClass = new CtClass[declaredMethods[i1].getParameterTypes().length];
for (int i2 = 0; i2 < declaredMethods[i1].getParameterTypes().length; i2++) {
String typeName = declaredMethods[i1].getParameterTypes()[i2].getName();
paramClass[i2] = pool.get(typeName);
}
CtMethod method = new CtMethod(pool.get(declaredMethods[i1].getReturnType().getName()), declaredMethods[i1].getName(), paramClass, ctClass);
method.setModifiers(Modifier.PUBLIC);
// 为什么写的这么麻烦? 因为没有通过java编译器,所以无法自动装箱和拆箱.
// 这里留下一个缺陷: 硬编码一下,留下改进的空间
method.setBody("{Integer a = (Integer)super.h.invoke(this, null, $args);return a.intValue();}");
ctClass.addMethod(method);
}
}
// 构造方法
CtConstructor ctConstructor = new CtConstructor(new CtClass[]{pool.get("java.lang.reflect.InvocationHandler")}, ctClass);
ctConstructor.setBody("super($1);");
ctClass.addConstructor(ctConstructor);
// 类加载器
MyClassLoader student = new MyClassLoader(ctClass.toBytecode());
try {
Class<?> c = student.findClass(proxyName);
Constructor<?> constructor = c.getConstructor(InvocationHandler.class);
return constructor.newInstance(h);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
}
测试动态代理
public class Demo02 {
// file:///home/liziyun/Desktop/动态代理
public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Comparable c = (Comparable)MyProxy.newProxyInstance(new Class[]{Comparable.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("执行");
return args[0];
}
});
c.compareTo(11);
}
}
以上是关于jdk动态代理: 从源码,到字节码,到自己手写动态代理的主要内容,如果未能解决你的问题,请参考以下文章