¿Cuál es la diferencia entre Class.getResource () y ClassLoader.getResource ()?

194

Me pregunto cuál es la diferencia entre Class.getResource()y ClassLoader.getResource().

editar: especialmente quiero saber si hay algún almacenamiento en caché a nivel de archivo / directorio. Como en "¿se almacenan en caché las listas de directorios en la versión Class?"

AFAIK lo siguiente esencialmente debería hacer lo mismo, pero no lo son:

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

Descubrí esto al jugar con un código de generación de informes que crea un nuevo archivo WEB-INF/classes/desde un archivo existente en ese directorio. Al usar el método de Class, pude encontrar archivos que estaban allí durante el despliegue usando getClass().getResource(), pero al tratar de recuperar el archivo recién creado, recibí un objeto nulo. Navegar por el directorio muestra claramente que el nuevo archivo está allí. Los nombres de los archivos se anteponen con una barra diagonal como en "/myFile.txt".

La ClassLoaderversión de, getResource()por otro lado, encontró el archivo generado. Según esta experiencia, parece que hay algún tipo de almacenamiento en caché de la lista del directorio. ¿Tengo razón, y si es así, dónde está documentado?

De los documentos de API enClass.getResource()

Encuentra un recurso con un nombre de pila. El cargador de clases definitorias de la clase implementa las reglas para buscar recursos asociados con una clase determinada. Este método delega al cargador de clases de este objeto. Si este objeto fue cargado por el cargador de clases bootstrap, el método delega a ClassLoader.getSystemResource (java.lang.String).

Para mí, esto dice "Class.getResource realmente está llamando a getResource () de su propio cargador de clases". Lo cual sería lo mismo que hacer getClass().getClassLoader().getResource(). Pero obviamente no lo es. ¿Podría alguien proporcionarme alguna iluminación sobre este asunto?

oligofren
fuente

Respuestas:

6

Para responder a la pregunta de si hay algún almacenamiento en caché.

Investigué este punto aún más al ejecutar una aplicación Java independiente que cargaba continuamente un archivo desde el disco usando el método getResourceAsStream ClassLoader. Pude editar el archivo, y los cambios se reflejaron de inmediato, es decir, el archivo se volvió a cargar desde el disco sin almacenar en caché.

Sin embargo: estoy trabajando en un proyecto con varios módulos maven y proyectos web que tienen dependencias entre sí. Estoy usando IntelliJ como mi IDE para compilar y ejecutar los proyectos web.

Noté que lo anterior ya no parecía ser cierto, la razón es que el archivo que estaba cargando ahora se hornea en un jar y se implementa en el proyecto web correspondiente. Solo noté esto después de intentar cambiar el archivo en mi carpeta de destino, pero fue en vano. Esto hizo que pareciera que había un almacenamiento en caché.

mchlstckl
fuente
Yo también usé Maven e IntelliJ, por lo que esta es la respuesta en un entorno que se asemeja más al mío y tiene una explicación razonable para la pregunta # 2.
oligofren
249

Class.getResourcepuede tomar un nombre de recurso "relativo", que se trata en relación con el paquete de la clase. Alternativamente, puede especificar un nombre de recurso "absoluto" utilizando una barra diagonal. Las rutas de recursos del cargador de clases siempre se consideran absolutas.

Entonces, los siguientes son básicamente equivalentes:

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

Y así son estos (pero son diferentes de los anteriores):

foo.bar.Baz.class.getResource("/data/xyz.txt");
foo.bar.Baz.class.getClassLoader().getResource("data/xyz.txt");
Jon Skeet
fuente
Buena respuesta con ejemplos claros. Aunque la publicación en realidad estaba destinada a obtener respuestas a dos preguntas, ahora veo que la segunda pregunta está oculta. No estoy seguro de cómo / si debo actualizar la publicación para reflejar esto, pero lo que me gustaría saber en segundo lugar es esto (siguiente comentario):
oligofren
2
¿Hay algún tipo de almacenamiento en caché en la versión Class.getResource ()? Lo que me hizo creer que esto es la generación de algunos informes jasper: Utilizamos getClass (). GetResource ("/ aDocument.jrxml") para obtener el archivo jasper xml. Luego se genera un archivo jasper binario en el mismo directorio. getClass (). getResource ("/ aDocument.jasper") no puede encontrarlo, aunque puede encontrar claramente documentos en el mismo nivel (el archivo de entrada). Aquí es donde ClassLoader.getResource () demostró ser útil, ya que parece que no utiliza el almacenamiento en caché de la lista del directorio. Pero no puedo encontrar documentación sobre esto.
oligofren
2
@oligofren: Hmm ... no esperaría que Class.getResource () hiciera ningún almacenamiento en caché allí ...
Jon Skeet
1
@ JonSkeet, ¿por qué this.getClass().getClassLoader().getResource("/");devolver nulo? No debería ser lo mismo quethis.getClass().getClassLoader().getResource(".");
Asif Mushtaq
@ Desconocido: Creo que probablemente deberías hacer una nueva pregunta al respecto.
Jon Skeet
22

La primera llamada busca en relación con el .classarchivo, mientras que la última busca en relación con la raíz de classpath.

Para depurar problemas como ese, imprimo la URL:

System.out.println( getClass().getResource(getClass().getSimpleName() + ".class") );
Aaron Digulla
fuente
3
Creo que la "raíz del cargador de clases" sería más precisa que la "raíz de classpath", solo para ser exigente.
Jon Skeet
44
Ambos pueden buscar "rutas absolutas" si el nombre del archivo está precedido por "/"
oligofren
2
Interesante ... Estoy llegando a un caso donde getClass (). GetResource ("/ someAbsPath") devuelve una URL en el tipo /path/to/mylib.jar!/someAbsPath y getClass (). GetClassLoafer (). GetResource (" / someAbsPath ") return null ... Entonces" root of classloader "parece no ser una noción muy bien definida ...
Pierre Henry
8
@PierreHenry: getClassLoader().getResource("/...")siempre regresa null: el cargador de clases no elimina el inicio /de la ruta, por lo que la búsqueda siempre falla. Solo getClass().getResource()maneja un inicio /como una ruta absoluta relativa a la ruta de clase.
Aaron Digulla
17

Tuve que buscarlo en las especificaciones:

Clase getResource () - la documentación establece la diferencia:

Este método delega la llamada a su cargador de clases, después de realizar estos cambios en el nombre del recurso: si el nombre del recurso comienza con "/", no cambia; de lo contrario, el nombre del paquete se antepone al nombre del recurso después de convertir "." a "/". Si el cargador de arranque cargó este objeto, la llamada se delega a ClassLoader.getSystemResource.

Bernd Elkemann
fuente
2
¿Tiene alguna información sobre si también almacena en caché la lista del directorio? Esta fue la principal diferencia entre los dos métodos cuando se busca por primera vez un archivo de entrada y luego se crea un archivo usando eso en el mismo directorio. La versión de Class no lo encontró, la versión de ClassLoader sí (ambos usando "/file.txt").
oligofren
11

Todas estas respuestas por aquí, así como las respuestas en esta pregunta , sugieren que cargar URL absolutas, como "/foo/bar.properties", se trata de la misma manera por class.getResourceAsStream(String)y class.getClassLoader().getResourceAsStream(String). Este NO es el caso, al menos no en mi configuración / versión de Tomcat (actualmente 7.0.40).

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

Lo siento, no tengo ninguna explicación satisfactoria, pero supongo que Tomcat hace trucos sucios y su magia negra con los cargadores de clases y causa la diferencia. Siempre lo usé class.getResourceAsStream(String)en el pasado y no he tenido ningún problema.

PD: también publiqué esto aquí

Tim Büthe
fuente
Este comportamiento parece ser un error en Tomcat que se corrigió en la versión 8.
Agregué
2

Class.getResourcesrecuperaría el recurso por el cargador de clases que carga el objeto. Mientras ClassLoader.getResourcerecuperaría el recurso usando el cargador de clases especificado.

lwpro2
fuente
0

Intenté leer desde input1.txt que estaba dentro de uno de mis paquetes junto con la clase que intentaba leerlo.

Los siguientes trabajos:

String fileName = FileTransferClient.class.getResource("input1.txt").getPath();

System.out.println(fileName);

BufferedReader bufferedTextIn = new BufferedReader(new FileReader(fileName));

La parte más importante fue llamar getPath()si desea el nombre de ruta correcto en formato de cadena. NO UTILICEtoString() porque agregará texto de formato adicional que TOTALMENTE ARRIBA el nombre del archivo (puede probarlo y ver la impresión).

Pasé 2 horas depurando esto ... :(

Kevin Lee
fuente
¿Qué pasa con Class.getResourceAsStream () ?
Lu55
Los recursos no son archivos. Es posible que no se desempaqueten del archivo JAR o WAR, y si no se puede FileReadero FileInputStreamno se puede usar para acceder a ellos. La respuesta no es correcta.
Marqués de Lorne