快速反射调用

java的反射调用比起直接调用要慢很多倍。而反射调用又是被广泛使用的。如何才能提高反射调用的速度呢?


一种想法就是对于每一个bean动态生成一个类,由这个类根据不同的方法名进行直接的方法调用。这样就把反射调用转换成了直接调用,速度应该有很大提高。而动态生成类虽然比较慢,但是在服务器运行期间只运行一次,这个代价还是值得的。

首先需要定义一个IBeanWrapper。这个wrapper负责进行反射调用到直接调用的转换。每个bean类会有一个自己的wrapper类实现。

public interface IBeanWrapper {
	public void setValue(Object obj, String field, Object value);
	
	public Object getValue(Object obj, String field);
}

然后是BeanWrapperFactory,负责获取wrapper的类实例。

public class BeanWrapperFactory {
	private static Map<Class, IBeanWrapper> wrappers = new HashedMap();
	
	public static IBeanWrapper getBeanWrapper(Class clz) {
		IBeanWrapper inst = wrappers.get(clz);
		if (inst == null) {
			File outFile = new File(System.getProperties("java.io.temp"), "wrappers");
			if (!outFile.exists()) {
				outFile.mkdirs();
			}
			String s = new BeanWrapperFactory().getCode(UserEx.class);
			CompilerUtil.compile(outFile.getAbsolutePath(), clz.getSimpleName()+"Wrapper", s);
		
			DynamicClassLoader cl = new DynamicClassLoader(outFile);
			try {
				inst = (IBeanWrapper)cl.loadClass("com.temp.wrapper."+clz.getSimpleName()+"Wrapper").newInstance();
			} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
				e.printStackTrace();
			}
			wrappers.put(clz, inst);
		}
		return inst;
	}

	public String getCode(Class clz) {
		StringBuffer sb = new StringBuffer();
		
		sb.append("package com.temp.wrapper;\n");
		sb.append("public class "+clz.getSimpleName()+"Wrapper implements IBeanWrapper {\n");
		
		sb.append("public void setValue(Object obj, String field, Object value) {\n");
		sb.append("  "+clz.getName()+" o = ("+clz.getName()+")obj;\n");
		sb.append("  switch(field.hashCode()) {\n");
		List<Field> fields = ReflectionUtils.getDeclaredFields(clz);
		for (int i = 0 ; i < fields.size() ; i++) {
			Field field = fields.get(i); 
			String name = field.getName();
			String methodName = StringUtils.capitalise(name);
			String type = getType(field.getType());
			if ("id".equals(name)) {
				try {
					type = getType(clz.getMethod("getId").getReturnType());
				} catch (NoSuchMethodException | SecurityException e) {
					e.printStackTrace();
				}
			}
			
			sb.append("    case ").append(Integer.toString(name.hashCode())).append(":\n");
			if (field.getType() == int.class) {
				sb.append("    o.set").append(methodName).append("(((Number)value).intValue());\n");
			} else if (field.getType() == long.class) {
				sb.append("    o.set").append(methodName).append("(((Number)value).longValue());\n");
			} else if (field.getType() == double.class) {
				sb.append("    o.set").append(methodName).append("(((Number)value).doubleValue());\n");
			} else if (field.getType() == byte.class) {
				sb.append("    o.set").append(methodName).append("(((Number)value).byteValue());\n");
			} else if (field.getType() == float.class) {
				sb.append("    o.set").append(methodName).append("(((Number)value).floatValue());\n");
			} else if (field.getType() == boolean.class) {
				sb.append("    o.set").append(methodName).append("(((Boolean)value).booleanValue());\n");
			} else if (field.getType() == char.class) {
				sb.append("    o.set").append(methodName).append("(((Character)value).charValue());\n");
			} else if (field.getType() == byte[].class) {
				sb.append("    o.set").append(methodName).append("((byte[])value);\n");
			} else {
				sb.append("    o.set").append(methodName).append("((").append(type).append(")value);\n");
			}
			sb.append("    break;\n");
		}
		sb.append("  }\n");
		sb.append("}\n\n");
		
		sb.append("public Object getValue(Object obj, String field) {\n");
		sb.append("  "+clz.getName()+" o = ("+clz.getName()+")obj;\n");
		sb.append("  switch(field.hashCode()) {\n");
		for (int i = 0 ; i < fields.size() ; i++) {
			Field field = fields.get(i); 
			String name = field.getName();
			String methodName = StringUtils.capitalise(name);
			sb.append("    case ").append(Integer.toString(name.hashCode())).append(":\n");
			if (field.getType() == boolean.class) {
				sb.append("    return o.is").append(methodName).append("();\n");
			} else {
				sb.append("    return o.get").append(methodName).append("();\n");
			}
		}
		sb.append("  }\n");
		sb.append("  throw new RuntimeException(\"field not found:\"+field);\n");
		sb.append("}");
		sb.append("}");
		return sb.toString();
	}
	
	private String getType(Class clz) {
		if (clz == boolean.class) {
			return "boolean";
		} else if (clz == int.class) {
			return "int";
		} else if (clz == long.class) {
			return "long";
		} else if (clz == double.class) {
			return "double";
		} else if (clz == byte.class) {
			return "byte";
		} else if (clz == float.class) {
			return "float";
		} else {
			return clz.getName();
		}
	}
}

其中静态方法
public static IBeanWrapper getBeanWrapper(Class clz)
负责获取一个给定类的wrapper实例。getCode()方法负责生成wrapper类的源代码。然后CompilerUtil进行编译,然后动态装载。静态变量wrappers用于缓存类对应的wrapper实例。为了提高速度,生成代码中没使用属性名字符串比较,而是直接用属性名的hash值比较。

DynamicClassLoader用于动态装载类。

public class DynamicClassLoader extends URLClassLoader {
	public DynamicClassLoader(File... files)//, ClassLoader parent) 
	{
		super(getUrls(files));//, parent);
	}

	public DynamicClassLoader(ClassLoader parent, File... files) 
	{
		super(getUrls(files), parent);
	}
	
	private static final URL[] getUrls(File... files) {
		URL[] urls = new URL[files.length];
		try{
			int i = 0;
			for(File file : files) {
				urls[i] = file.toURI().toURL();
			}
		}catch(Exception ex){
		}
		return urls;
	}
}


动态编译类CompilerUtil可以参考点击打开链接

下面写段程序测试一下性能:

		IBeanWrapper inst = new BeanWrapperFactory().getBeanWrapper(UserEx.class);
		User u = new User();
		Method method = User.class.getMethod("setUserCode", String.class);
		
		long t0 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			u.setUserCode("123");
		}
		long t1 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			inst.setValue(u, "userCode", "123");
		}		
		long t2 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			try {
				method.invoke(u, "123");
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		long t3 = System.currentTimeMillis();
		
		System.out.println("time:"+(t1 - t0)+", "+(t2 - t1)+", "+(t3 - t2));
输出为:

time:396, 3480, 15499

即快速反射调用大约是直接调用的9倍,是反射调用的1/5。提高还是很大的。这还是Method对象被缓存的情况下。如果不缓存Method对象,

                IBeanWrapper inst = new BeanWrapperFactory().getBeanWrapper(UserEx.class);
		UserEx u = new UserEx();
		Double d = new Double(123D);
		
		long t0 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			u.setD5(d);
		}
		long t1 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			inst.setValue(u, "userCode", "123");
		}		
		long t2 = System.currentTimeMillis();
		for (int i = 0 ; i < 100000000 ; i++) {
			try {
				Method method = UserEx.class.getMethod("setUserCode", String.class);
				method.invoke(u, "123");
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			}
		}
		long t3 = System.currentTimeMillis();
		
		System.out.println("time:"+(t1 - t0)+", "+(t2 - t1)+", "+(t3 - t2));

输出为

time:314, 3777, 246019

快速反射调用大约是反射调用的1/62。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值