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

动态调用方法,大多数情况下,只能使用反射机制实现。反射性能上远远逊于直接调用(虽然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。


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

Java代码 

 收藏代码

@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,相信相关库的性能也会跟着大幅提升。

 

在通用接口调用上,装箱和拆箱确实无可避免的,实际上,自带反射的性能问题主要是三方面:1、数组装卸;2、基本类型装箱和拆箱;3、各种check;从测试中,Invokers即使考虑进1和2两点,速度依然是JDK的数倍甚至十倍(JDK6环境下),原因是少了3(当然,这个并不会影响使用)。
对于1和2,终极解决办法就是我示例一中所写的,在知道参数数量和类型的情况下,可先行声明好调用接口,以类似C#中委托的方式动态调用,通过Invokers生成其接口实例进行调用,得到的性能将和直接调用一致,同时保留了一定的动态性(至少方法名是动态设置的,这一点也是最常用的动态调用;而如果仅仅知道参数数量而没有准确类型,也可以将接口参数声明为通用类型,此时可避免1造成的性能损失)。

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值