java反射会影响性能, 所以考虑用javasist能否去除反射, 且保留根据字段名获取或设置值的功能。
Entity如下:
package com.test.other;
public class Entity {
private String userName;
private Integer age;
private Integer sex;
private Integer count;
private String hobbits;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
public String getHobbits() {
return hobbits;
}
public void setHobbits(String hobbits) {
this.hobbits = hobbits;
}
}
以getter为例, 思路如下: 对Entity类型写一个接口,实现根据字段名查找getter函数的功能。
map的key为字段名, 值为Entity::getXXX方法引用。
使用javasist根据entity字段动态生成接口实现。
package com.test.other;
import java.util.Map;
import java.util.function.Function;
public interface FieldInfo<T> {
Map<String, Function<T, Object>> getterInfo();
}
其中javasist生成实现类代码如下:
package com.test.other;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.bytecode.AccessFlag;
import javassist.bytecode.BootstrapMethodsAttribute;
import javassist.bytecode.BootstrapMethodsAttribute.BootstrapMethod;
import javassist.bytecode.Bytecode;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.Descriptor;
import javassist.bytecode.DuplicateMemberException;
import javassist.bytecode.MethodInfo;
import javassist.bytecode.Opcode;
public class ReflectionTest {
public static void main(String[] args) throws Exception {
// 生成反射helper对象
FieldInfo<Entity> newInstance = makeReflectionHelperClass(Entity.class);
// 调用,验证helper对象
Map<String, Function<Entity, Object>> getterInfo = newInstance.getterInfo();
Entity entity = new Entity();
entity.setAge(1004);
entity.setUserName("userNameTest");
entity.setCount(53567);
System.out.println(getterInfo.get("age").apply(entity));
System.out.println(getterInfo.get("userName").apply(entity));
System.out.println(getterInfo.get("count").apply(entity));
}
/**
* 生成实现类的helper方法
*
* @param <T> entity类型
* @param clazz entity类型
* @return FieldInfo接口实现类对象
* @throws Exception 异常
*/
private static <T> FieldInfo<T> makeReflectionHelperClass(Class<T> clazz) throws Exception {
String classname = "FieldInfoImpl" + clazz.getSimpleName();
ClassFile cf = new ClassFile(false, classname, null);
cf.addInterface(FieldInfo.class.getName());
cf.setAccessFlags(AccessFlag.PUBLIC);
// 添加无参构造器
addInitMethod(cf);
// 生成getterInfo对应逻辑
addGetterMethod(clazz, cf);
// 写入class文件
cf.write(new DataOutputStream(new FileOutputStream(classname + ".class")));
// 加载class文件并生成对应的对象
File file = new File(".");
URLClassLoader classLoader = new URLClassLoader(new URL[] { file.toURI().toURL() });
Object instance = classLoader.loadClass(classname).newInstance();
return (FieldInfo<T>) instance;
}
/**
* 生成getterInfo对应逻辑
*
* @param <T> entity类型
* @param clazz entity类型
* @param cf class文件
* @throws Exception 异常
*/
private static <T> void addGetterMethod(Class<T> clazz, ClassFile cf) throws Exception {
ConstPool constPool = cf.getConstPool();
// getterInfo函数声明
MethodInfo getterMethodInfo = new MethodInfo(constPool, "getterInfo", "()Ljava/util/Map;");
getterMethodInfo.setAccessFlags(AccessFlag.PUBLIC);
// 方法体
Bytecode code = new Bytecode(constPool);
// new Hashmap
code.addNew("java/util/HashMap");
code.addOpcode(Opcode.DUP);
code.addInvokespecial("java/util/HashMap", MethodInfo.nameInit, "()V");
code.addOpcode(Opcode.ASTORE_1);
// entity的class常量池引用
int entityClassRef = constPool.addClassInfo(clazz.getName());
// metafactory的方法常量池引用(用于执行真正的lambda函数调用)
int mRefIdxMetaFactory = constPool.addMethodrefInfo(
constPool.addClassInfo("java/lang/invoke/LambdaMetafactory"), "metafactory",
"(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;");
// metafactory方法的参数
int arg1 = constPool.addMethodTypeInfo(constPool.addUtf8Info("(Ljava/lang/Object;)Ljava/lang/Object;"));
int arg3 = constPool.addMethodTypeInfo(constPool.addUtf8Info("(Lcom/test/other/Entity;)Ljava/lang/Object;"));
List<BootstrapMethod> boList = new ArrayList<>();
ClassPool pool = ClassPool.getDefault();
int bootstrapMethodIndex = 0;
for (Method method : clazz.getDeclaredMethods()) {
String methodName = method.getName();
if (methodName.startsWith("get") || methodName.startsWith("is")) {
// 获取getter方法描述符
String getMethodDesc = Descriptor.ofMethod(pool.get(method.getReturnType().getName()), new CtClass[0]);
// 把getter方法添加到方法常量池
int mRefIdx = constPool.addMethodrefInfo(entityClassRef, methodName, getMethodDesc);
// 添加BootstrapMethod方法指向getter方法引用
BootstrapMethod addBootstrapMethod = addBootstrapMethod(constPool, mRefIdxMetaFactory, entityClassRef,
arg1, arg3, mRefIdx);
// 添加map.put("userName", Entity::getUserName);逻辑代码, 其中后面的lambda方法引用指向上面的BootstrapMethod
putMethodRefToMap(code, convert(methodName.substring(3)), bootstrapMethodIndex++);
boList.add(addBootstrapMethod);
}
}
// 方法结束,return
code.addOpcode(Opcode.ALOAD_1);
code.addOpcode(Opcode.ARETURN);
code.setMaxLocals(2);
code.setMaxStack(3);
getterMethodInfo.setCodeAttribute(code.toCodeAttribute());
cf.addMethod(getterMethodInfo);
cf.addAttribute(new BootstrapMethodsAttribute(constPool, boList.toArray(new BootstrapMethod[0])));
}
/**
* 把方法指令放入map, 并把lambda函数引用指向BootstrapMethod的真实引用
*
* @param code 字节码
* @param fieldName 字段名
* @param bootstrapMethodRef BootstrapMethod的lambda方法引用
*/
private static void putMethodRefToMap(Bytecode code, String fieldName, int bootstrapMethodRef) {
code.addOpcode(Opcode.ALOAD_1);
code.addLdc(fieldName);
code.addInvokedynamic(bootstrapMethodRef, "apply", "()Ljava/util/function/Function;");
code.addInvokeinterface("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;", 3);
code.addOpcode(Opcode.POP);
}
/**
* 添加BootstrapMethod, 供lambda方法运行时调用
*
* @param constPool 常量池
* @param mRefIdxMetaFactory lambda方法函数工厂
* @param entityClassRef 实体类class引用
* @param constantMethodRef 固定方法引用
* @param instantiatedMethodTypeRef 固定方法引用
* @param methodRef 真实的getter方法引用
* @return BootstrapMethod
*/
private static BootstrapMethod addBootstrapMethod(ConstPool constPool, int mRefIdxMetaFactory, int entityClassRef,
int constantMethodRef, int instantiatedMethodTypeRef, int methodRef) {
// 对真实的方法引用添加invokeVirtual指令
int arg2 = constPool.addMethodHandleInfo(ConstPool.REF_invokeVirtual, methodRef);
// lambda方法的真实引用
int lambdaMethodIndex = constPool.addMethodHandleInfo(ConstPool.REF_invokeStatic, mRefIdxMetaFactory);
BootstrapMethod bootstrapMethodGetAge = new BootstrapMethod(lambdaMethodIndex,
new int[] { constantMethodRef, arg2, instantiatedMethodTypeRef });
return bootstrapMethodGetAge;
}
/**
* 为实现类添加init构造器
*
* @param cf 类文件
* @throws DuplicateMemberException 异常
*/
private static void addInitMethod(ClassFile cf) throws DuplicateMemberException {
ConstPool constPool = cf.getConstPool();
MethodInfo minfoInit = new MethodInfo(constPool, MethodInfo.nameInit, "()V");
minfoInit.setAccessFlags(AccessFlag.PUBLIC);
Bytecode codeInit = new Bytecode(constPool);
// 调用Object无参构造器
codeInit.addOpcode(Opcode.ALOAD_0);
codeInit.addInvokespecial("java/lang/Object", MethodInfo.nameInit, "()V");
codeInit.addOpcode(Opcode.RETURN);
codeInit.setMaxLocals(1);
codeInit.setMaxStack(1);
minfoInit.setCodeAttribute(codeInit.toCodeAttribute());
cf.addMethod(minfoInit);
}
private static String convert(String str) {
char[] charArray = str.toCharArray();
charArray[0] = Character.toLowerCase(charArray[0]);
return new String(charArray);
}
}
执行结果如下:
set方法暂未来得及写, 也是同理, 待后续补充。
字节码可参照编译后生成的class文件: