135 votos

URL para cargar los recursos desde el classpath de Java

En Java, se pueden cargar todos los tipos de recursos con la misma API, pero con diferentes URL de los protocolos:

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

Esta muy bien desacopla la carga real de los recursos de la aplicación que necesita el recurso, y desde una dirección URL es simplemente una Cadena, la carga de recursos es también muy fácilmente configurable.

Existe un protocolo de carga de los recursos mediante el cargador de clases actual? Esto es similar a la Jarra de protocolo, excepto que yo no necesito saber que archivo jar de la clase o de la carpeta de recursos.

Puedo hacer que el uso de Class.getResourceAsStream("a.xml"), por supuesto, pero que requieren el uso de una API diferente, y por lo tanto los cambios en el código existente. Quiero ser capaz de utilizar esto en todos los lugares donde se puede especificar una dirección URL para el recurso ya, por sólo la actualización de un archivo de propiedades.

274voto

Stephen Puntos 8670

Introducción y Aplicación de base

Primero, vas a necesitar al menos un URLStreamHandler. Esto va a abrir la conexión a una dirección URL dada. Aviso que esto es simplemente llamados Handler; esto le permite especificar java -Djava.protocol.handler.pkgs=org.my.protocols y pasará automáticamente a ser recogido, usando el "simple" nombre del paquete como el protocolo compatible (en este caso "classpath").

Uso

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

Código

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

Lanzamiento temas

Si eres como yo, no quieren depender de una propiedad que se establece en el bote para llegar a algún lugar (en mi caso, me gusta mantener mis opciones abiertas como Java WebStart - que es la razón por la que me falta todo esto).

Soluciones O Mejoras En El

Manual de Controlador de código de especificación

Si controlas el código, usted puede hacer

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

y esto va a utilizar el controlador para abrir la conexión.

Pero de nuevo, esto es menos que satisfactorio, ya que no es necesario una dirección URL para ello - de que quieres hacer esto porque algunas lib usted no puede (o no quiere) control quiere url...

JVM Controlador de registro

La mejor opción es registrar un URLStreamHandlerFactory que se encargará de todas las direcciones url a través de 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);
    }
}

Para registrar el controlador, llame URL.setURLStreamHandlerFactory() con su configurado de fábrica. A continuación, haga new URL("classpath:org/my/package/resource.extension") como en el primer ejemplo y listo.

JVM Controlador Problema de Registro de

Tenga en cuenta que este método sólo se puede llamar una vez por JVM, y nótese bien que Tomcat va a utilizar este método para registrar un JNDI controlador (AFAIK). Intente Jetty (I); en el peor, puede utilizar el método en primer lugar y, a continuación, se tiene que trabajar alrededor de usted!

Licencia

Yo la liberación de esta en el dominio público, y pedimos que si usted desea modificar la que inicia un proyecto OSS en algún lugar y comentar aquí con los detalles. Una mejor aplicación sería tener un URLStreamHandlerFactory que utiliza ThreadLocals para almacenar URLStreamHandlers para cada Thread.currentThread().getContextClassLoader(). Incluso voy a dar mi modificaciones y las clases de prueba.

Ahora me dan la gran cantidad de votos! :)

59voto

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



8voto

eis Puntos 14687

Creo que es digno de su propia respuesta - si usas primavera, ya tienes esto con

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

Como explica en la documentación de primavera y señaló en los comentarios por skaffman.

7voto

subes Puntos 640

También puede establecer la propiedad mediante programación durante el arranque:

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

Usando esta clase:

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

Así tendrás la manera de hacerlo menos intrusiva. :) java.net.URL siempre utilizará el valor actual de las propiedades del sistema.

3voto

Dilum Ranatunga Puntos 7677

(Similar a Azder la respuesta, pero un poco diferente al tacto.)

Yo no creo que exista un protocolo predefinido controlador para el contenido de la ruta de clases. (Los llamados classpath: protocolo).

Sin embargo, Java no permite añadir sus propios protocolos. Esto se hace a través de la provisión de implementaciones concretas java.net.URLStreamHandler y java.net.URLConnection.

En este artículo se describe cómo una secuencia personalizada controlador puede ser implementado: http://java.sun.com/developer/onlineTraining/protocolhandlers/.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X