首先,您应该考虑不同的解决方案,因为其他所有解决方案都比这个更好,所以可能的解决方案:
- 只需创建单独的模块。
- 在编译时使用一些代码生成来生成这些模块,这样您就不需要复制代码,例如查看https://github.com/vigna/fastutil。
但是,如果您真的想以非常肮脏的方式来做:
使用 java 代理。这需要使用 jdk jvm 或/和其他启动参数。如果您想在没有启动参数的情况下在运行时执行此操作,您可能应该使用 byte-buddy-agent 库,并且即使没有来自 jdk 的正确文件,java 8 也有一个肮脏的技巧可以在运行时运行代理 - 只需手动注入它们,可能在 java 9+ 上也可以,但到目前为止我没有时间,需要找到一种方法来做到这一点。您可以在此处查看我的说明https://github.com/raphw/byte-buddy/issues/374#issuecomment-343786107
但如果可能的话,最好的方法是使用命令行参数将代理 .jar 作为单独的东西附加。
首先要做的是编写一个类文件转换器,它将完成您需要的所有逻辑:
public class DynamicLibraryReferenceTransformer implements ClassFileTransformer {
private final String packageToProcess;
private final String originalPackage;
private final String resolvedPackage;
DynamicLibraryReferenceTransformer(String packageToProcess, String originalPackage, String resolvedPackage) {
this.packageToProcess = packageToProcess;
this.originalPackage = originalPackage;
this.resolvedPackage = resolvedPackage;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) {
if (! className.startsWith(this.packageToProcess)) {
return null; // return null if you don't want to perform any changes
}
Remapper remapper = new Remapper() {
@Override
public String map(String typeName) {
return typeName.replace(originalPackage, resolvedPackage);
}
};
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassRemapper classRemapper = new ClassRemapper(cw, remapper);
ClassReader classReader = new ClassReader(classfileBuffer);
classReader.accept(classRemapper, 0);
return cw.toByteArray();
}
}
然后你只需要在运行时将它应用为 java 代理:
static {
Instrumentation instrumentation= ByteBuddyAgent.install();
// note that this uses internal names, with / instead of dots, as I'm using simple .replace it's good idea to keep that last / to prevent conflicts between libraries using similar packages. (like com/assist vs com/assistance)
instrumentation.addTransformer(new DynamicLibraryReferenceTransformer("my/pckg/", "original/pckg/", "relocated/lib/"), true);
// you can retransform existing classes if needed but I don't suggest doing it. Only needed if some classes you might need to transform are already loaded
// (classes are loaded on first use, with some smaller exceptions, like return types of methods of loaded class are also loaded if I remember correctly, where fields are not)
// you can also just retransform only known classes
instrumentation.retransformClasses(install.getAllLoadedClasses());
}
这段代码应该尽可能快地运行,就像在你的主类中的静态代码块中一样。
更好的选择是在启动时使用命令行将代理包含到 JVM:
首先,您需要创建新项目,因为这将是单独的 .jar,并创建清单 Premain-Class: mypckg.AgentMainClass
,您将包含在 agent 的 meta-inf 中.jar
。
使用与上面相同的转换器,然后您只需要编写非常简单的代理,如下所示:
public class AgentMainClass {
public static void premain(String agentArgs, Instrumentation instrumentation) {
instrumentation.addTransformer(new DynamicLibraryReferenceTransformer("my/pckg/", "original/pckg/", "relocated/lib/"), true);
}
}
现在只需将它包含在您的 java 命令中即可运行应用程序(或可能是服务器)-javaagent:MyAgent.jar
。
请注意,您可以在主(插件?).jar 中包含代理和清单的代码,只要确保不要混淆依赖项,代理的类将使用不同的类加载器加载,所以不要在应用程序和代理之间进行调用,这将是单个 .jar 中的 2 个独立的东西。
这使用 org.ow2.asm.asm-all 库和 net.bytebuddy.byte-buddy-agent(仅适用于运行时版本)库。