远程方法动态调用之参数的处理

来源:互联网 发布:万国数据 员工待遇怎样 编辑:程序博客网 时间:2024/05/29 13:53
最近处理了一个通过类名、方法名、参数值远程调用的功能。在处理的过程中,使用反射的方式进行动态调用,其中的难点是对泛型的处理,特别是多层泛型的情况。现将开发过程中的思路以及遇到的问题进行总结。
方法声明:public synchronized String invoke(String beanName, String methodName, List<Map<String,String>> params)
        beanName:调用的Service在Spring容器中的名称
        methodName:调用的Service方法名
        params:调用方法参数, 每个参数对应一个Map,Map的key为参数的类型,如java.lang.String, Map的value为JSON格式
        return:JSON字符串:{"code":"1","result":""},code=1,执行成功,其他执行失败
一、现有方案的流程如下:
    1、判断当前参数是否为空,如果为空,则调用的方法没有参数,直接进行反射调用。
    2、参数不为空,遍历所有的参数列表,获取参数的类型字符串和值JSON串
    3、判断参数的类型字符串中是否包含泛型,如果不包括,直接使用ReflectUtils.name2class(paramType)将参数字符串类型转换为class类型,并通过class将值JSON串转换为值对象。
    4、如果参数的类型字符串中包含泛型,再次判断包含的泛型层数,如果是一层泛型,则调用handleOnlyParameterizedType进行处理。
    5、如果是多层泛型,则将多层泛型进行拆分,从最里层开始封装JavaType。调用方法splitParameterizedClassName进行拆分泛型,调用constructJavaTypeByName进行构造JavaType。在其中存在一种情况,如Map等一层泛型中有多个的情况,在拆分时,如果遇到这种情况,就停止拆分,获取如A<B,C>的情况,分别递归创建B,C的JavaType,然后再创建A的JavaType。


