source

Java의 클래스 경로에서 리소스를 로드하는 URL

manysource 2022. 10. 25. 17:59

Java의 클래스 경로에서 리소스를 로드하는 URL

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 파일 또는 클래스 폴더에서 왔는지 알 필요가 없습니다.

물론 그것을 사용할 수 있지만, 다른 API를 사용해야 하기 때문에 기존 코드로 변경합니다.속성 파일을 갱신하는 것만으로 자원의 URL을 지정할 수 있는 모든 장소에서 사용할 수 있도록 하고 싶습니다.

도입 및 기본 구현

「URL 스트림 핸들러」를 선택합니다., 특정의 「URL」이라고 불립니다.Handler를할 수 .「」를 지정할 수 있습니다.java -Djava.protocol.handler.pkgs=org.my.protocols지원되는 프로토콜(이 경우 "classpath")로 "package" 패키지 이름을 사용하여 자동으로 픽업됩니다.

사용.

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();
    }
}

기동 문제

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).

회피책/확장

수동 코드 핸들러 사양

코드를 제어하면

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

그러면 핸들러를 사용하여 연결을 엽니다.

그러나 이것도 만족스럽지 않습니다.URL이 필요 없기 때문입니다.제어할 수 없는(또는 제어하고 싶지 않은) lib가 URL을 원하기 때문입니다.

JVM 핸들러 등록

입니다.URLStreamHandlerFactoryjvm의 을 모두 합니다.

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별로 1회만 호출할 수 있으며 Tomcat은 이 메서드를 사용하여 JNDI 핸들러(AFAIK)를 등록합니다.Jetty를 시험해 보세요.최악의 경우, 먼저 이 방법을 사용하고 나서, 그 후에 주변에서 동작할 필요가 있습니다.

면허증.

이것을 퍼블릭 도메인에 공개합니다만, 수정을 희망하시는 경우는, 어딘가에서 OSS 프로젝트를 개시해 주시고, 상세한 것에 대해서는 이쪽에서 코멘트를 해 주세요.더 나은 구현은 다음과 같습니다.URLStreamHandlerFactory 쓰죠.ThreadLocalURLStreamHandler ""에 s"Thread.currentThread().getContextClassLoader()수정사항도 알려드리고 시험 수업도 해드릴게요.

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

그 정도면 됐다.

이 답변은 그만한 가치가 있다고 생각합니다.스프링을 사용하고 있다면, 이미 Spring을 사용하고 있습니다.

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

스프링 문서에서 설명하고 skaffman의 코멘트에서 지적한 바와 같습니다.

기동시에, 다음의 속성을 프로그래밍 방식으로 설정할 수도 있습니다.

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

이 클래스 사용:

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();
    }
}

따라서 이 작업을 수행하는 데 가장 방해가 되지 않는 방법을 사용할 수 있습니다.:) java.net 를 참조해 주세요.URL은 항상 시스템 속성의 현재 값을 사용합니다.

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

}

하다라는 을 만들어 보세요.java.net.spi.URLStreamHandlerProvider META-INF/services다음과 같이 합니다.

org.example.ClasspathURLStreamHandlerProvider

여기서 URL 클래스는 다음과 같은 경우 공급자를 사용합니다.

URL url = new URL("classpath:myfile.txt");

(Azder의 답변과 비슷하지만 약간 다른 요령입니다.)

패스의 미리 것 classpath:( ( ( (

그러나 Java에서는 자체 프로토콜을 추가할 수 있습니다.이는 구체적인 구현 및 을 통해 이루어집니다.

이 문서에서는 커스텀스트림 핸들러의 실장 방법에 대해 설명합니다.http://java.sun.com/developer/onlineTraining/protocolhandlers/

커스텀 핸들러의 설정 오류를 줄이고 시스템 속성을 활용하여 먼저 메서드를 호출해도 문제가 없고 올바른 컨테이너에 들어가지 않아도 문제가 없는 클래스를 만들었습니다.틀렸을 경우 예외 클래스도 있습니다.

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

}

@Stephen https://stackoverflow.com/a/1769454/980442 및 http://docstore.mik.ua/orelly/java/exp/ch09_06.htm에서 영감을 얻습니다.

사용방법

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

는 '만들기'로 .sun.net.www.protocol.classpathOracle 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();
    }
}

JVM 하고 있는 는, 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.String)

