反射可以说是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
// type 为参数类型:value为参数值
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) {
// xxx
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 的使用姿势来实现我们的目标, 源码如下

IMAGE

所以我们自己的实现方式也相对明了,下面是关键的代码

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
/**
* 将value转换为包含泛型的参数类型
*
* @param value 对象json串
* @param clzType 对象类型
* @param tTypes 泛型参数类型
* @return
*/
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);
}

// 这里借助fastjson指定精确的Type来实现反序列化
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);
}
}

/**
* 获取参数类型
*
* @param type
* @return
* @throws ClassNotFoundException
*/
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 {
/**
* default empty arguments
*/
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;
}

/**
* 将传入的String类型参数封装为目标对象
*
* @param arg 以#分割,根据我们的定义,
* 第一个#前为目标对象类型,
* 最后一个#后为目标对象值(如果为JOPO,则采用json方式进行反序列化)
* 中间的作为泛型的参数类型传入
*
* 几个常见的case如:
*
* "Hello World" 返回 "Hello Word"
* "int#10" 返回 10
* "com.git.hui.fix.core.binder.DefaultServerBinder#{}" 返回的是对象 defaultServerBinder
* "java.util.List#java.lang.String#["ads","bcd"] 返回的是List集合, 相当于 Arrays.asList("asd", "bcd")
* @return
*/
private static Object buildArgObj(String arg) {
String[] typeValue = arg.split("#");
if (typeValue.length == 1) {
// 没有 #,把参数当成String
return arg;
} else if (typeValue.length == 2) {
// 标准的kv参数, 前面为参数类型,后面为参数值
return parseStrToObj(typeValue[0], typeValue[1]);
} else if (typeValue.length >= 3) {
// 对于包含泛型的参数类型
// java.util.List#java.lang.String#["ads","bcd"]
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);
}
}

/**
* 将value转换为包含泛型的参数类型
*
* @param value 对象json串
* @param clzType 对象类型
* @param tTypes 泛型参数类型
* @return
*/
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);
}

// 这里借助fastjson指定精确的Type来实现反序列化
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);
}
}

/**
* 获取参数类型
*
* @param type
* @return
* @throws ClassNotFoundException
*/
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));
}

测试结果如下:

IMAGE

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));
}

测试结果如下:

IMAGE

3. Class类型

这个就比较简单了,直接看测试

IMAGE

4. 泛型

我们先直接使用反序列化方式, 返回的map的value为int类型,而我么预期的是long类型

IMAGE

然后再改用我们上面封装的方式,截图如下,正好和我们预期的一致

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));
}

IMAGE

III. 其他

0. 项目相关

项目地址:

博文地址:

1. 一灰灰Bloghttps://liuyueyi.github.io/hexblog

一灰灰的个人博客,记录所有学习和工作中的博文,欢迎大家前去逛逛

2. 声明

尽信书则不如,已上内容,纯属一家之言,因个人能力有限,难免有疏漏和错误之处,如发现bug或者有更好的建议,欢迎批评指正,不吝感激

3. 扫描关注

一灰灰blog

QrCode

知识星球

goals