Introducción e implementación básica
Primero, necesitará al menos un URLStreamHandler. Esto realmente abrirá la conexión a una URL dada. Tenga en cuenta que esto simplemente se llama Handler
; esto le permite especificar java -Djava.protocol.handler.pkgs=org.my.protocols
y se recogerá automáticamente, utilizando el nombre del paquete "simple" como 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();
}
}
Problemas de lanzamiento
Si eres como yo, no quieres depender de una propiedad establecida en el lanzamiento para llevarte a algún lugar (en mi caso, me gusta mantener mis opciones abiertas como Java WebStart, por
eso necesito todo esto )
Soluciones / Mejoras
Código manual Especificación del controlador
Si controlas el código, puedes hacer
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
y esto usará su controlador para abrir la conexión.
Pero, de nuevo, esto es menos que satisfactorio, ya que no necesita una URL para hacer esto; desea hacerlo porque alguna biblioteca que no puede (o no quiere) controlar quiere URL ...
Registro de controlador JVM
La última opción es registrar un URLStreamHandlerFactory
que maneje todas las URL en 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()
a su fábrica configurada. Entonces haz new URL("classpath:org/my/package/resource.extension")
como el primer ejemplo y listo.
Problema de registro del controlador JVM
Tenga en cuenta que este método solo se puede llamar una vez por JVM, y tenga en cuenta que Tomcat utilizará este método para registrar un controlador JNDI (AFAIK). Prueba Jetty (lo estaré); en el peor de los casos, puede usar el método primero y luego tiene que funcionar a su alrededor.
Licencia
Lanzo esto al dominio público y le pido que, si desea modificarlo, inicie un proyecto OSS en algún lugar y comente aquí con los detalles. Una mejor implementación sería tener una URLStreamHandlerFactory
que use ThreadLocal
s para almacenar URLStreamHandler
s para cada uno Thread.currentThread().getContextClassLoader()
. Incluso te daré mis modificaciones y clases de prueba.
com.github.fommil.common-utils
paquete que planeo actualizar y lanzar pronto a través de Sonatype.System.setProperty()
para registrar el protocolo. Me gustaSystem.setProperty("java.protocol.handler.pkgs", "org.my.protocols");
Deberias hacer eso.
fuente
Creo que esto vale su propia respuesta: si está utilizando Spring, ya lo tiene con
Como se explica en la documentación de primavera y se señala en los comentarios de skaffman.
fuente
ResourceLoader.getResource()
es más apropiada para la tarea (losApplicationContext.getResource()
delegados a ella bajo el capó)También puede establecer la propiedad mediante programación durante el inicio:
Usando esta clase:
Por lo tanto, obtienes la forma menos intrusiva de hacer esto. :) java.net.URL siempre usará el valor actual de las propiedades del sistema.
fuente
java.protocol.handler.pkgs
variables del sistema solo se puede usar si el controlador apunta a procesar un protocolo aún no "conocido" comogopher://
. Si la intención es anular el protocolo "popular", comofile://
ohttp://
, podría ser demasiado tarde para hacerlo, ya que eljava.net.URL#handlers
mapa ya ha agregado un controlador "estándar" para ese protocolo. Entonces, la única salida es pasar esta variable a JVM.(Similar a la respuesta de Azder , pero con un tacto ligeramente diferente).
No creo que haya un controlador de protocolo predefinido para el contenido del classpath. (El llamado
classpath:
protocolo).Sin embargo, Java le permite agregar sus propios protocolos. Esto se hace mediante la implementación de implementaciones concretas
java.net.URLStreamHandler
yjava.net.URLConnection
.Este artículo describe cómo se puede implementar un controlador de flujo personalizado: http://java.sun.com/developer/onlineTraining/protocolhandlers/ .
fuente
He creado una clase que ayuda a reducir los errores en la configuración de controladores personalizados y aprovecha la propiedad del sistema para que no haya problemas al llamar a un método primero o no estar en el contenedor correcto. También hay una clase de excepción si te equivocas:
fuente
Inspírate en @Stephen https://stackoverflow.com/a/1769454/980442 y http://docstore.mik.ua/orelly/java/exp/ch09_06.htm
Usar
solo cree esta clase en un
sun.net.www.protocol.classpath
paquete y ejecútelo en la implementación de Oracle JVM para que funcione como un encanto.En caso de que esté utilizando otra implementación de JVM, configure la
java.protocol.handler.pkgs=sun.net.www.protocol
propiedad del sistema.FYI: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL(java.lang.String,%20java.lang.String,%20int,%20java.lang .Cuerda)
fuente
La solución con el registro de URLStreamHandlers es la más correcta, por supuesto, pero a veces se necesita la solución más simple. Entonces, utilizo el siguiente método para eso:
fuente
Desde Java 9+ y superior, puede definir uno nuevo
URLStreamHandlerProvider
. losURL
clase utiliza el marco del cargador de servicios para cargarlo en tiempo de ejecución.Crea un proveedor:
Cree un archivo llamado
java.net.spi.URLStreamHandlerProvider
en elMETA-INF/services
directorio con el contenido:Ahora la clase URL usará el proveedor cuando vea algo como:
fuente
No sé si ya hay uno, pero puedes hacerlo tú mismo fácilmente.
Ese ejemplo de protocolos diferentes me parece un patrón de fachada. Tiene una interfaz común cuando hay implementaciones diferentes para cada caso.
Podría usar el mismo principio, crear una clase ResourceLoader que tome la cadena de su archivo de propiedades y verifique si hay un protocolo personalizado nuestro
elimina el myprotocol: desde el inicio de la cadena y luego toma una decisión sobre la forma de cargar el recurso, y simplemente le da el recurso.
fuente
Una extensión a la respuesta de Dilums :
Sin cambiar el código, es probable que necesite implementar implementaciones personalizadas de interfaces relacionadas con URL como recomienda Dilum. Para simplificar las cosas para usted, puedo recomendar mirar la fuente de los recursos de Spring Framework . Si bien el código no tiene la forma de un controlador de flujo, ha sido diseñado para hacer exactamente lo que está buscando hacer y está bajo la licencia ASL 2.0, lo que lo hace lo suficientemente amigable para su reutilización en su código con el debido crédito.
fuente
En una aplicación Spring Boot, utilicé lo siguiente para obtener la URL del archivo,
fuente
Si tienes tomcat en el classpath, es tan simple como:
Esto registrará controladores para los protocolos "war" y "classpath".
fuente
Intento evitar la
URL
clase y en su lugar confíoURI
. Por lo tanto, para las cosas que necesitanURL
donde me gustaría hacer Spring Resource, como la búsqueda sin Spring, hago lo siguiente:Para crear un URI que puede usar
URI.create(..)
. De esta manera también es mejor porque controlas loClassLoader
que hará la búsqueda de recursos.Noté algunas otras respuestas tratando de analizar la URL como una cadena para detectar el esquema. Creo que es mejor pasar URI y usarlo para analizar en su lugar.
De hecho, he presentado un problema hace un tiempo con Spring Source rogándoles que separen su código de Recursos
core
para que no necesites todas las demás cosas de Spring.fuente