Servlets y JSP básicos
Introducción
Los Servlets y JSP (JavaServer Pages) son tecnologías fundamentales para el desarrollo web con Java. Estas herramientas nos permiten crear aplicaciones web dinámicas, procesando solicitudes del cliente y generando respuestas personalizadas. Mientras que los Servlets son clases Java especializadas en manejar peticiones HTTP, los JSP complementan esta funcionalidad permitiendo la creación de contenido dinámico mediante una sintaxis más cercana al HTML. En este artículo, aprenderemos los fundamentos de estas tecnologías y cómo combinarlas para crear aplicaciones web básicas con Java.
Entendiendo los Servlets
¿Qué es un Servlet?
Un Servlet es una clase Java que extiende las capacidades de un servidor web para generar contenido dinámico. Es el equivalente Java a otras tecnologías de servidor como PHP o ASP.NET.
Los Servlets:
- Procesan peticiones HTTP (GET, POST, etc.)
- Generan respuestas dinámicas
- Mantienen el estado entre peticiones
- Se ejecutan en un contenedor de Servlets (como Tomcat o Jetty)
Configuración del entorno para Servlets
Para desarrollar con Servlets necesitamos:
- Un servidor compatible con Servlets (Tomcat, Jetty, GlassFish, etc.)
- Las dependencias de la API de Servlet (Jakarta EE o Java EE)
Si estamos utilizando Maven, podemos añadir la dependencia:
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
Estructura de un proyecto web Java
Un proyecto web Java típico tiene la siguiente estructura:
webapp/
├── WEB-INF/
│ ├── web.xml // Descriptor de despliegue
│ ├── classes/ // Clases compiladas (.class)
│ └── lib/ // Bibliotecas JAR
├── index.html // Páginas estáticas
└── recursos/ // Imágenes, CSS, JavaScript, etc.
Creación de un Servlet básico
Para crear un Servlet, debemos:
- Crear una clase que extienda
HttpServlet
- Sobrescribir los métodos
doGet
,doPost
, etc., según las peticiones que queramos manejar - Configurar el Servlet en el descriptor de despliegue (web.xml) o con anotaciones
Veamos un ejemplo básico:
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet("/hola")
public class HolaMundoServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Establecer el tipo de contenido de la respuesta
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// Obtener el writer para escribir la respuesta
PrintWriter out = response.getWriter();
// Escribir el contenido HTML
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head>");
out.println("<title>Hola Mundo Servlet</title>");
out.println("</head>");
out.println("<body>");
out.println("<h1>¡Hola desde mi primer Servlet!</h1>");
out.println("<p>La hora actual es: " + new java.util.Date() + "</p>");
out.println("</body>");
out.println("</html>");
}
}
La anotación @WebServlet("/hola")
indica que este Servlet responderá a peticiones en la ruta /hola
.
Ciclo de vida de un Servlet
Los Servlets siguen un ciclo de vida gestionado por el contenedor:
- Carga: El contenedor carga la clase del Servlet
- Inicialización: Se llama al método
init()
una sola vez - Servicio: El método
service()
procesa las peticiones, delegando endoGet()
,doPost()
, etc. - Destrucción: Se llama al método
destroy()
cuando se detiene el Servlet
Este ciclo nos permite realizar tareas de inicialización y limpieza:
@Override
public void init() throws ServletException {
// Código de inicialización (conexiones a BD, cargar configuración, etc.)
System.out.println("Servlet inicializado");
}
@Override
public void destroy() {
// Código de limpieza (cerrar conexiones, liberar recursos, etc.)
System.out.println("Servlet destruido");
}
Procesamiento de formularios
Los Servlets pueden procesar datos enviados mediante formularios HTML:
@WebServlet("/procesar-formulario")
public class FormularioServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Obtener parámetros del formulario
String nombre = request.getParameter("nombre");
String email = request.getParameter("email");
// Establecer tipo de contenido
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
// Generar respuesta
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE html>");
out.println("<html>");
out.println("<head><title>Datos recibidos</title></head>");
out.println("<body>");
out.println("<h1>Datos recibidos</h1>");
out.println("<p>Nombre: " + nombre + "</p>");
out.println("<p>Email: " + email + "</p>");
out.println("</body></html>");
}
}
El formulario HTML correspondiente:
<form action="procesar-formulario" method="post">
<label for="nombre">Nombre:</label>
<input type="text" id="nombre" name="nombre" required><br>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required><br>
<button type="submit">Enviar</button>
</form>
Introducción a JavaServer Pages (JSP)
¿Qué es JSP?
JSP (JavaServer Pages) es una tecnología que permite crear páginas web dinámicas usando código Java incrustado en HTML. Cada archivo JSP se compila automáticamente en un Servlet la primera vez que se accede a él.
Las ventajas de JSP incluyen:
- Separación más clara entre presentación y lógica
- Sintaxis más amigable para los diseñadores web
- Compilación automática
Estructura básica de un JSP
Un archivo JSP tiene extensión .jsp
y puede contener:
- HTML estático
- Elementos JSP (directivas, declaraciones, scriptlets, expresiones)
- Etiquetas personalizadas y EL (Expression Language)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Mi primera página JSP</title>
</head>
<body>
<h1>¡Hola desde JSP!</h1>
<p>La fecha actual es: <%= new java.util.Date() %></p>
<%
// Esto es un scriptlet (código Java)
for (int i = 1; i <= 5; i++) {
out.println("<p>Número " + i + "</p>");
}
%>
</body>
</html>
Elementos JSP principales
Los elementos JSP más importantes son:
-
Directivas: Configuran la página JSP
<%@ page import="java.util.*" %> <%@ include file="cabecera.jsp" %>
-
Declaraciones: Definen variables y métodos
<%! private int contador = 0; private String getSaludo() { return "¡Hola visitante!"; } %>
-
Scriptlets: Bloques de código Java
<% String nombre = request.getParameter("nombre"); if (nombre != null && !nombre.isEmpty()) { out.println("Bienvenido, " + nombre); } %>
-
Expresiones: Evaluación y salida de valores
<p>La hora es: <%= new java.util.Date() %></p> <p>Contador: <%= ++contador %></p>
Objetos implícitos en JSP
JSP proporciona varios objetos predefinidos (implícitos) que podemos usar directamente:
- request: El objeto HttpServletRequest
- response: El objeto HttpServletResponse
- session: El objeto HttpSession
- application: El contexto de la aplicación (ServletContext)
- out: El objeto JspWriter para la salida
- page: Referencia al Servlet generado (this)
- pageContext: Contexto de la página JSP
- config: Configuración del Servlet
- exception: Objeto Exception (solo en páginas de error)
<p>Tu dirección IP es: <%= request.getRemoteAddr() %></p>
<p>Nombre de servidor: <%= request.getServerName() %></p>
<%
// Almacenando datos en la sesión
session.setAttribute("usuario", "Juan");
%>
Acciones JSP estándar
Las acciones JSP son etiquetas que ejecutan acciones en tiempo de ejecución:
<!-- Incluir contenido de otro JSP -->
<jsp:include page="menu.jsp" />
<!-- Reenviar la petición a otro recurso -->
<jsp:forward page="resultado.jsp" />
<!-- Crear un JavaBean -->
<jsp:useBean id="usuario" class="es.miempresa.modelo.Usuario" />
<!-- Establecer propiedades del bean -->
<jsp:setProperty name="usuario" property="nombre" value="Ana" />
<!-- Obtener propiedades del bean -->
Nombre: <jsp:getProperty name="usuario" property="nombre" />
Expression Language (EL)
El Lenguaje de Expresiones (EL) proporciona una forma concisa de acceder a datos:
<!-- Acceder a parámetros, atributos y beans -->
<p>Nombre: ${param.nombre}</p>
<p>Usuario: ${sessionScope.usuario}</p>
<p>Email: ${usuario.email}</p>
<!-- Operadores -->
<p>${10 + 5}</p>
<p>${empty param.busqueda ? "No hay búsqueda" : "Buscando: ".concat(param.busqueda)}</p>
Integración de Servlets y JSP: Patrón MVC
El patrón Modelo-Vista-Controlador (MVC)
La combinación de Servlets y JSP nos permite implementar el patrón MVC:
- Modelo: Clases Java (JavaBeans) que representan los datos
- Vista: Páginas JSP que presentan la información
- Controlador: Servlets que procesan peticiones y coordinan el flujo
Implementación básica del patrón MVC
- Modelo (JavaBean):
package es.miempresa.modelo;
public class Producto {
private int id;
private String nombre;
private double precio;
// Constructor, getters y setters
public Producto(int id, String nombre, double precio) {
this.id = id;
this.nombre = nombre;
this.precio = precio;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getNombre() { return nombre; }
public void setNombre(String nombre) { this.nombre = nombre; }
public double getPrecio() { return precio; }
public void setPrecio(double precio) { this.precio = precio; }
}
- Controlador (Servlet):
@WebServlet("/productos")
public class ProductosServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Simular obtención de datos (en un caso real, vendría de una BD)
List<Producto> productos = new ArrayList<>();
productos.add(new Producto(1, "Portátil", 899.99));
productos.add(new Producto(2, "Smartphone", 499.99));
productos.add(new Producto(3, "Tablet", 299.99));
// Almacenar datos en el request
request.setAttribute("listaProductos", productos);
// Reenviar a la vista JSP
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/vistas/productos.jsp");
dispatcher.forward(request, response);
}
}
- Vista (JSP):
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>Listado de Productos</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>Catálogo de Productos</h1>
<table>
<tr>
<th>ID</th>
<th>Nombre</th>
<th>Precio</th>
</tr>
<c:forEach var="producto" items="${listaProductos}">
<tr>
<td>${producto.id}</td>
<td>${producto.nombre}</td>
<td>${producto.precio} €</td>
</tr>
</c:forEach>
</table>
</body>
</html>
JSTL (JavaServer Pages Standard Tag Library)
JSTL es una biblioteca de etiquetas que facilita las tareas comunes en JSP:
Para usarla, primero debemos añadir la dependencia:
<dependency>
<groupId>jakarta.servlet.jsp.jstl</groupId>
<artifactId>jakarta.servlet.jsp.jstl-api</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>jakarta.servlet.jsp.jstl</artifactId>
<version>2.0.0</version>
</dependency>
Ejemplos de uso de JSTL:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!-- Condicionales -->
<c:if test="${not empty param.nombre}">
<p>Bienvenido, ${param.nombre}</p>
</c:if>
<c:choose>
<c:when test="${producto.precio < 100}">
<p>Precio bajo</p>
</c:when>
<c:when test="${producto.precio < 500}">
<p>Precio medio</p>
</c:when>
<c:otherwise>
<p>Precio alto</p>
</c:otherwise>
</c:choose>
<!-- Bucles -->
<c:forEach var="i" begin="1" end="5">
<p>Elemento ${i}</p>
</c:forEach>
<!-- Formato -->
<p>Precio: <fmt:formatNumber value="${producto.precio}" type="currency" currencySymbol="€" /></p>
<p>Fecha: <fmt:formatDate value="${ahora}" pattern="dd/MM/yyyy HH:mm" /></p>
Manejo de sesiones y cookies
Trabajando con sesiones
Las sesiones nos permiten mantener el estado entre peticiones:
// En un Servlet
HttpSession session = request.getSession();
session.setAttribute("usuario", "admin");
session.setAttribute("carrito", listaProductos);
// Tiempo de expiración (en segundos)
session.setMaxInactiveInterval(1800); // 30 minutos
// Eliminar la sesión
session.invalidate();
<!-- En un JSP -->
<p>Usuario: ${sessionScope.usuario}</p>
<p>Productos en carrito: ${sessionScope.carrito.size()}</p>
Manejo de cookies
// Crear una cookie
Cookie cookie = new Cookie("preferencia", "oscuro");
cookie.setMaxAge(60 * 60 * 24 * 30); // 30 días
cookie.setPath("/");
response.addCookie(cookie);
// Leer cookies
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie c : cookies) {
if ("preferencia".equals(c.getName())) {
String tema = c.getValue();
// Usar el valor
}
}
}
// Eliminar una cookie
Cookie cookie = new Cookie("preferencia", "");
cookie.setMaxAge(0);
cookie.setPath("/");
response.addCookie(cookie);
Aplicación de ejemplo: Gestión de tareas
Para consolidar lo aprendido, crearemos una pequeña aplicación de gestión de tareas con Servlets y JSP:
1. Modelo (JavaBean):
package es.miempresa.modelo;
import java.util.Date;
public class Tarea {
private int id;
private String titulo;
private String descripcion;
private boolean completada;
private Date fechaCreacion;
// Constructor, getters y setters
public Tarea(int id, String titulo, String descripcion) {
this.id = id;
this.titulo = titulo;
this.descripcion = descripcion;
this.completada = false;
this.fechaCreacion = new Date();
}
// Getters y setters (omitidos por brevedad)
}
2. Controlador (Servlet):
@WebServlet("/tareas/*")
public class TareasServlet extends HttpServlet {
// Lista de tareas (en producción usaríamos una BD)
private List<Tarea> tareas = new ArrayList<>();
private int contadorId = 1;
@Override
public void init() {
// Añadir algunas tareas de ejemplo
tareas.add(new Tarea(contadorId++, "Aprender Servlets", "Estudiar la API de Servlets"));
tareas.add(new Tarea(contadorId++, "Practicar JSP", "Crear páginas dinámicas con JSP"));
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String accion = request.getPathInfo();
if (accion == null || accion.equals("/")) {
// Mostrar lista de tareas
request.setAttribute("tareas", tareas);
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/vistas/lista-tareas.jsp");
dispatcher.forward(request, response);
} else if (accion.equals("/nueva")) {
// Mostrar formulario de nueva tarea
RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/vistas/form-tarea.jsp");
dispatcher.forward(request, response);
} else if (accion.startsWith("/completar/")) {
// Marcar tarea como completada
int id = Integer.parseInt(accion.substring(11));
for (Tarea tarea : tareas) {
if (tarea.getId() == id) {
tarea.setCompletada(true);
break;
}
}
response.sendRedirect(request.getContextPath() + "/tareas");
}
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Procesar formulario de nueva tarea
String titulo = request.getParameter("titulo");
String descripcion = request.getParameter("descripcion");
if (titulo != null && !titulo.trim().isEmpty()) {
tareas.add(new Tarea(contadorId++, titulo, descripcion));
}
response.sendRedirect(request.getContextPath() + "/tareas");
}
}
3. Vistas (JSP):
lista-tareas.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<title>Gestión de Tareas</title>
<style>
.tarea { margin-bottom: 15px; padding: 10px; border: 1px solid #ddd; }
.completada { background-color: #e8f5e9; }
.pendiente { background-color: #fffde7; }
</style>
</head>
<body>
<h1>Mis Tareas</h1>
<a href="${pageContext.request.contextPath}/tareas/nueva">Nueva Tarea</a>
<div>
<c:choose>
<c:when test="${empty tareas}">
<p>No hay tareas disponibles.</p>
</c:when>
<c:otherwise>
<c:forEach var="tarea" items="${tareas}">
<div class="tarea ${tarea.completada ? 'completada' : 'pendiente'}">
<h3>${tarea.titulo}</h3>
<p>${tarea.descripcion}</p>
<p>Creada: <fmt:formatDate value="${tarea.fechaCreacion}" pattern="dd/MM/yyyy HH:mm" /></p>
<c:if test="${!tarea.completada}">
<a href="${pageContext.request.contextPath}/tareas/completar/${tarea.id}">Marcar como completada</a>
</c:if>
</div>
</c:forEach>
</c:otherwise>
</c:choose>
</div>
</body>
</html>
form-tarea.jsp:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
<title>Nueva Tarea</title>
<style>
form { max-width: 500px; margin: 0 auto; }
label, input, textarea { display: block; margin-bottom: 10px; width: 100%; }
textarea { height: 100px; }
</style>
</head>
<body>
<h1>Nueva Tarea</h1>
<form action="${pageContext.request.contextPath}/tareas" method="post">
<div>
<label for="titulo">Título:</label>
<input type="text" id="titulo" name="titulo" required>
</div>
<div>
<label for="descripcion">Descripción:</label>
<textarea id="descripcion" name="descripcion"></textarea>
</div>
<div>
<button type="submit">Guardar</button>
<a href="${pageContext.request.contextPath}/tareas">Cancelar</a>
</div>
</form>
</body>
</html>
Resumen
En este artículo hemos explorado los fundamentos de los Servlets y JSP, dos tecnologías clave para el desarrollo web con Java. Hemos aprendido cómo los Servlets procesan peticiones HTTP y generan respuestas dinámicas, mientras que los JSP facilitan la creación de vistas mezclando HTML con código Java. También hemos visto cómo integrar ambas tecnologías siguiendo el patrón MVC, donde los Servlets actúan como controladores, los JavaBeans como modelos y los JSP como vistas.
Estos conocimientos constituyen la base del desarrollo web con Java y son fundamentales antes de adentrarse en frameworks más avanzados como Spring o Jakarta EE. En próximos artículos exploraremos cómo estos frameworks facilitan y mejoran el desarrollo de aplicaciones web, construyendo sobre los conceptos aprendidos aquí.