论坛首页 Java企业应用论坛

比FastMethod更快的Java反射调用API,以及比Cglib更快的BeanMap实现

浏览 18454 次
精华帖 (3) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-03-08   最后修改:2013-03-11
动态调用方法,大多数情况下,只能使用反射机制实现。反射性能上远远逊于直接调用(虽然JDK7中的MethodHandle也可以稍微提高动态调用性能,但JDK7在企业应用中暂时还无法普及)。

反射调用之所以性能低下,主要是因为通用接口对参数进行数组封装和解封导致,而大多数情况下,反射调用时的参数数量和类型,我们是知道的。那么,能不能动态调用方法时不去对参数进行封装和解封,从而达到提高动态调用的效果呢?
基于此理念,实现了Invokers类。使用该类时,先行定义好调用接口,调用接口包含了需要反射调用的方法的参数列表以及返回值的接口方法(方法名可以和反射方法名不一致,且参数列表的第一个参数是反射调用的对象,其它参数才是反射调用时需要的参数)。接口定义好之后,可以通过Invokers生成该接口的实例,然后通过该实例来实现动态调用。例子如下:


	public static interface Getter {
		Object get(Object obj);
	}

	public static interface Setter {
		void set(Object obj, Object value);
	}

	@Test
	public void testInvoke() {
		try {
			// 创建getter调用器,用于调用getTime方法
			Getter getter = Invokers.newInvoker(Getter.class, Date.class,
					"getTime", null, Long.TYPE);
			// 创建setter调用器,用于调用setTime方法
			Setter setter = Invokers.newInvoker(Setter.class, Date.class,
					"setTime", new Class<?>[] { Long.TYPE }, null);
			Date date = new Date();
			System.out.println("time=" + getter.get(date));
			setter.set(date, 33333333L);
			System.out.println("time1=" + getter.get(date));
			Method getTime = Date.class.getMethod("getTime");
			Method setTime = Date.class.getMethod("setTime", Long.TYPE);
			getTime.setAccessible(true);
			setTime.setAccessible(true);
			FastClass fastClass = FastClass.create(Date.class);
			FastMethod fastGetTime = fastClass.getMethod(getTime);
			FastMethod fastSetTime = fastClass.getMethod(setTime);
			System.out.println("time2=" + getTime.invoke(date));
			long t = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				date.setTime(33333333L);
				date.getTime();
			}
			long t1 = System.currentTimeMillis();
			System.out.println("直接调用耗时:" + (t1 - t) + "ms");
			t1 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				setter.set(date, 33333333L);
				getter.get(date);
			}
			long t2 = System.currentTimeMillis();
			System.out.println("Invokers调用耗时:" + (t2 - t1) + "ms");
			t2 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				setTime.invoke(date, 6666666L);
				getTime.invoke(date);
			}
			long t3 = System.currentTimeMillis();
			System.out.println("JDK反射调用耗时:" + (t3 - t2) + "ms");
			t3 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				fastSetTime.invoke(date, new Object[] { 6666666L });
				fastGetTime.invoke(date, new Object[] {});
			}
			long t4 = System.currentTimeMillis();
			System.out.println("FastMethod调用耗时:" + (t4 - t3) + "ms");
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}


上面的单元测试中,分别通过不同方式调用一亿次Date类型的getTime和setTime方法,输出如下:

time=1362806708572
time1=33333333
time2=33333333
直接调用耗时:18ms
Invokers调用耗时:120ms
JDK反射调用耗时:5352ms
FastMethod调用耗时:5057ms

可见Invokes的性能和直接调用已经接近了(实际上,如果接口处声明为准确参数类型而不是通用类型的话,会和直接调用性能一样),和传统反射相比相差两个数量级。
注:测试机器是Macbook Pro 374,虚拟机是JDK 6。


另一个例子,实现传统的通用反射接口(性能也是最好的)。测试代码如下:

	@Test
	public void testInvoker() {
		try {
			Date date = new Date();
			Method getMethod = Date.class.getMethod("getTime");
			getMethod.setAccessible(true);
			Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
			setMethod.setAccessible(true);
			Invoker get = Invokers.newInvoker(getMethod);
			Invoker set = Invokers.newInvoker(setMethod);
			FastClass fastClass = FastClass.create(Date.class);
			FastMethod fastGetMethod = fastClass.getMethod(getMethod);
			FastMethod fastSetMethod = fastClass.getMethod(setMethod);

			System.out.println(get.invoke(date, new Object[] {}));
			set.invoke(date, new Object[] { 333333L });
			System.out.println(get.invoke(date, new Object[] {}));
			long t0 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				get.invoke(date, new Object[] {});
				set.invoke(date, new Object[] { 333333L });
			}
			long t1 = System.currentTimeMillis();
			System.out.println("Invoker调用耗时:" + (t1 - t0) + "ms");
			t1 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				getMethod.invoke(date, new Object[] {});
				setMethod.invoke(date, new Object[] { 333333L });
			}
			long t2 = System.currentTimeMillis();
			System.out.println("JDK反射调用耗时:" + (t2 - t1) + "ms");
			t2 = System.currentTimeMillis();
			for (int i = 0; i < 100000000; i++) {
				fastGetMethod.invoke(date, new Object[] {});
				fastSetMethod.invoke(date, new Object[] { 333333L });
			}
			long t3 = System.currentTimeMillis();
			System.out.println("CGLIB反射调用耗时:" + (t3 - t2) + "ms");
		} catch (Exception e) {
			e.printStackTrace();
		}

	}


输出结果如下(和上面的单元测试一样的环境):

1362857121037
333333
Invoker调用耗时:131ms
JDK反射调用耗时:5106ms
CGLIB反射调用耗时:4610ms



Invokers的代码如下(需javassist支持,asm生成字节码太麻烦 ):


import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;

import org.apache.commons.lang3.reflect.MethodUtils;

/**
 * 快速动态方法调用器
 * 
 * @author
 * 
 */
public class Invokers {
	/**
	 * 调用器池
	 */
	final private static Map<Method, Invoker> INVOKER_MAP = new WeakHashMap<Method, Invokers.Invoker>();
	/**
	 * 公共调用器池
	 */
	final private static Map<Integer, Invoker> PUBLIC_INVOKER_MAP = new WeakHashMap<Integer, Invoker>();

	/**
	 * 调用器接口
	 */
	public static interface Invoker {
		/**
		 * 获取方法本身
		 * 
		 * @return
		 */
		Method method();

		/**
		 * 调用方法
		 * 
		 * @param host
		 *            执行对象
		 * @param args
		 *            执行参数
		 * @return
		 */
		Object invoke(Object host, Object[] args);
	}

	/**
	 * 输出类型信息
	 * 
	 * @param argTypes
	 * @return
	 */
	private static String argumentTypesToString(Class<?>[] argTypes) {
		StringBuilder buf = new StringBuilder();
		buf.append("(");
		if (argTypes != null) {
			for (int i = 0; i < argTypes.length; i++) {
				if (i > 0) {
					buf.append(", ");
				}
				Class<?> c = argTypes[i];
				buf.append((c == null) ? "null" : c.getName());
			}
		}
		buf.append(")");
		return buf.toString();
	}

	/**
	 * 快捷调用公共方法(性能较差)
	 * 
	 * @param host
	 *            宿主对象
	 * @param name
	 *            方法名
	 * @param args
	 *            方法参数
	 * @return 执行结果
	 * @throws NoSuchMethodException
	 *             如果没有相应的方法
	 */
	public static Object invokePublic(Object host, String name, Object... args)
			throws NoSuchMethodException {
		final Class<?> clazz = host instanceof Class ? (Class<?>) host : host
				.getClass();
		args = args == null ? new Object[] { null } : args;
		Class<?>[] paramTypes = new Class[args.length];
		for (int i = 0; i < paramTypes.length; i++) {
			paramTypes[i] = args[i] == null ? null : args[i].getClass();
		}
		int[] keys = new int[3];
		keys[0] = clazz.hashCode();
		keys[1] = name.hashCode();
		keys[2] = Arrays.hashCode(paramTypes);
		int key = Arrays.hashCode(keys);
		Invoker invoker = PUBLIC_INVOKER_MAP.get(key);
		if (invoker == null) {
			Method method = MethodUtils.getMatchingAccessibleMethod(clazz,
					name, paramTypes);
			if (method == null) {
				throw new NoSuchMethodException(clazz.getName() + "." + name
						+ argumentTypesToString(paramTypes));
			}
			invoker = newInvoker(method);
			PUBLIC_INVOKER_MAP.put(key, invoker);
		}
		return invoker.invoke(host, args);
	}

	/**
	 * 根据传入的方法创建快速调用器。 比cglib的性能快4到40倍之间。
	 * 
	 * @param method
	 *            方法对象
	 * @return 调用器
	 */
	public static Invoker newInvoker(Method method) {
		Invoker invoker = INVOKER_MAP.get(method);
		if (invoker == null) {
			StringBuilder proxyClassNameBuilder = new StringBuilder();
			proxyClassNameBuilder.append("proxy.invoker.method$");
			proxyClassNameBuilder.append(method.hashCode());
			String proxyClassName = proxyClassNameBuilder.toString();
			try {
				Class<?> proxyClass;
				try {
					proxyClass = Class.forName(proxyClassName);
				} catch (Throwable e) {
					ClassPool cp = new ClassPool(true);
					CtClass cc = cp.makeClass(proxyClassName);
					cc.addField(CtField.make(
							"private java.lang.reflect.Method m;", cc));
					CtConstructor ctConstructor = new CtConstructor(
							new CtClass[] { cp.get(Method.class.getName()) },
							cc);
					ctConstructor
							.setBody("{this.m=(java.lang.reflect.Method)$1;}");
					cc.addConstructor(ctConstructor);
					cc.addInterface(cp.get(Invoker.class.getName()));
					cc.addMethod(CtMethod
							.make("public java.lang.reflect.Method method(){return m;}",
									cc));
					StringBuilder invokeCode = new StringBuilder();
					invokeCode
							.append("public Object invoke(Object host, Object[] args){");
					StringBuilder parameterCode = new StringBuilder();
					for (int i = 0; i < method.getParameterTypes().length; i++) {
						if (i > 0) {
							parameterCode.append(",");
						}
						Class<?> parameterType = method.getParameterTypes()[i];
						parameterCode.append(generateCast("args[" + i + "]",
								Object.class, parameterType));
					}
					if (method.getParameterTypes().length > 0) {
						invokeCode.append("if(args==null||args.length!=");
						invokeCode.append(method.getParameterTypes().length);
						invokeCode
								.append(")throw new IllegalArgumentException(\"wrong number of arguments\");");
					}

					StringBuilder executeCode = new StringBuilder();

					executeCode.append("((");
					executeCode.append(method.getDeclaringClass()
							.getCanonicalName());
					executeCode.append(")");
					String objCode = Modifier.isStatic(method.getModifiers()) ? ""
							: "host";
					executeCode.append(objCode);
					executeCode.append(").");
					executeCode.append(method.getName());
					executeCode.append("(");
					executeCode.append(parameterCode);
					executeCode.append(")");

					if (!method.getReturnType().equals(Void.TYPE)) {
						invokeCode.append("return ");
						invokeCode.append(generateCast(executeCode.toString(),
								method.getReturnType(), Object.class));
						invokeCode.append(";");
					} else {
						invokeCode.append(executeCode.toString());
						invokeCode.append(";return null;");
					}
					invokeCode.append("}");
					cc.addMethod(CtMethod.make(invokeCode.toString(), cc));
					proxyClass = cc.toClass();
				}
				invoker = (Invoker) proxyClass.getConstructor(Method.class)
						.newInstance(method);
				INVOKER_MAP.put(method, invoker);
			} catch (Throwable e) {
				if (e instanceof RuntimeException) {
					throw (RuntimeException) e;
				}
				throw new RuntimeException(e);
			}
		}
		return invoker;
	}

