====== 05 - Servlets ======
Un **servlet** es un componente Java que procesa peticiones HTTP dentro de un contenedor. El desarrollador escribe el método que responde; el contenedor se ocupa de sockets, hilos, parseo y envío.
===== 1. Un servlet =====
@WebServlet("/books")
public class BookServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
String id = request.getParameter("id");
response.setContentType("application/json");
response.setCharacterEncoding("UTF-8");
response.getWriter().print("{\"id\":" + id + "}");
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws IOException {
// procesar el cuerpo y crear el recurso
response.setStatus(HttpServletResponse.SC_CREATED);
}
}
^ Elemento ^ Función ^
| ''HttpServlet'' | Clase base con el ciclo de vida gestionado por el contenedor |
| ''doGet'' / ''doPost'' | Responden a los métodos HTTP correspondientes |
| ''HttpServletRequest'' | Petición: parámetros, cabeceras, cuerpo |
| ''HttpServletResponse'' | Respuesta: estado, cabeceras, cuerpo |
| ''@WebServlet'' | Asocia el servlet con una ruta |
===== 2. Ciclo de vida =====
* **''init()''** — el contenedor crea el servlet una vez al arrancar (o a la primera petición).
* **''service()''** — cada petición delega en ''doGet''/''doPost''. **Una sola instancia** atiende todas las peticiones, cada una en un hilo del pool del contenedor.
* **''destroy()''** — el contenedor lo retira al parar.
@WebServlet("/counter")
public class CounterServlet extends HttpServlet {
private int counter = 0; // ¡compartido por todas las peticiones!
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
counter++; // condición de carrera entre peticiones concurrentes
response.getWriter().print(counter);
}
}
El estado de instancia del servlet es compartido: las reglas de hilos del tema 03 se aplican tal cual. El estado por petición debe ser local o gestionarse con cuidado.
===== 3. Filtros =====
Un **filtro** se ejecuta en la cadena antes (o después) del servlet. Sirve para lógica transversal: registro, compresión, **autenticación**.
@WebFilter("/*")
public class TokenFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String header = ((HttpServletRequest) request).getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
// validar el token; si es válido, continuar
chain.doFilter(request, response);
} else {
((HttpServletResponse) response).setStatus(401);
}
}
}
''chain.doFilter(...)'' pasa al siguiente filtro o al servlet; sin llamarlo, la petición termina ahí.
===== 4. Qué resuelve y qué no =====
^ Resuelve el contenedor ^ Sigue siendo responsabilidad del desarrollador ^
| Sockets, pool de hilos, HTTP | Qué hace cada ruta (la lógica) |
| Enrutado a servlets por ruta | Estructura de la aplicación |
| Parseo de parámetros y cabeceras | Reglas de negocio y acceso a datos |
| Devolución de errores de protocolo | Cuerpo y formato de los datos (JSON) |
Un servlet atiende muchas peticiones con una sola instancia: no guardes en sus campos nada que pertenezca a una petición concreta. La autenticación por token se resuelve con filtros que validan la cabecera ''Authorization''; sin sesiones de servidor.