一灰灰blog 一灰灰blog
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)

一灰灰blog

资深搬运工
首页
  • InfluxDB
  • MongoDB
  • MySql
  • 基础系列
  • DB系列
  • 搜索系列
  • MQ系列
  • WEB系列
  • 中间件
  • 运维
  • SpringSecurity
  • SpringCloud
  • QuickAlarm
  • QuickCrawer
  • QuickFix
  • QuickMedia
  • QuickSpi
  • QuickTask
  • 分类
  • 标签
  • 归档
  • 收藏
  • 关于
GitHub (opens new window)
  • QuickAlarm

  • QuickCrawer

  • QuickFix

    • Quick-Fix 从0到1构建一个应用内服务/数据访问订正工具包
    • Quick-Fix 纯Jar应用及扩展手册
    • Quick-Fix 如何优雅的实现应用内外交互之接口设计篇
    • Quick-Fix 通过反射执行任意类目标方法的实现全程实录(上篇)
      • I. 参数类型封装
        • 1. 基本类型解析
        • 2. POJO对象转换
        • 3. Class参数
        • 4. 泛型处理
      • II. 测试
        • 1. 基本类型
        • 2. POJO对象
        • 3. Class类型
        • 4. 泛型
      • III. 其他
        • 0. 项目相关
        • 1. 一灰灰Blog: https://liuyueyi.github.io/hexblog
        • 2. 声明
        • 3. 扫描关注
    • Quick-Fix 通过反射执行任意类目标方法的实现全程实录(中篇)
    • Quick-Fix 通过反射执行任意类目标方法的实现全程实录(下篇)
  • QuickMedia

  • QuickSpi

  • QuickTask

  • Quick开源系列
  • QuickFix
一灰灰
2019-03-11

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

反射可以说是java中非常强大的一个特性了,而我们的quick-fix整个项目,也都是基于反射的基础实现任意目标方法的调用执行,对于fix项目而已,核心在于以下几点

  • 如何将外部请求定位我们需要执行的类、方法
  • 如何将外部参数转换为目标方法的可执行参数
  • 如何执行目标方法

简单来讲,就是封装参数为目标类型,定位目标,然后执行

# I. 参数类型封装

这对前面提出三个要点,我们先来看如何进行参数解析,将传入的String格式的参数,封装为我们预期的对象

根据上一篇参数的定义,可以对参数进行简单的分类,如基本类型,如JOPO对象,如Class对象,如包括泛型的对象,不同的case,将会有不同的处理方式

# 1. 基本类型解析

通过反射方式创建对象,对于普通类型来说还好,而对于基本类型,就是直接实现了

// 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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

注意下,这里对BigDecimal和BigInteger类型也进行了兼容,将String转换为目标对象

# 2. POJO对象转换

根据前面的定义,对于POJO对象,采用json格式输出,因此我们需要的是将json字符串转换为对应的POJO对象;对于简单的(不包含泛型)POJO而言,可以直接使用常见的json库来实现反序列化

这里已fastjson进行处理,首先引入依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.45</version>
</dependency>
1
2
3
4
5

关键代码如下

Class clz = ArgumentParser.class.getClassLoader().loadClass(type);
return JSON.parseObject(value, clz);
1
2

# 3. Class参数

这也是一种特殊的参数传入,比如我希望调用com.alibaba.fastjson.JSON#parseObject(java.lang.String, java.lang.Class<T>)这个方法,需要传入的第二个参数就是Class类型,这种case也是不同于前面两种,这里我们借助 java.lang.ClassLoader#loadClass(java.lang.String) 来实现

if ("Class".equalsIgnoreCase(type)) {
    return ArgumentParser.class.getClassLoader().loadClass(value);
}
1
2
3

# 4. 泛型处理

关于泛型参数的转换,这个相对而言就麻烦一点,假设我们有个目标方法

public String print(Map<String, Long> params) {
  // xxx
  return "hello";
}
1
2
3
4

如果要执行上面这个方法,我们传入的参数是怎样的呢?

  • 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

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

/**
 * 将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

下面贴一下完整的参数解析代码

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

# II. 测试

# 1. 基本类型

@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));
}
1
2
3
4
5
6
7

测试结果如下:

IMAGE

# 2. POJO对象

首先创建一个pojo对象

@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));
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

测试结果如下:

IMAGE

# 3. Class类型

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

IMAGE

# 4. 泛型

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

IMAGE

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

@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));
}
1
2
3
4
5
6
7
8
9
10

IMAGE

# III. 其他

# 0. 项目相关

项目地址:

  • https://github.com/liuyueyi/quick-fix (opens new window)

博文地址:

  • 190108-Quick-Fix 如何优雅的实现应用内外交互之接口设计篇 (opens new window)
  • 190104-Quick-Fix 纯Jar应用及扩展手册 (opens new window)
  • 190102-Quick-Fix 从0到1构建一个应用内服务/数据访问订正工具包 (opens new window)
  • 190311-Quick-Fix 通过反射执行任意类目标方法的实现全程实录(上篇) (opens new window)
  • 190315-Quick-Fix 通过反射执行任意类目标方法的实现全程实录(中篇) (opens new window)
  • 190317-Quick-Fix 通过反射执行任意类目标方法的实现全程实录(下篇) (opens new window)

# 1. 一灰灰Blog (opens new window): https://liuyueyi.github.io/hexblog

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

# 2. 声明

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

  • 微博地址: 小灰灰Blog (opens new window)
  • QQ: 一灰灰/3302797840

# 3. 扫描关注

一灰灰blog

QrCode

知识星球

goals

编辑 (opens new window)
#反射
上次更新: 2021/10/15, 19:56:22
Quick-Fix 如何优雅的实现应用内外交互之接口设计篇
Quick-Fix 通过反射执行任意类目标方法的实现全程实录(中篇)

← Quick-Fix 如何优雅的实现应用内外交互之接口设计篇 Quick-Fix 通过反射执行任意类目标方法的实现全程实录(中篇)→

最近更新
01
【WEB系列】从0到1实现自定义web参数映射器
01-23
02
【WEB系列】如何支持下划线驼峰互转的传参与返回
01-17
03
【DB系列】Mybatis之批量插入的几种姿势
01-11
更多文章>
Theme by Vdoing | Copyright © 2017-2022 一灰灰Blog
MIT License | 鄂ICP备18017282号 |
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×