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

Thi*_*ilo 194 java url classloader

在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
Run Code Online (Sandbox Code Playgroud)

这很好地将资源的实际加载与需要资源的应用程序分离,并且由于URL只是一个String,因此资源加载也很容易配置.

是否有使用当前类加载器加载资源的协议?这与Jar协议类似,不同之处在于我不需要知道资源来自哪个jar文件或类文件夹.

Class.getResourceAsStream("a.xml")当然,我可以使用它,但这需要我使用不同的API,因此更改现有代码.我希望能够在所有我可以通过更新属性文件指定资源URL的地方使用它.

Ste*_*hen 343

介绍和基本实施

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

用法

new URL("classpath:org/my/package/resource.extension").openConnection();
Run Code Online (Sandbox Code Playgroud)

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

发布问题

如果你和我一样,你不想依赖于在发布中设置的属性来让你到达某个地方(就我而言,我喜欢像Java WebStart一样保持我的选择 - 这就是为什么需要这一切).

解决方法/增强

手动代码处理程序规范

如果你控制代码,你可以做到

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

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

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

JVM Handler注册

最终的选择是注册一个URLStreamHandlerFactory将处理整个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);
    }
}
Run Code Online (Sandbox Code Playgroud)

要注册处理程序,请URL.setURLStreamHandlerFactory()使用配置的工厂进行调用.然后new URL("classpath:org/my/package/resource.extension")就像第一个例子那样离开你.

JVM处理程序注册问题

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

