如何在Java类路径中正确加载来自网络目录的资源?

4

我们的Java应用程序依赖于一些位于网络共享上的资源。这个网络共享位于类路径上,资源在运行时使用MyClass.class.getResourceAsStream("/myfile.jpg")读取。

java -Djava.class.path=\\myserver\myshare:C:\myjar.jar MainClass

当共享文件在启动时可用时,一切都运行顺利。位于共享文件中的图像和属性文件可以使用getResourceAsStream()读取。然而,如果应用程序启动时共享不在线上,即使在读取任何资源之前共享上线,也无法使用getResourceAsStream()读取它们。
通过使用eclispse +反编译器进行一些挖掘,我注意到一个区别。默认的类加载器继承自URLClassLoader,并且其ucp成员(URLClassPath)包含URLClassPath.Loader实例列表。在第一种情况下,它包含一个URLClassPath.FileLoader和一个URLClassPath.JarLoader。在第二种情况下,它只包含一个jar加载器。
就好像Java确定了类路径条目是无效的并完全舍弃了它。
为什么会这样?我该如何避免?
更新: 由于以下原因,我无法更改我们正在加载资源的机制:
1. 目前有太多区域以这种方式加载文件,我暂时无法更改 2. 有些情况下,资源实际上是由第三方组件加载的
我没有问题创建自定义类加载器,我只需要一些指导。
我尝试过这个,但无法获得预期的结果:
import java.net.URL;
import java.net.URLClassLoader;

public class MyUrlClassLoader extends URLClassLoader {
    public MyUrlClassLoader(ClassLoader parent) {
        super(new URL[0], parent);
        System.out.println("MyUrlClassLoader ctor");
    }

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        System.out.println("url find class " + name);
        return super.findClass(name);
    }

    @Override
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        System.out.println("url load class " + name);
        return super.loadClass(name);
    }

    @Override
    public URL getResource(String name) {
        System.out.println("url get resource " + name);
        return super.getResource(name);
    }
}


import java.net.URL;

public class ClassLoaderMain {
    public static void main(String[] args) throws ClassNotFoundException {
        URL url = ClassLoaderMain.class.getResource("/myfile.txt");
        System.out.print("Loaded? ");
        System.out.println(url != null);

        System.out.println(ClassLoaderMain.class.getClassLoader().toString());
        System.out.println(MyUrlClassLoader.class.getClassLoader().toString());
        System.out.println(FakeClass.class.getClassLoader().toString());
    }
}

当我运行java -cp . -Djava.system.class.loader=MyUrlClassLoader ClassLoaderMain时,
会输出:
MyUrlClassLoader ctor
url load class java.lang.System
url load class java.nio.charset.Charset
url load class java.lang.String
url load class ClassLoaderMain
Loaded? true
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30
sun.misc.Launcher$AppClassLoader@923e30

我的类加载器正在创建,load class方法被调用,但它似乎不是加载所需类的类加载器?


最安全的方法可能是编写自己的类加载器。这意味着您完全掌控。 - biziclop
我该如何在简单情况下完成这个任务?到目前为止,我看到的所有资源似乎都是在运行时按名称加载类(例如Class.forName(“string”))。我不想每次调用getResourceAsStream()时都手动创建一个新的类加载器并调用它。 - Travis
@Travis:你可以使用java.net.URL API来定位和读取资源,从而避免类加载器问题。 - ag112
为什么不能使用File和FileInputStream来访问文件? - pillingworth
以上只是一个更为简单的示例,真实应用程序所做的事情要复杂得多。真实应用程序实际上在JBoss容器中运行,但我已经使用一个简单的Java应用程序复制了这个问题。当外部代码(JBoss本身)加载自己的配置文件时,我也遇到了这个问题。 - Travis
如果您留下答案,我会接受它。 - Travis
1个回答

1

我最终通过创建自己的ClassLoader,并从URLClassLoader派生解决了这个问题。

import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class CustomClassLoader extends URLClassLoader {

    public CustomClassLoader(ClassLoader parent) {
        // System classloader will filter inaccessible URLs. 
        // Force null parent to avoid using system classloader.
        super(createURLReferences(), null);
    }

    /**
     * Build an array of URLs based on the java.class.path property.
     * @return An array of urls to search for classes.
     */
    private static URL[] createURLReferences() {
        String classpath = System.getProperty("java.class.path");
        String[] classpathEntries = classpath.split(System.getProperty("path.separator"));
        List<URL> urls = new ArrayList<URL>();
        for (String classpathEntry : classpathEntries) {
            File classpathFile = new File(classpathEntry);
            URI uri = classpathFile.toURI();
            try {
                URL url = uri.toURL();
                urls.add(url);
            } catch (MalformedURLException e) {
                System.out.println("Ignoring classpath entry: " + classpathEntry);
            }
        }

        return urls.toArray(new URL[urls.size()]);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接