【问题标题】:Java URLClassLoader not recognising a StringJava URLClassLoader 无法识别字符串
【发布时间】:2013-02-17 11:49:07
【问题描述】:

我遇到了一个非常奇怪的问题。

我正在使用 URLClassLoader 从目录中动态导入文件。如果我使用文字字符串,代码可以正常工作,如果我将变量用于文字字符串,代码也可以正常工作,但这不是我需要的。

package test;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
    public static void main(String[] args) {

        try {
            File subfolder = new File("C:\\temp\\");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {

                String name = f.getName()
                        .substring(0, f.getName().lastIndexOf(".")).trim();
                if (name.equals("TestClass"))
                        System.out.println(name);
                try {
                    MyInterface de = (MyInterface) Class.forName("TestClass", true, ucl)
                            .newInstance();
                    de.printSomething();
                } catch (ClassNotFoundException e) {
                }

                ucl.close();

            }
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

我需要的是能够做到这一点:

MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();

但即使“name”是一个有效的字符串并且等于“TestClass”,它也不起作用。

编辑:我得到错误:

java.lang.ClassNotFoundException: TestClass
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.net.URLClassLoader$1.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Unknown Source)
    at test.Test.main(Test.java:25)

怎么了?

【问题讨论】:

  • 当你说它对你有用时,它是如何工作的?您是否确保可以正确使用该类(即初始化并执行某些方法)?
  • 我正在使用此处显示的示例:stackoverflow.com/questions/2786416/… 当我说它有效时,我的意思是上面编写的代码打印出“Hello World,来自 TestClass”,就像它应该的那样,但是使用ucl.loadClass(STRING) 或 Class.forName(STRING, true, ucl) 中的变量而不是文字字符串“TestClass”会引发异常。即使在上面的代码中 name.equals("TestClass") 是真的。它们完全一样。
  • 您确定TestClass.class 存在于文件夹c:\temp 中吗?因为看起来您在该目录中有TestClass.java 而不是TestClass.class,因为您尚未测试文件名endswith .class String..
  • 上面的代码确实可以完美运行,而且应该可以。绝对是 C:\temp 中的 TestClass.class
  • 那么如果上面的代码完美运行,问题是什么?

标签: java string file-handling urlclassloader


【解决方案1】:

我猜这是因为 for 循环中的 ucl.close() 。如果有目录,我添加了一些测试:如果在根包中声明,并且如果 eclipse 配置为在“bin”目录中生成 .class 文件,则以下类可以工作并实例化自身:

public class Toto {

    public Toto(){
        // this writes "Toto" in the console if the class is well instanciated
        System.out.println("Toto");
    }

    public static void main(String[] args) {
        try {
            File subfolder = new File("bin");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);

            for (File f : subfolder.listFiles()) {
                String fileName= f.getName();
                int suffix = fileName.lastIndexOf('.');
                if(f.isDirectory() || suffix==-1){
                    continue;
                }
                String name = fileName.substring(0, suffix);
                try {
                    Class.forName(name, true, ucl).newInstance();

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
            ucl.close();
        } catch (MalformedURLException e) {
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

【讨论】:

    【解决方案2】:

    您的问题似乎与您正在加载的类有一个包有关。当 Java 加载这些类时,它希望找到与该包相关的目录结构。因此,以下代码有效:

    try {
            File subfolder = new File("/home/glen/TestClass");
            URL classUrl = subfolder.toURI().toURL();
            URL[] classUrls = { classUrl };
            URLClassLoader ucl = new URLClassLoader(classUrls);
    
            for (File f : subfolder.listFiles()[0].listFiles()) {
    
                System.out.println(f.getName());
    
                String name = f.getName()
                        .substring(0, f.getName().lastIndexOf(".")).trim();// "TestClass";
                if (name.equals("TestClass"))
                        System.out.println(name);
                try {
                    MyInterface de = (MyInterface) Class.forName("test." + name, true, ucl)
                            .newInstance();
                    de.printSomething();
                } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }
    
                ucl.close();
    
            }
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    

    注意我的情况:

    1. Class.forName 方法中指定完全限定的类名test.TestClass
    2. 枚举加载文件夹的第一个子目录(在我的例子中是“test”目录)。 TestClass.class 文件位于其中。如果我尝试直接通过那里加载,无论是否指定包名,都会失败。

    本质上,URLClassLoader 需要一个类似 JAR 的目录结构,如 test/TestClass.class,同时具有一个 包含目录结构的 URL 根。

    我的工作理论是,您不只是将 name 变量更改为字符串文字,当我这样做时它仍然可以正常工作。仔细检查你没有改变任何其他东西。无论哪种方式,我希望这会为您指明正确的方向。

    【讨论】:

      【解决方案3】:

      怎么了?

      错误的第一件事是这不是真正的代码。此代码捕获并忽略 ClassNotFoundException:

      } catch (ClassNotFoundException e) {
      }
      

      所以它不可能产生所示的输出。

      第二个错误是,当它被纠正后,代码按预期工作。

      无法复制。

      很明显,您没有运行您认为正在运行的代码。 Java 根本不像声称的那样运行。

      【讨论】:

        【解决方案4】:

        代替

        MyInterface de = (MyInterface) Class.forName(name, true, ucl).newInstance();
        

        使用

        MyInterface de = (MyInterface) Class.forName(name + "Class", true, ucl).newInstance();
        

        为我工作。

        【讨论】:

        • 仅当 name = "Test" 并且 OP 明确声明它是 "TestClass"。所以很难理解你在测试什么。但是也无法理解 OP 正在测试什么,因为他的代码和 Java 都没有像描述的那样运行。
        猜你喜欢
        • 1970-01-01
        • 2017-12-31
        • 2013-11-30
        • 1970-01-01
        • 1970-01-01
        • 2018-05-10
        • 2019-02-18
        • 2021-03-03
        • 2020-12-18
        相关资源
        最近更新 更多