문제

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을 지정할 수있는 모든 장소 에서이 제품을 사용할 수 있기를 원합니다.

도움이 되었습니까?

해결책

소개 및 기본 구현

먼저, 당신은 최소한 UrlStreamHandler가 필요합니다. 이것은 실제로 주어진 URL에 대한 연결을 열 것입니다. 이것을 간단하게 호출합니다 Handler; 이를 통해 지정할 수 있습니다 java -Djava.protocol.handler.pkgs=org.my.protocols 또한 "간단한"패키지 이름을 지원되는 프로토콜 (이 경우 "ClassPath")으로 사용하여 자동으로 선택됩니다.

용법

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

발사 문제

당신이 나와 같은 사람이라면, 당신은 런칭 중에 설정중인 부동산에 의존하고 싶지 않습니다. 이 모든 것이 필요합니다).

해결 방법/향상

수동 코드 핸들러 사양

코드를 제어하면 수행 할 수 있습니다

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

그리고 이것은 핸들러를 사용하여 연결을 열 것입니다.

그러나 다시 말하지만, 이것은 URL이 필요하지 않기 때문에 만족스럽지 않습니다.

JVM 핸들러 등록

궁극적 인 옵션은 a를 등록하는 것입니다 URLStreamHandlerFactory JVM의 모든 URL을 처리합니다.

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를 보관할 URLStreamHandler각각에 대한 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은 항상 시스템 속성의 현재 값을 사용합니다.

(비슷하다 Azder의 대답, 그러나 약간 다른 전술.)

ClassPath의 컨텐츠에 대한 사전 정의 된 프로토콜 핸들러가 있다고 생각하지 않습니다. (소위 classpath: 규약).

그러나 Java를 사용하면 자신의 프로토콜을 추가 할 수 있습니다. 이것은 구체적인 구현을 제공함으로써 이루어집니다 java.net.URLStreamHandler 그리고 java.net.URLConnection.

이 기사는 사용자 정의 스트림 핸들러를 구현할 수있는 방법에 대해 설명합니다.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.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();
    }
}

다른 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 )

솔루션 URLStreamHandlers를 등록하는 것이 가장 정확하지만 때로는 가장 간단한 솔루션이 필요합니다. 따라서 다음 방법을 사용합니다.

/**
 * 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을 제거한 다음 문자열의 시작부터 리소스를로드 할 수있는 방법을 결정하고 리소스를 제공합니다.

연장 딜 룸의 대답:

코드를 변경하지 않으면 Dilum이 권장하는대로 URL 관련 인터페이스의 사용자 정의 구현을 추구해야 할 것입니다. 당신을 위해 물건을 단순화하려면 소스를 보는 것이 좋습니다. 봄 프레임 워크의 리소스. 코드는 스트림 핸들러의 형태가 아니지만, 원하는대로 정확하게 수행하도록 설계되었으며 ASL 2.0 라이센스에 따라 적절한 신용으로 코드를 재사용하기에 충분히 친숙합니다.

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

}

호출 된 파일을 만듭니다 java.net.spi.URLStreamHandlerProvider 에서 META-INF/services 내용이있는 디렉토리 :

org.example.ClasspathURLStreamHandlerProvider

이제 URL 클래스는 다음과 같은 것을 볼 때 제공자를 사용합니다.

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

Spring Boot 앱에서는 다음을 사용하여 파일 URL을 얻었습니다.

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

ClassPath에 Tomcat이 있다면 간단합니다.

TomcatURLStreamHandlerFactory.register();

이것은 "전쟁"및 "클래스 경로"프로토콜에 대한 핸들러를 등록합니다.

나는 피하려고 노력한다 URL 수업과 대신 의존합니다 URI. 따라서 필요한 것들을 위해 URL 봄이 튀어 나와 같은 스프링 리소스를하고 싶은 곳은 다음을 수행합니다.

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를 돌아 다니면서 대신 구문 분석하는 것이 낫다고 생각합니다.

나는 실제로 스프링 소스와 함께 자신의 자원 코드를 core 다른 모든 봄 물건이 필요하지 않도록합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top