且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-01-11 10:48:40

简介和基本实现



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

Intro and basic Implementation

First up, you're going to need at least a URLStreamHandler. This will actually open the connection to a given URL. Notice that this is simply called Handler; this allows you to specify java -Djava.protocol.handler.pkgs=org.my.protocols and it will automatically be picked up, using the "simple" package name as the supported protocol (in this case "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一样保持我的选择 - 这就是为什么需要所有这些)。

Launch issues

If you're anything like me, you don't want to rely on a property being set in the launch to get you somewhere (in my case, I like to keep my options open like Java WebStart - which is why I need all this).

如果您控制代码,则可以执行

If you control the code, you can do

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

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

and this will use your handler to open the connection.

但是,这不太令人满意,因为你不需要一个URL到这样做 - 你想这样做,因为你不能(或不想)控制的某些lib需要url ...

But again, this is less than satisfactory, as you don't need a URL to do this - you want to do this because some lib you can't (or don't want to) control wants urls...

最终选项是注册 URLSt reamHandlerFactory 将处理jvm中的所有网址:

The ultimate option is to register a URLStreamHandlerFactory that will handle all urls across the jvm:

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),就像第一个例子一样,然后离开。

To register the handler, call URL.setURLStreamHandlerFactory() with your configured factory. Then do new URL("classpath:org/my/package/resource.extension") like the first example and away you go.

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

Note that this method may only be called once per JVM, and note well that Tomcat will use this method to register a JNDI handler (AFAIK). Try Jetty (I will be); at worst, you can use the method first and then it has to work around you!

我将此版本发布到公共领域,并询问您是否希望修改它在某处启动OSS项目并在此处评论详细信息。更好的实现是使用 URLStreamHandlerFactory 使用 ThreadLocal 来存储 URLStreamHandler 每个 Thread.currentThread()。getContextClassLoader()的code> s。我甚至会给你我的修改和测试课程。

I release this to the public domain, and ask that if you wish to modify that you start a OSS project somewhere and comment here with the details. A better implementation would be to have a URLStreamHandlerFactory that uses ThreadLocals to store URLStreamHandlers for each Thread.currentThread().getContextClassLoader(). I'll even give you my modifications and test classes.