Java反射,,Methods.java
分享于 点击 39941 次 点评:106
Java反射,,Methods.java
Methods.java
package canghailan.lang.reflect;import canghailan.util.CachedLookup;import canghailan.util.Lookup;import canghailan.util.SimpleLRUCache;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/** * @author canghailan 2012-04-10 21:03 */public class Methods { private static final ExecutorService executorService = Executors.newCachedThreadPool(); private static final Lookup<MethodIdentifier, Method> methodLookup = buildMethodLookup(); private static Lookup<MethodIdentifier, Method> buildMethodLookup() { return new MethodLookup(buildOverloadingLookup()); } private static Lookup<ClassNamePair, MethodSignature[]> buildOverloadingLookup() { return new CachedLookup<>( new OverloadingLookup(), new SimpleLRUCache<ClassNamePair, Future<MethodSignature[]>>(1000), executorService ); } private static final Class<?>[] EMPTY_ARGUMENT_TYPES = new Class<?>[0]; public static Class<?>[] getArgumentTypes(Object... args) { if (args.length == 0) { return EMPTY_ARGUMENT_TYPES; } Class<?>[] argTypes = new Class<?>[args.length]; for (int i = 0; i < args.length; ++i) { argTypes[i] = args[i] == null ? null : args[i].getClass(); } return argTypes; } public static Method getMethod(Class<?> cls, String name, Class<?>... argTypes) { return methodLookup.find(new MethodIdentifier(cls, name, argTypes)); } public static Object invoke(Object object, String name, Object... args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { return invoke(object.getClass(), name, getArgumentTypes(args), object, args); } public static Object invokeStatic(Class<?> cls, String name, Object... args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { return invoke(cls, name, getArgumentTypes(args), null, args); } private static Object invoke(Class<?> cls, String name, Class<?>[] argTypes, Object object, Object... args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { Method method = getMethod(cls, name, argTypes); if (method != null) { return method.invoke(object, args); } throw new NoSuchMethodException(invokeToString(cls, name, args)); } private static String invokeToString(Class<?> cls, String name, Object... args) { StringBuilder buffer = new StringBuilder(); buffer.append(cls.getName()); buffer.append('.'); buffer.append(name); buffer.append('('); if (args.length > 0) { for (Object arg : args) { buffer.append(arg).append(','); } buffer.setLength(buffer.length() - 1); } buffer.append(')'); return buffer.toString(); }}
Fields.java
package canghailan.lang.reflect;import canghailan.util.CachedLookup;import canghailan.util.Lookup;import canghailan.util.SimpleLRUCache;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/** * @author canghailan 2012-04-10 21:03 */public class Fields { private static final ExecutorService executorService = Executors.newCachedThreadPool(); private static final Lookup<ClassNamePair, Field> fieldLookup = buildFieldLookup(); private static final Lookup<ClassNamePair, Method> propertyGetterLookup = buildPropertyGetterLookup(); private static Lookup<ClassNamePair, Field> buildFieldLookup() { return new CachedLookup<>( new FieldLookup(), new SimpleLRUCache<ClassNamePair, Future<Field>>(1000), executorService ); } private static Lookup<ClassNamePair, Method> buildPropertyGetterLookup() { return new CachedLookup<>( new PropertyGetterLookup(), new SimpleLRUCache<ClassNamePair, Future<Method>>(1000), executorService ); } public static Field getField(Class<?> cls, String name) { return fieldLookup.find(new ClassNamePair(cls, name)); } public static Method getGetter(Class<?> cls, String name) { return propertyGetterLookup.find(new ClassNamePair(cls, name)); } public static Object get(Object object, String name) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { Method getter = propertyGetterLookup.find(new ClassNamePair(object.getClass(), name)); if (getter != null) { return getter.invoke(object); } throw new NoSuchMethodException("Could not find getter for " + object.getClass().getName() + "." + name); } public static void set(Object object, String name, Object... value) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException { StringBuilder setter = new StringBuilder("set".length() + name.length()); setter.append("set").append(name) .setCharAt("set".length(), Character.toUpperCase(name.charAt(0))); Methods.invoke(object, setter.toString(), value); } public static Object directGet(Field field, Object object) { try { if (field.isAccessible()) { return field.get(object); } else { field.setAccessible(true); Object value = field.get(object); field.setAccessible(false); return value; } } catch (IllegalAccessException e) { // should never reach here throw new RuntimeException(e); } } public static void directSet(Field field, Object object, Object value) { try { if (field.isAccessible()) { field.set(object, value); } else { field.setAccessible(true); field.set(object, value); field.setAccessible(false); } } catch (IllegalAccessException e) { // should never reach here throw new RuntimeException(e); } }}
FieldLookup.java
package canghailan.lang.reflect;import canghailan.util.Lookup;import java.lang.reflect.Field;/** * @author canghailan 2012-02-23 20:31 */public class FieldLookup implements Lookup<ClassNamePair, Field> { @Override public Field find(ClassNamePair key) { Field field = getDeclaredField(key.getClazz(), key.getName()); if (field == null) { field = getInheritedFromInterfaces(key.getClazz(), key.getName()); if (field == null) { field = getInheritedFromSuperclass(key.getClazz(), key.getName()); } } return field; } private static Field getDeclaredField(Class<?> cls, String name) { for (Field field : cls.getDeclaredFields()) { if (field.getName().equals(name)) { return field; } } return null; } private static Field getInheritedFromInterfaces(Class<?> cls, String name) { for (Class<?> i : cls.getInterfaces()) { Field field = getDeclaredField(i, name); if (field != null) { return field; } } return null; } private static Field getInheritedFromSuperclass(Class<?> cls, String name) { for (Class<?> s = cls.getSuperclass(); s != null; s = s.getSuperclass()) { Field field = getDeclaredField(s, name); if (field != null) { return field; } } return null; }}
MethodLookup.java
package canghailan.lang.reflect;import canghailan.util.Lookup;import java.lang.reflect.Method;/** * @author canghailan 2012-04-19 22:21 */public class MethodLookup implements Lookup<MethodIdentifier, Method> { private final Lookup<ClassNamePair, MethodSignature[]> overloadingLookup; public MethodLookup(Lookup<ClassNamePair, MethodSignature[]> overloadingLookup) { this.overloadingLookup = overloadingLookup; } @Override public Method find(MethodIdentifier key) { MethodSignature signature = determine(findOverloading(key), key); return signature == null ? null : signature.getMethod(); } private MethodSignature[] findOverloading(MethodIdentifier id) { return overloadingLookup.find(new ClassNamePair(id.getClazz(), id.getName())); } private static MethodSignature determine(MethodSignature[] overloading, MethodIdentifier id) { MethodSignatureDeterminer determiner = new MethodSignatureDeterminer(); for (int index = maxAcceptIndex(overloading, id); index >= 0; --index) { determiner.accept(overloading[index], id); if (determiner.getCurrentPhase() == MethodSignatureDeterminer.Phase.FINISH) { break; } } return determiner.getMostSpecific(); } private static int maxAcceptIndex(MethodSignature[] methods, MethodIdentifier id) { int argsLength = id.getArgumentTypes().length; // binary search int from = 0; int to = methods.length - 1; while (from <= to) { int index = (from + to) >>> 1; if (methods[index].getParameterTypes().length > argsLength) { to = index - 1; } else { if (index == methods.length - 1 || methods[index + 1].getParameterTypes().length > argsLength) { return index; } else { from = index + 1; } } } return -1; }}
OverloadingLookup.java
package canghailan.lang.reflect;import canghailan.util.Lookup;import java.lang.reflect.Method;import java.util.Arrays;import java.util.Comparator;import java.util.LinkedList;import java.util.List;/** * overloading methods and sorted by parameter length * * @author canghailan 2012-04-10 21:02 */public class OverloadingLookup implements Lookup<ClassNamePair, MethodSignature[]> { public static final Comparator<MethodSignature> PARAM_LENGTH_COMPARATOR = new Comparator<MethodSignature>() { @Override public int compare(MethodSignature o1, MethodSignature o2) { // 按参数长度、是否变元排序,较长、定元较大,便于搜索 int c1 = Integer.compare( o1.getParameterTypes().length, o2.getParameterTypes().length ); if (c1 == 0) { if (o1.isVarArgs()) { return o2.isVarArgs() ? 0 : -1; } else { return o2.isVarArgs() ? 1 : 0; } } else { return c1; } } }; @Override public MethodSignature[] find(ClassNamePair key) { return sort(filter(key.getClazz(), key.getName())); } private static MethodSignature[] filter(Class<?> cls, String name) { List<MethodSignature> overloadingMethods = new LinkedList<>(); for (Method method : cls.getMethods()) { if (method.getName().equals(name)) { overloadingMethods.add(new MethodSignature(method)); } } return overloadingMethods.toArray(new MethodSignature[overloadingMethods.size()]); } private static MethodSignature[] sort(MethodSignature[] overloadingMethods) { Arrays.sort(overloadingMethods, PARAM_LENGTH_COMPARATOR); return overloadingMethods; }}
PropertyGetterLookup.java
package canghailan.lang.reflect;import canghailan.util.Lookup;import java.lang.reflect.Method;import java.util.Arrays;import java.util.List;/** * @author canghailan 2012-04-10 20:19 */public class PropertyGetterLookup implements Lookup<ClassNamePair, Method> { @Override public Method find(ClassNamePair key) { return filterAndChoose(key.getClazz().getMethods(), getterNames(key.getName())); } private static Method filterAndChoose(Method[] methods, List<String> names) { Method getter = null; int nameIndex = -1; for (Method method : methods) { int theIndex = names.indexOf(method.getName()); if (theIndex >= 0 && method.getParameterTypes().length == 0) { if (theIndex == 0) { return method; } else { if (getter == null || theIndex < nameIndex) { getter = method; nameIndex = theIndex; } } } } return getter; } private static List<String> getterNames(String name) { StringBuilder buffer = new StringBuilder(3 + name.length()); buffer.append("get").append(name) .setCharAt("get".length(), Character.toUpperCase(name.charAt(0))); String get = buffer.toString(); buffer.setLength(0); buffer.append("is").append(name) .setCharAt("is".length(), Character.toUpperCase(name.charAt(0))); String is = buffer.toString(); return Arrays.asList(get, is, name); }}
Lookup.java
package canghailan.util;/** * @author canghailan 2012-04-10 18:34 */public interface Lookup<K, V> { V find(K key);}
ClassNamePair.java
package canghailan.lang.reflect;/** * @author canghailan 2012-04-10 19:54 */public class ClassNamePair { private final Class<?> clazz; private final String name; public ClassNamePair(Class<?> clazz, String name) { assert clazz != null; assert name != null; this.clazz = clazz; this.name = name; } public Class<?> getClazz() { return clazz; } public String getName() { return name; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ClassNamePair that = (ClassNamePair) o; return clazz == that.clazz && name.equals(that.name); } @Override public int hashCode() { return clazz.hashCode() * 31 + name.hashCode(); }}
MethodIdentifier.java
package canghailan.lang.reflect;import java.util.Arrays;/** * @author canghailan 2012-04-19 22:19 */public class MethodIdentifier { private final Class<?> clazz; private final String name; private final Class<?>[] argumentTypes; public MethodIdentifier(Class<?> clazz, String name, Class<?>[] argumentTypes) { this.clazz = clazz; this.name = name; this.argumentTypes = argumentTypes; } public Class<?> getClazz() { return clazz; } public String getName() { return name; } public Class<?>[] getArgumentTypes() { return argumentTypes; } @Override public String toString() { return Arrays.toString(argumentTypes); }}
MethodSignature.java
package canghailan.lang.reflect;import java.lang.reflect.Method;/** * @author canghailan 2012-04-19 20:03 */public class MethodSignature { private final Method method; private final Class<?>[] parameterTypes; private final Class<?> varArgType; public MethodSignature(Method method) { this.method = method; parameterTypes = method.getParameterTypes(); varArgType = method.isVarArgs() ? parameterTypes[parameterTypes.length - 1].getComponentType() : null; } public Method getMethod() { return method; } public Class<?>[] getParameterTypes() { return parameterTypes; } public boolean isVarArgs() { return varArgType != null; } public Class<?> getVarArgType() { return varArgType; } @Override public String toString() { return method.toString(); }}
MethodInvocationConversion.java
package canghailan.lang.reflect;/** * @author canghailan 2012-04-21 22:54 */public class MethodInvocationConversion { public static boolean isWideningPrimitiveConversion(Class<?> from, Class<?> to) { if (to == int.class) { return from == char.class || from == short.class || from == byte.class; } if (to == double.class) { return from == float.class || from == long.class || from == int.class || from == char.class || from == short.class || from == byte.class; } if (to == short.class) { return from == byte.class; } if (to == long.class) { return from == int.class || from == char.class || from == short.class || from == byte.class; } if (to == float.class) { return from == long.class || from == int.class || from == char.class || from == short.class || from == byte.class; } return false; } public static boolean isWideningReferenceConversion(Class<?> from, Class<?> to) { return to.isAssignableFrom(from); } public static Class<?> boxing(Class<?> cls) { if (cls == int.class) { return Integer.class; } if (cls == double.class) { return Double.class; } if (cls == boolean.class) { return Boolean.class; } if (cls == byte.class) { return Byte.class; } if (cls == char.class) { return Character.class; } if (cls == short.class) { return Short.class; } if (cls == long.class) { return Long.class; } if (cls == float.class) { return Float.class; } return null; } public static Class<?> unboxing(Class<?> cls) { if (cls == Integer.class) { return int.class; } if (cls == Double.class) { return double.class; } if (cls == Boolean.class) { return boolean.class; } if (cls == Byte.class) { return byte.class; } if (cls == Character.class) { return char.class; } if (cls == Short.class) { return short.class; } if (cls == Long.class) { return long.class; } if (cls == Float.class) { return float.class; } return null; }}
MethodSignatureDeterminer.java
package canghailan.lang.reflect;import static canghailan.lang.reflect.MethodInvocationConversion.*;/** * @author canghailan 2012-04-21 21:47 */public class MethodSignatureDeterminer { private Phase currentPhase; private MethodSignature mostSpecific; public MethodSignatureDeterminer() { this.currentPhase = Phase.START; } public Phase getCurrentPhase() { return currentPhase; } public MethodSignature getMostSpecific() { return mostSpecific; } public void accept(MethodSignature signature, MethodIdentifier id) {// Phase bp = currentPhase;// MethodSignature bms = mostSpecific; currentPhase.accept(this, signature, id);// Phase ap = currentPhase;// MethodSignature ams = mostSpecific;// System.out.print("accept: " + signature.getMethod());// System.out.println(" <= (" + id + ")");// System.out.println(bms + " => " + ams);// System.out.println(bp + " => " + ap);// System.out.println(); } /** * <ul> * <li>Phase 1: Identify Matching Arity Methods Applicable by Subtyping</li> * <li>Phase 2: Identify Matching Arity Methods Applicable by Method Invocation Conversion</li> * <li>Phase 3: Identify Applicable Variable Arity Methods</li> * </ul> */ public static enum Phase { START { @Override final void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id) { if (isMatchingArity(signature, id)) { ApplicableKind kind = checkApplicableMatchingArity(signature, id); if (kind != null) { determiner.mostSpecific = signature; switch (kind) { case MATCHING: { determiner.currentPhase = FINISH; break; } case SUBTYPING: { determiner.currentPhase = MATCHING_ARITY_BY_SUBTYPING; break; } case METHOD_INVOCATION_CONVERSION: { determiner.currentPhase = MATCHING_ARITY_BY_CONVERSION; break; } } } } else if (isVariableArity(signature, id)) { if (checkApplicableVariableArity(signature, id) != null) { determiner.mostSpecific = signature; determiner.currentPhase = VARIABLE_ARITY; } } } }, MATCHING_ARITY_BY_SUBTYPING { @Override final void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id) { if (isMatchingArity(signature, id)) { ApplicableKind kind = checkApplicableMatchingArity(signature, id); if (kind != null) { switch (kind) { case MATCHING: { determiner.mostSpecific = signature; determiner.currentPhase = FINISH; break; } case SUBTYPING: { if (isMoreSpecificFixArity(signature, determiner.mostSpecific)) { determiner.mostSpecific = signature; } break; } } } } else { if (signature.getParameterTypes().length != id.getArgumentTypes().length) { determiner.currentPhase = FINISH; } } } }, MATCHING_ARITY_BY_CONVERSION { @Override final void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id) { if (isMatchingArity(signature, id)) { ApplicableKind kind = checkApplicableMatchingArity(signature, id); if (kind != null) { switch (kind) { case MATCHING: { determiner.mostSpecific = signature; determiner.currentPhase = FINISH; break; } case SUBTYPING: { determiner.mostSpecific = signature; determiner.currentPhase = MATCHING_ARITY_BY_SUBTYPING; break; } case METHOD_INVOCATION_CONVERSION: { if (isMoreSpecificFixArity(signature, determiner.mostSpecific)) { determiner.mostSpecific = signature; } break; } } } } else { if (signature.getParameterTypes().length != id.getArgumentTypes().length) { determiner.currentPhase = FINISH; } } } }, VARIABLE_ARITY { @Override final void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id) { if (isVariableArity(signature, id)) { if (checkApplicableVariableArity(signature, id) != null) { if (isMoreSpecificVariableArity(signature, determiner.mostSpecific)) { determiner.mostSpecific = signature; } } } } }, FINISH { @Override final void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id) { //do nothing } }; abstract void accept(MethodSignatureDeterminer determiner, MethodSignature signature, MethodIdentifier id); } private static boolean isMatchingArity(MethodSignature signature, MethodIdentifier id) { return !signature.isVarArgs() && signature.getParameterTypes().length == id.getArgumentTypes().length; } private static boolean isVariableArity(MethodSignature signature, MethodIdentifier id) { return signature.isVarArgs() && signature.getParameterTypes().length <= id.getArgumentTypes().length; } private static boolean isMoreSpecificFixArity(MethodSignature m1, MethodSignature m2) { final Class<?>[] paramTypes1 = m1.getParameterTypes(); final Class<?>[] paramTypes2 = m2.getParameterTypes(); for (int i = 0; i < paramTypes1.length; ++i) { if (checkApplicable(paramTypes1[i], paramTypes2[i]) == null) { return false; } } return true; } private static boolean isMoreSpecificVariableArity(MethodSignature m1, MethodSignature m2) { final Class<?>[] paramTypes1 = m1.getParameterTypes(); final int paramsLength1 = paramTypes1.length; final Class<?>[] paramTypes2 = m2.getParameterTypes(); final int paramsLength2 = paramTypes2.length; if (paramsLength1 >= paramsLength2) { for (int i = 0; i < paramsLength2 - 1; ++i) { if (checkApplicable(paramTypes1[i], paramTypes2[i]) == null) { return false; } } Class<?> varArgType = m2.getVarArgType(); for (int i = paramsLength2 - 1; i < paramsLength1; ++i) { if (checkApplicable(paramTypes1[i], varArgType) == null) { return false; } } return true; } else { for (int i = 0; i < paramsLength1 - 1; ++i) { if (checkApplicable(paramTypes1[i], paramTypes2[i]) == null) { return false; } } Class<?> varArgType = m1.getVarArgType(); for (int i = paramsLength1 - 1; i < paramsLength2; ++i) { if (checkApplicable(varArgType, paramTypes2[i]) == null) { return false; } } return true; } } private static ApplicableKind checkApplicableMatchingArity(MethodSignature method, MethodIdentifier id) { if (method.getParameterTypes().length == 0) { return ApplicableKind.MATCHING; } else { ApplicableKind kind = null; final Class<?>[] paramTypes = method.getParameterTypes(); final Class<?>[] argTypes = id.getArgumentTypes(); for (int i = 0; i < paramTypes.length; ++i) { ApplicableKind paramKind = checkApplicable(argTypes[i], paramTypes[i]); if (paramKind == null) { return null; } kind = kind == null ? paramKind : kind.combine(paramKind); } return kind; } } private static ApplicableKind checkApplicableVariableArity(MethodSignature method, MethodIdentifier id) { ApplicableKind kind = null; final Class<?>[] paramTypes = method.getParameterTypes(); final int paramsLength = paramTypes.length; final Class<?>[] argTypes = id.getArgumentTypes(); final int argsLength = argTypes.length; for (int i = 0; i < paramsLength - 1; ++i) { ApplicableKind paramKind = checkApplicable(argTypes[i], paramTypes[i]); if (paramKind == null) { return null; } kind = kind == null ? paramKind : kind.combine(paramKind); } Class<?> varArgType = method.getVarArgType(); for (int i = paramsLength - 1; i < argsLength; ++i) { ApplicableKind paramKind = checkApplicable(argTypes[i], varArgType); if (paramKind == null) { return null; } kind = kind == null ? paramKind : kind.combine(paramKind); } return kind; } private static ApplicableKind checkApplicable(Class<?> from, Class<?> to) { if (to == from) { return ApplicableKind.MATCHING; } if (to.isPrimitive()) { if (from == null) { return null; } if (from.isPrimitive()) { return isWideningPrimitiveConversion(from, to) ? ApplicableKind.SUBTYPING : null; } else { Class<?> unboxingClass = unboxing(from); return (to == unboxingClass || isWideningPrimitiveConversion(unboxingClass, to)) ? ApplicableKind.METHOD_INVOCATION_CONVERSION : null; } } else { if (from == null) { return ApplicableKind.SUBTYPING; } if (from.isPrimitive()) { Class<?> boxingClass = boxing(from); return (to == boxingClass || isWideningReferenceConversion(boxingClass, to)) ? ApplicableKind.METHOD_INVOCATION_CONVERSION : null; } else { return isWideningReferenceConversion(from, to) ? ApplicableKind.SUBTYPING : null; } } } static enum ApplicableKind { MATCHING, SUBTYPING, METHOD_INVOCATION_CONVERSION; ApplicableKind combine(ApplicableKind that) { return this.compareTo(that) > 0 ? this : that; } }}
CachedLookup.java
package canghailan.util;import java.util.Map;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Future;/** * @author canghailan 2012-03-12 18:57 */public class CachedLookup<K, V> implements Lookup<K, V> { private final Lookup<K, V> lookup; private final Map<K, Future<V>> cache; private final ExecutorService executorService; public CachedLookup(Lookup<K, V> lookup, Map<K, Future<V>> cache, ExecutorService executorService) { this.lookup = lookup; this.cache = cache; this.executorService = executorService; } @Override public V find(final K key) { Future<V> value = cache.get(key); if (value == null) { synchronized (cache) { value = cache.get(key); if (value == null) { value = executorService.submit(new Callable<V>() { @Override public V call() throws Exception { return lookup.find(key); } }); cache.put(key, value); } } } try { return value.get(); } catch (Exception e) { return lookup.find(key); } }}
SimpleLRUCache.java
package canghailan.util;import java.util.LinkedHashMap;import java.util.Map;/** * @author canghailan 2012-04-18 18:09 */public class SimpleLRUCache<K, V> extends LinkedHashMap<K, V> { private final int threshold; public SimpleLRUCache(int threshold) { this(threshold, 0.75f); } public SimpleLRUCache(int threshold, float loadFactor) { super((int) (threshold / loadFactor) + 1, loadFactor, true); this.threshold = threshold; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > threshold; }}
用户点评