在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的地方使用它,只需更新属性文件即可。


当前回答

如果你在类路径中有tomcat,它就像这样简单:

TomcatURLStreamHandlerFactory.register();

这将为“war”和“classpath”协议注册处理程序。

其他回答

介绍和基本实现

首先,您至少需要一个URLStreamHandler。这实际上会打开到给定URL的连接。注意,这被简单地称为Handler;这允许你指定java -Djava.protocol.handler.pkgs=org.my。协议,它将自动被拾取,使用“简单”包名作为支持的协议(在本例中为“classpath”)。

使用

new URL("classpath:org/my/package/resource.extension").openConnection();

Code

package org.my.protocols.classpath;

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

/** A {@link URLStreamHandler} that handles resources on the classpath. */
public class Handler extends URLStreamHandler {
    /** The classloader to find resources from. */
    private final ClassLoader classLoader;

    public Handler() {
        this.classLoader = getClass().getClassLoader();
    }

    public Handler(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

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

启动问题如果你和我一样,你不想依赖于在启动中设置的属性来到达某个地方(在我的情况下,我喜欢像Java WebStart那样保持我的选项开放-这就是为什么我需要所有这些)。

解决方案/增强

手动代码处理程序规范

如果你能控制代码,你就能做到

new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))

这将使用您的处理程序打开连接。

但同样,这不是令人满意的,因为你不需要一个URL来做这件事——你想这样做是因为一些你不能(或不想)控制的库想要URL…

JVM Handler注册

最终的选择是注册一个URLStreamHandlerFactory,它将处理jvm中的所有url:

package my.org.url;

import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.HashMap;
import java.util.Map;

class ConfigurableStreamHandlerFactory implements URLStreamHandlerFactory {
    private final Map<String, URLStreamHandler> protocolHandlers;

    public ConfigurableStreamHandlerFactory(String protocol, URLStreamHandler urlHandler) {
        protocolHandlers = new HashMap<String, URLStreamHandler>();
        addHandler(protocol, urlHandler);
    }

    public void addHandler(String protocol, URLStreamHandler urlHandler) {
        protocolHandlers.put(protocol, urlHandler);
    }

    public URLStreamHandler createURLStreamHandler(String protocol) {
        return protocolHandlers.get(protocol);
    }
}

要注册处理程序,请使用已配置的工厂调用URL.setURLStreamHandlerFactory()。然后像第一个例子那样输入新的URL("classpath:org/my/package/resource.extension"),然后就可以继续了。

JVM处理器注册问题

请注意,每个JVM只能调用该方法一次,并且请注意Tomcat将使用此方法注册JNDI处理程序(AFAIK)。试试Jetty(我会的);在最坏的情况下,您可以先使用方法,然后它必须在您周围工作!

许可证

我将此发布到公共领域,并询问如果您希望修改,您将在某处启动OSS项目并在这里评论详细信息。一个更好的实现是有一个URLStreamHandlerFactory,它使用ThreadLocals来存储每个Thread.currentThread(). getcontextclassloader()的URLStreamHandlers。我甚至会给你我的修改和测试课程。

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

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

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

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

我试图避免URL类,而是依赖URI。因此,对于需要URL的事情,我想做Spring资源查找,我做以下工作:

public static URL toURL(URI u, ClassLoader loader) throws MalformedURLException {
    if ("classpath".equals(u.getScheme())) {
        String path = u.getPath();
        if (path.startsWith("/")){
            path = path.substring("/".length());
        }
        return loader.getResource(path);
    }
    else if (u.getScheme() == null && u.getPath() != null) {
        //Assume that its a file.
        return new File(u.getPath()).toURI().toURL();
    }
    else {
        return u.toURL();
    }
}

要创建一个URI,可以使用URI.create(..)。这种方式也更好,因为您控制了将执行资源查找的ClassLoader。

我注意到其他一些答案试图将URL解析为字符串以检测该方案。我认为最好是传递URI并使用它来解析。

事实上,我在不久前提出了一个问题,要求Spring Source将他们的资源代码从核心中分离出来,这样你就不需要所有其他Spring的东西了。

从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");

我认为这是值得回答的问题——如果您正在使用Spring,那么您已经有了这个功能

Resource firstResource =
    context.getResource("http://www.google.fi/");
Resource anotherResource =
    context.getResource("classpath:some/resource/path/myTemplate.txt");

就像春季文档中解释的,斯卡夫曼在评论中指出的那样。