Qual é a diferença entre Class.getResource () e ClassLoader.getResource ()?

Gostaria de saber qual é a diferença entre Class.getResource() e ClassLoader.getResource() ?

edit: Eu quero especialmente saber se algum caching está envolvido no nível do arquivo / diretório. Como em “as listagens de diretório são armazenadas em cache na versão da class?”

AFAIK o seguinte deve essencialmente fazer o mesmo, mas eles não são:

 getClass().getResource() getClass().getClassLoader().getResource() 

Descobri isso ao mexer em algum código de geração de relatório que cria um novo arquivo em WEB-INF/classs/ partir de um arquivo existente nesse diretório. Ao usar o método da class, eu poderia encontrar arquivos que estavam lá na implantação usando getClass().getResource() , mas ao tentar buscar o arquivo recém-criado, recebi um object nulo. Navegar pelo diretório mostra claramente que o novo arquivo está lá. Os nomes dos arquivos foram prefixados com uma barra como em “/myFile.txt”.

A versão ClassLoader de getResource() por outro lado, encontrou o arquivo gerado. Dessa experiência, parece que há algum tipo de armazenamento em cache da listview de diretórios em andamento. Estou certo e, em caso afirmativo, onde isso é documentado?

Da documentação da API em Class.getResource()

Encontra um recurso com um nome determinado. As regras para pesquisar resources associados a uma determinada class são implementadas pelo carregador de classs de definição da class. Este método delega para o carregador de classs deste object. Se esse object foi carregado pelo carregador de classs de autoboot, o método delegará para ClassLoader.getSystemResource (java.lang.String).

Para mim, isso significa que “Class.getResource está realmente chamando o getResource () de seu próprio classloader”. Qual seria o mesmo que fazer getClass().getClassLoader().getResource() . Mas obviamente não é. Alguém poderia me fornecer alguma iluminação sobre esse assunto?

Para responder à pergunta se há algum cache acontecendo.

Eu investiguei mais esse ponto executando um aplicativo Java independente que carregava continuamente um arquivo do disco usando o método getResourceAsStream ClassLoader. Consegui editar o arquivo e as alterações foram refletidas imediatamente, ou seja, o arquivo foi recarregado do disco sem o cache.

No entanto: estou trabalhando em um projeto com vários módulos maven e projetos da web que dependam uns dos outros. Estou usando o IntelliJ como meu IDE para compilar e executar os projetos da web.

Percebi que o problema acima não parecia mais ser verdade, a razão é que o arquivo que estava sendo carregado agora é colocado em um flask e implantado no projeto da Web dependente. Eu só notei isso depois de tentar mudar o arquivo na minha pasta de destino, sem sucesso. Isso fez parecer que havia caching acontecendo.

Class.getResource pode receber um nome de recurso “relativo”, que é tratado em relação ao pacote da class. Como alternativa, você pode especificar um nome de recurso “absoluto” usando uma barra inicial. Os caminhos de resources do Classloader são sempre considerados absolutos.

Então, os seguintes são basicamente equivalentes:

 foo.bar.Baz.class.getResource("xyz.txt"); foo.bar.Baz.class.getClassLoader().getResource("foo/bar/xyz.txt"); 

E assim são estes (mas são diferentes do acima):

 foo.bar.Baz.class.getResource("/data/xyz.txt"); foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt"); 

A primeira chamada procura em relação ao arquivo .class enquanto a segunda pesquisa em relação à raiz do caminho de class.

Para depurar problemas como esse, imprimo o URL:

 System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") ); 

Tive que procurá-lo nas especificações:

  • Class.getResource (recurso de cadeia)

  • ClassLoader.getResource (recurso String)

A documentação getResource () da class indica a diferença:

Este método delega a chamada ao seu carregador de classs, depois de fazer essas alterações no nome do recurso: se o nome do recurso começar com “/”, ele não será alterado; caso contrário, o nome do pacote será anexado ao nome do recurso após a conversão “.” para “/”. Se este object foi carregado pelo carregador de bootstrap, a chamada será delegada para ClassLoader.getSystemResource.

Todas essas respostas por aqui, bem como as respostas nesta questão , sugerem que carregar URLs absolutas, como “/foo/bar.properties”, é tratado da mesma forma por class.getResourceAsStream(String) e class.getClassLoader().getResourceAsStream(String) . Este não é o caso, pelo menos não na minha configuração / versão do Tomcat (atualmente 7.0.40).

 MyClass.class.getResourceAsStream("/foo/bar.properties"); // works! MyClass.class.getClassLoader().getResourceAsStream("/foo/bar.properties"); // does NOT work! 

Desculpe, eu não tenho absolutamente nenhuma explicação satisfatória, mas eu acho que o gato faz truques sujos e sua magia negra com os classloaders e causam a diferença. Eu sempre usei class.getResourceAsStream(String) no passado e não tive nenhum problema.

PS: Eu também postei isso aqui

Class.getResources recuperaria o recurso pelo carregador de class que carrega o object. Enquanto ClassLoader.getResource recuperaria o recurso usando o classloader especificado.

Eu tentei ler de input1.txt que estava dentro de um dos meus pacotes junto com a class que estava tentando lê-lo.

Os seguintes trabalhos:

 String fileName = FileTransferClient.class.getResource("input1.txt").getPath(); System.out.println(fileName); BufferedReader bufferedTextIn = new BufferedReader(new FileReader(fileName)); 

A parte mais importante foi chamar getPath() se você quiser o nome do caminho correto no formato String. NÃO USE toString() porque ele adicionará algum texto de formatação extra que TOTALMENTE SUFICIARÁ o fileName (você pode experimentá-lo e ver a impressão).

Passei 2 horas depurando isso … 🙁