물론 URLtream Handlers 등록 솔루션이 가장 정확하지만 가장 간단한 솔루션이 필요할 수 있습니다.그 때문에, 다음의 방법을 사용하고 있습니다.

/**
 * Opens a local file or remote resource represented by given path.
 * Supports protocols:
 * <ul>
 * <li>"file": file:///path/to/file/in/filesystem</li>
 * <li>"http" or "https": http://host/path/to/resource - gzipped resources are supported also</li>
 * <li>"classpath": classpath:path/to/resource</li>
 * </ul>
 *
 * @param path An URI-formatted path that points to resource to be loaded
 * @return Appropriate implementation of {@link InputStream}
 * @throws IOException in any case is stream cannot be opened
 */
public static InputStream getInputStreamFromPath(String path) throws IOException {
    InputStream is;
    String protocol = path.replaceFirst("^(\\w+):.+$", "$1").toLowerCase();
    switch (protocol) {
        case "http":
        case "https":
            HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();
            int code = connection.getResponseCode();
            if (code >= 400) throw new IOException("Server returned error code #" + code);
            is = connection.getInputStream();
            String contentEncoding = connection.getContentEncoding();
            if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip"))
                is = new GZIPInputStream(is);
            break;
        case "file":
            is = new URL(path).openStream();
            break;
        case "classpath":
            is = Thread.currentThread().getContextClassLoader().getResourceAsStream(path.replaceFirst("^\\w+:", ""));
            break;
        default:
            throw new IOException("Missed or unsupported protocol in path '" + path + "'");
    }
    return is;
}

이미 있는지는 모르겠지만, 직접 간단하게 만들 수 있습니다.

다른 프로토콜의 예는 내게 겉모양처럼 보인다.케이스마다 다른 실장이 있는 경우는, 공통의 인터페이스를 사용할 수 있습니다.

동일한 원리를 사용하여 속성 파일에서 문자열을 가져와 우리의 커스텀 프로토콜을 확인하는 ResourceLoader 클래스를 만들 수 있습니다.

myprotocol:a.xml
myprotocol:file:///tmp.txt
myprotocol:http://127.0.0.1:8080/a.properties
myprotocol:jar:http://www.foo.com/bar/baz.jar!/COM/foo/Quux.class

는 myprotocol:을 문자열의 선두에서 삭제한 후 리소스를 로드할 방법을 결정하고 리소스를 사용자에게 제공합니다.

Dilums의 답변 연장:

코드를 변경하지 않고 Dilum이 권장하는 대로 URL 관련 인터페이스의 커스텀 구현을 추진해야 합니다.간단하게 하기 위해 Spring Framework의 Resources 소스를 참조할 것을 권장합니다.이 코드는 스트림핸들러의 형식은 아니지만 원하는 대로 동작하도록 설계되어 있으며 ASL 2.0 라이선스를 받고 있기 때문에 코드 내에서 재사용할 수 있을 정도로 사용하기 편리합니다.

Spring Boot 앱에서 파일 URL을 얻기 위해 다음을 사용했습니다.

Thread.currentThread().getContextClassLoader().getResource("PromotionalOfferIdServiceV2.wsdl")

클래스 경로에 Tomcat이 있는 경우 다음과 같이 간단합니다.

TomcatURLStreamHandlerFactory.register();

그러면 "war" 및 "classpath" 프로토콜에 대한 핸들러가 등록됩니다.

나는 피하려고 한다.URL클래스 대신 에 의존하다URI따라서 필요한 것은URLSpring Resource out 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에 자원 코드를 분리해 달라고 의뢰한 적이 있습니다.core그래야 다른 봄 물건이 다 필요없을 거야

언급URL : https://stackoverflow.com/questions/861500/url-to-load-resources-from-the-classpath-in-java