Einführung und grundlegende Implementierung
Zunächst benötigen Sie mindestens einen URLStreamHandler. Dadurch wird die Verbindung zu einer bestimmten URL tatsächlich geöffnet. Beachten Sie, dass dies einfach aufgerufen wird Handler
. Auf diese Weise können Sie angeben, dass java -Djava.protocol.handler.pkgs=org.my.protocols
es automatisch unter Verwendung des "einfachen" Paketnamens als unterstütztes Protokoll (in diesem Fall "Klassenpfad") abgeholt wird.
Verwendung
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();
}
}
Probleme starten
Wenn Sie so etwas wie ich sind, möchten Sie sich nicht darauf verlassen, dass beim Start eine Eigenschaft festgelegt wird, um Sie irgendwohin zu bringen (in meinem Fall möchte ich meine Optionen wie Java WebStart offen halten - deshalb brauche
ich all dies ).
Problemumgehungen / Verbesserungen
Manuelle Code-Handler-Spezifikation
Wenn Sie den Code steuern, können Sie dies tun
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
und dies wird Ihren Handler verwenden, um die Verbindung zu öffnen.
Aber auch dies ist weniger als zufriedenstellend, da Sie dazu keine URL benötigen - Sie möchten dies tun, weil eine Bibliothek, die Sie nicht steuern können (oder wollen), URLs benötigt ...
Registrierung des JVM-Handlers
Die ultimative Option besteht darin, eine zu registrieren URLStreamHandlerFactory
, die alle URLs im gesamten JVM verarbeitet:
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);
}
}
Um den Handler zu registrieren, wenden Sie sich URL.setURLStreamHandlerFactory()
an Ihre konfigurierte Fabrik. Dann new URL("classpath:org/my/package/resource.extension")
gefällt dir das erste Beispiel und los geht's.
Problem bei der Registrierung des JVM-Handlers
Beachten Sie, dass diese Methode nur einmal pro JVM aufgerufen werden darf, und beachten Sie, dass Tomcat diese Methode verwendet, um einen JNDI-Handler (AFAIK) zu registrieren. Versuchen Sie Jetty (ich werde sein); im schlimmsten Fall können Sie die Methode zuerst anwenden und dann muss sie um Sie herum funktionieren!
Lizenz
Ich gebe dies für die Öffentlichkeit frei und bitte Sie, wenn Sie Änderungen vornehmen möchten, irgendwo ein OSS-Projekt zu starten und hier mit den Details zu kommentieren. Eine bessere Implementierung wäre eine URLStreamHandlerFactory
, die ThreadLocal
s verwendet, um URLStreamHandler
s für jede zu speichern Thread.currentThread().getContextClassLoader()
. Ich werde Ihnen sogar meine Modifikationen und Testklassen geben.
com.github.fommil.common-utils
Pakets hinzufügen, dass ich in Kürze über Sonatype aktualisieren und veröffentlichen möchte.System.setProperty()
das Protokoll auch registrieren können. LikeSystem.setProperty("java.protocol.handler.pkgs", "org.my.protocols");
Das sollte es tun.
quelle
Ich denke, das ist eine eigene Antwort wert - wenn Sie Spring verwenden, haben Sie dies bereits mit
Wie in der Frühjahrsdokumentation erklärt und in den Kommentaren von skaffman hervorgehoben.
quelle
ResourceLoader.getResource()
ist besser für die Aufgabe geeignet (ApplicationContext.getResource()
Delegierte unter der Haube)Sie können die Eigenschaft auch beim Start programmgesteuert festlegen:
Verwenden dieser Klasse:
Auf diese Weise erhalten Sie den am wenigsten aufdringlichen Weg, dies zu tun. :) java.net.URL verwendet immer den aktuellen Wert aus den Systemeigenschaften.
quelle
java.protocol.handler.pkgs
Systemvariablen ein zusätzliches Paket für die Suche hinzufügt , kann nur verwendet werden, wenn der Handler darauf abzielt, noch nicht "bekannte" Protokolle wie zgopher://
. Wenn beabsichtigt wird, "populäres" Protokoll wiefile://
oder zu überschreibenhttp://
, könnte es zu spät sein, dies zu tun, dajava.net.URL#handlers
map bereits ein "Standard" -Handler für dieses Protokoll hinzugefügt wurde. Der einzige Ausweg besteht darin, diese Variable an JVM zu übergeben.(Ähnlich wie Azder's Antwort , aber ein etwas anderer Takt.)
Ich glaube nicht, dass es einen vordefinierten Protokollhandler für Inhalte aus dem Klassenpfad gibt. (Das sogenannte
classpath:
Protokoll).Mit Java können Sie jedoch Ihre eigenen Protokolle hinzufügen. Dies geschieht durch die Bereitstellung konkreter Implementierungen
java.net.URLStreamHandler
undjava.net.URLConnection
.Dieser Artikel beschreibt, wie ein benutzerdefinierter Stream-Handler implementiert werden kann: http://java.sun.com/developer/onlineTraining/protocolhandlers/ .
quelle
Ich habe eine Klasse erstellt, die dazu beiträgt, Fehler beim Einrichten benutzerdefinierter Handler zu reduzieren und die Systemeigenschaft zu nutzen, sodass es keine Probleme gibt, eine Methode zuerst aufzurufen oder sich nicht im richtigen Container zu befinden. Es gibt auch eine Ausnahmeklasse, wenn Sie etwas falsch machen:
quelle
Inspirieren Sie von @Stephen https://stackoverflow.com/a/1769454/980442 und http://docstore.mik.ua/orelly/java/exp/ch09_06.htm
Benutzen
Erstellen Sie diese Klasse einfach in einem
sun.net.www.protocol.classpath
Paket und führen Sie sie in der Oracle JVM-Implementierung aus, damit sie wie ein Zauber funktioniert.Wenn Sie eine andere JVM-Implementierung verwenden, stellen Sie die
java.protocol.handler.pkgs=sun.net.www.protocol
Systemeigenschaft fest.Zu Ihrer Information: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL(java.lang.String,%20java.lang.String,%20int,%20java.lang .String)
quelle
Die Lösung mit der Registrierung von URLStreamHandlers ist natürlich am korrektesten, aber manchmal wird die einfachste Lösung benötigt. Also benutze ich dafür die folgende Methode:
quelle
Ab Java 9+ können Sie eine neue definieren
URLStreamHandlerProvider
. DieURL
Klasse verwendet das Service Loader-Framework, um es zur Laufzeit zu laden.Erstellen Sie einen Anbieter:
Erstellen Sie eine
java.net.spi.URLStreamHandlerProvider
imMETA-INF/services
Verzeichnis aufgerufene Datei mit dem Inhalt:Jetzt verwendet die URL-Klasse den Anbieter, wenn sie Folgendes sieht:
quelle
Ich weiß nicht, ob es bereits eine gibt, aber Sie können es einfach selbst machen.
Dieses Beispiel für verschiedene Protokolle sieht für mich wie ein Fassadenmuster aus. Sie haben eine gemeinsame Schnittstelle, wenn es für jeden Fall unterschiedliche Implementierungen gibt.
Sie können dasselbe Prinzip verwenden, eine ResourceLoader-Klasse erstellen, die die Zeichenfolge aus Ihrer Eigenschaftendatei übernimmt, und nach einem benutzerdefinierten Protokoll von uns suchen
entfernt das myprotocol: vom Anfang der Zeichenfolge und entscheidet dann, wie die Ressource geladen werden soll, und gibt Ihnen nur die Ressource.
quelle
Eine Erweiterung von Dilums 'Antwort :
Ohne Code zu ändern, müssen Sie wahrscheinlich benutzerdefinierte Implementierungen von URL-bezogenen Schnittstellen durchführen, wie von Dilum empfohlen. Um Ihnen die Arbeit zu vereinfachen, kann ich empfehlen, in der Quelle nach den Ressourcen von Spring Framework zu suchen . Obwohl der Code nicht in Form eines Stream-Handlers vorliegt, wurde er so konzipiert, dass er genau das tut, was Sie möchten. Er steht unter der ASL 2.0-Lizenz und ist daher so benutzerfreundlich, dass er in Ihrem Code mit angemessener Gutschrift wiederverwendet werden kann.
quelle
In einer Spring Boot-App habe ich Folgendes verwendet, um die Datei-URL abzurufen:
quelle
Wenn Sie Tomcat auf dem Klassenpfad haben, ist es so einfach wie:
Dadurch werden Handler für die Protokolle "Krieg" und "Klassenpfad" registriert.
quelle
Ich versuche, die
URL
Klasse zu meiden und verlasse mich stattdessen aufURI
. DaherURL
mache ich für Dinge, die benötigt werden, wo ich Spring Resource ausführen möchte, wie das Nachschlagen ohne Spring, Folgendes:Um einen URI zu erstellen, können Sie verwenden
URI.create(..)
. Dieser Weg ist auch besser, weil Sie steuernClassLoader
, wer die Ressourcensuche durchführen soll.Ich bemerkte einige andere Antworten, die versuchten, die URL als Zeichenfolge zu analysieren, um das Schema zu erkennen. Ich denke, es ist besser, URI weiterzugeben und stattdessen zum Parsen zu verwenden.
Ich habe vor einiger Zeit ein Problem bei Spring Source eingereicht und sie gebeten, ihren Ressourcencode von
core
diesem zu trennen, damit Sie nicht alle anderen Spring-Inhalte benötigen.quelle