我当前正在尝试在运行时中编辑类文件,例如:
Example.java使用以下代码:
public static void execute(){
System.out.println("hello worl");
}
在此示例中,没有简单的方法来编辑文本,现在我需要代码来将“ hello worl”编辑为“ hello world”,而无需访问Example.java
且无需重新启动程序即可编辑字节代码,这可能吗?我搜索了很多文章,但没有找到明确的答案。
答案 0 :(得分:0)
这取决于您拥有多少访问权限。
最简单的方法是在加载该类之前警告该类,并迫使JVM加载您的版本,但是随后必须由适当的ClassLoader来加载它,以您的昵称我可以假设您正在尝试使用Spigot进行一些魔术我的世界引擎?然后,如果您想从其他插件更改类,您要做的就是将此类实际复制到您的项目中,并将该类加载到主类的静态块中-只需确保您的插件将在其他插件之前加载即可。 br />
这将导致JVM在原始类之前加载该类,并且由于spigot类加载器的工作方式-它将被添加到插件类的全局映射中,因此不会加载具有该名称的其他类,而其他插件将使用您的类代替。
这在其他地方也是可行的,不仅是插销-而且不是在每个应用程序中,因为它必须具有类似的类加载-并共享存储要编辑的plugin / jar和plugin / jar的类。
其他方法是使用Javassist库在运行时执行类似的操作:
ClassPool classPool = ClassPool.getDefault();
CtClass ctToEdit = classPool.getCtClass("my.class.to.Edit");
CtMethod execute = ctToEdit.getDeclaredMethod("execute");
execute.setBody("{System.out.println(\"hello world\");}");
ctToEdit.toClass(); // force load that class
Javassist将找到该类的.class文件,并允许您对其进行编辑,然后将其注入到选定的类加载器中(我认为它默认是系统加载器,您也可以使用.toClass(ClassLoader)
方法)
此技巧最重要的部分是无法在执行此代码之前加载类。这就是为什么您需要手动提供类名,而从不做类似MyClass.class.getName()
的事情,这将打破这个窍门。
请注意,javassist Java编译器有些棘手且受限制,请参阅其网页以找到更多信息和有用的技巧。
如果已经加载了类,那么您有最后的选择。
您可以通过Instrumentation
类使用Java代理来完成此操作-因为它允许在运行时重新加载类(有一些限制,就像调试器一样,您不能更改类架构,但是可以使用{{ 1}}之前加载)。
因此,您需要创建一个特殊的Java代理,以使用https://docs.oracle.com/javase/7/docs/api/java/lang/instrument/Instrumentation.html#redefineClasses(java.lang.instrument.ClassDefinition...)
在此类加载之前或之后对其进行编辑(但有少量限制)但是通常情况下,您需要在可运行的.jar中添加特殊标志或清单条目,如果要在JDK上启动此代码,则要容易得多-您可以通过一些技巧创建代理并将其附加到VM在运行时,需要一些代码,因此我只建议使用ClassFileTransformer
库,然后可以使用单行代码进行检测:
byte-buddy-agent
您可以重新定义所有想要的类,在您的情况下,我还建议使用Javassist库来编辑代码-因为它是最简单的可用代码,但是对于Javassist,您也可以使用ByteBuddy或raw ASM。看起来像这样:
Instrumentation install = ByteBuddyAgent.install();
如果已经加载了类,并且您在JRE上,并且由于某些原因您无法更改应用程序启动参数,那么任何一种方法都不可能-编写注释并描述方式-我知道其他一些魔术,但是将需要更多时间来描述它,所以我需要更多信息。