在编译的类中没有反射的序列化

Jua*_*cia 17 java serialization code-generation bytecode-manipulation

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

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

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

谢谢.

Dan*_*rry 8

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

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

例如,您可以这样做:

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

如果你想深入研究字节码生成,你可以尝试asm.Asm将允许您直接编写字节码,但它似乎对您的问题类型有点过分.

用javassist实现

以下是使用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);
}
Run Code Online (Sandbox Code Playgroud)

相关性: org.javassist:javassist:3.19.0-GA


Raf*_*ter 5

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

如果您的类必须在应用程序启动之前加载,Byte Buddy 允许您重新定义类而不加载它们。为此,您可以使用 Byte Buddys 类型池。