执照

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

  • 请注意,您还可以使用`System.setProperty()`来注册协议.像`System.setProperty("java.protocol.handler.pkgs","org.my.protocols"); (5认同)

小智 96

URL url = getClass().getClassLoader().getResource("someresource.xxx");
Run Code Online (Sandbox Code Playgroud)

应该这样做.

  • getResource和getResourceAsStream是不同的方法.同意getResourceAsStream不适合API,但getResource返回一个URL,这正是OP所要求的. (13认同)
  • "当然,我可以使用Class.getResourceAsStream("a.xml")来做到这一点,但这需要我使用不同的API,因此更改现有代码.我希望能够在所有地方使用它我可以通过更新属性文件来指定资源的URL." (10认同)
  • 我认为这是正确的答案.它避免了所有的配置混乱. (7认同)
  • OP 要求提供属性文件解决方案,但其他人也因为问题的标题而来到这里。他们喜欢这个动态解决方案:) (3认同)
  • -1正如Thilo指出的,这是OP所考虑并拒绝的。 (2认同)
  • 谢谢你的回答. (2认同)

eis*_*eis 12

我认为这是值得的答案 - 如果你使用的是Spring,你已经拥有了它

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

就像在spring文档中解释并在skaffman的评论中指出的那样.

  • 恕我直言,Spring [`ResourceLoader.getResource()`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/ResourceLoader.html#getResource-java. lang.String-) 更适合该任务(`ApplicationContext.getResource()` 在后台委托给它) (2认同)

sub*_*bes 10

您还可以在启动期间以编程方式设置属性:

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);
Run Code Online (Sandbox Code Playgroud)

使用这个类:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,您可以获得最少侵入性的方法.:) java.net.URL将始终使用系统属性中的当前值.

  • 仅当处理程序旨在处理尚未“已知”的协议(例如“gopher://”)时,才能使用添加额外包以查找“java.protocol.handler.pkgs”系统变量的代码。如果目的是覆盖“流行”协议,例如“file://”或“http://”,那么可能为时已晚,因为“java.net.URL#handlers”映射已经添加了该协议的“标准”处理程序。所以唯一的出路就是把这个变量传递给JVM。 (2认同)

mhv*_*und 8

从 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

创建一个java.net.spi.URLStreamHandlerProviderMETA-INF/services目录中调用的文件,内容如下:

org.example.ClasspathURLStreamHandlerProvider
Run Code Online (Sandbox Code Playgroud)

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

URL url = new URL("classpath:myfile.txt");
Run Code Online (Sandbox Code Playgroud)


Dil*_*nga 6

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

我不相信类路径中的内容有预定义的协议处理程序。(所谓classpath:协议)。

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

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

  • 您知道JVM附带哪些协议的列表吗? (4认同)

小智 5

我创建了一个类,该类有助于减少设置自定义处理程序时的错误,并利用了系统属性,因此首先调用方法或不在正确的容器中没有问题。如果您弄错了,还有一个异常类:

CustomURLScheme.java:
/*
 * The CustomURLScheme class has a static method for adding cutom protocol
 * handlers without getting bogged down with other class loaders and having to
 * call setURLStreamHandlerFactory before the next guy...
 */
package com.cybernostics.lib.net.customurl;

import java.net.URLStreamHandler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Allows you to add your own URL handler without running into problems
 * of race conditions with setURLStream handler.
 * 
 * To add your custom protocol eg myprot://blahblah:
 * 
 * 1) Create a new protocol package which ends in myprot eg com.myfirm.protocols.myprot
 * 2) Create a subclass of URLStreamHandler called Handler in this package
 * 3) Before you use the protocol, call CustomURLScheme.add(com.myfirm.protocols.myprot.Handler.class);
 * @author jasonw
 */
public class CustomURLScheme
{

    // this is the package name required to implelent a Handler class
    private static Pattern packagePattern = Pattern.compile( "(.+\\.protocols)\\.[^\\.]+" );

    /**
     * Call this method with your handlerclass
     * @param handlerClass
     * @throws Exception 
     */
    public static void add( Class<? extends URLStreamHandler> handlerClass ) throws Exception
    {
        if ( handlerClass.getSimpleName().equals( "Handler" ) )
        {
            String pkgName = handlerClass.getPackage().getName();
            Matcher m = packagePattern.matcher( pkgName );

            if ( m.matches() )
            {
                String protocolPackage = m.group( 1 );
                add( protocolPackage );
            }
            else
            {
                throw new CustomURLHandlerException( "Your Handler class package must end in 'protocols.yourprotocolname' eg com.somefirm.blah.protocols.yourprotocol" );
            }

        }
        else
        {
            throw new CustomURLHandlerException( "Your handler class must be called 'Handler'" );
        }
    }

    private static void add( String handlerPackage )
    {
        // this property controls where java looks for
        // stream handlers - always uses current value.
        final String key = "java.protocol.handler.pkgs";

        String newValue = handlerPackage;
        if ( System.getProperty( key ) != null )
        {
            final String previousValue = System.getProperty( key );
            newValue += "|" + previousValue;
        }
        System.setProperty( key, newValue );
    }
}


CustomURLHandlerException.java:
/*
 * Exception if you get things mixed up creating a custom url protocol
 */
package com.cybernostics.lib.net.customurl;

/**
 *
 * @author jasonw
 */
public class CustomURLHandlerException extends Exception
{

    public CustomURLHandlerException(String msg )
    {
        super( msg );
    }

}
Run Code Online (Sandbox Code Playgroud)


Dan*_*eón 5

灵感来自@Stephen /sf/answers/123861811/http://docstore.mik.ua/orelly/java/exp/ch09_06.htm

使用

new URL("classpath:org/my/package/resource.extension").openConnection()
Run Code Online (Sandbox Code Playgroud)

只需将这个类创建到sun.net.www.protocol.classpath包中并将其运行到 Oracle JVM 实现中,就可以像魅力一样工作。

package sun.net.www.protocol.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(URL u) throws IOException {
        return Thread.currentThread().getContextClassLoader().getResource(u.getPath()).openConnection();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您使用另一个 JVM 实现,请设置java.protocol.handler.pkgs=sun.net.www.protocol系统属性。

仅供参考:http : //docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL( java.lang.String,%20java.lang.String,%20int,% 20java.lang 。细绳)