Introdução e Implementação básica
Primeiro, você precisará de pelo menos um URLStreamHandler. Isso realmente abrirá a conexão com um determinado URL. Observe que isso é chamado simplesmente Handler
; isso permite que você especifique java -Djava.protocol.handler.pkgs=org.my.protocols
e ele será automaticamente escolhido, usando o nome do pacote "simples" como o protocolo suportado (neste caso, "caminho da classe").
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 lançamento
Se você é como eu, não quer depender de uma propriedade configurada no lançamento para levá-lo a algum lugar (no meu caso, gosto de manter minhas opções em aberto como o Java WebStart - é por isso
que preciso de tudo isso )
Soluções alternativas / aprimoramentos
Código manual Especificação do manipulador
Se você controlar o código, poderá fazer
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
e isso usará seu manipulador para abrir a conexão.
Mas, novamente, isso é menos do que satisfatório, pois você não precisa de um URL para fazer isso - você quer fazer isso porque alguma lib que você não pode (ou não quer) controlar quer URLs ...
Registro de manipulador da JVM
A opção final é registrar um URLStreamHandlerFactory
que manipulará todos os URLs na 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 o manipulador, ligue URL.setURLStreamHandlerFactory()
para a fábrica configurada. Então faça new URL("classpath:org/my/package/resource.extension")
o primeiro exemplo e você vai embora.
Problema de registro de manipulador da JVM
Observe que esse método pode ser chamado apenas uma vez por JVM e observe bem que o Tomcat usará esse método para registrar um manipulador JNDI (AFAIK). Tente Jetty (eu serei); na pior das hipóteses, você pode usar o método primeiro e, em seguida, ele deve contornar você!
Licença
Eu libero isso para o domínio público e solicito que, se você deseja modificar, inicie um projeto OSS em algum lugar e comente aqui com os detalhes. Uma implementação melhor seria ter um URLStreamHandlerFactory
que use ThreadLocal
s para armazenar URLStreamHandler
s para cada um Thread.currentThread().getContextClassLoader()
. Eu até darei minhas modificações e aulas de teste.
com.github.fommil.common-utils
pacote que planejo atualizar e lançar em breve via Sonatype.System.setProperty()
para registrar o protocolo. ComoSystem.setProperty("java.protocol.handler.pkgs", "org.my.protocols");
Isso deve resolver.
fonte
Eu acho que isso vale a sua própria resposta - se você estiver usando o Spring, você já tem isso com
Como explicado na documentação da primavera e apontado nos comentários de skaffman.
fonte
ResourceLoader.getResource()
é mais apropriado para a tarefa (ApplicationContext.getResource()
delegados a ele sob o capô)Você também pode definir a propriedade programaticamente durante a inicialização:
Usando esta classe:
Assim, você obtém a maneira menos invasiva de fazer isso. :) java.net.URL sempre usará o valor atual das propriedades do sistema.
fonte
java.protocol.handler.pkgs
variável do sistema só pode ser usado se o manipulador tiver como objetivo processar protocolos ainda não "conhecidos", comogopher://
. Se a intenção é substituir o protocolo "popular", comofile://
ouhttp://
, pode ser tarde demais para fazê-lo, pois ojava.net.URL#handlers
map já possui um manipulador "padrão" para esse protocolo. Portanto, a única saída é passar essa variável para a JVM.(Semelhante à resposta de Azder , mas com um tato um pouco diferente.)
Não acredito que exista um manipulador de protocolo predefinido para o conteúdo do caminho de classe. (O assim chamado
classpath:
protocolo).No entanto, o Java permite adicionar seus próprios protocolos. Isso é feito através do fornecimento de implementações concretas
java.net.URLStreamHandler
ejava.net.URLConnection
.Este artigo descreve como um manipulador de fluxo personalizado pode ser implementado: http://java.sun.com/developer/onlineTraining/protocolhandlers/ .
fonte
Criei uma classe que ajuda a reduzir erros na configuração de manipuladores personalizados e tira proveito da propriedade do sistema, para que não haja problemas em chamar um método primeiro ou não estar no contêiner certo. Há também uma classe de exceção se você errar:
fonte
Inspire-se com @Stephen https://stackoverflow.com/a/1769454/980442 e http://docstore.mik.ua/orelly/java/exp/ch09_06.htm
Usar
basta criar essa classe no
sun.net.www.protocol.classpath
pacote e executá-la na implementação do Oracle JVM para funcionar como um encanto.Caso você esteja usando outra implementação da JVM, configure a
java.protocol.handler.pkgs=sun.net.www.protocol
propriedade do sistema.FYI: http://docs.oracle.com/javase/7/docs/api/java/net/URL.html#URL(java.lang.String,%20java.lang.String,%20int,%20java.lang .Corda)
fonte
A solução com o registro de URLStreamHandlers é a mais correta, é claro, mas às vezes a solução mais simples é necessária. Então, eu uso o seguinte método para isso:
fonte
Do Java 9+ ou superior, você pode definir um novo
URLStreamHandlerProvider
. oURL
classe usa a estrutura do carregador de serviço para carregá-la em tempo de execução.Crie um provedor:
Crie um arquivo chamado
java.net.spi.URLStreamHandlerProvider
noMETA-INF/services
diretório com o conteúdo:Agora, a classe de URL usará o provedor quando vir algo como:
fonte
Não sei se já existe um, mas você pode fazê-lo facilmente.
Esse exemplo de protocolos diferentes me parece um padrão de fachada. Você tem uma interface comum quando há implementações diferentes para cada caso.
Você pode usar o mesmo princípio, criar uma classe ResourceLoader que retire a string do seu arquivo de propriedades e verifique um protocolo personalizado nosso
retira o myprotocol: desde o início da sequência e, em seguida, toma uma decisão de como carregar o recurso e fornece apenas o recurso.
fonte
Uma extensão da resposta de Dilums :
Sem alterar o código, você provavelmente precisará implementar implementações personalizadas de interfaces relacionadas à URL, como recomenda a Dilum. Para simplificar as coisas para você, recomendo procurar a fonte dos Recursos do Spring Framework . Embora o código não esteja na forma de um manipulador de fluxo, ele foi projetado para fazer exatamente o que você está procurando e está sob a licença ASL 2.0, tornando-o amigável o suficiente para reutilizar seu código com o devido crédito.
fonte
Em um aplicativo Spring Boot, usei o seguinte para obter o URL do arquivo,
fonte
Se você tiver o tomcat no caminho de classe, é tão simples quanto:
Isso registrará manipuladores para os protocolos "war" e "classpath".
fonte
Eu tento evitar a
URL
aula e, em vez disso, confioURI
. Assim, para coisas que precisam deURL
onde eu gostaria de fazer o Spring Resource, como pesquisa sem o Spring, faço o seguinte:Para criar um URI, você pode usar
URI.create(..)
. Dessa forma, também é melhor porque você controla oClassLoader
que fará a pesquisa de recursos.Notei algumas outras respostas tentando analisar o URL como uma String para detectar o esquema. Eu acho que é melhor passar pelo URI e usá-lo para analisar.
Na verdade, eu arquivei um problema há um tempo atrás, com o Spring Source implorando para que eles separassem seu código de recurso,
core
para que você não precisasse de todos os outros itens do Spring.fonte