	/**
	 * 快速动态调用宿主方法。 如果指定了方法名,则执行方法时只会调用指定了的方法。 <br/>
	 * 如果没有指定方法名,则调用宿主中对应接口类的同名方法。
	 * 
	 * @param superClass
	 *            接口类
	 * @param hostClass
	 *            宿主类
	 * @param methodName
	 *            宿主方法名(可选)
	 * @param hostMethodParameterTypes
	 *            宿主方法参数(可选)
	 * @param hostMethodReturnType
	 *            宿主方法返回类型(可选)
	 * @return 代理实例
	 */
	@SuppressWarnings("unchecked")
	public static <T> T newInvoker(Class<T> superClass, Class<?> hostClass,
			String methodName, Class<?>[] hostMethodParameterTypes,
			Class<?> hostMethodReturnType) {
		try {
			methodName = methodName == null ? null : methodName.trim();
			StringBuilder proxyClassNameBuilder = new StringBuilder();
			proxyClassNameBuilder.append("proxy.invoker$");
			proxyClassNameBuilder.append(superClass.hashCode() + 10000000000L);
			proxyClassNameBuilder.append("$");
			proxyClassNameBuilder.append(hostClass.hashCode() + 10000000000L);
			proxyClassNameBuilder.append("$");
			if (methodName != null && !methodName.equals("")) {
				proxyClassNameBuilder.append(methodName);
			}
			proxyClassNameBuilder.append("$");
			if (hostMethodParameterTypes != null
					&& hostMethodParameterTypes.length > 0) {
				proxyClassNameBuilder.append(10000000000L + Arrays
						.hashCode(hostMethodParameterTypes));
			}
			proxyClassNameBuilder.append("$");
			if (hostMethodReturnType != null) {
				proxyClassNameBuilder
						.append(10000000000L + hostMethodReturnType.hashCode());
			}
			String proxyClassName = proxyClassNameBuilder.toString();
			Class<?> proxyClass;
			try {
				proxyClass = Class.forName(proxyClassName);
			} catch (Exception ex) {
				ClassPool cp = new ClassPool(true);
				CtClass cc = cp.makeClass(proxyClassName);
				if (superClass.isInterface()) {
					cc.addInterface(cp.get(superClass.getName()));
				} else {
					cc.setSuperclass(cp.get(superClass.getName()));
				}
				Method[] methods = superClass.getMethods();
				for (Method method : methods) {
					int mod = method.getModifiers();
					if (Modifier.isFinal(mod) || Modifier.isStatic(mod)) {
						continue;
					}
					Class<?>[] parameterTypes = method.getParameterTypes();
					if (parameterTypes.length < 1
							|| (!hostClass.isAssignableFrom(parameterTypes[0]) && !parameterTypes[0]
									.isAssignableFrom(hostClass))) {
						throw new IllegalArgumentException(
								"The first argument is not a host instance");
					}
					if (hostMethodParameterTypes != null
							&& hostMethodParameterTypes.length != parameterTypes.length - 1) {
						throw new IllegalArgumentException(
								String.format(
										"The host method parameter types'number should be %d",
										parameterTypes.length - 1));
					}
					Class<?> returnType = method.getReturnType();
					StringBuilder methodCode = new StringBuilder();
					StringBuilder paramCode = new StringBuilder();
					methodCode.append("public ");
					methodCode.append(returnType.getCanonicalName());
					methodCode.append(" ");
					methodCode.append(method.getName());
					methodCode.append("(");
					for (int i = 0; i < parameterTypes.length; i++) {
						String canonicalName = parameterTypes[i]
								.getCanonicalName();
						if (i > 0) {
							methodCode.append(",");
							if (i > 1) {
								paramCode.append(",");
							}

							if (hostMethodParameterTypes != null) {
								String param = generateCast("p" + i,
										parameterTypes[i],
										hostMethodParameterTypes[i - 1]);
								paramCode.append(param);
							} else {
								String param = generateCast("p" + i,
										parameterTypes[i],
										parameterTypes[i - 1]);
								paramCode.append(param);
							}
						}
						methodCode.append(canonicalName);
						methodCode.append(" p");
						methodCode.append(i);
					}
					methodCode.append("){");
					StringBuilder executeCode = new StringBuilder();
					executeCode.append("((");
					executeCode.append(hostClass.getCanonicalName());
					executeCode.append(")p0).");
					if (methodName == null) {
						executeCode.append(method.getName());
					} else {
						executeCode.append(methodName);
					}
					executeCode.append("(");
					executeCode.append(paramCode);
					executeCode.append(")");
					if (!returnType.equals(Void.TYPE)) {
						methodCode.append("return ");
						hostMethodReturnType = hostMethodReturnType == null ? returnType
								: hostMethodReturnType;
						String returnCode = generateCast(
								executeCode.toString(), hostMethodReturnType,
								returnType);
						methodCode.append(returnCode);
					} else {
						methodCode.append(executeCode);
					}
					methodCode.append(";");
					methodCode.append("}");
					cc.addMethod(CtMethod.make(methodCode.toString(), cc));
				}
				proxyClass = cc.toClass();
			}
			return (T) proxyClass.newInstance();
		} catch (Exception e) {
			if (e instanceof RuntimeException) {
				throw (RuntimeException) e;
			}
			throw new RuntimeException(e);
		}
	}

