反射可以说是java中非常强大的一个特性了,而我们的quick-fix整个项目,也都是基于反射的基础实现任意目标方法的调用执行,对于fix项目而已,核心在于以下几点
- 如何将外部请求定位我们需要执行的类、方法
- 如何将外部参数转换为目标方法的可执行参数
- 如何执行目标方法
简单来讲,就是封装参数为目标类型,定位目标,然后执行
I. 参数类型封装
这对前面提出三个要点,我们先来看如何进行参数解析,将传入的String格式的参数,封装为我们预期的对象
根据上一篇参数的定义,可以对参数进行简单的分类,如基本类型,如JOPO对象,如Class对象,如包括泛型的对象,不同的case,将会有不同的处理方式
1. 基本类型解析
通过反射方式创建对象,对于普通类型来说还好,而对于基本类型,就是直接实现了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| if ("int".equals(type) || "Integer".equals(type)) { return Integer.parseInt(value); } else if ("long".equals(type) || "Long".equals(type)) { return Long.parseLong(value); } else if ("float".equals(type) || "Float".equals(type)) { return Float.parseFloat(value); } else if ("double".equals(type) || "Double".equals(type)) { return Double.parseDouble(value); } else if ("byte".equals(type) || "Character".equals(type)) { return Byte.parseByte(value); } else if ("boolean".equals(type) || "Boolean".equals(type)) { return Boolean.parseBoolean(value); } else if ("short".equals(type) || "Short".equals(type)) { return Short.parseShort(value); } else if ("BigDecimal".equals(type)) { return new BigDecimal(value); } else if ("BigInteger".equals(type)) { return new BigInteger(type); } else if ("String".equals(type)) { return value; }
|
注意下,这里对BigDecimal和BigInteger类型也进行了兼容,将String转换为目标对象
2. POJO对象转换
根据前面的定义,对于POJO对象,采用json格式输出,因此我们需要的是将json字符串转换为对应的POJO对象;对于简单的(不包含泛型)POJO而言,可以直接使用常见的json库来实现反序列化
这里已fastjson进行处理,首先引入依赖
1 2 3 4 5
| <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency>
|
关键代码如下
1 2
| Class clz = ArgumentParser.class.getClassLoader().loadClass(type); return JSON.parseObject(value, clz);
|
3. Class参数
这也是一种特殊的参数传入,比如我希望调用com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.Class<T>)
这个方法,需要传入的第二个参数就是Class类型,这种case也是不同于前面两种,这里我们借助 java.lang.ClassLoader#loadClass(java.lang.String)
来实现
1 2 3
| if ("Class".equalsIgnoreCase(type)) { return ArgumentParser.class.getClassLoader().loadClass(value); }
|
4. 泛型处理
关于泛型参数的转换,这个相对而言就麻烦一点,假设我们有个目标方法
1 2 3 4
| public String print(Map<String, Long> params) { return "hello"; }
|
如果要执行上面这个方法,我们传入的参数是怎样的呢?
java.util.Map#java.lang.String#java.lang.Long#{"world":456,"hello":123}
我们现在的目标是,将{"world":456,"hello":123}
转换为Map对象,对于JSON的反序列化,直接使用com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.Class<T>)
返回的只会是Map对象,而不是我们希望的Map<String, Long>
因此我们考虑使用另外一种反序列化方式,com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.reflect.Type, com.alibaba.fastjson.parser.Feature...)
, 使用这个方法,主要就是如何创建这个Type对象,参考 TypeReference
的使用姿势来实现我们的目标, 源码如下
所以我们自己的实现方式也相对明了,下面是关键的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
private static Object parseStr2GenericObj(String value, String clzType, String... tTypes) { try { Type[] paramsType = new Type[tTypes.length]; int count = 0; for (String t : tTypes) { paramsType[count++] = getType(t); }
Type type = new ParameterizedTypeImpl(paramsType, null, getType(clzType)); return JSONObject.parseObject(value, type); } catch (Exception e) { throw new IllegalInvokeArgumentException( "Pare Argument to Object Error! type: " + clzType + " # " + Arrays.asList(tTypes) + " value: " + value, e); } }
private static Type getType(String type) throws ClassNotFoundException { return ArgumentParser.class.getClassLoader().loadClass(type); }
|
下面贴一下完整的参数解析代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
| public class ArgumentParser {
private static final Object[] EMPTY_ARGS = new Object[]{};
public static Object[] parse(String[] args) { if (args == null || args.length == 0) { return EMPTY_ARGS; }
Object[] result = new Object[args.length]; for (int i = 0; i < args.length; i++) { result[i] = buildArgObj(args[i]); } return result; }
private static Object buildArgObj(String arg) { String[] typeValue = arg.split("#"); if (typeValue.length == 1) { return arg; } else if (typeValue.length == 2) { return parseStrToObj(typeValue[0], typeValue[1]); } else if (typeValue.length >= 3) { String[] reflectTypes = new String[typeValue.length - 2]; System.arraycopy(typeValue, 1, reflectTypes, 0, typeValue.length - 2); return parseStr2GenericObj(typeValue[typeValue.length - 1], typeValue[0], reflectTypes); } else { throw new IllegalInvokeArgumentException("Illegal invoke arg: " + arg); } }
private static Object parseStrToObj(String type, String value) { try { if ("int".equals(type) || "Integer".equals(type)) { return Integer.parseInt(value); } else if ("long".equals(type) || "Long".equals(type)) { return Long.parseLong(value); } else if ("float".equals(type) || "Float".equals(type)) { return Float.parseFloat(value); } else if ("double".equals(type) || "Double".equals(type)) { return Double.parseDouble(value); } else if ("byte".equals(type) || "Character".equals(type)) { return Byte.parseByte(value); } else if ("boolean".equals(type) || "Boolean".equals(type)) { return Boolean.parseBoolean(value); } else if ("short".equals(type) || "Short".equals(type)) { return Short.parseShort(value); } else if ("BigDecimal".equals(type)) { return new BigDecimal(value); } else if ("BigInteger".equals(type)) { return new BigInteger(type); } else if ("String".equals(type)) { return value; } else if ("Class".equalsIgnoreCase(type)) { return ArgumentParser.class.getClassLoader().loadClass(value); } else { Class clz = ArgumentParser.class.getClassLoader().loadClass(type); return JSON.parseObject(value, clz); } } catch (Exception e) { throw new IllegalInvokeArgumentException( "Pare Argument to Object Error! type: " + type + " value: " + value, e); } }
private static Object parseStr2GenericObj(String value, String clzType, String... tTypes) { try { Type[] paramsType = new Type[tTypes.length]; int count = 0; for (String t : tTypes) { paramsType[count++] = getType(t); }
Type type = new ParameterizedTypeImpl(paramsType, null, getType(clzType)); return JSONObject.parseObject(value, type); } catch (Exception e) { throw new IllegalInvokeArgumentException( "Pare Argument to Object Error! type: " + clzType + " # " + Arrays.asList(tTypes) + " value: " + value, e); } }
private static Type getType(String type) throws ClassNotFoundException { return ArgumentParser.class.getClassLoader().loadClass(type); } }
|
II. 测试
1. 基本类型
1 2 3 4 5 6 7
| @Test public void testArugmentParser() { String[] params = new String[]{"Hello World", "int#120", "long#330", "BigDecimal#1.2", "boolean#true"};
Object[] result = ArgumentParser.parse(params); System.out.println(JSON.toJSONString(result)); }
|
测试结果如下:
2. POJO对象
首先创建一个pojo对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Data @NoArgsConstructor @AllArgsConstructor public class PoJo { private String name; private Integer age; private Boolean male; }
@Test public void testPOJO() { PoJo pojo = new PoJo("一灰灰", 18, true); String s = JSON.toJSONString(pojo);
String[] params = new String[]{"git.hui.fix.test.PoJo#" + s};
Object[] result = ArgumentParser.parse(params); System.out.println(JSON.toJSONString(result)); }
|
测试结果如下:
3. Class类型
这个就比较简单了,直接看测试
4. 泛型
我们先直接使用反序列化方式, 返回的map的value为int类型,而我么预期的是long类型
然后再改用我们上面封装的方式,截图如下,正好和我们预期的一致
1 2 3 4 5 6 7 8 9 10
| @Test public void testGenericClass() { Map<String, Long> demo = new HashMap<>(); demo.put("hello", 123L); demo.put("world", 456L);
String[] params = new String[]{"java.util.Map#java.lang.String#java.lang.Long#" + JSON.toJSONString(demo)}; Object[] result = ArgumentParser.parse(params); System.out.println(JSON.toJSONString(result)); }
|
III. 其他
0. 项目相关
项目地址:
博文地址:
一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛
2. 声明
尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激
3. 扫描关注
一灰灰blog
知识星球