ReflectASM => Java 高性能反射
Posted 思想累积
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReflectASM => Java 高性能反射相关的知识,希望对你有一定的参考价值。
1、ReflectASM 简介
1.1 什么是反射?
Java 反射实在运行的过程中,对任意一个类,都可以知道这个类的所有属性和方法,对一个对象,都能够调用任意一个方法和属性,这种动态获取的信息和调用对象方法的功能称为 java 的反射机制。
1.2 什么是 reflectASM?
ReflectASM 是一个非常小的 Java 类库,只有五个类,却提供了非常高性能的属性操作、方法调用、构造方法调用等,在底层使用了 asm。 使用字节码生成的方式实现了更为高效的反射机制,执行时会生成一个存取类来 set/get 字段,访问方法或者创建实例。不是依赖于 Java 本身的反射机制实现的,所以更快,而且避免了访问原始类型因自动装箱而产生的问题
2、maven 依赖
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>reflectasm</artifactId>
<version>1.11.0</version>
</dependency>
reflectASM 提供了根据匹配的字符串操作变量、函数的特性,reflectASM 中常用常用的类只有 MethodAccess,FieldAccess,Constructor 这几个
3、反射调用测试
反射对象
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class PlanBarCodeExcel {
private String barCode;
public String brandName;
private String packingNum;
private String channelCode;
}
reflectASM 使用
public class ReflectASMDemo {
/**
* jdk 反射调用方法
* @throws Exception
*/
public static void jdkReflect() throws Exception {
PlanBarCodeExcel planBarCodeExcel = new PlanBarCodeExcel();
Method setBarCode = planBarCodeExcel.getClass().getMethod("setBarCode", String.class);
setBarCode.invoke(planBarCodeExcel, "barCode");
}
/**
* reflectASM反射调用方法
* 需要注意的是,reflectASM 提供的方法只能调用非私有的属性和方法,私有属性需要通过 get/set 方法调用
* @throws Exception
*/
public static void reflectASMByName() throws Exception {
// 使用 MethodAccess 反射调用方法
PlanBarCodeExcel planBarCodeExcel = new PlanBarCodeExcel();
MethodAccess access = MethodAccess.get(PlanBarCode.class);
methodAccess.invoke(planBarCodeExcel, "setBarCode", "barCode");
String getBarCode = (String) methodAccess.invoke(planBarCodeExcel, "getBarCode");
System.out.println(getBarCode);
// 如果方法重载有同名方法的话,找到方法的索引执行方法,相比通过名称访问成员,索引的方式会更快
int setChannelCodeIndex = methodAccess.getIndex("setChannelCode", String.class);
methodAccess.invoke(planBarCodeExcel, setChannelCodeIndex, "111");
// 使用 FieldAccess 反射 set/get 字段
FieldAccess fieldAccess = FieldAccess.get(PlanBarCode.class);
fieldAccess.set(planBarCodeExcel, "brandName", "brandName");
String brandName = (String) fieldAccess.get(planBarCodeExcel, "brandName");
System.out.println(brandName);
// ConstructorAccess反射调用构造方法
ConstructorAccess<PlanBarCodeExcel> planBarCodeExcelConstructorAccess = ConstructorAccess.get(PlanBarCodeExcel.class);
PlanBarCodeExcel planBarCodeExcel1 = planBarCodeExcelConstructorAccess.newInstance();
}
}
4、reflectASM 原理解析
4.1 MethodAccess 源码
public abstract class MethodAccess {
private String[] methodNames;
private Class[][] parameterTypes;
private Class[] returnTypes;
static public MethodAccess get (Class type) {
// 准备反射信息
// ToDo:为什么不先找动态生成的 MethodAccess 类,然后再准备反射信息?
ArrayList<Method> methods = new ArrayList<Method>();
boolean isInterface = type.isInterface();
if (!isInterface) {
Class nextClass = type;
while (nextClass != Object.class) {
addDeclaredMethodsToList(nextClass, methods);
nextClass = nextClass.getSuperclass();
}
} else {
recursiveAddInterfaceMethodsToList(type, methods);
}
int n = methods.size();
String[] methodNames = new String[n];
Class[][] parameterTypes = new Class[n][];
Class[] returnTypes = new Class[n];
for (int i = 0; i < n; i++) {
Method method = methods.get(i);
methodNames[i] = method.getName();
parameterTypes[i] = method.getParameterTypes();
returnTypes[i] = method.getReturnType();
}
String className = type.getName();
String accessClassName = className + "MethodAccess";
if (accessClassName.startsWith("java.")) accessClassName = "reflectasm." + accessClassName;
Class accessClass;
AccessClassLoader loader = AccessClassLoader.get(type);
synchronized (loader) {
try {
// 如果动态生成的类已经生成过了,第二次调用 MethodAccess.get 是不会操作字节码生成的
accessClass = loader.loadClass(accessClassName);
} catch (ClassNotFoundException ignored) {
String accessClassNameInternal = accessClassName.replace('.', '/');
String classNameInternal = className.replace('.', '/');
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
MethodVisitor mv;
// 类路径进行替换,换成 "com/esotericsoftware/reflectasm/MethodAccess"
cw.visit(V1_1, ACC_PUBLIC + ACC_SUPER, accessClassNameInternal, null, "com/esotericsoftware/reflectasm/MethodAccess",
null);
{
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "com/esotericsoftware/reflectasm/MethodAccess", "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_VARARGS, "invoke",
"(Ljava/lang/Object;I[Ljava/lang/Object;)Ljava/lang/Object;", null, null);
mv.visitCode();
if (!methods.isEmpty()) {
mv.visitVarInsn(ALOAD, 1);
mv.visitTypeInsn(CHECKCAST, classNameInternal);
mv.visitVarInsn(ASTORE, 4);
mv.visitVarInsn(ILOAD, 2);
Label[] labels = new Label[n];
for (int i = 0; i < n; i++)
labels[i] = new Label();
Label defaultLabel = new Label();
mv.visitTableSwitchInsn(0, labels.length - 1, defaultLabel, labels);
StringBuilder buffer = new StringBuilder(128);
for (int i = 0; i < n; i++) {
mv.visitLabel(labels[i]);
if (i == 0)
mv.visitFrame(Opcodes.F_APPEND, 1, new Object[] {classNameInternal}, 0, null);
else
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitVarInsn(ALOAD, 4);
buffer.setLength(0);
buffer.append('(');
Class[] paramTypes = parameterTypes[i];
Class returnType = returnTypes[i];
for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) {
mv.visitVarInsn(ALOAD, 3);
mv.visitIntInsn(BIPUSH, paramIndex);
mv.visitInsn(AALOAD);
Type paramType = Type.getType(paramTypes[paramIndex]);
switch (paramType.getSort()) {
case Type.BOOLEAN:
mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
break;
case Type.BYTE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Byte");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B");
break;
case Type.CHAR:
mv.visitTypeInsn(CHECKCAST, "java/lang/Character");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Character", "charValue", "()C");
break;
case Type.SHORT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Short");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S");
break;
case Type.INT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
break;
case Type.FLOAT:
mv.visitTypeInsn(CHECKCAST, "java/lang/Float");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F");
break;
case Type.LONG:
mv.visitTypeInsn(CHECKCAST, "java/lang/Long");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J");
break;
case Type.DOUBLE:
mv.visitTypeInsn(CHECKCAST, "java/lang/Double");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D");
break;
case Type.ARRAY:
mv.visitTypeInsn(CHECKCAST, paramType.getDescriptor());
break;
case Type.OBJECT:
mv.visitTypeInsn(CHECKCAST, paramType.getInternalName());
break;
}
buffer.append(paramType.getDescriptor());
}
buffer.append(')');
buffer.append(Type.getDescriptor(returnType));
int invoke;
if (isInterface)
invoke = INVOKEINTERFACE;
else if (Modifier.isStatic(methods.get(i).getModifiers()))
invoke = INVOKESTATIC;
else
invoke = INVOKEVIRTUAL;
mv.visitMethodInsn(invoke, classNameInternal, methodNames[i], buffer.toString());
switch (Type.getType(returnType).getSort()) {
case Type.VOID:
mv.visitInsn(ACONST_NULL);
break;
case Type.BOOLEAN:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Boolean", "valueOf", "(Z)Ljava/lang/Boolean;");
break;
case Type.BYTE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Byte", "valueOf", "(B)Ljava/lang/Byte;");
break;
case Type.CHAR:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Character", "valueOf", "(C)Ljava/lang/Character;");
break;
case Type.SHORT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Short", "valueOf", "(S)Ljava/lang/Short;");
break;
case Type.INT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
break;
case Type.FLOAT:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Float", "valueOf", "(F)Ljava/lang/Float;");
break;
case Type.LONG:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Long", "valueOf", "(J)Ljava/lang/Long;");
break;
case Type.DOUBLE:
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
break;
}
mv.visitInsn(ARETURN);
}
mv.visitLabel(defaultLabel);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
}
mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
mv.visitInsn(DUP);
mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitLdcInsn("Method not found: ");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
mv.visitVarInsn(ILOAD, 2);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init&g以上是关于ReflectASM => Java 高性能反射的主要内容,如果未能解决你的问题,请参考以下文章
项目工具:两行代码快速生成测试的数据的FakeDataMaker