使用javasist去除反射

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文件:

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值