2017-05-08 7 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"; 
    } 
} 

을 변경할 수있는 유일한 방법 그리고 클래스를 포함한 클래스 어떤에서 나는 오류를 수정하는 방법을 이해하기 위해 두 번째 호출 (다시로드 페이지), 오류

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 

도움 후에 방법

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; 
    } 

변경

+0

복제 된 클래스 정의'name : "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 메서드를 오버라이드 (override)합니다. 여기서 명백한 문제는 생성 된 많은 클래스로이 메소드를 호출 한 횟수에 달려있다. 따라서 생성하기 전에이 클래스가 이미로드되어 있는지 검사를 추가 할 수 있습니다.

이 방법은 페이지를 새로 고침 /로드 할 때 실행되므로 한 번만 실행하려면이 코드를 응용 프로그램이 시작될 때 실행되는 싱글 톤에 넣는 것이 좋습니다. 나는 당신이 웹 애플리케이션을 위해 무엇을 사용하고 있는지 확실하지 않지만 JSF와 Spring은이 유형의 싱글 톤 빈을 가지고있다.