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

最近处理了一个通过类名、方法名、参数值远程调用的功能。在处理的过程中,使用反射的方式进行动态调用,其中的难点是对泛型的处理,特别是多层泛型的情况。现将开发过程中的思路以及遇到的问题进行总结。
方法声明: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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值