我有以下代码。我想改变hello类的say方法。我用javassist。我有以下错误。
public class TestJavasisit {
/**
* @param args the command line arguments
* @throws java.lang.Exception
*/
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
// version original
Hello h1 = new Hello();
h1.say();
CtClass cc = pool.get("testjavasisit.Hello");
cc.defrost();
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");
cc.writeFile(".");
cc.toClass();
// version modifie
Hello h2 = new Hello();
h2.say();
}
}
你好的课程:
public class Hello {
public void say() {
System.out.println("Hello");
}
}
错误消息:
run:
Hello
Exception in thread "main" javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "testjavasisit/Hello"
答案 0 :(得分:8)
package testjavasisit;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class TestJavasisit {
/**
* @param args
* the command line arguments
* @throws java.lang.Exception
*/
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
// version original
Hello h1 = new Hello(); // remove this line
h1.say(); // remove this line
CtClass cc = pool.get("testjavasisit.Hello");
cc.defrost();
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");
cc.writeFile(".");
// This throws a java.lang.LinkageError ... attempted duplicate class definition for name: ...
cc.toClass();
// version modifie
Hello h2 = new Hello();
h2.say();
}
}
如果删除以下2行,则会成功运行。
// Hello h1 = new Hello();
// h1.say();
<强>输出:强>
Hello.say():
您好
第一次使用Hello h1 = new Hello();
时,类加载器会加载Hello类。
之后当您再次尝试使用cc.toClass();
加载Hello类时,会出现此错误。
Rafael Winterhalter告诉了link这个问题的原因和解决方法 如
cc.toClass()
接受一个加载的类[Hello
]并重新定义它 相同的类而不更改其名称。在重新定义之后,你 尝试再次加载更改的类。然而,这不是 在Java中可能ClassLoader可以only load a class of a given name one single time。要解决您的问题,您有不同的选择:
- 创建参数类的子类(或使用接口) 使用随机名称。然后子类与您的类型兼容 参数类但永远不会加载。
- 使用Instrumentation API重新定义已加载的课程 运行时。
- 确保加载了输入类和输出类 不同的ClassLoaders。 (不推荐)
醇>
在tomcat中,他们已经解决了这个问题。
答案 1 :(得分:0)
不知道但不知何故“Hello h1 = new Hello();”线创造问题。请参阅以下更新后的代码。
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("testjavasisit.Hello");
cc.defrost();
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"Hello11.say():\"); }");
cc.writeFile("build");
cc.toClass();
Hello h2 = new Hello();
h2.say();
答案 2 :(得分:0)
您应该在不同的类加载器中加载类的原始版本和修改版本。 试试这个:
public static void main(String[] args) {
try {
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool); //javassist.Loader
// version original
Class origin = cl.loadClass("testjavasisit.Hello");
Object h1 = origin.newInstance();
Method sayMethod = origin.getMethod("say", null);
sayMethod.invoke(h1);
CtClass cc = pool.get("testjavasisit.Hello");
cc.defrost();
CtMethod m = cc.getDeclaredMethod("say");
m.insertBefore("{ System.out.println(\"Hello.say():\"); }");
cc.writeFile(".");
cc.toClass();
// version modifie
Hello h2 = new Hello();
h2.say();
} catch (Throwable e) {
e.printStackTrace();
}
}
如果您的目标是在同一个类加载器中修改已加载的类,则可以使用Java Instrument API 在javaasist工作之后到retransform课程
答案 3 :(得分:0)
//0.just use javaassist ,gradle dependency :
//compile 'org.javassist:javassist:3.21.0-GA'
//1.try to find the class
ctClazz.refClasses.forEach {
if (it == "android.widget.Toast") {
//yes, you find the target class
}
}
ctClazz.replaceClassName("com.xx.the.class","com.xx.the.target.class")
ctClazz.writeFile(classDirectory)
ctClazz.xxx() //if need
//2try to recompile the jar and check out .