【问题标题】:How does the class loader loads an instance of extended class which is declared inside a method? (java.lang.NoClassDefFoundError)类加载器如何加载在方法中声明的扩展类的实例? (java.lang.NoClassDefFoundError)
【发布时间】:2019-03-18 11:15:52
【问题描述】:

我有一个具有实用功能的类,可以按需调用。

复制步骤:

  1. TestMainWebApp(这个项目有TestMainImp的依赖)
  2. TestMainImpl(本项目实现TestMainInterface)
  3. TestMainInterface

TestMainWebApp > TestMainServlet.java

package com.main;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.test.util.Util;
/**
 * Servlet implementation class TestMainServlet
 */
@WebServlet("/TestMainServlet")
public class TestMainServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {   
         System.out.println("\nCurrent ClassLoader chain: "+JavaEETrainingUtil.getCurrentClassloaderDetail());
         Util prov = new Util();
        // prov.test();
        }
}

TestMainImpl > Util.java & TLSErrorRedirectListener.java

package com.test.util;
public class Util {
     private final static String CLAZZ = Util.class.getName();
    static {
            System.out.println("Classloading of "+CLAZZ+" in progress..."+JavaEETrainingUtil.getCurrentClassloaderDetail());

     }
        public boolean checkForTLSErrorRedirection(boolean b) {                         
            test.intf.ConfigurationListener listener = new com.listener.TLSErrorRedirectListener();   
            listener.valueChanged("test", "test");
            return true;

        }    

public void test() {  
        System.out.println(" test util");  
    }

}

TLSErrorRedirectionListener.java

package com.listener;
import com.test.util.JavaEETrainingUtil;
public class TLSErrorRedirectListener implements test.intf.ConfigurationListener {
     final static String CLAZZ = TLSErrorRedirectListener.class.getName();
     static {
            System.out.println("Classloading of "+CLAZZ+" in progress..."+JavaEETrainingUtil.getCurrentClassloaderDetail());
     }
    public void valueChanged(String key, String value) {
        switch(key){
        case "test1": 
        default : break;
        }
    }
}

TestMainInterface >ConfigurationListener.java

package test.intf;
public abstract interface ConfigurationListener
{
  public abstract void valueChanged(String paramString1, String paramString2);
}

案例:

  1. TestMainInterface.jar 将位于 TestMainImpl.jar 的类路径中(仅限@compiletime)
  2. 在运行时(我不会有 TestMainInterface.jar)并且我不调用方法“checkForTLSErrorRedirection()”。我只调用 test() 方法。
  3. 但我得到了,java.lang.NoClassDefFoundError: test/intf/ConfigurationListener
    当我创建实例本身时。

你能帮忙找出根本原因吗? java如何加载方法中声明的类?

注意:JavaEETrainingUtil.java 用于调试目的

package noclassdef.example1;
import java.util.Stack;
import java.lang.ClassLoader;

/**
 * JavaEETrainingUtil
 * @author Pierre-Hugues Charbonneau
 *
 */
public class JavaEETrainingUtil {

        /**
         * getCurrentClassloaderDetail
         * @return
         */
        public static String getCurrentClassloaderDetail() {

               StringBuffer classLoaderDetail = new StringBuffer();       
               Stack<ClassLoader> classLoaderStack = new Stack<ClassLoader>();

               ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();

               classLoaderDetail.append("\n-----------------------------------------------------------------\n");

               // Build a Stack of the current ClassLoader chain
               while (currentClassLoader != null) {

                       classLoaderStack.push(currentClassLoader);

                       currentClassLoader = currentClassLoader.getParent();
               }

               // Print ClassLoader parent chain
               while(classLoaderStack.size() > 0) {

                       ClassLoader classLoader = classLoaderStack.pop();

                       // Print current                     
                       classLoaderDetail.append(classLoader);

                       if (classLoaderStack.size() > 0) {
                              classLoaderDetail.append("\n--- delegation ---\n");           } else {
                              classLoaderDetail.append(" **Current ClassLoader**");
                       }
               }
               classLoaderDetail.append("\n-----\n");
               return classLoaderDetail.toString();
        }
}

【问题讨论】:

    标签: java noclassdeffounderror contextclassloader


    【解决方案1】:

    这是不正确的。您不应将 abstract 关键字添加到接口或其方法中。接口方法上不需要public;它们必须是公开的。

    这是正确的:

    package test.intf;
    public interface ConfigurationListener {
      void valueChanged(String paramString1, String paramString2);
    }
    

    确保test.intf.ConfigurationListener.class 文件出现在您的部署中。如果没有,类加载器将永远找不到它。

    【讨论】:

    • 谢谢,我的用例是,如果我调用 checkForTLSErrorRedirection() API,我的类路径中会有 test.intf.ConfigurationListener.class。如果我只需要 test() API,我不想拥有 test.intf.ConfigurationListener.class。但是为什么我在调用 test() API 时会得到 java.lang.NoClassDefFoundError: test/intf/ConfigurationListener ?为什么类加载器试图找到仅在 checkForTLSErrorRedirection() 中定义的 ConfigurationListener 类?
    • 如果我移动 `test.intf.ConfigurationListener listener = new com.listener.TLSErrorRedirectListener(); 我能够实现我的用例listener.valueChanged("test", "test");` 到其他一些 util 文件。但找不到背后的原因
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    相关资源
    最近更新 更多