在 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 的所有地方使用它,只需更新属性文件即可。
Class.getResourceAsStream("a.xml")
首先,您至少需要一个 URLStreamHandler。这实际上将打开到给定 URL 的连接。请注意,这只是简单地调用Handler;这允许您指定java -Djava.protocol.handler.pkgs=org.my.protocols并且它会自动被拾取,使用“简单”包名称作为支持的协议(在本例中为“类路径”)。
Handler
java -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 来执行此操作 - 您想要这样做是因为您无法(或不想)控制的某些库想要 url…
最终的选择是注册一个URLStreamHandlerFactory将处理整个 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")像第一个例子一样,然后离开。
URL.setURLStreamHandlerFactory()
new URL("classpath:org/my/package/resource.extension")
请注意,每个 JVM 只能调用此方法一次,请注意 Tomcat 将使用此方法注册 JNDI 处理程序 (AFAIK)。试试码头(我会的);在最坏的情况下,您可以先使用该方法,然后它必须在您周围工作!
我将其发布到公共领域,并询问您是否希望修改您在某处启动 OSS 项目并在此处评论详细信息。 更好的实现是URLStreamHandlerFactory使用s为每个ThreadLocals 存储s 。我什至会给你我的修改和测试课程。URLStreamHandler``Thread.currentThread().getContextClassLoader()
ThreadLocal
URLStreamHandler``Thread.currentThread().getContextClassLoader()