在编译类中不带反射的序列化



由于客户端JVM的限制,由于不支持反射,我无法使用任何流行的序列化程序。我正在寻找一种执行字节码操作的工具,通过将编写器和读取器方法注入已编译的类来实现序列化。我需要字节码操作 java 代码将其与我的代码绑定以进行构建过程。

我一直在通过生成代码并将其注入源代码来执行此操作,然后再编译为使用我的自定义序列化程序。我想避免这种方法,因为我不想以任何方式修改源文件。

我知道 Kryo 和其他 XML 和 JSON 序列化程序,但它们不符合我的需求。

谢谢。

试试javassist。它可能是您的特定项目最直接的字节码生成库。

这是因为您将能够重用当前代码生成器的一部分,因为javassist能够解析一些简单形式的java代码。

例如,您可以执行以下操作:

CtClass clazz = ...;
CtMethod m = CtNewMethod.make(
    "public void serialize(java.io.DataOutput out) {  out.writeInt(x); }",
     clazz);

如果你想深入了解字节码生成,你可以试试asm。Asm 将允许您直接编写字节码,但对于您的问题类型来说,这似乎是一种矫枉过正。

使用贾夫辅助实施

以下是使用 javassist 执行此操作的基本框架:

Path inputDir = Paths.get("target/classes");
Path outputDir = Paths.get("target/classes2");
ClassPool classPool = new ClassPool(true);
classPool.appendClassPath(inputDir.toString());
// get all class names from a certain directory
String[] classNames = Files.walk(inputDir)
        .filter(p -> (!Files.isDirectory(p)) && p.toString().endsWith(".class"))
        .map(p -> inputDir.relativize(p).toString())
        .map(s -> s.substring(0, s.length() - 6).replace(File.separatorChar, '.'))
        .toArray(size -> new String[size]);
for (String className : classNames) {
    CtClass clazz = classPool.get(className);
    // add further filtering to select the classes you want.
    // ex: "public void serializer(java.io.DataOutput out) { out.writeInt(x); } }"
    String serializerBody = generateSerializer(clazz);
    clazz.addMethod(CtNewMethod.make(serializerBody, clazz));
    // ex: "public void deserializer(java.io.DataInput in) { x = in.readInt(); } }";
    String deserializerBody = generateDeserializer(clazz);
    clazz.addMethod(CtNewMethod.make(deserializerBody, clazz));
    // save the modified class
    clazz.setModifiers(clazz.getModifiers() & ~Modifier.ABSTRACT);
    byte[] bytes = clazz.toBytecode();
    Path outFile = outputDir.resolve(className.replace('.', '/') + ".class");
    Files.createDirectories(outFile.getParent());
    Files.write(outFile, bytes);
}

依赖关系:org.javassist:javassist:3.19.0-GA

您可以使用我的库 Byte Buddy 来实现此目的。Byte Buddy是一个字节码操作库,它允许您轻松地将方法添加到任何现有类中。此外,它还允许您将重新定义的代码注入到 jar 文件中。这样,您可以要求 Byte Buddy 重新定义类以添加所需的方法。但是,请注意,向类添加方法可能会更改其隐式序列化 uuid。

如果在应用程序启动之前不能加载类,则 Byte Buddy 允许您在不加载类的情况下重新定义类。为此,您可以使用字节好友类型的池。

一些可能的替代方案:

  • 如果可以使用 scala,则此项目会编译时序列化程序生成:https://github.com/scala/pickling
  • 这个堆栈溢出问题指出了使用aspectJ和注释处理的可能性:AspectJ/使用编译时反射
    生成方法顺便说一句,如果您的注释处理器声称处理"*"所有注释,则不需要注释您的类。
  • Seren(SERialization ENhancer)声称可以"增强你的类,使它们更快地序列化。但是,像许多其他工具一样,它在加载时执行此操作...也许可以对其进行调整以执行构建时检测,或者向它的创建者建议。
  • Leshy,也做运行时检测。同样的事情:调整它以编译时间或向作者建议。

最新更新