Wprowadzenie i podstawowe wdrożenie
Po pierwsze, potrzebujesz przynajmniej URLStreamHandler. To faktycznie otworzy połączenie z danym adresem URL. Zauważ, że jest to po prostu nazywane Handler
; pozwala to określić java -Djava.protocol.handler.pkgs=org.my.protocols
i zostanie on automatycznie pobrany, używając „prostej” nazwy pakietu jako obsługiwanego protokołu (w tym przypadku „ścieżka klasy”).
Stosowanie
new URL("classpath:org/my/package/resource.extension").openConnection();
Kod
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();
}
}
Uruchom problemy
Jeśli masz coś takiego jak ja, nie chcesz polegać na nieruchomości będącej w zestawie z uruchomieniem, aby dostać się gdzieś (w moim przypadku, jak zachować moje opcje otworzyć jak Java Web Start - dlatego
ja potrzebuję to wszystko ).
Obejścia / ulepszenia
Instrukcja obsługi kodu obsługi
Jeśli kontrolujesz kod, możesz to zrobić
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
a to użyje twojego programu obsługi do otwarcia połączenia.
Ale znowu, to jest mniej niż zadowalające, ponieważ nie potrzebujesz do tego adresu URL - chcesz to zrobić, ponieważ niektóre biblioteki, których nie możesz kontrolować (lub nie chcesz) kontrolować, chcą adresów URL ...
Rejestracja JVM Handler
Ostateczną opcją jest zarejestrowanie pliku, URLStreamHandlerFactory
który będzie obsługiwał wszystkie adresy URL w 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);
}
}
Aby zarejestrować program obsługi, zadzwoń URL.setURLStreamHandlerFactory()
do skonfigurowanej fabryki. Zrób new URL("classpath:org/my/package/resource.extension")
więc pierwszy przykład i już nie ma go.
Problem z rejestracją modułu obsługi JVM
Należy pamiętać, że tę metodę można wywołać tylko raz dla JVM, i należy pamiętać, że Tomcat użyje tej metody do zarejestrowania procedury obsługi JNDI (AFAIK). Spróbuj Jetty (będę); w najgorszym wypadku możesz najpierw użyć tej metody, a potem musi ona obejść cię!
Licencja
Udostępniam to do domeny publicznej i proszę, jeśli chcesz zmodyfikować, rozpocznij gdzieś projekt OSS i skomentuj tutaj ze szczegółami. Lepszą implementacją byłoby posiadanie s URLStreamHandlerFactory
używającego ThreadLocal
s do przechowywania URLStreamHandler
s dla każdego Thread.currentThread().getContextClassLoader()
. Dam ci nawet moje modyfikacje i klasy testowe.
com.github.fommil.common-utils
pakietu, który planuję wkrótce zaktualizować i wydać za pośrednictwem Sonatype.System.setProperty()
do zarejestrowania protokołu. LikeSystem.setProperty("java.protocol.handler.pkgs", "org.my.protocols");
Że należy to zrobić.
źródło
Myślę, że jest to warte własnej odpowiedzi - jeśli używasz Wiosny, już to masz
Jak wyjaśniono w wiosennej dokumentacji i wskazano w komentarzach skaffmana.
źródło
ResourceLoader.getResource()
jest bardziej odpowiednia do zadania (ApplicationContext.getResource()
delegaci do niego pod maską)Możesz także ustawić właściwość programowo podczas uruchamiania:
Korzystanie z tej klasy:
W ten sposób otrzymujesz najmniej inwazyjny sposób na zrobienie tego. :) java.net.URL zawsze użyje bieżącej wartości z właściwości systemowych.
źródło
java.protocol.handler.pkgs
zmiennej systemowej, może być użyty tylko wtedy, gdy program obsługi zamierzał przetworzyć jeszcze „nieznany” protokół, taki jakgopher://
. Jeśli celem jest zastąpienie „popularnego” protokołu, takiego jakfile://
lubhttp://
, może być za późno, aby to zrobić, ponieważjava.net.URL#handlers
mapa została już dodana „standardowa” procedura obsługi tego protokołu. Jedynym wyjściem jest przekazanie tej zmiennej do JVM.(Podobne do odpowiedzi Azdera , ale nieco inny takt.)
Nie sądzę, aby istniała predefiniowana procedura obsługi protokołu dla treści z ścieżki klasy. (Tak zwany
classpath:
protokół).Jednak Java pozwala dodawać własne protokoły. Odbywa się to poprzez zapewnienie konkretnych wdrożeń
java.net.URLStreamHandler
ijava.net.URLConnection
.W tym artykule opisano, jak można zaimplementować niestandardową procedurę obsługi strumienia: http://java.sun.com/developer/onlineTraining/protocolhandlers/ .
źródło
Stworzyłem klasę, która pomaga zmniejszyć liczbę błędów podczas konfigurowania niestandardowych procedur obsługi i korzysta z właściwości systemowej, dzięki czemu nie ma problemów z pierwszym wywołaniem metody lub brakiem odpowiedniego kontenera. Istnieje również klasa wyjątków, jeśli coś jest nie tak:
źródło
Inspiruj @Stephen https://stackoverflow.com/a/1769454/980442 i http://docstore.mik.ua/orelly/java/exp/ch09_06.htm
Używać
po prostu utwórz tę klasę w
sun.net.www.protocol.classpath
pakiecie i uruchom ją w implementacji Oracle JVM, aby działała jak urok.Jeśli używasz innej implementacji JVM, ustaw
java.protocol.handler.pkgs=sun.net.www.protocol
właściwość systemową.FYI: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL(java.lang.String,%20java.lang.String,%20int,%20java.lang .Strunowy)
źródło
Rozwiązanie z rejestracją URLStreamHandlers jest oczywiście najbardziej poprawne, ale czasami potrzebne jest najprostsze rozwiązanie. Dlatego używam do tego następującej metody:
źródło
Począwszy od wersji Java 9+, możesz zdefiniować nowy
URLStreamHandlerProvider
.URL
Klasa wykorzystuje ramy ładowarka usług załadować go w czasie wykonywania.Utwórz dostawcę:
Utwórz plik o nazwie
java.net.spi.URLStreamHandlerProvider
wMETA-INF/services
katalogu z zawartością:Teraz klasa URL użyje dostawcy, gdy zobaczy coś takiego:
źródło
Nie wiem, czy już istnieje, ale możesz to zrobić samemu.
Ten przykład różnych protokołów wygląda dla mnie jak wzór elewacji. Masz wspólny interfejs, gdy dla każdej sprawy istnieją różne implementacje.
Możesz użyć tej samej zasady, stworzyć klasę ResourceLoader, która pobiera ciąg z pliku właściwości i sprawdza nasz niestandardowy protokół
usuwa myprotocol: od początku łańcucha, a następnie decyduje, w jaki sposób załadować zasób, i po prostu daje ci zasób.
źródło
Rozszerzenie odpowiedzi Dilums :
Bez zmiany kodu prawdopodobnie będziesz potrzebować niestandardowych implementacji interfejsów związanych z adresami URL, jak zaleca Dilum. Aby uprościć ci wszystko, mogę polecić przyjrzenie się źródłu zasobów Spring Framework . Chociaż kod nie ma postaci procedury obsługi strumienia, został zaprojektowany do robienia dokładnie tego, co chcesz, i jest objęty licencją ASL 2.0, dzięki czemu jest wystarczająco przyjazny dla ponownego użycia w kodzie z należytym uznaniem.
źródło
W aplikacji Spring Boot użyłem następującego polecenia, aby uzyskać adres URL pliku,
źródło
Jeśli masz tomcat na ścieżce klasy, jest to tak proste, jak:
Spowoduje to zarejestrowanie procedur obsługi protokołów „war” i „classpath”.
źródło
Staram się unikać
URL
zajęć i polegam na nichURI
. Tak więc dla rzeczy, które potrzebują miejsca, wURL
którym chciałbym wykonać Spring Resource, takich jak wyszukiwanie bez Spring, wykonuję następujące czynności:Aby utworzyć identyfikator URI, możesz użyć
URI.create(..)
. Ten sposób jest również lepszy, ponieważ kontrolujesz,ClassLoader
który będzie wyszukiwał zasoby.Zauważyłem kilka innych odpowiedzi próbujących parsować adres URL jako ciąg znaków w celu wykrycia schematu. Myślę, że lepiej jest przekazać URI i użyć go do analizy.
Właściwie już jakiś czas temu zgłosiłem problem z Spring Source, prosząc ich o oddzielenie kodu zasobów
core
, abyś nie potrzebował wszystkich innych rzeczy z Springa.źródło