在Java中,你可以使用相同的API但使用不同的URL协议加载各种资源:

file:///tmp.txt
http://127.0.0.1:8080/a.properties
jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

这很好地将资源的实际加载与需要资源的应用程序分离开来,而且由于URL只是一个字符串,资源加载也非常容易配置。

是否存在使用当前类加载器加载资源的协议? 这与Jar协议类似,只是我不需要知道资源来自哪个Jar文件或类文件夹。

当然,我可以使用Class.getResourceAsStream(“a.xml”)做到这一点,但这需要我使用不同的API,因此需要对现有代码进行更改。我希望能够在所有可以为资源指定URL的地方使用它,只需更新属性文件即可。


当前回答

(类似于Azder的回答,但策略略有不同。)

我认为对于类路径中的内容没有预定义的协议处理程序。(所谓的类路径:协议)。

但是,Java允许您添加自己的协议。这是通过提供具体实现java.net.URLStreamHandler和java.net.URLConnection来实现的。

本文描述了如何实现自定义流处理程序: http://java.sun.com/developer/onlineTraining/protocolhandlers/。

其他回答

我不知道是否已经有了,但你可以很容易地自己做。

在我看来,不同协议的例子就像外观模式。当每种情况都有不同的实现时,您就有了一个公共接口。

您可以使用相同的原理,创建一个ResourceLoader类,它从属性文件中获取字符串,并检查我们的自定义协议

myprotocol:a.xml
myprotocol:file:///tmp.txt
myprotocol:http://127.0.0.1:8080/a.properties
myprotocol:jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

从字符串开始剥离myprotocol,然后决定用哪种方式加载资源,只给你资源。

再延伸一下Dilums的回答:

在不更改代码的情况下,您可能需要按照Dilum的建议,使用URL相关接口的自定义实现。为了简化事情,我建议查看Spring Framework的资源的源代码。虽然代码不是流处理程序的形式,但它的设计完全是为了做您想做的事情,并且是在ASL 2.0许可下进行的,这使得它足够友好,可以在您的代码中重用。

当然,使用注册URLStreamHandlers的解决方案是最正确的,但有时需要最简单的解决方案。所以,我使用下面的方法:

/**
 * Opens a local file or remote resource represented by given path.
 * Supports protocols:
 * <ul>
 * <li>"file": file:///path/to/file/in/filesystem</li>
 * <li>"http" or "https": http://host/path/to/resource - gzipped resources are supported also</li>
 * <li>"classpath": classpath:path/to/resource</li>
 * </ul>
 *
 * @param path An URI-formatted path that points to resource to be loaded
 * @return Appropriate implementation of {@link InputStream}
 * @throws IOException in any case is stream cannot be opened
 */
public static InputStream getInputStreamFromPath(String path) throws IOException {
    InputStream is;
    String protocol = path.replaceFirst("^(\\w+):.+$", "$1").toLowerCase();
    switch (protocol) {
        case "http":
        case "https":
            HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();
            int code = connection.getResponseCode();
            if (code >= 400) throw new IOException("Server returned error code #" + code);
            is = connection.getInputStream();
            String contentEncoding = connection.getContentEncoding();
            if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip"))
                is = new GZIPInputStream(is);
            break;
        case "file":
            is = new URL(path).openStream();
            break;
        case "classpath":
            is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path.replaceFirst("^\\w+:", ""));
            break;
        default:
            throw new IOException("Missed or unsupported protocol in path '" + path + "'");
    }
    return is;
}

你也可以在启动时以编程方式设置属性:

final String key = "java.protocol.handler.pkgs";
String newValue = "org.my.protocols";
if (System.getProperty(key) != null) {
    final String previousValue = System.getProperty(key);
    newValue += "|" + previousValue;
}
System.setProperty(key, newValue);

使用这个类:

package org.my.protocols.classpath;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;

public class Handler extends URLStreamHandler {

    @Override
    protected URLConnection openConnection(final URL u) throws IOException {
        final URL resourceUrl = ClassLoader.getSystemClassLoader().getResource(u.getPath());
        return resourceUrl.openConnection();
    }
}

这样你就得到了侵入性最小的方法。url将始终使用系统属性中的当前值。

从Java 9+及更高版本开始,您可以定义一个新的URLStreamHandlerProvider。URL类使用服务加载器框架在运行时加载它。

创建一个提供者:

package org.example;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.net.spi.URLStreamHandlerProvider;

public class ClasspathURLStreamHandlerProvider extends URLStreamHandlerProvider {

    @Override
    public URLStreamHandler createURLStreamHandler(String protocol) {
        if ("classpath".equals(protocol)) {
            return new URLStreamHandler() {
                @Override
                protected URLConnection openConnection(URL u) throws IOException {
                    return ClassLoader.getSystemClassLoader().getResource(u.getPath()).openConnection();
                }
            };
        }
        return null;
    }

}

在META-INF/services目录中创建一个名为java.net.spi.URLStreamHandlerProvider的文件,内容如下:

org.example.ClasspathURLStreamHandlerProvider

现在URL类将在看到如下内容时使用提供程序:

URL url = new URL("classpath:myfile.txt");