package com.timeyang.jkes.core.util; import com.timeyang.jkes.core.exception.IllegalMemberAccessException; import com.timeyang.jkes.core.exception.JkesException; import com.timeyang.jkes.core.exception.ReflectiveInvocationTargetException; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Reflection Utils * * @author chaokunyang */ public class ReflectionUtils { /** * @param method {@link Method} object * @return the actual type parameters used in the source code. Return <strong>empty</strong> list if no parametrized type */ public static List<String> getReturnTypeParameters(Method method) { List<String> typeParameters = new ArrayList<>(); Type type = method.getGenericReturnType(); String typeName = type.getTypeName(); // ex: java.util.List<com.timeyang.search.entity.Person>, java.lang.Long Pattern p = Pattern.compile("<((\\S+\\.?),?\\s*)>"); Matcher m = p.matcher(typeName); while (m.find()) { typeParameters.add(m.group(2)); } return typeParameters; } public static String getTypeName(Method method) { Type type = method.getGenericReturnType(); String typeName = type.getTypeName(); // ex: java.util.List<com.timeyang.search.entity.Person>, java.lang.Long return typeName; } /** * If return type is genetic type, then return last parameterized type, else return the formal return type name of the method represented by this {@code Method} object. * @param method {@link Method} method * @return If return type is genetic type, then return last parameterized type, else return the formal return type name of the method represented by this {@code Method} object. */ public static String getInnermostType(Method method) { Type type = method.getGenericReturnType(); String typeName = type.getTypeName(); // ex: java.util.List<com.timeyang.search.entity.Person>, java.lang.Long String[] types = typeName.split(",\\s*|<|<|>+"); return types[types.length - 1]; } /** * If return type is genetic type, then return class of last parameterized type, else return the formal class of return type of the method represented by this {@code Method} object. * @param method {@link Method} method * @return If return type is genetic type, then return class of last parameterized type, else return the formal class of return type of the method represented by this {@code Method} object. */ public static Class<?> getInnermostTypeClass(Method method) { Class<?> clazz; try { clazz = Class.forName(getInnermostType(method)); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return clazz; } /** * If the field object is a generic type, return innermost type; else return field type name directly * @param field Field object * @return If the field object is a generic type, return innermost type; else return field type name directly */ public static String getInnermostType(Field field) { Type type = field.getGenericType(); String typeName = type.getTypeName(); // ex: java.util.List<com.timeyang.search.entity.Person>, java.lang.Long String[] types = typeName.split(",\\s*|<|<|>+"); return types[types.length - 1]; } /** * If the field object is a generic type, return innermost type class; else return field class directly * @param field Field object * @return If the field object is a generic type, return innermost type class; else return field class directly */ public static Class<?> getInnermostTypeClass(Field field) { Class<?> clazz; try { clazz = Class.forName(getInnermostType(field)); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return clazz; } public static String getFieldNameForGetter(String methodName) { Asserts.notBlank(methodName, "methodName must have content"); Asserts.check(methodName.startsWith("get") || methodName.startsWith("is"), "the method is not a getter method"); if(methodName.startsWith("get")) { char c[] = methodName.toCharArray(); c[3] = Character.toLowerCase(c[3]); return String.valueOf(Arrays.copyOfRange(c, 3, c.length)); }else { char c[] = methodName.toCharArray(); c[2] = Character.toLowerCase(c[2]); return String.valueOf(Arrays.copyOfRange(c, 2, c.length)); } } public static String getFieldNameForGetter(Method method) { String methodName = method.getName(); return getFieldNameForGetter(methodName); } /** * Invoke specified method in target class or super class, regardless of access level of method * * @param target target object * @param methodName the name of the method * @param parameterTypes parameter type array * @param params method params * @return method invoke result */ public static Object invokeMethod(Object target, String methodName, Class<?>[] parameterTypes, Object... params) { Class<?> clazz = target.getClass(); do { try { // getMethods() return only public methods, though includes super methods // getDeclaredMethods() return current class all methods, include non public methods, but doesn't include super methods, so clazz = clazz.getSuperclass() is needed. Method method = clazz.getDeclaredMethod(methodName, parameterTypes); method.setAccessible(true); return method.invoke(target, params); } catch (NoSuchMethodException e) { clazz = clazz.getSuperclass(); if(clazz == null) throw new RuntimeException(new NoSuchMethodException(target.getClass() + " and all its super class doesn't have " + methodName + " with parameterTypes: " + Arrays.toString(parameterTypes))); } catch (IllegalAccessException e) { throw new IllegalMemberAccessException(e); } catch (InvocationTargetException e) { throw new ReflectiveInvocationTargetException(e); } } while (true); } /** * Get value of annotated field. event if field is private * @param target current object * @param annotation annotation * @return value of annotated field */ public static Object getAnnotatedFieldValue(Object target, Class<? extends Annotation> annotation) { Class<?> clazz = target.getClass(); do { Field[] fields = clazz.getDeclaredFields(); for(Field field : fields) { if(field.isAnnotationPresent(annotation)) { field.setAccessible(true); try { return field.get(target); } catch (IllegalAccessException e) { throw new JkesException(e); } } } clazz = clazz.getSuperclass(); }while (clazz != null); return null; } /** * Get annotated field. The field can be in parent field * * @param clazz clazz * @param fieldName fieldName * @param annotation annotation * @return annotated field. */ public static Field getAnnotatedField(Class<?> clazz, String fieldName, Class<? extends Annotation> annotation) { do { Field[] fields = clazz.getDeclaredFields(); for(Field field : fields) { if(Objects.equals(fieldName, field.getName()) && field.isAnnotationPresent(annotation)) { return field; } } clazz = clazz.getSuperclass(); }while (clazz != null); return null; } /** * Get annotated field. The field can be in parent field * * @param clazz clazz * @param fieldName fieldName * @param annotationClass annotationClass * @return annotated field. */ public static <T extends Annotation> T getFieldAnnotation(Class<?> clazz, String fieldName, Class<T> annotationClass) { Field annotatedField = getAnnotatedField(clazz, fieldName, annotationClass); if (annotatedField != null) { return annotatedField.getAnnotation(annotationClass); } return null; } /** * Get return value of annotated method * @param target current object * @param annotation annotation * @return return value of annotated method */ public static Object getAnnotatedMethodReturnValue(Object target, Class<? extends Annotation> annotation) { Class<?> clazz = target.getClass(); do { Method[] methods = target.getClass().getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(annotation)) { try { return method.invoke(target); } catch (IllegalAccessException | InvocationTargetException e) { throw new JkesException(e); } } } clazz = clazz.getSuperclass(); }while (clazz != null); return null; } }