	private static String generateCast(String arg, Class<?> fromClass,
			Class<?> toClass) {
		StringBuilder cast = new StringBuilder();
		if (fromClass.isPrimitive() && !toClass.isPrimitive()) {
			Class<?> wraperClass = toClass;
			if (!isWraper(toClass)) {
				wraperClass = getWraper(fromClass);
			}
			cast.append("(");
			cast.append(toClass.getCanonicalName());
			cast.append(")");
			cast.append(wraperClass.getCanonicalName());
			cast.append(".valueOf((");
			cast.append(getPrimitive(wraperClass).getCanonicalName());
			cast.append(")");
			cast.append(arg);
			cast.append(")");
		} else if (!fromClass.isPrimitive() && toClass.isPrimitive()) {
			cast.append("(");
			cast.append(toClass.getCanonicalName());
			cast.append(")");
			Class<?> wraperClass = fromClass;
			if (!isWraper(fromClass)) {
				wraperClass = getWraper(toClass);
				cast.append("((");
				if (Number.class.isAssignableFrom(wraperClass)) {
					cast.append(Number.class.getCanonicalName());
				} else {
					cast.append(wraperClass.getCanonicalName());
				}
				cast.append(")");
				cast.append(arg);
				cast.append(")");
			} else {
				cast.append(arg);
			}
			cast.append(".");
			cast.append(getPrimitive(wraperClass).getCanonicalName());
			cast.append("Value()");
		} else {
			cast.append("(");
			cast.append(toClass.getCanonicalName());
			cast.append(")");
			cast.append(arg);
		}
		return cast.toString();
	}

	private static Class<?> getPrimitive(Class<?> wraperClass) {
		if (wraperClass.equals(Integer.class)) {
			return Integer.TYPE;
		}
		if (wraperClass.equals(Short.class)) {
			return Short.TYPE;
		}
		if (wraperClass.equals(Long.class)) {
			return Long.TYPE;
		}
		if (wraperClass.equals(Float.class)) {
			return Float.TYPE;
		}
		if (wraperClass.equals(Double.class)) {
			return Double.TYPE;
		}
		if (wraperClass.equals(Byte.class)) {
			return Byte.TYPE;
		}
		if (wraperClass.equals(Character.class)) {
			return Character.TYPE;
		}
		if (wraperClass.equals(Boolean.class)) {
			return Boolean.TYPE;
		}
		if (wraperClass.equals(Void.class)) {
			return Void.TYPE;
		}
		return wraperClass;
	}

	private static Class<?> getWraper(Class<?> toClass) {
		if (toClass.equals(Integer.TYPE)) {
			return Integer.class;
		}
		if (toClass.equals(Short.TYPE)) {
			return Short.class;
		}
		if (toClass.equals(Long.TYPE)) {
			return Long.class;
		}
		if (toClass.equals(Float.TYPE)) {
			return Float.class;
		}
		if (toClass.equals(Double.TYPE)) {
			return Double.class;
		}
		if (toClass.equals(Byte.TYPE)) {
			return Byte.class;
		}
		if (toClass.equals(Character.TYPE)) {
			return Character.class;
		}
		if (toClass.equals(Boolean.TYPE)) {
			return Boolean.class;
		}
		if (toClass.equals(Void.TYPE)) {
			return Void.class;
		}
		return toClass;
	}

	private static boolean isWraper(Class<?> toClass) {
		if (toClass.equals(Integer.class)) {
			return true;
		}
		if (toClass.equals(Short.class)) {
			return true;
		}
		if (toClass.equals(Long.class)) {
			return true;
		}
		if (toClass.equals(Float.class)) {
			return true;
		}
		if (toClass.equals(Double.class)) {
			return true;
		}
		if (toClass.equals(Byte.class)) {
			return true;
		}
		if (toClass.equals(Character.class)) {
			return true;
		}
		if (toClass.equals(Boolean.class)) {
			return true;
		}
		if (toClass.equals(Void.class)) {
			return true;
		}
		return false;
	}

	/**
	 * 快速动态调用宿主方法。 如果指定了方法名,则执行方法时只会调用指定了的方法。 <br/>
	 * 如果没有指定方法名,则调用宿主中对应接口类的同名方法。
	 * 
	 * @param superClass
	 *            接口类
	 * @param hostClass
	 *            宿主类
	 * @param methodName
	 *            方法名(可选)
	 * @return 代理实例
	 */
	public static <T> T newInvoker(Class<T> superClass, Class<?> hostClass,
			String methodName) {
		return newInvoker(superClass, hostClass, methodName, null, null);
	}

	/**
	 * 快速动态调用宿主方法。调用宿主中对应接口类的同名方法。
	 * 
	 * @param superClass
	 *            接口类
	 * @param hostClass
	 *            宿主类
	 * @return 代理实例
	 */
	public static <T> T newInvoker(Class<T> superClass, Class<?> hostClass) {
		return newInvoker(superClass, hostClass, null);
	}
}


以Invokers为基础,自己实现了一个BeanMap,代码如下:
package com.vssq.framework.lang.data;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.WeakHashMap;

import com.vssq.framework.lang.util.Invokers;

/**
 * 自己实现的一个BeanMap,使用Invokers调用,最大限度提高性能
 * 
 * 
 */
public class BeanMap extends AbstractMap<String, Object> implements
		Serializable {
	public static interface Getter {
		Object get(Object obj);
	}

	public static interface Setter {
		void set(Object obj, Object value);
	}

	public class PropertyMeta {
		PropertyDescriptor propertyDescriptor;
		String name;
		Getter reader;
		Setter writer;
		Class<?> type;
		private boolean hasReader;
		private boolean hasWriter;

		PropertyMeta(PropertyDescriptor propertyDescriptor) {
			this.propertyDescriptor = propertyDescriptor;
		}

		public String getName() {
			if (name == null) {
				name = propertyDescriptor.getName();
			}
			return name;
		}

		public Getter getReader() {
			if (reader == null && !hasReader) {
				Method method = propertyDescriptor.getReadMethod();
				if (method != null) {
					reader = Invokers.newInvoker(Getter.class,
							method.getDeclaringClass(), method.getName(),
							method.getParameterTypes(), method.getReturnType());
				}
				hasReader = true;
			}
			return reader;
		}

		public Setter getWriter() {
			if (writer == null && !hasWriter) {
				Method method = propertyDescriptor.getWriteMethod();
				if (method != null) {
					writer = Invokers.newInvoker(Setter.class,
							method.getDeclaringClass(), method.getName(),
							method.getParameterTypes(), method.getReturnType());
				}
				hasWriter = true;
			}
			return writer;
		}

		public Class<?> getPropertyType() {
			if (type == null) {
				type = propertyDescriptor.getPropertyType();
			}
			return type;
		}

	}

	public interface PropertyEntry<K, V> extends Entry<K, V> {
		public PropertyMeta getProperty();

		public Class<?> getType();

		public boolean canRead();

		public boolean canWrite();
	}

	/**
	 * 类字段映射
	 */
	private static final Map<Class<?>, Map<String, PropertyMeta>> classBeanMap = new WeakHashMap<Class<?>, Map<String, PropertyMeta>>();
	/**
	 * 
	 */
	private static final long serialVersionUID = -3627407279602086245L;

	final private Object source;

	final private Class<?> sourceClass;

	private Map<String, PropertyMeta> propertyMetaMap;

	public BeanMap(Object source) {
		if (source == null) {
			throw new NullPointerException("The source object should't be null");
		}
		this.source = source;
		this.sourceClass = source.getClass();
		generateBeanMap();
	}

	public Set<java.util.Map.Entry<String, Object>> entrySet() {
		final Map<String, PropertyMeta> propertyMap = propertyMetaMap;
		Set<java.util.Map.Entry<String, Object>> entrySet = new AbstractSet<Map.Entry<String, Object>>() {
			public Iterator<java.util.Map.Entry<String, Object>> iterator() {
				final Iterator<PropertyMeta> propertyIterator = propertyMap
						.values().iterator();
				return new Iterator<java.util.Map.Entry<String, Object>>() {
					public boolean hasNext() {
						return propertyIterator.hasNext();
					}

					public java.util.Map.Entry<String, Object> next() {
						final PropertyMeta property = propertyIterator.next();
						return new PropertyEntry<String, Object>() {
							public String getKey() {
								return property.getName();
							}

							public Object getValue() {
								try {
									Getter read = property.getReader();
									Object value = read == null ? null : read
											.get(source);
									return value;
								} catch (Exception e) {
									throw wrapCause(e);
								}
							}

							public Object setValue(Object value) {
								try {
									Setter write = property.getWriter();
									Getter read = property.getReader();
									Object old = read == null ? null : read
											.get(source);
									if (write != null) {
										write.set(source, value);
									}
									return old;
								} catch (Throwable e) {
									throw wrapCause(e);
								}
							}

							public Class<?> getType() {
								return property.getPropertyType();
							}

							public boolean canWrite() {
								return property.getWriter() != null;
							}

							public PropertyMeta getProperty() {
								return property;
							}

							public boolean canRead() {
								return property.getReader() != null;
							}
						};
					}

					public void remove() {
						throw new UnsupportedOperationException();
					}
				};
			}

			public int size() {
				return propertyMap.size();
			}
		};
		return entrySet;
	}

	public Map<String, java.util.Map.Entry<String, Object>> entryMap() {
		final Map<String, PropertyMeta> propertyMap = propertyMetaMap;
		return new AbstractMap<String, Map.Entry<String, Object>>() {

			public int size() {
				return propertyMap.size();
			}

			public boolean containsKey(Object key) {
				return propertyMap.containsKey(key);
			}

			public Entry<String, Object> get(Object key) {
				final PropertyMeta property = propertyMap.get(key);
				return new PropertyEntry<String, Object>() {
					public String getKey() {
						return property.getName();
					}

					public Object getValue() {
						try {
							Getter read = property.getReader();
							Object value = read == null ? null : read
									.get(source);
							return value;
						} catch (Exception e) {
							throw wrapCause(e);
						}
					}

					public Object setValue(Object value) {
						try {
							Setter write = property.getWriter();
							Getter read = property.getReader();
							Object old = read == null ? null : read.get(source);
							if (write != null) {
								write.set(source, value);
							}
							return old;
						} catch (Throwable e) {
							throw wrapCause(e);
						}
					}

					public Class<?> getType() {
						return property.getPropertyType();
					}

					public boolean canWrite() {
						return property.getWriter() != null;
					}

					public PropertyMeta getProperty() {
						return property;
					}

					public boolean canRead() {
						return property.getReader() != null;
					}
				};
			}

			public Set<java.util.Map.Entry<String, java.util.Map.Entry<String, Object>>> entrySet() {
				Set<java.util.Map.Entry<String, java.util.Map.Entry<String, Object>>> entrySet = new AbstractSet<java.util.Map.Entry<String, java.util.Map.Entry<String, Object>>>() {

					public int size() {
						return propertyMap.size();
					}

					public Iterator<java.util.Map.Entry<String, java.util.Map.Entry<String, Object>>> iterator() {
						final Iterator<PropertyMeta> propertyIterator = propertyMap
								.values().iterator();
						return new Iterator<Map.Entry<String, Entry<String, Object>>>() {
							public boolean hasNext() {
								return propertyIterator.hasNext();
							}

							public java.util.Map.Entry<String, java.util.Map.Entry<String, Object>> next() {

								return new java.util.Map.Entry<String, java.util.Map.Entry<String, Object>>() {
									PropertyMeta property = propertyIterator
											.next();

									public String getKey() {
										return property.getName();
									}

									public java.util.Map.Entry<String, Object> getValue() {
										return new PropertyEntry<String, Object>() {
											public String getKey() {
												return property.getName();
											}

											public Object getValue() {
												try {
													Getter read = property
															.getReader();
													Object value = read == null ? null
															: read.get(source);
													return value;
												} catch (Exception e) {
													throw wrapCause(e);
												}
											}

											public Object setValue(Object value) {
												try {
													Setter write = property
															.getWriter();
													Getter read = property
															.getReader();
													Object old = read == null ? null
															: read.get(source);
													if (write != null) {
														write.set(source, value);
													}
													return old;
												} catch (Throwable e) {
													throw wrapCause(e);
												}
											}

											public Class<?> getType() {
												return property
														.getPropertyType();
											}

											public boolean canWrite() {
												return property.getWriter() != null;
											}

											public PropertyMeta getProperty() {
												return property;
											}

											public boolean canRead() {
												return property.getReader() != null;
											}
										};
									}

									public java.util.Map.Entry<String, Object> setValue(
											java.util.Map.Entry<String, Object> value) {
										throw new UnsupportedOperationException();
									}
								};
							}

							public void remove() {
								throw new UnsupportedOperationException();
							}
						};
					}
				};
				return entrySet;
			}
		};
	}

	public Set<String> keySet() {
		return propertyMetaMap.keySet();
	}

	private Map<String, PropertyMeta> generateBeanMap() {
		try {
			if (propertyMetaMap == null) {
				propertyMetaMap = classBeanMap.get(sourceClass);
				if (propertyMetaMap == null) {
					propertyMetaMap = new TreeMap<String, PropertyMeta>();
					PropertyDescriptor[] propertys = Introspector.getBeanInfo(
							sourceClass).getPropertyDescriptors();
					for (PropertyDescriptor property : propertys) {
						String name = property.getName();
						if ("class".equals(name)) {
							continue;
						}
						PropertyMeta propertyMeta = new PropertyMeta(property);
						propertyMetaMap.put(name, propertyMeta);
					}
					classBeanMap.put(sourceClass, propertyMetaMap);
				}
			}
			return propertyMetaMap;
		} catch (Throwable e) {
			throw wrapCause(e);
		}
	}

	public int size() {
		return propertyMetaMap.size();
	}

	public boolean isEmpty() {
		return propertyMetaMap.isEmpty();
	}

	public boolean containsKey(Object key) {
		return propertyMetaMap.containsKey(key);
	}

	public Object get(Object key) {
		try {
			PropertyMeta property = propertyMetaMap.get(key);
			Getter read = property.getReader();
			Object value = read == null ? null : read.get(source);
			return value;
		} catch (IllegalArgumentException e) {
			throw wrapCause(e);
		}
	}

	public Object put(String key, Object value) {
		try {
			PropertyMeta property = propertyMetaMap.get(key);
			Setter write = property.getWriter();
			Getter read = property.getReader();
			Object old = read == null ? null : read.get(source);
			if (write != null) {
				write.set(source, value);
			}
			return old;
		} catch (IllegalArgumentException e) {
			throw wrapCause(e);
		}
	}

	/**
	 * 设置值(避免put中返回旧值的性能损失)
	 * 
	 * @param key
	 *            键
	 * @param value
	 *            值
	 */
	public void set(String key, Object value) {
		try {
			PropertyMeta property = propertyMetaMap.get(key);
			Setter write = property.getWriter();
			if (write != null) {
				write.set(source, value);
			}
		} catch (IllegalArgumentException e) {
			throw wrapCause(e);
		}
	}

	/**
	 * 包裹异常
	 * 
	 * @param cause
	 * @return
	 */
	public static RuntimeException wrapCause(Throwable cause) {
		if (cause instanceof RuntimeException) {
			return (RuntimeException) cause;
		}
		return new RuntimeException(cause);
	}
}

