如何动态修改java.lang类?

Nit*_*art 16 java classloader dynamic-class-loaders dynamic-class-creation

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

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

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

澄清用例:

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


class A {
   ThreadLocal<Counter> counter;
   ...
   counter.get().inc()
}
Run Code Online (Sandbox Code Playgroud)

我想用注释替换它:


class A {
   @ThreadLocal
   Counter counter;
   ...
   counter.inc()
}
Run Code Online (Sandbox Code Playgroud)

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


class A {
   // Nothing here, field is now in Thread
   ...
   Thread.currentThread().Acounter.inc()
}
Run Code Online (Sandbox Code Playgroud)

Nit*_*art 11

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

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

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

所以现在,没有.


Chr*_*ris 5

如果您想在运行时更改"类"的行为,可以尝试使用javassist.API就在这里


Ceb*_*nce 5

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

我认为不可能以Thread这种方式调整Java的内部类,因为你无法控制System ClassLoader.一个可能的解决方案是创建一个CustomThreadWeaver类,该类将生成一个Thread使用您需要的变量扩展的新类,并使用自定义DynamicWeavedThreadClassLoader来加载它们.

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


Pos*_*ble 5

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

//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));
Run Code Online (Sandbox Code Playgroud)

不要忘记添加Can-Redefine-Classes: true到java代理的清单中.

真实的例子 - string.replace(CharSequence, CharSequence)使用javassist 优化java <9 :

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()));
Run Code Online (Sandbox Code Playgroud)