Ciclo de Vida
Lo más habitual es que un servidor web mantenga una única instancia de un servlet concreto. O uno por cada máquina virtual en entornos distribuidos.
Cuando llega una petición, el servidor resuelve que servlet concreto tiene que atenderla, y si la instancia de dicho servlet no existe entonces la crea. No obstante, se puede forzar a que se instancie un determinado servlet al desplegar la aplicación utilizando la etiqueta load-on-startup
en el fichero web.xml:
<servlet> <servlet-name>HelloServlet</servlet-name> <servlet-class>com.company.servlet.HelloServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
El valor numérico de la etiqueta indica el orden en que tiene que instanciarse. Un valor negativo indica que no tiene que instanciarse automáticamente al realizar el despliegue.
El ciclo de vida de un servlet está definido por la interface javax.servlet.Servlet
que todos los servlets implementan. Cuando se crea una instancia se llama al método init
para dar la oportunidad a que se inicialice. Cuando se recibe una petición que tiene que atender se llama al método service
para que la atienda. Y cuando se va a destruir la instancia se llama al método destroy
para que pueda terminar en orden.
public class HelloServlet extends HttpServlet { @Override public void init() { ... @Override public void destroy() { ...
El método init
tiene otra forma en la que recibe un parámetro de tipo ServletConfig
con detalles de la configuración del entorno de ejecución. Un servlet puede decidir que no debe instanciarse por código dentro del método init
elevando una excepción de tipo ServletException
o UnavailableException
. Aunque el servidor web puede tratar de instanciarlo de nuevo más adelante. En el caso de UnavailableException
se permite indicar un tiempo (en segundos) durante el cual no se debe intentar volver a instanciar.
El método service
no se suele sobrecargar cuando se hereda de HttpServlet
, ya que la clase base lo que hace es analizar la petición y llamar a los métodos doGet
, doPost
, …
El tiempo que está instanciado en memoria un servlet es responsabilidad del servidor web, que puede decidir mantenerlo unos pocos milisegundos, o durante todo el tiempo que esté la aplicación desplegada.
Un mismo servlet puede ser invocado de forma concurrente por varias peticiones simultáneas, por lo que el código debe ser seguro en ese aspecto, evitando el uso de variables de instancia y sincronizando el acceso a recursos si es necesario. El uso de synchronized
sobre los métodos está desaconsejado, ya que impide la ejecución en paralelo obligando a serializar las llamadas.
Dispatchers
Dentro de una aplicación web a veces es necesario tener que redirigir el flujo de proceso de un servlet a otro, o tener que añadir al resultado dinámico generado por un servlet el generado por otro. Los dispatchers son las clases que permiten realizar este tipo de tareas.
A los dispatchers se accede a través de dos métodos de Servlet Context. El método getRequestDispatcher
devuelve un objeto de tipo RequestDispatcher
correspondiente a un path dado, que necesariamente tiene que empezar con el caracter separador «/». El método getNamedDispatcher
devuelve un objeto de tipo RequestDispatcher
correspondiente a un nombre de servlet dado.
... RequestDispatcher dispatcher = context.getRequestDispatcher("/otra/url"); ...
Una vez obtenida una instancia del dispatcher se puede llamar a sus métodos. El método forward
redirige el flujo de proceso, aunque es necesario que se llame antes de haber enviado ninguna información al cliente y con el buffer de respuesta limpio. El método include
permite que se añada información al buffer de respuesta, aunque en ese caso el uso del objeto respuesta es limitado, no pudiéndose añadir cabeceras HTTP por ejemplo.
... dispatcher.forward(request, response); ...