如何动态修改java.lang类?

如何动态修改java.lang类?,java,classloader,dynamic-class-loaders,dynamic-class-creation,Java,Classloader,Dynamic Class Loaders,Dynamic Class Creation,我正在寻找一种方法,通过重写字节码并重新加载类来动态地向线程添加字段,但不确定这是否可行。欢迎指点。我发现了一些关于修改和加载类的信息,我知道JRebel可以无缝地热交换您的代码,但不确定这里是否适用相同的方法/工具 这里的动机是探索一种理论上更好的替代线程本地对象的方法。如果该方法有效,我应该能够用注释替换threadlocal,并且结果应该优于当前的JDK实现 PS:请拯救我“万恶言论之根” 澄清用例: 假设我有一个带有ThreadLocal的类: class A { ThreadL

我正在寻找一种方法,通过重写字节码并重新加载类来动态地向线程添加字段,但不确定这是否可行。欢迎指点。我发现了一些关于修改和加载类的信息,我知道JRebel可以无缝地热交换您的代码,但不确定这里是否适用相同的方法/工具

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

PS:请拯救我“万恶言论之根”

澄清用例:

假设我有一个带有ThreadLocal的类:


class A {
   ThreadLocal<Counter> counter;
   ...
   counter.get().inc()
}
我想用注释来代替它:


class A {
   @ThreadLocal
   Counter counter;
   ...
   counter.inc()
}
但我不想生成上面的代码,而是想对线程进行修改,这样线程现在将有一个计数器字段,实际代码将是:


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

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

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

我认为不可能这样调整Java的内部
线程
类,因为您无法控制
系统类加载器
。一个可能的解决方案是使用一个
CustomThreadWeaver
类,该类将生成一个新类,该类使用所需变量扩展
Thread
,并使用自定义
DynamicWeavedThreadClassLoader
加载它们


祝你好运,当你成功的时候给我们看看你的怪物;-)

您试图做的是不可能的

因为您已经了解ThreadLocal,所以您已经知道建议的解决方案是什么

或者,您可以将线程划分为子类并添加自己的字段;但是,只有您显式创建该类的线程才会有这些字段,因此您仍然必须能够“退回”到使用本地线程


真正的问题是“为什么?”,如“为什么本地线程不足以满足您的需求?”

这似乎是一个为工作使用正确工具的问题。这里提出了一个类似的问题:Javaassist字节码操纵库是一个可能的解决方案


但是,在没有进一步详细说明为什么尝试这样做的原因的情况下,似乎真正的答案是使用合适的工具来完成这项工作。例如,使用Groovy.

可以尝试创建一个JVM代理,该代理使用,更具体地说,使用“促进已加载类的插入”的,然后使用前面提到的Javassist(或ASM)来处理字节码


关于

的更多信息目前,不可能在运行时重新定义类,这样重新定义将产生新的方法或字段。这是由于扫描堆中所有现有实例并转换它们+它们的引用+潜在的不安全字段偏移量基更新程序(如AtomicFieldUpdater)所涉及的复杂性

这一限制可能会作为的一部分被取消,但正如并发兴趣邮件组所讨论的,这是一个巨大的影响变化,因此可能永远不会发生

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


所以现在,不。

要想做你想做的事情,更简单的选择是使用Thread的子类,运行它,然后在该线程内执行示例中的代码(以及对子类的currentThread()的转换)。

可能 使用工具,可能还有javassist之类的库来动态修改代码。(但是,当前无法添加和删除字段、方法或构造函数)


请添加您当前拥有的或正在处理的代码,以便我们能够更好地帮助您。对不起,这处于初步研究阶段,目前没有可用的代码。我将编辑Q以澄清意图。我正在寻找一种方法来更改已经加载和分配的类。我可以找到足够的文档来支持在加载类时对其进行更改,但无法找到如何用我的新版本替换正在运行的线程。这是你真正的问题,而不是我的:)。测量线程上有一个字段和使用ThreadLocal在性能上的差异,这就是为什么。为什么不可能呢?有一些产品,比如JRebel,它们声称能够在运行时热交换您的代码,那么为什么不可能热交换线程呢?但是答案有什么不正确的呢?顺便说一句,JRebel的工作原理是在加载时更改类,向需要修改的任何类添加一个附加字段。您可以通过更改rt.jar中的Thread.class来实现这一点,因为您似乎不喜欢合理的建议。添加新方法与添加新字段不同,因此这没有帮助。如果您知道如何使用Javaassist动态添加新字段,请告诉我。动机是一种智力活动,一种思想/想法。啊,我明白了。我自己没有使用过Javaassist,但我确实发现本教程声称能够添加字段:遗憾的是,文档声称这是不可能的:“重新定义可能会更改方法体、常量池和属性。重新定义不能添加、删除或重命名字段。”。。
//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));
String replace_src = 
    "{String str_obj = this;\n"
    + "char[] str = this.value;\n"
    + "String find_obj = $1.toString();\n"
    + "char[] find = find_obj.value;\n"
    + "String repl_obj = $2.toString();\n"
    + "char[] repl = repl_obj.value;\n"
    + "\n"
    + "if(str.length == 0 || find.length == 0 || find.length > str.length) {\n"
    + "    return str_obj;\n"
    + "}\n"
    + "int start = 0;\n"
    + "int end = str_obj.indexOf(find_obj, start);\n"
    + "if(end == -1) {\n"
    + "    return str_obj;\n"
    + "}\n"
    + "int inc = repl.length - find.length;\n"
    + "int inc2 = str.length / find.length / 512;\ninc2 = ((inc2 < 16) ? 16 : inc);\n"
    + "int sb_len = str.length + ((inc < 0) ? 0 : (inc * inc2));\n"
    + "StringBuilder sb = (sb_len < 0) ? new StringBuilder(str.length) : new StringBuilder(sb_len);\n"
    + "while(end != -1) {\n"
    + "    sb.append(str, start, end - start);\n"
    + "    sb.append(repl);\n"
    + "    start = end + find.length;\n"
    + "    end = str_obj.indexOf(find_obj, start);\n"
    + "}\n"
    + "if(start != str.length) {\n"
    + "    sb.append(str, start, str.length - start);\n"
    + "}\n"
    + "return sb.toString();\n"
    +"}";


ClassPool cp = new ClassPool(true);
CtClass clz = cp.get("java.lang.String");
CtClass charseq = cp.get("java.lang.CharSequence");

clz.getDeclaredMethod("replace", new CtClass[] {
        charseq, charseq
}).setBody(replace_src);

instrumentationInstance.redefineClasses(new ClassDefinition(Class.forName(clz.getName(), false, null), clz.toBytecode()));