¿Cómo funcionan los servlets? Creación de instancias, sesiones, variables compartidas y subprocesos múltiples.
Supongamos que tengo un servidor web que contiene numerosos servlets. Para que la información pase entre esos servlets, estoy configurando variables de sesión e instancia.
Ahora bien, si 2 o más usuarios envían una solicitud a este servidor, ¿qué sucede con las variables de sesión?
¿Serán todos comunes para todos los usuarios o serán diferentes para cada usuario?
Si son diferentes, ¿cómo pudo el servidor diferenciar entre diferentes usuarios?
Una pregunta similar más: si hay n
usuarios que acceden a un servlet en particular, entonces se crea una instancia de este servlet solo la primera vez que el primer usuario accedió a él o se crea una instancia para todos los usuarios por separado.
En otras palabras, ¿qué pasa con las variables de instancia?
ServletContext
Cuando se inicia el contenedor de servlets (como Apache Tomcat ), implementará y cargará todas sus aplicaciones web. Cuando se carga una aplicación web, el contenedor de servlets crea una instancia de ServletContext
una vez y la mantiene en la memoria del servidor. Se analizan los archivos de la aplicación web web.xml
y todos los incluidos , y cada uno y encontrado (o cada clase anotada con y respectivamente ) se creará una instancia una vez y también se mantendrá en la memoria del servidor, registrado a través del archivo . Para cada filtro instanciado, su método se invoca con una instancia de as argumento que a su vez contiene el archivo .web-fragment.xml
<servlet>
<filter>
<listener>
@WebServlet
@WebFilter
@WebListener
ServletContext
init()
FilterConfig
ServletContext
Cuando a Servlet
tiene un valor <servlet><load-on-startup>
o mayor, su método también se invoca durante el inicio. Esos servlets se inicializan en el mismo orden especificado por ese valor. Si se especifica el mismo valor para más de un servlet, entonces cada uno de esos servlets se carga en el mismo orden en que aparecen en , o carga de clases. En caso de que el valor "carga al inicio" esté ausente o sea negativo, el método se invocará cada vez que la solicitud HTTP llegue a ese servlet por primera vez. Hay dos métodos, uno que toma una instancia de como argumento que a su vez contiene el elemento involucrado y otro que no toma ningún argumento pero está disponible mediante un método heredado .@WebServlet(loadOnStartup)
0
init()
web.xml
web-fragment.xml
@WebServlet
init()
init()
ServletConfig
ServletContext
ServletContext
getServletContext()
Cuando el contenedor de servlets finalice con todos los pasos de inicialización descritos anteriormente, se ServletContextListener#contextInitialized()
invocará con un ServletContextEvent
argumento que a su vez contiene el archivo ServletContext
. Esto permitirá al desarrollador la oportunidad de registrar mediante programación otro archivo Servlet
, Filter
o Listener
.
Cuando el contenedor de servlets se cierra, descarga todas las aplicaciones web, invoca el destroy()
método de todos sus servlets y filtros inicializados, y todos los archivos Servlet
, Filter
y Listener
las instancias registradas a través de se ServletContext
eliminan. Finalmente ServletContextListener#contextDestroyed()
, se invocará el will y ServletContext
se eliminará.
HttpServletRequest
yHttpServletResponse
El contenedor de servlets está conectado a un servidor web que escucha solicitudes HTTP en un número de puerto determinado (el puerto 8080 generalmente se usa durante el desarrollo y el puerto 80 en producción). Cuando un cliente (por ejemplo, un usuario con un navegador web o mediante programaciónURLConnection
) envía una solicitud HTTP, el contenedor de servlets crea nuevas instancias HttpServletRequest
y HttpServletResponse
las pasa a través de cualquiera definida Filter
en la cadena y, eventualmente, la Servlet
instancia.
En el caso de filtros , doFilter()
se invoca el método. Cuando el código del contenedor de servlet llama chain.doFilter(request, response)
, la solicitud y la respuesta continúan con el siguiente filtro o llegan al servlet si no quedan filtros.
En el caso de servlets , service()
se invoca el método. De forma predeterminada, este método determina cuál de los doXxx()
métodos invocar en función de request.getMethod()
. Si el método determinado no está en el servlet, se devuelve un error HTTP 405 en la respuesta.
El objeto de solicitud proporciona acceso a toda la información sobre la solicitud HTTP, como su URL , encabezados , cadena de consulta y cuerpo. El objeto de respuesta brinda la capacidad de controlar y enviar la respuesta HTTP de la manera que desee, por ejemplo, permitiéndole configurar los encabezados y el cuerpo (generalmente con contenido HTML generado a partir de un archivo JSP). Cuando la respuesta HTTP se confirma y finaliza, tanto los objetos de solicitud como de respuesta se reciclan y quedan disponibles para su reutilización.
HttpSession
Cuando un cliente visita la aplicación web por primera vez y/o la HttpSession
obtiene por primera vez a través de request.getSession()
, el contenedor de servlets crea un nuevo HttpSession
objeto, genera una identificación larga y única (que puede obtener mediante session.getId()
) y lo almacena en el servidor. memoria. El contenedor de servlets también establece un Cookie
en el Set-Cookie
encabezado de la respuesta HTTP JSESSIONID
como nombre y el ID de sesión único como valor.
Según la especificación de cookies HTTP (un contrato que debe cumplir cualquier navegador web y servidor web decente), el cliente (el navegador web) debe enviar esta cookie de vuelta en solicitudes posteriores en el Cookie
encabezado mientras la cookie sea válida ( es decir, el ID único debe hacer referencia a una sesión vigente y el dominio y la ruta son correctos). Usando el monitor de tráfico HTTP integrado de su navegador, puede verificar que la cookie sea válida (presione F12 en Chrome/Edge/Firefox 23+/IE9+ y verifique la pestaña Red/Red ). El contenedor de servlets comprobará el Cookie
encabezado de cada solicitud HTTP entrante para detectar la presencia de la cookie con el nombre JSESSIONID
y utilizará su valor (el ID de la sesión) para obtener la asociada HttpSession
de la memoria del servidor.
Permanece HttpSession
activo hasta que ha estado inactivo (es decir, no se ha utilizado en una solicitud) durante más del valor de tiempo de espera especificado en <session-timeout>
, una configuración en web.xml
. El valor de tiempo de espera predeterminado depende del contenedor de servlet y suele ser de 30 minutos. Entonces, cuando el cliente no visita la aplicación web durante más tiempo del especificado, el contenedor de servlets descarta la sesión . Cada solicitud posterior, incluso con la cookie especificada, ya no tendrá acceso a la misma sesión; el contenedor de servlets creará una nueva sesión.
En el lado del cliente, la cookie de sesión permanece mientras la instancia del navegador se esté ejecutando (normalmente). A menos que el navegador esté configurado para restaurar la última sesión del navegador , cuando el cliente cierra la instancia del navegador (todas las pestañas/ventanas), la sesión se pierde por parte del cliente. En una nueva instancia del navegador, la cookie asociada con la sesión no existiría, por lo que ya no se enviaría. Esto hace que HttpSession
se cree una cookie de sesión completamente nueva y se utilice una cookie de sesión completamente nueva.
En una palabra
- Viven
ServletContext
mientras dure la aplicación web. Se comparte entre todas las solicitudes en todas las sesiones. - Vive
HttpSession
mientras el cliente interactúe con la aplicación web con la misma instancia del navegador y la sesión no haya expirado en el lado del servidor. Se comparte entre todas las solicitudes en la misma sesión. - El
HttpServletRequest
yHttpServletResponse
está activo desde el momento en que el servlet recibe una solicitud HTTP del cliente hasta que llega la respuesta completa (la página web). No se comparte en ningún otro lugar. - Todas
Servlet
las instanciasFilter
yListener
tienen vigencia mientras dure la aplicación web. Se comparten entre todas las solicitudes en todas las sesiones. - Cualquiera
attribute
que esté definido enServletContext
y vivirá mientras viva el objeto en cuestiónHttpServletRequest
.HttpSession
El objeto en sí representa el "alcance" en los marcos de administración de beans como JSF, CDI, Spring, etc. Esos marcos almacenan sus beans con alcance comoattribute
su alcance más cercano.
Seguridad del hilo
Dicho esto, su principal preocupación posiblemente sea la seguridad de los hilos . Ahora deberías saber que los servlets y filtros se comparten entre todas las solicitudes. Eso es lo bueno de Java, es multiproceso y diferentes subprocesos (léase: solicitudes HTTP) pueden hacer uso de la misma instancia. De lo contrario, sería demasiado costoso recrearlos init()
y destroy()
hacerlos para cada solicitud.
También debe tener en cuenta que nunca debe asignar datos de ámbito de solicitud o sesión como una variable de instancia de un servlet o filtro. Se compartirá entre todas las demás solicitudes en otras sesiones. ¡ Eso no es seguro para subprocesos! El siguiente ejemplo ilustra esto:
public class ExampleServlet extends HttpServlet {
private Object thisIsNOTThreadSafe;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Object thisIsThreadSafe;
thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
}
}
Ver también:
- ¿Cuál es la diferencia entre JSF, Servlet y JSP?
- La mejor opción para la gestión de sesiones en Java
- Diferencia entre / y /* en el patrón de URL de mapeo de servlets
- doGet y doPost en Servlets
- Servlet parece manejar múltiples solicitudes simultáneas del navegador de forma sincrónica
- ¿Por qué los servlets no son seguros para subprocesos?
- Diferentes formas de obtener contexto de servlet
Sesiones
En resumen: el servidor web emite un identificador único a cada visitante en su primera visita. El visitante debe traer esa identificación para que lo reconozcan la próxima vez. Este identificador también permite al servidor segregar adecuadamente los objetos que pertenecen a una sesión frente a los de otra.
Creación de instancias de servlet
Si la carga al inicio es falsa :
Si la carga al inicio es verdadera :
Una vez que esté en el modo de servicio y en el ritmo, el mismo servlet funcionará en las solicitudes de todos los demás clientes.
¿Por qué no es una buena idea tener una instancia por cliente? Piensa en esto: ¿contratarás a un pizzero por cada pedido que llegue? Haga eso y estará fuera del negocio en poco tiempo.
Sin embargo, conlleva un pequeño riesgo. Recuerde: este único tipo guarda toda la información del pedido en su bolsillo: por lo tanto, si no tiene cuidado con la seguridad de los subprocesos en los servlets , puede terminar dando el pedido incorrecto a un determinado cliente.
La sesión en los servlets de Java es la misma que la sesión en otros lenguajes como PHP. Es exclusivo del usuario. El servidor puede realizar un seguimiento de él de diferentes maneras, como cookies, reescritura de URL, etc. Este artículo del documento Java lo explica en el contexto de los servlets Java e indica que exactamente cómo se mantiene la sesión es un detalle de implementación que se deja a los diseñadores del servidor. La especificación sólo estipula que debe mantenerse como exclusivo para un usuario en múltiples conexiones al servidor. Consulte este artículo de Oracle para obtener más información sobre sus dos preguntas.
Editar Aquí hay un excelente tutorial sobre cómo trabajar con sesiones dentro de servlets. Y aquí hay un capítulo de Sun sobre los Servlets Java, qué son y cómo usarlos. Entre esos dos artículos, debería poder responder todas sus preguntas.