/**  * 处理泛型的情况,如果只存在一层泛型如List<String>,Map<String,Integer>等  * 使用handleOnlyParameterizedType进行处理,  * 如果存在多层泛型如A<B<C>>等,构建JavaType进行处理  * @param className  * @param value  * @param typeList  * @param valueList  * @throws Exception  */ private void handleParameterizedType(String className,String value,List<Class<?>> typeList,List<Object> valueList) throws Exception {  //存在多层泛型  if(StringUtils.countMatches(className, "<") > 1) {   String baseClsName = className.substring(0,className.indexOf("<"));   Class<?> baseCls = ReflectUtils.name2class(baseClsName);   typeList.add(baseCls);      List<String> baseClsList = new ArrayList<String>();   String childClsName = splitParameterizedClassName(className, baseClsList);   //倒叙遍历list,从内层开始构建javaType   /**    * Map<String,List<Object>>===>    * 1.type = (List.class,Object.javatype)    * 2.type = Map(String.javatype,1.type)    */   JavaType type = constructJavaTypeByName(childClsName);   for(int i=baseClsList.size()-1;i>=0;i--) {    String listBaseClsName = baseClsList.get(i);    Class<?> listBaseCls = ReflectUtils.name2class(listBaseClsName);    type = mapper.getTypeFactory().constructParametricType(listBaseCls, type);   }   Object obj = mapper.readValue(value, type);   valueList.add(obj);  } else {   handleOnlyParameterizedType(className, value, typeList, valueList);  } }  /**  * 处理泛型的情况,该方法只处理存在一层泛型的情况  * @param className 类型名:如java.util.List<java.lang.String>、java.util.Map<java.lang.String,java.lang.Object>  * @param value 参数值  * @param typeList  * @param valueList  * @throws Exception  */ private void handleOnlyParameterizedType(String className,String value,List<Class<?>> typeList,List<Object> valueList) throws Exception {  String baseClsName = className.substring(0,className.indexOf("<"));  String genericClsNames = className.substring(className.indexOf("<")+1,className.length()-1);  String[] genericClsNameArr = genericClsNames.split(",");  if(genericClsNameArr.length <= 0) {   throw new Exception("none parameterizedType");  }  Class<?> baseCls = ReflectUtils.name2class(baseClsName.trim());  typeList.add(baseCls);  JavaType javaType;  Class<?>[] genericClses = new Class<?>[genericClsNameArr.length];  int i = 0;  for(String genericClsName : genericClsNameArr) {   genericClses[i++] = ReflectUtils.name2class(genericClsName.trim());  }  javaType = mapper.getTypeFactory().constructParametricType(baseCls, genericClses);  Object obj = mapper.readValue(value, javaType);  valueList.add(obj); }  /**  * 解析两层以上的泛型如A<B<C<D>>>  * 如果均为单个泛型,则返回最内层的泛型 C<D>,baseClsList=[A,B]  * 如果里面存在多个泛型,如Map<String,Object>则遇到这种情况,直接返回  * @param str  * @param baseClsList  * @return  * @throws ClassNotFoundException  */ private String splitParameterizedClassName(String str,List<String> baseClsList) throws ClassNotFoundException {  String childrenClsName = str;  while(StringUtils.countMatches(str, "<") >= 2) {   String baseClsName = str.substring(0,str.indexOf("<"));   Class<?> baseCls = ReflectUtils.name2class(baseClsName);   //如果该类的泛型个数超过1个,如Map,直接返回   if(baseCls.getTypeParameters().length > 1) {    break;   }   baseClsList.add(baseClsName);   childrenClsName = str.substring(str.indexOf("<")+1,str.lastIndexOf(">"));   str = childrenClsName;  }  return childrenClsName; }  /**  * 通过类型构造JavaType,多个泛型的情况如Map<String,Object>,  * 分别构建String的JavaType和Object的JavaType,在构建Map的JavaType  * 此时使用递归调用  * @param className  * @return  */ private JavaType constructJavaTypeByName(String className) throws Exception {  if(StringUtils.countMatches(className, "<") == 0) {   return mapper.constructType(ReflectUtils.name2class(className));  }  String baseClsName = className.substring(0,className.indexOf("<"));  String genericClsNames = className.substring(className.indexOf("<")+1,className.length()-1);  String[] genericClsNameArr = genericClsNames.split(",");  if(genericClsNameArr.length <= 0) {   //不存在泛型,即className为List<>,此种情况不会发生   throw new Exception("none parameterizedType");  }  Class<?> baseCls = ReflectUtils.name2class(baseClsName.trim());  JavaType javaType;  JavaType[] genericTypes = new JavaType[genericClsNameArr.length];  int i = 0;  for(String genericClsName : genericClsNameArr) {   genericTypes[i++] = constructJavaTypeByName(genericClsName.trim());  }  javaType = mapper.getTypeFactory().constructParametricType(baseCls, genericTypes);  return javaType; }

二、曾经尝试的方法:Java动态编译
在尝试的过程中,发现jackson的TypeReference可以实现多层泛型的反射,但是发现通过构造嵌套构造JavaType不能使用(不知道当时是什么原因不能用,后来又可以使用),考虑使用Java动态编译,动态创建类TypeReference。代码完成后,在本地测试没有问题。但是部署后,远程调用,发现动态创建的类编译出错,不认识其中import的类。经排查,感觉可能是部署的时候,其他类是是由容器进行的加载,而Java动态编译时,在不指明classpath的情况下,是不会自动使用容器加载过的类的,造成ClassNotFound,但是有一些类的classpath是无法指定的,没有源文件,仍然无法编译成功。故该方案失败。但仍然学习了一些关于java动态编译的知识。在其中要特别注意多线程的情况,生成的类名要不相同,否则会出问题,动态生成的类使用完毕后要及时删除,否则会出现很多无效的临时文件。
public class TypeReferenceUtils { private static int count = 0; @SuppressWarnings({ "rawtypes" }) public static TypeReference getTypeReference(String paramType) throws Exception {  if(count == Integer.MAX_VALUE) {   count = 0;  }  count ++;  JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();  StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);   String className = "DynamicTypeReference"+count;  StringBuilder dynaCls = new StringBuilder();  dynaCls.append("public class ").append(className).append(" {")    .append("public com.alibaba.fastjson.TypeReference getTypeReference() {")    .append(" return new com.alibaba.fastjson.TypeReference<")    .append(paramType)    .append(">(){};")    .append("}")    .append("}");  StringObject so = new StringObject(className,dynaCls.toString());  JavaFileObject file = so;  Iterable<JavaFileObject> files = Arrays.asList(file);   System.out.println(TypeReferenceUtils.class.getResource("/"));   String path = TypeReferenceUtils.class.getResource("/").getPath();  File filePath = new File(path);  String webinfPath = filePath.getParent();  //,"-classpath",webinfPath+"/lib/fastjson-1.1.37.jar"  Iterable< String> options = Arrays.asList("-d", path);   JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, options, null, files);  Boolean r=t.call();  TypeReference typeReference = null;  if(r.booleanValue() == true) {   Class<?> clazz = TypeReferenceUtils.class.getClassLoader().loadClass(className);   Object instance = clazz.newInstance();   typeReference = (TypeReference)clazz.getMethod("getTypeReference").invoke(instance, new Object[]{});  }  try {   return typeReference;  } finally {   //删除Java动态编译生成的临时class文件   File f = new File(path+className+".class");   if(f.exists()) {    f.delete();   }   f = new File(path+className+"$1.class");   if(f.exists()) {    f.delete();   }  } }}@SuppressWarnings("restriction")class StringObject extends SimpleJavaFileObject { private String contents = null;  public StringObject(String className, String contents) throws Exception {  super(URI.create("string:///" + className.replace('.', '/')    + Kind.SOURCE.extension), Kind.SOURCE);  this.contents = contents; }  public CharSequence getCharContent(boolean ignoreEncodingErrors)   throws IOException {  return contents; }}


0 0
原创粉丝点击