212 votes

URL à charger les ressources du chemin de classe en Java

En Java, vous pouvez charger tous les types de ressources à l'aide de la même API, mais avec une URL différente protocoles:

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

Ce bien de dissocier le réel de chargement de la ressource à partir de l'application qui a besoin de la ressource, et depuis une URL est juste une Chaîne, de chargement de ressources est également très facilement configurable.

Est-il un protocole pour charger les ressources en utilisant le chargeur de classe actuel? Ceci est similaire à la Jar protocole, sauf que je n'ai pas besoin de savoir qui jar fichier ou dossier de classe de la ressource.

Je peux le faire à l'aide d' Class.getResourceAsStream("a.xml"), bien sûr, mais cela m'obligerait à utiliser une API différente, et donc des modifications au code existant. Je veux être en mesure de l'utiliser dans tous les endroits où je peux indiquer une URL de la ressource déjà, par simple mise à jour d'un fichier de propriétés.

363voto

Stephen Points 8670

D'introduction et de mise en Œuvre de base

Tout d'abord, vous allez avoir besoin d'au moins un URLStreamHandler. Ce sera fait d'ouvrir la connexion à une URL donnée. Remarquez que c'est simplement appelé Handler; cela vous permet de spécifier java -Djava.protocol.handler.pkgs=org.my.protocols et il sera automatiquement repris, à l'aide de la "simple" nom du package le protocole pris en charge (dans ce cas "classpath").

L'utilisation de la

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

Problèmes de lancement

Si vous êtes comme moi, vous ne voulez pas compter sur une propriété définie dans le lancement d'obtenir de vous quelque part (dans mon cas, je tiens à garder mes options ouvertes comme Java WebStart - c'est pourquoi j'ai besoin de tout cela).

Solutions/Améliorations

Manuel Gestionnaire de code de spécification

Si vous contrôlez le code, vous pouvez le faire

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

et ce sera d'utiliser votre gestionnaire pour ouvrir la connexion.

Mais encore une fois, c'est moins que satisfaisant, tant que vous n'avez pas besoin d'une URL pour ce faire, vous voulez faire cela, parce que certains lib vous ne pouvez pas (ou ne voulez pas) de contrôle veut url...

JVM Gestionnaire d'enregistrement

L'option ultime est d'enregistrer un URLStreamHandlerFactory qui se chargera de toutes les url à travers la 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);
    }
}

Pour enregistrer le gestionnaire, appelez URL.setURLStreamHandlerFactory() avec votre configurés en usine. Alors n' new URL("classpath:org/my/package/resource.extension") comme le premier exemple et vous allez loin.

JVM Gestionnaire de Problème d'Enregistrement

Notez que cette méthode ne peut être appelée qu'une seule fois par la JVM, et notez bien que Tomcat va utiliser cette méthode pour enregistrer un JNDI gestionnaire (autant que je sache). Essayez Jetty (je serai); au pire, vous pouvez utiliser la première méthode, puis il a de travailler autour de vous!

Licence

J'ai communiqué ce dans le domaine public, et vous demandons, si vous souhaitez modifier qui vous démarrez un projet OSS quelque part et commentaire ici avec plus de détails. Une meilleure mise en œuvre serait d'avoir un URLStreamHandlerFactory qui utilise ThreadLocals pour stocker URLStreamHandlers par Thread.currentThread().getContextClassLoader(). Je vais même vous donner mes modifications et les classes de test.

Maintenant, donnez-moi beaucoup de votes! :)

111voto

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

Cela devrait le faire.

Ce qui concerne

16voto

eis Points 14687

Je pense que cela vaut la peine de sa propre réponse - si vous utilisez le Printemps, vous disposez déjà de ce avec

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

Comme expliqué dans le ressort de la documentation et souligné dans les commentaires par skaffman.

10voto

subes Points 640

Vous pouvez également définir la propriété par programme lors du démarrage:

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

À l'aide de cette classe:

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

Donc vous avez la moins intrusive, la façon de le faire. :) java.net.URL de toujours utiliser la valeur actuelle de la fenêtre propriétés système.

6voto

Dilum Ranatunga Points 7677

(Similaire à Azder de réponse, mais un peu différente de tact.)

Je ne crois pas qu'il existe un protocole prédéfini gestionnaire de contenu depuis le classpath. (La soi-disant classpath: protocole).

Cependant, Java vous permet d'ajouter vos propres protocoles. Ceci est réalisé en fournissant des implémentations concrètes java.net.URLStreamHandler et java.net.URLConnection.

Cet article décrit comment un flux personnalisé gestionnaire peut être mis en œuvre: http://java.sun.com/developer/onlineTraining/protocolhandlers/.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X