All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gitee.l0km.aocache.AocacheUtils Maven / Gradle / Ivy

There is a newer version: 0.4.5
Show newest version
package com.gitee.l0km.aocache;

import static com.gitee.l0km.aocache.guava.common.base.Preconditions.checkArgument;
import static com.gitee.l0km.aocache.guava.common.base.Preconditions.checkNotNull;
import static com.gitee.l0km.aocache.ObjectUtils.STRING_VALUEOF_FUN;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;

import com.gitee.l0km.aocache.annotations.AoCacheable;
import com.gitee.l0km.aocache.config.CacheConfig;
import com.gitee.l0km.aocache.guava.common.base.CharMatcher;
import com.gitee.l0km.aocache.guava.common.base.Function;
import com.gitee.l0km.aocache.guava.common.base.Joiner;
import com.gitee.l0km.aocache.guava.common.base.Predicates;
import com.gitee.l0km.aocache.guava.common.base.Strings;
import com.gitee.l0km.aocache.guava.common.collect.FluentIterable;
import com.gitee.l0km.aocache.guava.common.collect.Iterables;
import com.gitee.l0km.aocache.guava.common.collect.Lists;

class AocacheUtils implements AocacheConstant{
    // --------------------------------------------------------- Private Members
	static  V computeIfAbsent(ConcurrentMapmap, K key,
			ThrowingFunction mappingFunction) throws E {
	    Objects.requireNonNull(mappingFunction);
	    V v, newValue;
	    return ((v = map.get(key)) == null &&
	            (newValue = mappingFunction.apply(key)) != null &&
	            (v = map.putIfAbsent(key, newValue)) == null) ? newValue : v;
	}
	/**
	 * 根据输入参数查找类型({@code clazz} )的方法或构造方法({@link Executable})
	 * @param clazz 目标类型
	 * @param methodName 方法名,为空则查找类的构造方法(Constructor)
	 * @param parameterTypes 方法或构造方法的参数类型数组
	 * @throws NoSuchMethodException
	 */
	static Executable getMatchingAccessibleExecutable(Classclazz,String methodName, Class[]parameterTypes) throws NoSuchMethodException {
		Executable executable;
        if (null == parameterTypes) {
        	parameterTypes = EMPTY_CLASS_PARAMETERS;
        }
		if(Strings.isNullOrEmpty(methodName)) {
			executable = getMatchingAccessibleConstructor(clazz,parameterTypes);
		}else {
			executable = getMatchingAccessibleMethod(clazz,methodName,parameterTypes);
		}
       if (null == executable) {
            throw new NoSuchMethodException(
                "No such accessible constructor on object: " + clazz.getName()+toMemberString(null,parameterTypes));
        }
       return executable;
	}
    /**
     * 

Returns new instance of klazz created using the actual arguments args. * The formal parameter types are inferred from the actual values of args. * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.

* *

The signatures should be assignment compatible.

* * @param the type of the object to be constructed * @param klass the class to be constructed. * @param args actual argument array. May be null (this will result in calling the default constructor). * @return new instance of klazz * * @throws NoSuchMethodException If the constructor cannot be found * @throws IllegalAccessException If an error occurs accessing the constructor * @throws InvocationTargetException If an error occurs invoking the constructor * @throws InstantiationException If an error occurs instantiating the class * * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) */ public static T invokeConstructor(Class klass, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (null == args) { args = EMPTY_OBJECT_ARRAY; } int arguments = args.length; Class parameterTypes[] = new Class[arguments]; for (int i = 0; i < arguments; i++) { parameterTypes[i] = args[i].getClass(); } return invokeConstructor(klass, args, parameterTypes); } /** *

Returns new instance of klazz created using constructor * with signature parameterTypes and actual arguments args.

* *

The signatures should be assignment compatible.

* * @param the type of the object to be constructed * @param klass the class to be constructed. * @param args actual argument array. May be null (this will result in calling the default constructor). * @param parameterTypes parameter types array * @return new instance of klazz * * @throws NoSuchMethodException if matching constructor cannot be found * @throws IllegalAccessException thrown on the constructor's invocation * @throws InvocationTargetException thrown on the constructor's invocation * @throws InstantiationException thrown on the constructor's invocation * @see Constructor#newInstance */ public static T invokeConstructor( Class klass, Object[] args, Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { if (parameterTypes == null) { parameterTypes = EMPTY_CLASS_PARAMETERS; } if (args == null) { args = EMPTY_OBJECT_ARRAY; } Constructor ctor = getMatchingAccessibleConstructor(klass, parameterTypes); if (null == ctor) { throw new NoSuchMethodException( "No such accessible constructor on object: " + klass.getName()+toMemberString(null,parameterTypes)); } return ctor.newInstance(args); } // -------------------------------------------------------- Private Methods /** *

Find an accessible constructor with compatible parameters. * Compatible parameters mean that every method parameter is assignable from * the given parameters. In other words, it finds constructor that will take * the parameters given.

* *

First it checks if there is constructor matching the exact signature. * If no such, all the constructors of the class are tested if their signatures * are assignment compatible with the parameter types. * The first matching constructor is returned.

* * @param the type of the class to be inspected * @param clazz find constructor for this class * @param parameterTypes find method with compatible parameters * @return a valid Constructor object. If there's no matching constructor, returns null. */ static Constructor getMatchingAccessibleConstructor( Class clazz, Class[] parameterTypes) { // see if we can find the method directly // most of the time this works and it's much faster try { Constructor ctor = clazz.getConstructor(parameterTypes); try { // // XXX Default access superclass workaround // // When a public class has a default access superclass // with public methods, these methods are accessible. // Calling them from compiled code works fine. // // Unfortunately, using reflection to invoke these methods // seems to (wrongly) to prevent access even when the method // modifer is public. // // The following workaround solves the problem but will only // work from sufficiently privilages code. // // Better workarounds would be greatfully accepted. // ctor.setAccessible(true); } catch (SecurityException se) { /* SWALLOW, if workaround fails don't fret. */ } return ctor; } catch (NoSuchMethodException e) { /* SWALLOW */ } // search through all methods int paramSize = parameterTypes.length; Constructor[] ctors = clazz.getDeclaredConstructors(); for (int i = 0, size = ctors.length; i < size; i++) { // compare parameters Class[] ctorParams = ctors[i].getParameterTypes(); int ctorParamSize = ctorParams.length; if (ctorParamSize == paramSize) { boolean match = true; for (int n = 0; n < ctorParamSize; n++) { if (!isAssignmentCompatible( ctorParams[n], parameterTypes[n])) { match = false; break; } } if (match) { // get accessible version of method Constructor ctor = ctors[i]; if (ctor != null) { try { ctor.setAccessible(true); } catch (SecurityException se) { /* Swallow SecurityException * TODO: Why? */ } @SuppressWarnings("unchecked") // Class.getConstructors() actually returns constructors // of type T, so it is safe to cast. Constructor typedCtor = (Constructor) ctor; return typedCtor; } } } } return null; } /** *

Determine whether a type can be used as a parameter in a method invocation. * This method handles primitive conversions correctly.

* *

In order words, it will match a Boolean to a boolean, * a Long to a long, * a Float to a float, * a Integer to a int, * and a Double to a double. * Now logic widening matches are allowed. * For example, a Long will not match a int. * * @param parameterType the type of parameter accepted by the method * @param parameterization the type of parameter being tested * * @return true if the assignment is compatible. */ private static final boolean isAssignmentCompatible(Class parameterType, Class parameterization) { // try plain assignment if (parameterType.isAssignableFrom(parameterization)) { return true; } if (parameterType.isPrimitive()) { // this method does *not* do widening - you must specify exactly // is this the right behaviour? Class parameterWrapperClazz = getPrimitiveWrapper(parameterType); if (parameterWrapperClazz != null) { return parameterWrapperClazz.equals(parameterization); } } return false; } /** *

Invoke a named method whose parameter type matches the object type.

* *

The behaviour of this method is less deterministic * than {@link #invokeExactMethod(Object object,String methodName,Object [] args)}. * It loops through all methods with names that match * and then executes the first it finds with compatible parameters.

* *

This method supports calls to methods taking primitive parameters * via passing in wrapping classes. So, for example, a Boolean class * would match a boolean primitive.

* *

This is a convenient wrapper for * {@link #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. *

* * @param object invoke method on this object * @param methodName get method with this name * @param args use these arguments - treat null as empty array (passing null will * result in calling the parameterless method with name {@code methodName}). * @return The value returned by the invoked method * * @throws NoSuchMethodException if there is no such accessible method * @throws InvocationTargetException wraps an exception thrown by the * method invoked * @throws IllegalAccessException if the requested method is not accessible * via reflection */ public static Object invokeMethod( Object object, String methodName, Object[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (args == null) { args = EMPTY_OBJECT_ARRAY; } int arguments = args.length; Class[] parameterTypes = new Class[arguments]; for (int i = 0; i < arguments; i++) { parameterTypes[i] = args[i].getClass(); } return invokeMethod(object, methodName, args, parameterTypes); } /** *

Invoke a named method whose parameter type matches the object type.

* *

The behaviour of this method is less deterministic * than {@link * #invokeExactMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. * It loops through all methods with names that match * and then executes the first it finds with compatible parameters.

* *

This method supports calls to methods taking primitive parameters * via passing in wrapping classes. So, for example, a Boolean class * would match a boolean primitive.

* * * @param object invoke method on this object * @param methodName get method with this name * @param args use these arguments - treat null as empty array (passing null will * result in calling the parameterless method with name {@code methodName}). * @param parameterTypes match these parameters - treat null as empty array * @return The value returned by the invoked method * * @throws NoSuchMethodException if there is no such accessible method * @throws InvocationTargetException wraps an exception thrown by the * method invoked * @throws IllegalAccessException if the requested method is not accessible * via reflection */ public static Object invokeMethod( Object object, String methodName, Object[] args, Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { if (parameterTypes == null) { parameterTypes = EMPTY_CLASS_PARAMETERS; } if (args == null) { args = EMPTY_OBJECT_ARRAY; } Method method = getMatchingAccessibleMethod( object.getClass(), methodName, parameterTypes); if (method == null) { throw new NoSuchMethodException("No such accessible method: " + methodName + "() on object: " + object.getClass().getName()+" with matched parameter type: "+toMemberString(methodName,parameterTypes)); } return method.invoke(object, args); } /** *

Find an accessible method that matches the given name and has compatible parameters. * Compatible parameters mean that every method parameter is assignable from * the given parameters. * In other words, it finds a method with the given name * that will take the parameters given.

* *

This method is slightly undeterministic since it loops * through methods names and return the first matching method.

* *

This method is used by * {@link * #invokeMethod(Object object,String methodName,Object [] args,Class[] parameterTypes)}. * *

This method can match primitive parameter by passing in wrapper classes. * For example, a Boolean will match a primitive boolean * parameter. * * @param clazz find method in this class * @param methodName find method with this name * @param parameterTypes find method with compatible parameters * @return The accessible method */ static Method getMatchingAccessibleMethod( Class clazz, String methodName, Class[] parameterTypes) { // see if we can find the method directly // most of the time this works and it's much faster try { Method method = clazz.getMethod(methodName, parameterTypes); setMethodAccessible(method); // Default access superclass workaround return method; } catch (NoSuchMethodException e) { /* SWALLOW */ } // search through all methods int paramSize = parameterTypes.length; Method bestMatch = null; Method[] methods = allDeclaredMethodOf(clazz); float bestMatchCost = Float.MAX_VALUE; float myCost = Float.MAX_VALUE; for (int i = 0, size = methods.length; i < size ; i++) { if (methods[i].getName().equals(methodName)) { // compare parameters Class[] methodsParams = methods[i].getParameterTypes(); int methodParamSize = methodsParams.length; if (methodParamSize == paramSize) { boolean match = true; for (int n = 0 ; n < methodParamSize; n++) { if (!isAssignmentCompatible(methodsParams[n], parameterTypes[n])) { match = false; break; } } if (match) { // get accessible version of method Method method = methods[i]; if (method != null) { setMethodAccessible(method); // Default access superclass workaround myCost = getTotalTransformationCost(parameterTypes,method.getParameterTypes()); if ( myCost < bestMatchCost ) { bestMatch = method; bestMatchCost = myCost; } } } } } } return bestMatch; } // Superclasses/Superinterfaces // ---------------------------------------------------------------------- /** *

Gets a {@code List} of superclasses for the given class.

* * @param cls the class to look up, may be {@code null} * @return the {@code List} of superclasses in order going up from this one * {@code null} if null input */ private static List> getAllSuperclasses(final Class cls) { if (cls == null) { return null; } final List> classes = new ArrayList<>(); Class superclass = cls.getSuperclass(); while (superclass != null) { classes.add(superclass); superclass = superclass.getSuperclass(); } return classes; } private static Method[] allDeclaredMethodOf(Class clazz) { // Address methods in superclasses FluentIterable methods = FluentIterable.from(Lists.newArrayList(clazz.getDeclaredMethods())); for (final Class superClass : getAllSuperclasses(clazz)) { methods = methods.append(superClass.getDeclaredMethods()); } return methods.toArray(Method.class); } /** * Try to make the method accessible * @param method The source arguments */ private static void setMethodAccessible(Method method) { if (!method.isAccessible()) { method.setAccessible(true); } } /** * Returns the sum of the object transformation cost for each class in the source * argument list. * @param srcArgs The source arguments * @param destArgs The destination arguments * @return The total transformation cost */ private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { float totalCost = 0.0f; for (int i = 0; i < srcArgs.length; i++) { Class srcClass, destClass; srcClass = srcArgs[i]; destClass = destArgs[i]; totalCost += getObjectTransformationCost(srcClass, destClass); } return totalCost; } /** * Gets the number of steps required needed to turn the source class into the * destination class. This represents the number of steps in the object hierarchy * graph. * @param srcClass The source class * @param destClass The destination class * @return The cost of transforming an object */ private static float getObjectTransformationCost(Class srcClass, Class destClass) { float cost = 0.0f; while (srcClass != null && !destClass.equals(srcClass)) { if (destClass.isPrimitive()) { Class destClassWrapperClazz = getPrimitiveWrapper(destClass); if (destClassWrapperClazz != null && destClassWrapperClazz.equals(srcClass)) { cost += 0.25f; break; } } if (destClass.isInterface() && isAssignmentCompatible(destClass,srcClass)) { // slight penalty for interface match. // we still want an exact match to override an interface match, but // an interface match should override anything where we have to get a // superclass. cost += 0.25f; break; } cost++; srcClass = srcClass.getSuperclass(); } /* * If the destination class is null, we've travelled all the way up to * an Object match. We'll penalize this by adding 1.5 to the cost. */ if (srcClass == null) { cost += 1.5f; } return cost; } /** * Gets the wrapper object class for the given primitive type class. * For example, passing boolean.class returns Boolean.class * @param primitiveType the primitive type class for which a match is to be found * @return the wrapper type associated with the given primitive * or null if no match is found */ private static Class getPrimitiveWrapper(Class primitiveType) { // does anyone know a better strategy than comparing names? if (boolean.class.equals(primitiveType)) { return Boolean.class; } else if (float.class.equals(primitiveType)) { return Float.class; } else if (long.class.equals(primitiveType)) { return Long.class; } else if (int.class.equals(primitiveType)) { return Integer.class; } else if (short.class.equals(primitiveType)) { return Short.class; } else if (byte.class.equals(primitiveType)) { return Byte.class; } else if (double.class.equals(primitiveType)) { return Double.class; } else if (char.class.equals(primitiveType)) { return Character.class; } else { return null; } } private static String typeNameOf(Class clazz) { if (clazz.isArray()) { try { Class cl = clazz; int dimensions = 0; while (cl.isArray()) { dimensions++; cl = cl.getComponentType(); } StringBuilder sb = new StringBuilder(); sb.append(cl.getName()); for (int i = 0; i < dimensions; i++) { sb.append("[]"); } return sb.toString(); } catch (Throwable e) { /*FALLTHRU*/ } } return clazz.getName(); } static String toMemberString(String name, Class[]parameterTypes) { try { StringBuilder sb = new StringBuilder(); if(null!=name) { sb.append(name); } sb.append('('); for (int j = 0; j < parameterTypes.length; j++) { sb.append(typeNameOf(parameterTypes[j])); if (j < (parameterTypes.length - 1)) sb.append(","); } sb.append(')'); return sb.toString(); } catch (Exception e) { return "<" + e + ">"; } } /** * Assert that the given String contains valid text content; that is, it must not * be {@code null} and must contain at least one non-whitespace character. *
Assert.hasText(name, "'name' must not be empty");
* @param text the String to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the text does not contain valid text content * @see StringUtils#hasText */ static void hasText(String str, String message) { checkArgument((str != null && !str.isEmpty() && CharMatcher.whitespace().negate().indexIn(str)>=0)); } /** * Convert a {@code String} array into a delimited {@code String} (e.g. CSV). *

Useful for {@code toString()} implementations. * @param arr the array to display (potentially {@code null} or empty) * @param delim the delimiter to use (typically a ",") * @return the delimited {@code String} */ static String arrayToDelimitedString(Object[] arr, String delim) { if ((arr == null || arr.length == 0)) { return ""; } if (arr.length == 1) { return ObjectUtils.nullSafeToString(arr[0]); } return Joiner.on(delim).join(Iterables.transform(Arrays.asList(arr), STRING_VALUEOF_FUN)); } /** * Convert a {@code String} array into a comma delimited {@code String} * (i.e., CSV). *

Useful for {@code toString()} implementations. * @param arr the array to display (potentially {@code null} or empty) * @return the delimited {@code String} */ static String arrayToCommaDelimitedString(Object[] arr) { return arrayToDelimitedString(arr, ","); } static Class[] nonnullTypesOf(Object... args){ if(null != args) { Class[] argTypes = new Class[args.length]; for(int i=0;i[] nonnullTypesOf(Class...argTypes){ if(null != argTypes) { for(int i=0;i * 优先查找 {@link AoCacheable}注解,如果没有找到, * 则查找所有注解, * 如果找到第一个将{@link AoCacheable}作为元注解的注解, * 则将之与元注解一起合成为{@link AoCacheable}注解返回 * 仍然没有找到则返回{@code null} * @param element 可注解实例 */ static AoCacheable readAoCacheable(AnnotatedElement element) { /** 从注解中获取缓存配置 */ AoCacheable annot = READ_AOCACHEABLE_FUN.apply(element); if(null != annot) { return annot; } return FluentIterable.from(element.getAnnotations()) .transform(READ_AOCACHEABLE_FUN) .filter(Predicates.notNull()) .first() .orNull(); } private static final Function READ_AOCACHEABLE_FUN = new Function(){ @Override public AoCacheable apply(Object input) { if(input instanceof Annotation) { AoCacheable annot = ((Annotation)input).annotationType().getAnnotation(AoCacheable.class); if(null!=annot) { CacheConfig meta= new CacheConfig(annot); meta.putAll(new AnnotationAttributes((Annotation)input)); return meta.synthesize(AoCacheable.class); } }else if(input instanceof AnnotatedElement) { return ((AnnotatedElement)input).getAnnotation(AoCacheable.class); } return null; }}; }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy