Tuesday, January 16, 2018

how to get generic class method return type and parameters class type by dynamic java reflect.

1.What

i want to get generic class method return type and method parameters and parameters class type by dynamic java reflect.

For example code:
////////////////////////////////////////////////////////////

class UserDomain {}

public interface BaseService<T> {
    T selectOne(T record); 
}

public interface UserService extends BaseService<UserDomain> { }

public interface MyMapper<T> { }

public interface UserDomainMapper extends MyMapper<UserDomain> { }

public abstract class BaseServiceImpl<MAP extends MyMapper<T>,T extends Object> implements BaseService<T> { 
    @Override
    public T selectOne(T record){
    return ...;
    }
}

@Service("userService")
public class UserServiceImpl extends BaseServiceImpl<UserDomainMapper,UserDomain> implements UserService { }

////////////////////////////////////////////////////////////


now, i have one UserService instance, and have it's selectOne method of java.lang.reflect.Method, by rpc proxy, i have the T result json string. how i unmarsher it to UserDomain?

2. how

Thanks google search one reference result, i have GenericClassHelper(make small change from GenericClass):

package com.myzc98.server.helper;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


/**
 * A wrapper around reflection to resolve generics.
 *
 * @author Simone Gianni <simoneg@apache.org>
 */
// TODO there is vast space for caching and optimizations here!
public class GenericClassHelper {

  private Class<?> myclass = null;

  private Map<String, Class<?>> genericMap = new HashMap<String, Class<?>>();

  private Map<String, String> reverseIntermediate = new HashMap<String, String>();

  private static void warnMsg(String msg) {
//   String msg = "Impossible situation, it's a bug : {0} generic is both {1} and bound to {2}" + target + genericMap.get(target) + search;
//  System.out.println(msg);
//  throw new RuntimeException(msg);
  }

  public static GenericClassHelper forClass(Class<?> concrete) {
    return new GenericClassHelper(concrete);
  }

  public static GenericClassHelper forField(Field field) {
    return forGenericType(field.getGenericType());
  }

  public static GenericClassHelper forReturnType(Method method) {
    return forGenericType(method.getGenericReturnType());
  }

  public static GenericClassHelper forParameter(Method method, int index) {
    return forGenericType(method.getGenericParameterTypes()[index]);
  }

  public static GenericClassHelper forGenericType(Type type) {
    if (type instanceof Class) {
      return forClass((Class<?>) type);
    } else if (type instanceof ParameterizedType) {
      return new GenericClassHelper((ParameterizedType) type);
    } else {
      return forClass(Object.class);
      // throw new MagmaException("Dont know how to build a GenericClassHelper out of
      // {0}", type.getClass());
    }
  }

  private GenericClassHelper(Class<?> concrete) {
//    TypeVariable<?>[] parameters = concrete.getTypeParameters();
    // if (parameters.length > 0) throw new MagmaException("Cannot parse {0}, it
    // is a generic class, use a concrete class instead", concrete);
    myclass = concrete;
    recurse(concrete);
    coalesceMap();
  }

  private GenericClassHelper(ParameterizedType type) {
    myclass = (Class<?>) type.getRawType();
    recurse(myclass, myclass, type);
    coalesceMap();
  }

  private void coalesceMap() {
    int cnt = reverseIntermediate.size();
    while (reverseIntermediate.size() > 0 && cnt > 0) {
      for (Iterator<Map.Entry<String, String>> iterator = this.reverseIntermediate.entrySet()
          .iterator(); iterator.hasNext();) {
        Map.Entry<String, String> entry = iterator.next();
        String target = entry.getValue();
        String search = entry.getKey();
        Class<?> clazz = genericMap.get(search);
        if (clazz == null)
          continue;
        if (genericMap.containsKey(target))
        warnMsg("Impossible situation, it's a bug : {0} generic is both {1} and bound to {2}" + target + genericMap.get(target) + search);
       
        genericMap.put(target, clazz);
        iterator.remove();
      }
      cnt--;
    }
    if (reverseIntermediate.size() > 0) {
      for (Iterator<Map.Entry<String, String>> iterator = this.reverseIntermediate.entrySet()
          .iterator(); iterator.hasNext();) {
        Map.Entry<String, String> entry = iterator.next();
        String target = entry.getValue();
        String search = entry.getKey();
        Class<?> clazz = genericMap.get(search);
        if (clazz == null)
          clazz = Object.class;
        if (genericMap.containsKey(target))
        warnMsg("Impossible situation, it's a bug : {0} generic is both {1} and bound to {2}"+ target+ genericMap.get(target)+ search);
        genericMap.put(target, clazz);
      }
    }
  }

  public Class<?> getBaseClass() {
    return this.myclass;
  }

  private void recurse(Class<?> clazz, Class<?> simplesup, ParameterizedType partype) {
    Type[] typeArguments = partype.getActualTypeArguments();
    TypeVariable<?>[] parameters = simplesup.getTypeParameters();

    for (int i = 0; i < typeArguments.length; i++) {
      if (typeArguments[i] instanceof Class) {
      String target = clazz.getName() + "--" + simplesup.getName() + "--" + parameters[i].getName();
      if (genericMap.containsKey(target))
          warnMsg("Impossible situation, it's a bug : {0} generic is both {1}"+ target+ genericMap.get(target));
   
        genericMap.put(target, (Class<?>) typeArguments[i]);
      } else if (typeArguments[i] instanceof TypeVariable) {
      String target = clazz.getName() + "--" + ((TypeVariable<?>) typeArguments[i]).getName();
      if (reverseIntermediate.containsKey(target))
            warnMsg("Impossible situation, it's a bug : {0} generic is both {1}"+ target+ reverseIntermediate.get(target));
        reverseIntermediate.put(target, simplesup.getName() + "--"
            + parameters[i].getName());
      }
    }

    recurse(simplesup);
  }

  private void recurse(Class<?> clazz) {
  recurse(clazz, null);
  }

  private void recurse(Class<?> clazz, Class<?> parent) {
  if(parent == null) {
  parent = clazz;
  }
  Type[] types = clazz.getGenericInterfaces();
  for(Type t : types) {
  if(t instanceof ParameterizedType ) {
  recurse(clazz, (Class<?>)((ParameterizedType) t).getRawType(), (ParameterizedType) t);
  if(!parent.equals(clazz))
  recurse(parent, (Class<?>)((ParameterizedType) t).getRawType(), (ParameterizedType) t);
  }else if(t instanceof TypeVariable ) {
  recurse(clazz, (Class<?>)t, (ParameterizedType) t);
  if(!parent.equals(clazz))
  recurse(parent, (Class<?>)t, (ParameterizedType) t);
  }else {
  recurse((Class<?>)t, clazz);
  }
  }

    Type supclass = clazz.getGenericSuperclass();
    Class<?> simplesup = clazz.getSuperclass();
    if (supclass == null)
      return;
    if (supclass instanceof ParameterizedType) {
      recurse(clazz, simplesup, (ParameterizedType) supclass);
  if(!parent.equals(clazz))
  recurse(parent, simplesup, (ParameterizedType) supclass);
    } else {
        recurse(parent, simplesup);
        if(!parent.equals(simplesup))
        recurse(simplesup);
    } 
  }

  /**
   * Return real, "generics dereferenced", parameter types for the given method.
   *
   * @param method
   *          The method to analyze
   * @return The real classes, dereferencing generics
   */
  public GenericClassHelper[] getParameterTypes(Method method) {
    Class<?> declaring = method.getDeclaringClass();
    String declname = declaring.getName();

    Type[] parameterTypes = method.getGenericParameterTypes();
    GenericClassHelper[] ret = new GenericClassHelper[parameterTypes.length];
    for (int i = 0; i < parameterTypes.length; i++) {
      Type type = parameterTypes[i];
      if (type instanceof Class) {
        ret[i] = forClass((Class<?>) type);
      } else if (type instanceof TypeVariable) {
        String name = ((TypeVariable<?>) type).getName();
        Class<?> sub = genericMap.get(declname + "--" + name);
        if (sub == null)
          sub = Object.class;
        ret[i] = forClass(sub);
      } else {
        ret[i] = forGenericType(type);
      }
    }
    return ret;
  }

  public GenericClassHelper resolveType(Class<?> cls, Type type) {
    if (type instanceof Class) {
      return forClass((Class<?>) type);
    } else if (type instanceof TypeVariable) {
      GenericDeclaration gd = ((TypeVariable<?>) type).getGenericDeclaration();
      Class<?> acclass = null;
      if (gd instanceof Class) {
        acclass = (Class<?>) gd;
      } else if (gd instanceof Method) {
        acclass = ((Method) gd).getDeclaringClass();
      } else if (gd instanceof Constructor) {
        acclass = ((Constructor<?>) gd).getDeclaringClass();
      }
      String name = ((TypeVariable<?>) type).getName();
      Class<?> objClass = genericMap.get(cls.getName() + "--" + acclass.getName() + "--" + name);
      if(objClass != null)
      return forClass(objClass);
      objClass = genericMap.get(cls.getName() + "--" + name);
      if(objClass != null)
      return forClass(objClass);
      objClass = genericMap.get(acclass.getName() + "--" + name);
      return forClass(objClass);
    } else {
      return forGenericType(type);
    }
  }

  /**
   * Search for all occurrencies of a specific method.
   * <p>
   * The type parameters passed in may be Class or null. If they are null, that
   * means that we don't know which class they should be, if they are a class,
   * that means we are searching for that class, and the comparison is made on
   * dereferenced generics.
   * </p>
   * <p>
   * Specifying no parameter types explicitly means "a method without
   * parameters".
   * </p>
   *
   * @param name
   *          The name of the method
   * @param parameterTypes
   *          The types of the parameters
   * @return A list of {@link MethodDef}, ordered with methods from current
   *         class first, then method from superclass and so on.
   */
  public List<MethodDef> findMethods(String name, Class<?>... parameterTypes) {
    List<MethodDef> founds = new ArrayList<MethodDef>();
    findMethods(founds, myclass, name, parameterTypes);
    return founds;
  }

  private void findMethods(List<MethodDef> founds, Class<?> current, String name, Class<?>... parameterTypes) {
    while (current != null) {
      Method[] methods = current.getDeclaredMethods();
      for (Method method : methods) {
        if (!method.isBridge() && !method.isSynthetic() && method.getName().equals(name)) {
          Type[] types = method.getGenericParameterTypes();
          if (types.length == parameterTypes.length) {
            GenericClassHelper[] GenericClassHelperes = getParameterTypes(method);
            Class<?>[] classes = toRawClasses(GenericClassHelperes);
            boolean good = true;
            for (int i = 0; i < types.length; i++) {
              if (parameterTypes[i] != null) {
                if (!classes[i].equals(parameterTypes[i])) {
                if (!Object.class.equals(parameterTypes[i])) {
                  good = false;
                  break;
                }
                }
              }
            }
            if (good) {
              MethodDef def = new MethodDef(method, GenericClassHelperes);
              if (!founds.contains(def))
                founds.add(def);
            }
          }
        }
      }
   
      Class<?>[] classes = current.getInterfaces();
      for(Class<?> cls : classes) {
      findMethods(founds, cls, name, parameterTypes);
      }
   
      current = current.getSuperclass();
    }
  }

  public static Class<?>[] toRawClasses(GenericClassHelper[] genclasses) {
    Class<?>[] ret = new Class<?>[genclasses.length];
    for (int i = 0; i < genclasses.length; i++) {
      ret[i] = genclasses[i].getBaseClass();
    }
    return ret;
  }

  /**
   * Search for all methods having that name, no matter which parameter they
   * take.
   *
   * @param name
   *          The name of the methods
   * @return A list of {@link MethodDef}, ordered with methods from current
   *         class first, then method from superclass and so on.
   */
  public List<MethodDef> findAllMethods(String name) {
    List<MethodDef> founds = new ArrayList<MethodDef>();
    findAllMethods(founds, myclass, name);
    return founds;
  }

  private void findAllMethods(List<MethodDef> founds, Class<?> current, String name) {
    while (current != null) {
      Method[] methods = current.getDeclaredMethods();
      for (Method method : methods) {
        if (!method.isBridge() && !method.isSynthetic() && method.getName().equals(name)) {
          MethodDef def = new MethodDef(method);
          if (!founds.contains(def))
            founds.add(def);
        }
      }

      Class<?>[] classes = current.getInterfaces();
      for(Class<?> cls : classes) {
      findAllMethods(founds, cls, name);
      }
   
      current = current.getSuperclass();
   
    }
  }

  public List<MethodDef> getMethods() {
    List<MethodDef> founds = new ArrayList<MethodDef>();
    Class<?> current = myclass;
    while (current != null) {
      Method[] methods = current.getDeclaredMethods();
      for (Method method : methods) {
        if (!method.isBridge() && !method.isSynthetic()) {
          MethodDef def = new MethodDef(method);
          if (!founds.contains(def))
            founds.add(def);
        }
      }
      current = current.getSuperclass();
    }
    return founds;
  }

  @Override
  public boolean equals(Object obj) {
    if (!(obj instanceof GenericClassHelper))
      return false;
    GenericClassHelper oth = (GenericClassHelper) obj;
    return this.getBaseClass().equals(oth.getBaseClass());
  }

  public class MethodDef {
    private Method method = null;

    private GenericClassHelper[] params = null;

    MethodDef(Method m) {
      this.method = m;
    }

    MethodDef(Method m, GenericClassHelper[] params) {
      this.method = m;
      this.params = params;
    }

    public String getName() {
      return this.method.getName();
    }

    public Method getBaseMethod() {
      return this.method;
    }

    public GenericClassHelper[] getParameterTypes() {
      if (this.params == null) {
        this.params = GenericClassHelper.this.getParameterTypes(method);
      }
      return this.params;
    }

    public GenericClassHelper getReturnType() {
      return getReturnType(myclass);
    }

    public GenericClassHelper getReturnType(Class<?> instanceClass) {
      return resolveType(instanceClass, method.getGenericReturnType());
    }

    @Override
    public boolean equals(Object other) {
      if (!(other instanceof MethodDef))
        return false;
      MethodDef oth = (MethodDef) other;
      return (method.getName().equals(oth.method.getName()))
          && (Arrays.equals(getParameterTypes(), oth.getParameterTypes()));
    }

    public Class<?> getDeclaringClass() {
      return this.method.getDeclaringClass();
    }
  }

}

How to used it:

static void testGeneric(Class<?> beanCls, String methodName) {
GenericClassHelper v1 = GenericClassHelper.forClass(beanCls);
java.util.List<GenericClassHelper.MethodDef> methods = v1.findAllMethods(methodName);
GenericClassHelper.MethodDef method = methods.get(0);
GenericClassHelper ret = method.getReturnType();
Class<?> cls = ret.getBaseClass();
System.out.println(beanCls.getName() + " " + methodName);
System.out.println("  ret:" + cls);

GenericClassHelper[] params = method.getParameterTypes();
for(int i=0,c=params.length;i<c;++i) {
System.out.println("   p" + (i+1) + ":" + params[i].getBaseClass() + "\n");
}
}

    testGeneric(UserServiceImpl.class, "selectOne");
    testGeneric(UserService.class, "selectOne");

Last:
Thanks for your readed

Reference:
A wrapper around reflection to resolve generics. : Generic « Reflection « Java

No comments: