用于从 Java 中的类路径加载资源的 URL

2022-08-31 06:04:18

在 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。


答案 1

介绍和基本实现

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

用法

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

法典

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来做到这一点 - 你想这样做,因为一些你不能(或不想)控制的库想要网址......

JVM 处理程序注册

最终的选择是注册一个将处理jvm上所有URL的:URLStreamHandlerFactory

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()new URL("classpath:org/my/package/resource.extension")

JVM 处理程序注册问题

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

许可证

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


答案 2
URL url = getClass().getClassLoader().getResource("someresource.xxx");

这应该可以做到。