java javassist.CannotCompileException:通过java.lang.LinkageError:加载程序 [英] java javassist.CannotCompileException: by java.lang.LinkageError: loader

查看:1027
本文介绍了java javassist.CannotCompileException:通过java.lang.LinkageError:加载程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要更改方法(计算公式)而不重新编译应用程序.我知道可以在javassist的帮助下完成此操作.到目前为止,我正在尝试一个简单的示例.在post方法中,我调用createMethodHelper()方法,该方法必须更改Helper2方法.一切都好.但是在重复调用(重新加载页面)之后,错误javassist.CannotCompileException:通过java.lang.LinkageError:加载程序 因此,包含我要更改的唯一方法的类

I need to change the method (calculation formula) without recompiling the application. I know that this can be done with the help of javassist. So far I'm trying on a simple example. In the post method, I call the createMethodHelper () method, which must change the Helper2 method. All OK. But after a repeated call (reloading the page), the error javassist.CannotCompileException: by java.lang.LinkageError: loader So, the class containing the only method I want to change

package ru.testScandJavaCafee.controller;

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

以及我用来更改方法的类

And the class from which I change the method

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

第二次调用(重新加载页面)后,出现错误

After a second call (reloading the page), the error

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

帮助了解如何纠正错误

推荐答案

我从文档和此教程.而且,如果您想多次修改方法,则可以通过以下方式进行修改:

I did some research on this from the documentation and this tutorial. And if you want to modify the method multiple times, you can do it this way:

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方法.这里最明显的问题是取决于调用此方法的次数,最后可以使用许多生成的类.因此,您可能需要添加在创建之前检查这些类是否已加载的检查.

Basically, everytime you call that method it creates a new class, makes the Helper class its superclass and overrides the createList method. The obvious problem here is depending on how many times you call this method you can wind up with a lot of generated classes. So you may want to add checking of if these class are already loaded before creating.

您说此方法在刷新/加载页面时运行,因此,如果您只希望运行一次,建议您将此代码放在应用程序启动时运行的单例中.我不确定您要为Web应用程序使用什么,但是JSF和Spring都具有用于这种类型使用的Singleton bean.

You say that this method runs when you refresh / load the page so if you just want it to run once, I suggest you put this code in a singleton that runs when your application starts. I'm not sure what you are using for the web application but JSF and Spring both have singleton beans for this type of use.

这篇关于java javassist.CannotCompileException:通过java.lang.LinkageError:加载程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