首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >JDK17 反射失效?用这招搞定 JSON 动态类加载和解析

JDK17 反射失效?用这招搞定 JSON 动态类加载和解析

原创
作者头像
叫我阿柒啊
发布2025-07-24 14:40:10
发布2025-07-24 14:40:10
7250
举报

前言

在上一篇Javassist 自动化生成实体类:让 Debezium 数据解析更高效 文章中,我们通过 javassist 来实现自动创建实体类,但是当我想要将创建的 RowData 类放入到 DebeziumEvent 的时候,才想起来我们没有改变运行中的类。

那么所以,我们也要动态生成 Debezium 这个类...在本篇文章中,我们用到了反射,为啥要用到反射呢。因为我们使用的 javassist 生成了 CtClass,但是熟悉 jvm 的同学就会知道,要想使用类,就要使用类加载器加载到 jvm 中,说起这个就让我想起来了 双亲委托模型 等等知识点。言归正传,反射的作用就是获取 defineClass,将 ctClass 加载到 JVM 中。

动态生成

一切的一切都是为了 Gson 解析准备的,所以先从解析开始:

代码语言:java
复制
gson.fromJson(record.value(), debeziumEvent);

debeziumEvent 是一个 class 类型,所以我们需要构建这个 debeziumEvent.class,同样使用 javassist 生成,然后使用反射加载。

代码语言:java
复制
public static Class<?> generateDebeziumEventClass(Class<?> rowDataClass) throws Exception {
    ClassPool pool = ClassPool.getDefault();
    CtClass ctClass = pool.makeClass("com.test.debezium.DebeziumEventDynamic");

    // 添加 before 字段
    CtField beforeField = new CtField(pool.get(rowDataClass.getName()), "before", ctClass);
    beforeField.setModifiers(Modifier.PUBLIC);
    ctClass.addField(beforeField);

    // 添加 after 字段
    CtField afterField = new CtField(pool.get(rowDataClass.getName()), "after", ctClass);
    afterField.setModifiers(Modifier.PUBLIC);
    ctClass.addField(afterField);

    // 添加 op 字段
    ctClass.addField(CtField.make("public String op;", ctClass));
    ctClass.addField(CtField.make("public long ts_ms;", ctClass));

    // 加载类
    byte[] bytes = ctClass.toBytecode();
    Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
    defineClass.setAccessible(true);
    return (Class<?>) defineClass.invoke(ClassLoader.getSystemClassLoader(), ctClass.getName(), bytes, 0, bytes.length);
}

我们看到通过 getDeclaredMethod 反射获取了 defineClass 这个方法,defineClass 就是用来加载类的。同时,这里需要一个 Class 类型的 rowDataClass 参数,所以我们就修改一下上一篇文章生成 ctClass 的代码,也让其返回一个 Class。

代码语言:java
复制
public static Class<?> generateClass(String className, String fields, String connector) {
    ClassPool pool = ClassPool.getDefault();
    // 初始化CtClas
    CtClass ctClass = pool.makeClass(className);
    for (String field : fields.split(",")) {
        // 添加字段
        try {
            ctClass.addField(CtField.make("private String " + field + ";", ctClass));
        } catch (CannotCompileException e) {
            throw new RuntimeException(e);
        }
    }
    // 添加toString方法
    try {
        ctClass.addMethod(CtMethod.make("public String toString() { String result = " + fields.replace(",", "+ \"" + connector + "\" +") + "; return result;}", ctClass));
    } catch (CannotCompileException e) {
        throw new RuntimeException(e);
    }
    byte[] byteClass;
    Class rowDataClass = null;
    try {
        byteClass = ctClass.toBytecode();
        // 反射
        Class<?> clazz = Class.forName("java.lang.ClassLoader");
        Method defineClass = clazz.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClass.setAccessible(true);
        rowDataClass = (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), className, byteClass, 0, byteClass.length);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return rowDataClass;
}

这样我们就有了一个 rowDataClass,然后就开始回到 gson 解析,开始构造 debeziumEvent 这个参数。

代码语言:java
复制
Class debeziumEvent = generateDebeziumEventClass(generateClass("RowData", "id,name,age,update_at", "|"));

我们执行程序,可以看到 a 这个变量被 gson 解析成了我们自定义的类。

但是我们没有办法明确指定 a 的类型为 DebeziumEvent,在解析的时候只能使用 Object 来接收,否则就会报错。

代码语言:java
复制
Object a = gson.fromJson(record.value(), debeziumEvent);

运行问题

因为我们使用的 Debezium 的版本必须大于 JDK17,但是 JDK17移除部分反射 API 的默认访问权限,所以如果我们直接运行程序就会报错:

所以我们就要添加 JVM 参数开放模块,在 IDEA 的启动配置中点击 Modify options 找到 Add VM options

添加 VM 参数开发模块:

代码语言:bash
复制
--add-opens java.base/java.lang=ALL-UNNAMED --add-opens jdk.internal.loader=ALL-UNNAMED

然后保存即可正常运行:

结语

本篇文章主要通过反射获取类加载方法,然后实现动态类的创建。但是它的弊端就是,DebeziumEvent 因为是通过反射加载的类,所以也没法使用它的具体类型,如果想要获取其中的私有变量和方法,也只能通过反射。通常这种方案适合直接调用toString()将json转换成csv的场景。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 动态生成
  • 运行问题
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档