190311-Quick-Fix 通过反射执行任意类目标方法的实现全程实录(上篇)

文章目录
  1. I. 参数类型封装
    1. 1. 基本类型解析
    2. 2. POJO对象转换
    3. 3. Class参数
    4. 4. 泛型处理
  2. II. 测试
    1. 1. 基本类型
    2. 2. POJO对象
    3. 3. Class类型
    4. 4. 泛型
  3. III. 其他
    1. 0. 项目相关
    2. 1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
    3. 2. 声明
    4. 3. 扫描关注

反射可以说是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

# 反射

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×