算上创建时间的话,比cglib的BeanMap快得多。但是如果仅仅是执行get或者put方法,则还是稍慢于cglib的BeanMap(大约是cglib的BeanMap所耗时间的1.5倍左右),因为cglib内部字节码生成的Map是使用switch定位key的,这比HashMap还要快,无能为力了 。但常规使用下,经常是对新对象创建Map并且只需要遍历执行一次get/put,所以创建时间慢的话还是严重影响性能的,使用此BeanMap在创建上速度是cglib的10倍以上。很多开源库都依赖于BeanMap,如JSON解析等等,如能使用此API,相信相关库的性能也会跟着大幅提升。
   发表时间:2013-03-11   最后修改:2013-03-11
自带的调用,hotspot加上-server之后还是可以减少不少时间的,但IBM j9 1.6以及JRockit 1.6则耗时非常之长,harmony 6.0上慢的令人发指
0 请登录后投票
   发表时间:2013-03-11  
一般情况下,反射并不是瓶颈,但现在几乎所有的主流架构都大量应用了反射调用,积累的时间效应还是很明显的,常常使得高并发下的优化止步在反射调用上,加上目前预备启动的架构设计也需要频繁反射调用,而又不想写大量的字节码增强程序,故此发展出这个提高反射调用性能的工具。
0 请登录后投票
   发表时间:2013-03-11  
