如何动态修改java.lang类?

时间:2013-06-07 14:11:35

标签: java classloader dynamic-class-loaders dynamic-class-creation

我正在寻找一种方法,通过重写字节代码并重新加载类来动态添加字段,不确定它是否完全可能。欢迎任何指示。我找到了一些关于修改和加载类的信息,我知道JRebel可以无缝热插拔代码,但不确定这里是否适用相同的方法/工具。

这里的动机是探索理论上更好的替代线程局部对象。如果方法有效,我应该能够用注释替换本地线程,结果应该优于当前的JDK实现。

PS:请救我所有邪恶言论的根源"

澄清用例:

想象一下,我有一个ThreadLocal类:


class A {
   ThreadLocal<Counter> counter;
   ...
   counter.get().inc()
}

我想用注释替换它:


class A {
   @ThreadLocal
   Counter counter;
   ...
   counter.inc()
}

但是代替上面生成的代码,我想改变Thread,以便Thread现在有一个Acounter字段,实际代码将是:


class A {
   // Nothing here, field is now in Thread
   ...
   Thread.currentThread().Acounter.inc()
}

8 个答案:

答案 0 :(得分:11)

目前,不可能在运行时重新定义类,以便重新定义将导致新的方法或字段。这是因为扫描所有现有实例的堆并转换它们的引用+其引用+潜在的不安全字段偏移基础更新程序(如AtomicFieldUpdater)所涉及的复杂性。

这个限制可以作为JEP-159的一部分解除,但正如并发利益邮件组所讨论的那样,这是一个很大的影响变化,所以可能永远不会发生。

使用Javaassist / similar将允许使用新方法/字段将类转换为新类。此类可以由ClassLoader加载并在运行时使用,但它的定义不会替换现有实例。因此,不可能将此方法与代理结合使用来重新定义类,因为检测重新定义受到限制,因此:“重新定义可能会更改方法主体,常量池和属性。重定义不得添加,删除或重命名字段......“请参阅here

所以现在,没有。

答案 1 :(得分:5)

如果您想在运行时更改“class”的行为,可以尝试javassist。 API为here

答案 2 :(得分:5)

我见过动态重新加载JAR的自定义类加载解决方案 - 您为每个JAR文件定义一个ClassLoader并使用它从该JAR加载类;要重新加载整个JAR,你只需“杀死”它的ClassLoader实例并创建另一个实例(在替换JAR文件之后)。

我认为不可能以这种方式调整Java的内部Thread类,因为您无法控制System ClassLoader。一个可能的解决方案是使用CustomThreadWeaver类生成一个新类,其中包含您需要的变量Thread,并使用自定义DynamicWeavedThreadClassLoader加载它们。

祝你好运,并在你成功时告诉我们你的怪物; - )

答案 3 :(得分:5)

<强>可能的 使用检测,可能还有像javassist这样的库来修改代码。 (但是,目前无法添加和删除字段,方法或构造函数)

var events_array = [{
        title: 'Test1',
        start: new Date(2015, 09, 14),
        tip: 'Personal tip 1',
        backgroundColor: 'red'
    }, {
        title: 'Test2',
        start: new Date(2015, 09, 15),
        tip: 'Personal tip 2',
        backgroundColor: 'green'
    }];

不要忘记将//Modify code using javassist and call CtClass#toBytecode() or load bytecode from file byte[] nevcode; Class<?> clz = Class.forName("any.class.Example"); instrumentationInstace.redefineClasses(new ClassDefinition(clz, nevcode)); 添加到您的java代理的清单中。

真实的例子 - 优化java&lt; 9 Can-Redefine-Classes: true使用javassist:

string.replace(CharSequence, CharSequence)

答案 4 :(得分:2)

这似乎是使用正确的工具来完成工作的问题。这里也提出了类似的问题:Another Stack Overflow Question和Javaassist字节代码操作库是一种可能的解决方案。

但是没有进一步详细说明为什么会这样做的原因,似乎真正的答案是使用正确的工具来完成工作。例如,使用Groovy the ability to dynamically add methods to the language

答案 5 :(得分:1)

您可以尝试创建一个使用java.lang.instrument API的JVM代理,更具体地说,使用retransform method“促进已加载类的检测”,然后使用Javassist(或者ASM)如上所述处理字节码。

有关java.lang.instrument API

的更多信息

答案 6 :(得分:0)

为了做你想做的事,更简单的替代方法是使用Thread的子类,运行它,然后在该线程内部执行你的例子中的代码(以及currentThread()的强制转换到你的子类)。 / p>

答案 7 :(得分:-2)

您尝试做的事情是不可能的。

由于您已经了解ThreadLocal,因此您已经知道建议的解决方案是什么。

或者,您可以对Thread进行子类并添加自己的字段;但是,只有你明确创建该类的那些线程才会有这些字段,所以你仍然必须能够“退回”#34;使用本地线程。

真正的问题是&#34;为什么?&#34;,如同&#34;为什么本地线程不足以满足您的要求?&#34;