2017-05-08 26 views
0

我需要更改方法(計算公式),而無需重新編譯應用程序。我知道這可以在javassist的幫助下完成。到目前爲止,我正在嘗試一個簡單的例子。在post方法中,我調用createMethodHelper()方法,該方法必須更改Helper2方法。一切都好。但是,重複調用(重新加載頁面),錯誤javassist.CannotCompileException後:通過java.lang.LinkageError的:裝載機 所以,包含我想改變java javassist.CannotCompileException:java.lang.LinkageError:loader

package ru.testScandJavaCafee.controller; 

public class Helper2 { 
public String createList() 
    { 
     System.out.println("++++"); 
     return "1000"; 
    } 
} 

的唯一方法和類的類從哪個我改變方法

public String createMethodHelper() throws NotFoundException, CannotCompileException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, IOException, InstantiationException, ClassNotFoundException { 

     ClassPool pool = ClassPool.getDefault(); 
pool.insertClassPath(new ClassClassPath(this.getClass())); 
     CtClass cc = pool.get("ru.testScandJavaCafee.controller.Helper2"); 
     cc.defrost(); 
     CtMethod cm = cc.getMethod("createList","()Ljava/lang/String;"); 
     cc.defrost(); 
     cm.setBody("{ return \"300 \" ;}"); 

     cc.defrost(); 
     Class c = cc.toClass(); 
     cc.defrost(); 
ru.testScandJavaCafee.controller.Helper2 test = (ru.testScandJavaCafee.controller.Helper2) c.newInstance(); 
     String sum = test.createList(); 

     return sum; 
    } 

第二個電話(重新加載頁面),錯誤

javassist.CannotCompileException: by java.lang.LinkageError: loader (instance of org/apache/catalina/loader/WebappClassLoader): attempted duplicate class definition for name: "ru/testScandJavaCafee/controller/Helper2" 
    at javassist.ClassPool.toClass(ClassPool.java:1085) 
    at javassist.ClassPool.toClass(ClassPool.java:1028) 
    at javassist.ClassPool.toClass(ClassPool.java:986) 
    at javassist.CtClass.toClass(CtClass.java:1110) 
    at ru.testScandJavaCafee.controller.CoffeeTypeController.createMethodHelper(CoffeeTypeController.java:108) 
    at ru.testScandJavaCafee.controller.CoffeeTypeController.doPost(CoffeeTypeController.java:56) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:650) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:731) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:218) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:110) 
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:962) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:452) 
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1087) 
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:637) 
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:318) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 
    at java.lang.Thread.run(Thread.java:745) 
Caused by: java.lang.LinkageError: loader (instance of org/apache/catalina/loader/WebappClassLoader): attempted duplicate class definition for name: "ru/testScandJavaCafee/controller/Helper2" 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:642) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:498) 
    at javassist.ClassPool.toClass2(ClassPool.java:1098) 
    at javassist.ClassPool.toClass(ClassPool.java:1079) 
    ... 27 more 

幫助後,以瞭解如何修正這個錯誤

+0

如果你已經看到了有關重複類定義'嘗試重複類定義的名稱:「ru/testScandJavaCafee/controller/Helper2」'這個錯誤嗎? – SubOptimal

+0

有一個相似的主題,但我不知道如何應用到我的項目http://stackoverflow.com/questions/23336172/adding-an-annotation-to-a-runtime-generated-class-using-javassist –

回答

0

我對documentationtutorial做了一些研究。如果你想方法多次修改,你可以這樣來做:

public static void main(String[] args) { 
    try { 
     // get a new instance of version 1 
     Object newClassInstance1 = m2("com.tima.Helper2", "public String createList() { System.out.println(\"++++\"); return \"200\";}"); 
     Method method1 = newClassInstance1.getClass().getMethod("createList"); 

     // this executes the createList in the new class com.tima.Helper2 
     String sum1 = (String) method1.invoke(newClassInstance1); 
     System.out.println("com.tima.Helper2: " + sum1); 

     // this shows that the original Helper method was not modified 
     Helper h = new Helper(); 
     System.out.println("com.tima.Helper: " + h.createList()); 

     // this shows that the com.tima.Helper2 overrides Helper and can be used as Helper with a modified method 
     Helper h2 = (Helper) newClassInstance1; 
     System.out.println("com.tima.Helper2 as Helper: " + h2.createList()); 

     // below does the same thing a second time 

     Object newClassInstance2 = m2("com.tima.Helper3", "public String createList() { System.out.println(\"++++\"); return \"300\";}"); 
     Method method2 = newClassInstance2.getClass().getMethod("createList"); 
     String sum2 = (String) method2.invoke(newClassInstance2); 
     System.out.println("com.tima.Helper3: " + sum2); 

     Helper h3 = new Helper(); 
     System.out.println("com.tima.Helper: " + h3.createList()); 

     Helper h4 = (Helper) newClassInstance2; 
     System.out.println("com.tima.Helper3 as Helper: " + h4.createList()); 
    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
} 

public static Object m2(String className, String methodBody) 
     throws CannotCompileException, InstantiationException, IllegalAccessException, NotFoundException { 
    // get the pool 
    ClassPool classPool = ClassPool.getDefault(); 

    // this seems optional, but if it isn't Main.class (my test class) should be replaced with this.getClass() 
    classPool.insertClassPath(new ClassClassPath(Main.class)); 

    // get the helper class 
    CtClass helperClass = classPool.get("com.tima.Helper"); 

    // create a new class 
    CtClass newCtClass = classPool.makeClass(className); 

    // make it child of Helper 
    newCtClass.setSuperclass(helperClass); 

    // this overrides the method in Helper 
    newCtClass.addMethod(CtNewMethod.make(methodBody, newCtClass)); 

    // get a new instance 
    Class<?> newClass = newCtClass.toClass(); 
    Object newClassInstance = newClass.newInstance(); 

    return newClassInstance; 
} 

輸出

++++ 
com.tima.Helper2: 200 
++++ 
com.tima.Helper: 1000 
++++ 
com.tima.Helper2 as Helper: 200 
++++ 
com.tima.Helper3: 300 
++++ 
com.tima.Helper: 1000 
++++ 
com.tima.Helper3 as Helper: 300 

基本上,每次你怎麼稱呼它創建一個新的類,方法,使Helper類它的超類並覆蓋了createList方法。這裏顯而易見的問題是取決於你調用這個方法的次數,你可以用很多生成的類來結束。所以你可能想要在創建之前添加這些類是否已經加載的檢查。

你說這個方法在刷新/加載頁面的時候運行,所以如果你只是想讓它運行一次,我建議你把這個代碼放到你的應用程序啓動時運行的單例中。我不確定你用於Web應用程序的是什麼,但JSF和Spring都有這種類型的singleton bean。