跑了几遍,结果有点意外,环境是win7 64位+java1.6.0_24默认配置
直接调用InvokerJDK反射CGLIB反射
163839805809
172848802855
169862807855
169834800849
173821790902
159872787827
153816787867


测试代码
public class TestInvoker {

	private static long l = 333333L;
	private static int times = 10000000;

	public static void main(String[] args) throws Exception {
		//要分别独立跑,一齐跑相互有影响的。
		test();
		//testJDK();
		//testFastMethod();
		//testInvoker();
	}

	public static void test() {
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			Date date = new Date(l);
		}
		long t1 = System.currentTimeMillis();
		System.out.println("直接调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testInvoker() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		Invoker get = Invokers.newInvoker(getMethod);
		Invoker set = Invokers.newInvoker(setMethod);
		for (int i = 0; i < times; i++) {
			get.invoke(date, new Object[] {});
			set.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("Invoker调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testJDK() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		for (int i = 0; i < times; i++) {
			getMethod.invoke(date, new Object[] {});
			setMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("JDK反射调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testFastMethod() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		FastClass fastClass = FastClass.create(Date.class);
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		FastMethod fastGetMethod = fastClass.getMethod(getMethod);
		FastMethod fastSetMethod = fastClass.getMethod(setMethod);
		for (int i = 0; i < times; i++) {
			fastGetMethod.invoke(date, new Object[] {});
			fastSetMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("CGLIB反射调用耗时:" + (t1 - t0) + "ms");
	}

}
0 请登录后投票
   发表时间:2013-03-11   最后修改:2013-03-11
jh108020 写道
跑了几遍,结果有点意外,环境是win7 64位+java1.6.0_24默认配置
直接调用InvokerJDK反射CGLIB反射
163839805809
172848802855
169862807855
169834800849
173821790902
159872787827
153816787867


测试代码
public class TestInvoker {

	private static long l = 333333L;
	private static int times = 10000000;

	public static void main(String[] args) throws Exception {
		//要分别独立跑,一齐跑相互有影响的。
		test();
		//testJDK();
		//testFastMethod();
		//testInvoker();
	}

	public static void test() {
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			Date date = new Date(l);
		}
		long t1 = System.currentTimeMillis();
		System.out.println("直接调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testInvoker() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		Invoker get = Invokers.newInvoker(getMethod);
		Invoker set = Invokers.newInvoker(setMethod);
		for (int i = 0; i < times; i++) {
			get.invoke(date, new Object[] {});
			set.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("Invoker调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testJDK() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		for (int i = 0; i < times; i++) {
			getMethod.invoke(date, new Object[] {});
			setMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("JDK反射调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testFastMethod() throws Exception {
		long t0 = System.currentTimeMillis();
		Date date = new Date();
		FastClass fastClass = FastClass.create(Date.class);
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		FastMethod fastGetMethod = fastClass.getMethod(getMethod);
		FastMethod fastSetMethod = fastClass.getMethod(setMethod);
		for (int i = 0; i < times; i++) {
			fastGetMethod.invoke(date, new Object[] {});
			fastSetMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("CGLIB反射调用耗时:" + (t1 - t0) + "ms");
	}

}


发现你把创建时间一起去算了,我承认,这里的反射优势在于调用处,而非创建(因为创建可是要创建新类的,你的测试已经让我感到惊喜了,实际上你把次数改为一亿,效果就出来了)。为此,我稍微修改了你的测试程序,麻烦你再跑跑看:

public class TestInvoker {

	private static long l = 333333L;
	private static int times = 10000000;

	public static void main(String[] args) throws Exception {
		// 要分别独立跑,一齐跑相互有影响的。
		// test();
		// testJDK();
		// testFastMethod();
		testInvoker();
	}

	public static void test() {
		Date date = new Date(l);
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			date.getTime();
			date.setTime(333333333L);
		}
		long t1 = System.currentTimeMillis();
		System.out.println("直接调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testInvoker() throws Exception {

		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		Invoker get = Invokers.newInvoker(getMethod);
		Invoker set = Invokers.newInvoker(setMethod);
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			get.invoke(date, new Object[] {});
			set.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("Invoker调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testJDK() throws Exception {

		Date date = new Date();
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			getMethod.invoke(date, new Object[] {});
			setMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("JDK反射调用耗时:" + (t1 - t0) + "ms");
	}

	public static void testFastMethod() throws Exception {

		Date date = new Date();
		FastClass fastClass = FastClass.create(Date.class);
		Method getMethod = Date.class.getMethod("getTime");
		getMethod.setAccessible(true);
		Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
		setMethod.setAccessible(true);
		FastMethod fastGetMethod = fastClass.getMethod(getMethod);
		FastMethod fastSetMethod = fastClass.getMethod(setMethod);
		long t0 = System.currentTimeMillis();
		for (int i = 0; i < times; i++) {
			fastGetMethod.invoke(date, new Object[] {});
			fastSetMethod.invoke(date, new Object[] { 333333L });
		}
		long t1 = System.currentTimeMillis();
		System.out.println("CGLIB反射调用耗时:" + (t1 - t0) + "ms");
	}

}


一般情况下,Invokers需要缓存来使用。
0 请登录后投票
   发表时间:2013-03-12  
其实你这些测试结果本身对反射不是很公平,关键点就在于原生类型的封箱和拆箱,否则测试结果其实和直接调用相差不大
0 请登录后投票
   发表时间:2013-03-12  
public class TestInvoker {

    private static long l     = 333333L;
    private static int  times = 100000000;

    public interface BinaryInvoker<T, A> {

        void invoke(T host, A arg);
    }

    public interface UnitaryInvoker<T> {

        void invoke(T host);
    }

    public interface ReturnedUnitaryInvoker<T, R> {

        R invoke(T host);
    }

    static UnitaryInvoker<Date>               dateGetter         = new UnitaryInvoker<Date>() {

                                                                     @Override
                                                                     public void invoke(Date host) {
                                                                         host.getTime();
                                                                     }

                                                                 };

    static ReturnedUnitaryInvoker<Date, Long> returnedDateGetter = new ReturnedUnitaryInvoker<Date, Long>() {

                                                                     @Override
                                                                     public Long invoke(Date host) {
                                                                         return host.getTime(); // 一次封箱
                                                                     }

                                                                 };

    static BinaryInvoker<Date, Long>          dateSetter         = new BinaryInvoker<Date, Long>() {

                                                                     @Override
                                                                     public void invoke(Date host, Long arg) {
                                                                         host.setTime(arg);
                                                                     }

                                                                 };

    static long date_get(Date host) {
        return host.getTime();
    }

    static void date_set(Date host, long time) {
        host.setTime(time);
    }

    public static void main(String[] args) throws Exception {
        // 要分别独立跑,一齐跑相互有影响的。
        // test();
        // testJDK();
        // testInvoker();
        testInterface();
        // testMethod();
    }

    public static void testMethod() {
        Date date = new Date(l);
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            date_get(date);
            date_set(date, 333333333L);
        }
        long t1 = System.currentTimeMillis();
        System.out.println("方法调用耗时:" + (t1 - t0) + "ms");
    }

    public static void testInterface() {
        Date date = new Date(l);
        Long time = new Long(333333333L);
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            dateGetter.invoke(date);
            //returnedDateGetter.invoke(date); //1328ms 因为封箱,所以慢
            dateSetter.invoke(date, time); // 22ms
            // dateSetter.invoke(date, 333333333L); //1206ms 因为封箱,所以慢
        }
        long t1 = System.currentTimeMillis();
        System.out.println("接口调用耗时:" + (t1 - t0) + "ms");
    }

    public static void test() {
        Date date = new Date(l);
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            date.getTime();
            date.setTime(333333333L);
        }
        long t1 = System.currentTimeMillis();
        System.out.println("直接调用耗时:" + (t1 - t0) + "ms");
    }

    public static void testInvoker() throws Exception {

        Date date = new Date();
        Method getMethod = Date.class.getMethod("getTime");
        getMethod.setAccessible(true);
        Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
        setMethod.setAccessible(true);
        Invoker get = Invokers.newInvoker(getMethod);
        Invoker set = Invokers.newInvoker(setMethod);
        Object[] argGet = new Object[] {};
        Object[] argSet = new Object[] { 333333L };
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            // get.invoke(date, new Object[] {});
            // set.invoke(date, new Object[] { 333333L });
            get.invoke(date, argGet);
            set.invoke(date, argSet);
        }
        long t1 = System.currentTimeMillis();
        System.out.println("Invoker调用耗时:" + (t1 - t0) + "ms");
    }

    public static void testJDK() throws Exception {

        Date date = new Date();
        Method getMethod = Date.class.getMethod("getTime");
        getMethod.setAccessible(true);
        Method setMethod = Date.class.getMethod("setTime", Long.TYPE);
        setMethod.setAccessible(true);
        Object[] argGet = new Object[] {};
        Object[] argSet = new Object[] { 333333L };
        long t0 = System.currentTimeMillis();
        for (int i = 0; i < times; i++) {
            // getMethod.invoke(date, new Object[] {});
            // setMethod.invoke(date, new Object[] { 333333L });
            getMethod.invoke(date, argGet);
            setMethod.invoke(date, argSet);
        }
        long t1 = System.currentTimeMillis();
        System.out.println("JDK反射调用耗时:" + (t1 - t0) + "ms");
    }
}


testInterface测试中,一旦存在封箱,数量级由10到1000,如果存在多次封拆,影响更大,反射或者invoker的入参,难免需要封装,而且内部还有各种checkcast行为,都会导致速度比较慢,其实,从测试结果来看,jdk自带的反射性能已经很不错了
0 请登录后投票
   发表时间:2013-03-12  
在通用接口调用上,装箱和拆箱确实无可避免的,实际上,自带反射的性能问题主要是三方面:1、数组装卸;2、基本类型装箱和拆箱;3、各种check;从测试中,Invokers即使考虑进1和2两点,速度依然是JDK的数倍甚至十倍(JDK6环境下),原因是少了3(当然,这个并不会影响使用)。
对于1和2,终极解决办法就是我示例一中所写的,在知道参数数量和类型的情况下,可先行声明好调用接口,以类似C#中委托的方式动态调用,通过Invokers生成其接口实例进行调用,得到的性能将和直接调用一致,同时保留了一定的动态性(至少方法名是动态设置的,这一点也是最常用的动态调用;而如果仅仅知道参数数量而没有准确类型,也可以将接口参数声明为通用类型,此时可避免1造成的性能损失)。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics