Un cargador de clases Java normalmente funciona buscando clases en uno o más lugares en una secuencia fija. Por ejemplo, el cargador de clases que carga su aplicación cuando lo ejecuta desde la línea de comando se ve primero en el archivo rt.jar
(y otros en el bootclasspath), y luego en los directorios y archivos JAR especificados por su classpath.
Una carga de clase de webapp es similar en principio, pero un poco más complicada en la práctica. Para una aplicación web particular, el cargador de clases de una aplicación busca clases en el siguiente orden. Por ejemplo Tomcat 6 busque las clases en este orden:
- clases de archivos de inicio de la JVM
- clases de cargador de clases del sistema (descritos here)
- /WEB-INF/classes de la aplicación web
- /WEB INF/lib/*. jar de la aplicación web
- $ CATALINA_HOME/lib
- $ CATALINA_HOME/lib/*. jar
Por supuesto, una vez que el cargador de clases ha encontrado la clase que busca, no busca más. Por lo tanto, las clases con el mismo nombre más adelante en el pedido no se cargarán.
La complicación es que el contenedor web tiene un cargador de clases para cada aplicación web, y estos cargadores de clases delegan a otros cargadores de clases que administran las clases comunes. En la práctica, esto significa que algunas clases solo se cargarán una vez para todo el contenedor (por ejemplo, 1. y 2.) y otras pueden ser cargadas varias veces por diferentes cargadores de clases.
(Cuando una clase se carga más de una vez, da como resultado distintos objetos Class
y estáticas de clase distintas. Las versiones de la clase son diferentes en cuanto a la JVM y no puede encasillar de una versión a otra .)
Finalmente, Tomcat se puede configurar para permitir que las webapps individuales se "carguen en caliente". Esto implica detener una aplicación web, crear un nuevo cargador de clases y reiniciarla.
FOLLOWUP
Así que ... la sincronización de un método estático no protegerá el acceso a un recurso compartido en el que la clase se ha cargado varias veces?
Depende de los detalles, pero probablemente no sea así. (O para mirar si de otra manera, si una clase tiene realidad ha cargado varias veces, a continuación, un método de cada "carga" de la clase static
accederá a un conjunto diferente de static
campos.)
Si realmente quiere que una instancia de clase de aplicación singleton sea compartida por varias aplicaciones web en el mismo contenedor, es más simple si coloca la clase en $CATALINA_HOME/lib
o su equivalente. Pero también debe preguntarse si este es un buen diseño del sistema. Considere combinar webapps, o usar el reenvío de solicitudes, etc., en lugar de una estructura de datos compartida. El patrón singleton tiende a ser problemático en webapps, y este sabor es aún más.
Gracias por la respuesta. Sí, me encontré con el problema del casting previamente ahora que lo mencionas. Entonces, para responder a mi pregunta, ¿sincronizar un método estático no protegerá el acceso a un recurso compartido donde la clase se ha cargado varias veces? – CodeClimber
@Stephane C Mientras trabajaba en varias aplicaciones web desplegadas en Tomcat, obtuve una excepción vinculada (recurso ya cargado en el cargador de clases diferente). Lo resolví moviendo el recurso a TC/bin dir para que TC Cloader lo cargue y se resolvió el problema. ¿Sabes por qué estaba recibiendo ese error? ¿Por qué el mismo recurso no puede ser cargado por 2 cargadores de clases diferentes de 2 diferentes aplicaciones web? – bluelurker
En esta respuesta, se puede leer que diferentes cargadores de clase WAR causan diferentes versiones de clase para la "misma" clase, i. mi. al cargar una clase 'A' de e. gramo. un JAR 'a.jar' en' WEB-INF/lib' que se distribuye como el mismo archivo exacto en diferentes WAR en el mismo servidor (o EAR). Cuando quiero construir un caché que abarque todos los WAR en el servidor (o en el EAR) de las instancias de la clase 'A', esto no funcionará (excepto que muevo JAR' a.jar' a '~/lib/ext' o más) Will 'serialVersionUID' rescatar mi caché en este punto? –