El mundo del desarrollo web con Java a menudo puede parecer una compleja red de tecnologías. Para un principiante, entender la diferencia entre Servlet y JSP es el primer gran paso.

Aunque son dos herramientas robustas que, a pesar de su antigüedad, siguen siendo la base para crear aplicaciones de servidor fiables, muchos no saben cuándo usar cada una.

En este artículo, explicaré de forma clara cuál es el rol de cada tecnología, por qué siguen siendo relevantes y cómo construir tu primera aplicación web en Java.

La diferencia entre Servlet y JSP radica en su propósito: un Servlet es una clase Java que maneja la lógica de negocio y procesa peticiones en el servidor (el “cerebro”), mientras que una JSP (JavaServer Page) es una plantilla HTML con código Java para generar la interfaz de usuario (la “vista”). Es la separación Servlet vs JSP la que permite aplicar el patrón MVC Java.

Diagrama explicando la diferencia entre Servlet como backend y JSP como frontend en Java.
Un Servlet actúa como el cerebro de la aplicación, mientras que la JSP es la cara visible que interactúa con el usuario.

Servlet vs JSP: Roles y Diferencias Fundamentales

Los Servlets y JSP (JavaServer Pages) son dos tecnologías clave de Jakarta EE (anteriormente Java EE) que permiten la creación de aplicaciones web dinámicas. A pesar de la aparición de numerosos frameworks modernos, la comprensión de estas tecnologías fundamentales es crítica para todo desarrollador web de Java.

Considéralo como la construcción de una casa: los Servlets son los cimientos sólidos y los muros de carga, mientras que JSP se encarga del diseño interior y los acabados. Sin un entendimiento de cómo funcionan los cimientos, es imposible construir una estructura estable.

TecnologíaDescripciónPropósito Principal
ServletsClases de Java que procesan peticiones HTTP y generan respuestas HTTPProcesamiento de la lógica de negocio, interacción con datos
JSPPlantillas de páginas HTML con fragmentos de código Java incrustadosRepresentación de datos, interfaz de usuario

Los Servlets son clases de Java que extienden la funcionalidad de un servidor web. Para una explicación más detallada, he escrito una guía sobre qué es un Servlet en Java.

Procesan peticiones HTTP, interactúan con bases de datos, invocan otros servicios web y formulan respuestas para el cliente. Los Servlets se ejecutan en el lado del servidor y pueden generar contenido dinámico.

Características principales de los servlets:

  • Se ejecutan dentro de un contenedor de servlets (por ejemplo, Apache Tomcat).
  • Pueden procesar diferentes tipos de peticiones HTTP (GET, POST, PUT, etc.).
  • Mantienen el estado entre peticiones a través de sesiones.
  • Tienen acceso a toda la potencia de la API de Java.

JSP (JavaServer Pages) es una tecnología que permite crear páginas web dinámicas utilizando HTML, XML y código Java incrustado. Las páginas JSP Java se compilan en servlets antes de su ejecución, lo que garantiza un alto rendimiento.

Una lección de mi propia experiencia: Cuando empecé a crear aplicaciones web en Java, me perdí investigando frameworks complejos. Mi primer gran avance fue dar un paso atrás y construir una pequeña app de finanzas personales usando solo Servlets y JSP. Ahí comprendí su poder. Este conocimiento de los fundamentos me permitió después dominar Spring mucho más rápido, porque ya entendía lo que sucedía “bajo el capó”.

Leo Byte

Ventajas de utilizar JSP:

  • Facilita la separación entre la vista (HTML) y la lógica de Java cuando se utiliza junto con servlets, EL y JSTL.
  • Posibilidad de utilizar etiquetas para simplificar el desarrollo y reducir código Java en la vista.
  • Soporte para Expression Language (EL) para el acceso a datos de forma declarativa.
  • Integración con JSTL (Jakarta Standard Tag Library) para tareas comunes en la capa de presentación.

Arquitectura de las Aplicaciones Web Java: Componentes Principales

La arquitectura estándar de las aplicaciones web Java basadas en Servlet y JSP suele seguir el patrón MVC Java, donde los servlets actúan como controladores, las JSP como vistas y las clases Java como modelos. Para entender mejor estos conceptos, te recomiendo mi guía esencial de arquitectura de software.

En resumen, los componentes clave son:

  • Controlador: Servlets para manejar la lógica.
  • Vista: JSPs para mostrar la interfaz.
  • Modelo: Clases Java (POJOs) para los datos.

Componentes principales de la arquitectura:

  • Contenedor Web (Servlet Container): Entorno de ejecución para servlets y JSP, como Apache Tomcat, que no es un servidor Jakarta EE completo pero cubre la mayoría de los casos web.
  • Servlets: Procesan las peticiones y gestionan el flujo de datos.
  • JSP: Construyen la interfaz de usuario.
  • Filtros (Filters): Interceptan las peticiones para su preprocesamiento o postprocesamiento.
  • Escuchadores (Listeners): Monitorizan eventos en la aplicación web.
  • Clases de modelo (POJOs / DTOs / entidades): Representan y transportan los datos de la aplicación.
Diagrama del patrón MVC Java con Servlet y JSP.
El patrón MVC organiza el código separando la lógica (Servlet), los datos (Modelo) y la presentación (JSP) para un desarrollo más limpio.

El ciclo de vida de una aplicación web comienza con el despliegue de un archivo WAR (Web Application Archive) en el contenedor web. El contenedor crea instancias de los servlets, los inicializa y procesa las peticiones HTTP entrantes.

ComponenteCiclo de VidaRol en la Arquitectura
Servletinit() → service() → destroy()Controlador (MVC)
JSPCompilación → Inicialización → Procesamiento → DestrucciónVista (MVC)
JavaBeanCreación → Uso → Destrucción por el recolector de basuraModelo (MVC)

Flujo típico de ejecución de una petición en una aplicación web Java:

  1. El cliente envía una petición HTTP al servidor.
  2. La petición atraviesa los filtros configurados.
  3. El contenedor web determina el servlet apropiado basándose en el mapeo de URL en web.xml.
  4. El servlet procesa la petición, interactúa con la lógica de negocio y prepara los datos.
  5. El servlet reenvía la petición a la página JSP correspondiente.
  6. La JSP renderiza los datos en formato HTML.
  7. La respuesta se envía al cliente.

Todos los componentes de una aplicación web Java Servlet JSP se configuran en el descriptor de despliegue web.xml o mediante anotaciones (en versiones más recientes de Jakarta EE). Este archivo define la estructura de la aplicación, los mapeos de URL, los parámetros de inicialización y otros aspectos.

La estructura de un proyecto web Java típico es la siguiente:

src/
├── main/
│   ├── java/
│   │   └── com/
│   │       └── example/
│   │           ├── servlets/
│   │           ├── models/
│   │           └── utils/
│   └── webapp/
│       ├── WEB-INF/
│       │   ├── web.xml
│       │   └── views/
│       ├── css/
│       ├── js/
│       └── images/

Esta estructura se puede generar fácilmente usando arquetipos como el maven-archetype-webapp.

Cómo Crear tu Primer Servlet: Ejemplo Práctico

El desarrollo del primer servlet es un paso clave para dominar la programación web con Java. A continuación, se presenta un ejemplo práctico para crear un servlet simple que procesa peticiones HTTP y genera respuestas.

Requisitos para trabajar con servlets:

  • JDK (Java Development Kit) versión 17 o superior (recomendamos JDK 21 como la opción más moderna y con soporte largo). Si no estás seguro de qué es o cómo instalarlo, puedes consultar mi artículo sobre qué es el JDK y para qué sirve.
  • Un contenedor web (Apache Tomcat 11.x es la versión recomendada y más actualizada; también funcionan Jetty 12+ o similares), que puedes descargar desde su página oficial.
  • La API de Servlet de Jakarta (generalmente incluida en el contenedor web).
  • Un IDE (por ejemplo, IntelliJ IDEA o Eclipse).

Paso 1: Crea un nuevo proyecto de aplicación web en tu IDE.

Paso 2: Añade la dependencia de la API de Servlet de Jakarta a tu pom.xml (para Maven) o build.gradle (para Gradle).

Nota importante: Las aplicaciones modernas utilizan Jakarta EE en lugar de la antigua Java EE. Esto implica que el espacio de nombres de los paquetes ha cambiado de javax.servlet a jakarta.servlet. Asegúrate de usar las dependencias y los imports correctos para evitar problemas de compatibilidad.

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

Nota: Estamos usando la versión 6.1.0, que es la estable y alineada con Jakarta EE 11 (la versión actual en esta publicación). La 6.2.0 está en milestone, pero para proyectos reales quédate con la 6.1.0 hasta que la 6.2 sea final.

Paso 3: Crea tu primer servlet:

package com.example.servlets;

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("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {

        response.setContentType("text/html");
        response.setCharacterEncoding("UTF-8");

        try (PrintWriter writer = response.getWriter()) {
            writer.println("<!DOCTYPE html>");
            writer.println("<html>");
            writer.println("<head>");
            writer.println("<title>Mi primer servlet</title>");
            writer.println("</head>");
            writer.println("<body>");
            writer.println("<h1>¡Hola, mundo de los servlets!</h1>");
            writer.println("<p>Hora actual: " + new java.util.Date() + "</p>");
            writer.println("</body>");
            writer.println("</html>");
        }
    }
}

Paso 4: Configura el descriptor de despliegue web.xml (si no utilizas anotaciones). Nota el cambio en el esquema para Jakarta EE:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
                             https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd"
         version="6.1">
    <servlet>
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.example.servlets.HelloServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
</web-app>

Paso 5: Despliega tu aplicación en el contenedor web (recomendamos Apache Tomcat 11.x, la versión más actual y alineada con Jakarta EE 11) y pruébala abriendo http://localhost:8080/tu-app/hello en el navegador. Consulta la documentación oficial de Tomcat si tienes dudas.

Métodos principales que puedes sobrescribir en un servlet:

  • doGet(): para procesar peticiones GET (obtención de datos).
  • doPost(): para procesar peticiones POST (envío de datos).
  • doPut(): para procesar peticiones PUT (actualización de recursos).
  • doDelete(): para procesar peticiones DELETE (eliminación de recursos).
  • init(): para la inicialización del servlet.
  • destroy(): para liberar recursos antes de la destrucción del servlet.

Para procesar parámetros de una petición, utiliza el método getParameter():

String username = request.getParameter("username");

Para trabajar con la sesión, utiliza los métodos getSession():

import jakarta.servlet.http.HttpSession;

//...
HttpSession session = request.getSession();
session.setAttribute("user", username);
String user = (String) session.getAttribute("user");

Caso práctico: un Servlet para un e-commerce. En un proyecto anterior, necesité integrar una pasarela de pagos con cambios mínimos en la arquitectura. La solución perfecta fue un PaymentProcessorServlet dedicado. Implementé un Filtro para la seguridad CSRF y, gracias al control directo que ofrecen los Servlets sobre el HTTP, la funcionalidad estuvo lista en tres días y ha procesado miles de transacciones de forma segura desde entonces.

Páginas JSP: Qué es JSP y Cómo Funciona

JavaServer Pages (JSP) es una tecnología que permite crear páginas web dinámicas combinando HTML estático con código Java. JSP simplifica considerablemente la creación de interfaces de usuario en comparación con la generación de HTML directamente desde los servlets.

Los archivos JSP tienen la extensión .jsp y contienen HTML junto con etiquetas y expresiones especiales. Históricamente, también permitían incrustar código Java directamente, aunque en el desarrollo moderno este enfoque se considera obsoleto.

En la primera solicitud a una página JSP, esta se compila en un servlet que luego se ejecuta. Los resultados se almacenan en caché, por lo que las solicitudes posteriores se procesan más rápidamente.

Elementos principales de JSP:

  • Directivas: Configuraciones para el proceso de compilación de JSP (<%@ … %>).
  • Acciones JSP: Operaciones estándar, como la inclusión de otros recursos ().
  • Expresiones EL (Expression Language): Acceso declarativo y seguro a los datos (${…}).
  • JSTL: Biblioteca de etiquetas estándar para lógica de presentación.
  • Scriptlets y expresiones Java: Elementos históricos de JSP que hoy solo se utilizan con fines educativos o de mantenimiento de sistemas legacy.

Ejemplo de una página JSP simple (uso de scriptlets solo con fines educativos):

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<!DOCTYPE html>
<html>
<head>
    <title>Mi primera página JSP</title>
</head>
<body>
    <h1>¡Hola, JSP!</h1>

    <% 
        // Esto es un scriptlet – un bloque de código Java
        java.time.LocalDateTime now = java.time.LocalDateTime.now();
    %>

    <p>Fecha y hora actuales: <%= now %></p>

    <% 
        for(int i = 1; i <= 5; i++) { 
    %>
        <p>Línea #<%= i %></p>
    <% 
        } 
    %>

    <!-- Uso de Expression Language (EL) -->
    <p>Acceso a un parámetro de la petición: ${param.name}</p>
</body>
</html>

Objetos implícitos en JSP, disponibles sin declaración:

ObjetoTipoDescripción
requestHttpServletRequestLa petición HTTP actual.
responseHttpServletResponseLa respuesta HTTP.
sessionHttpSessionLa sesión del usuario.
applicationServletContextEl contexto de la aplicación web.
pageContextPageContextEl contexto de la página JSP.

La Jakarta Standard Tag Library (JSTL) proporciona etiquetas predefinidas para tareas de programación comunes, definidas en su especificación oficial:

  • Core: Operaciones básicas (condicionales, bucles).
  • Formatting: Formateo de datos (números, fechas).
  • SQL: Etiquetas históricas para acceso a bases de datos, hoy en día desaconsejadas en aplicaciones modernas por mezclar lógica de datos con la vista.
  • XML: Procesamiento de XML.
  • Functions: Funciones útiles para el manejo de cadenas.

Ejemplo de uso de JSTL:

Para usar JSTL en un proyecto Jakarta EE, debes incluir la dependencia correspondiente (ej: jakarta.servlet.jsp.jstl:jakarta.servlet.jsp.jstl-api y su implementación org.glassfish.web:jakarta.servlet.jsp.jstl) y usar el URI jakarta.tags.core.

<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<html>
<body>
    <h1>Lista de usuarios</h1>
    <c:forEach var="user" items="${userList}">
        <p>${user.name} – ${user.email}</p>
    </c:forEach>

    <c:if test="${not empty message}">
        <p class="message">${message}</p>
    </c:if>
</body>
</html>

Ejemplo de Formulario JSP Servlet en Acción

La integración de servlets y JSP es clave para construir aplicaciones web bien estructuradas en Java. Este enfoque implementa el patrón MVC (Modelo-Vista-Controlador), donde los servlets actúan como controladores, las JSP como vistas y las clases Java como modelos.

Flujo de trabajo típico de integración:

  1. El cliente envía una petición HTTP al servidor.
  2. Un servlet intercepta la petición y determina la acción a realizar.
  3. El servlet interactúa con la lógica de negocio (el modelo).
  4. Los datos se almacenan en los objetos de la petición, la sesión o el contexto.
  5. El servlet reenvía la petición a una página JSP.
  6. La página JSP muestra los datos y genera la respuesta HTML.

La transferencia de datos del servlet a la JSP se realiza de varias maneras:

  • A través de atributos de la petición: para datos relevantes solo para la petición actual.
  • A través de atributos de la sesión: para datos que deben persistir entre peticiones de un mismo usuario.
  • A través de atributos del contexto de la aplicación: para datos globales accesibles a todos los usuarios.

Aquí está la clave: este es un ejemplo Servlet y JSP clásico que ilustra el flujo de datos para mostrar una lista de usuarios:

Servlet:

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// ... otros imports como List, User, UserService

@WebServlet("/users")
public class UserListServlet extends HttpServlet {

    private UserService userService = new UserService(); // Servicio para la lógica de negocio

    @Override
    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {

        // Obtener datos del modelo
        List<User> users = userService.getAllUsers();

        // Guardar los datos en un atributo de la petición
        request.setAttribute("userList", users);

        // Reenviar a la JSP para la visualización
        request.getRequestDispatcher("/WEB-INF/views/user-list.jsp")
               .forward(request, response);
    }
}

Página JSP (user-list.jsp):

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="jakarta.tags.core" prefix="c" %>
<html>
<head>
    <title>Lista de Usuarios</title>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/style.css">
</head>
<body>
    <h1>Usuarios del Sistema</h1>

    <table>
        <tr>
            <th>ID</th>
            <th>Nombre</th>
            <th>Email</th>
            <th>Acciones</th>
        </tr>
        <c:forEach var="user" items="${userList}">
            <tr>
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.email}</td>
                <td>
                    <a href="user?id=${user.id}">Ver</a>
                    <a href="user/edit?id=${user.id}">Editar</a>
                    <a href="user/delete?id=${user.id}">Eliminar</a>
                </td>
            </tr>
        </c:forEach>
    </table>

    <a href="user/create">Añadir nuevo usuario</a>
</body>
</html>

Para el procesamiento de formularios, se suele utilizar una combinación de peticiones GET y POST. Este es un ejemplo de un formulario JSP Servlet:

  • GET: para mostrar el formulario.
  • POST: para procesar los datos enviados.

Ejemplo de procesamiento de un formulario:

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
// ... otros imports

@WebServlet("/user/create")
public class UserCreateServlet extends HttpServlet {

    private UserService userService = new UserService();

    // Muestra el formulario
    @Override
    protected void doGet(HttpServletRequest request, 
                         HttpServletResponse response) 
                         throws ServletException, IOException {

        request.getRequestDispatcher("/WEB-INF/views/user-form.jsp")
               .forward(request, response);
    }

    // Procesa los datos enviados
    @Override
    protected void doPost(HttpServletRequest request, 
                          HttpServletResponse response) 
                          throws ServletException, IOException {

        String name = request.getParameter("name");
        String email = request.getParameter("email");
        String password = request.getParameter("password");

        try {
            User user = new User(name, email, password);
            userService.createUser(user);

            // Añadir mensaje de éxito a la sesión
            request.getSession().setAttribute("message", 
                "¡Usuario creado con éxito!");

            // Redirigir a la lista de usuarios
            response.sendRedirect(request.getContextPath() + "/users");
        } catch (Exception e) {
            // En caso de error, volver al formulario con un mensaje
            request.setAttribute("error", e.getMessage());
            request.setAttribute("user", new User(name, email, ""));
            request.getRequestDispatcher("/WEB-INF/views/user-form.jsp")
                   .forward(request, response);
        }
    }
}

Prácticas recomendadas al integrar Servlets y JSP:

  • Ubica las páginas JSP en el directorio WEB-INF para impedir el acceso directo.
  • Utiliza EL y JSTL en lugar de scriptlets para mejorar la legibilidad de las JSP.
  • Aplica una plantilla unificada para todas las páginas (cabecera, pie de página, navegación).
  • Valida los datos de entrada en los servlets antes de procesarlos.
  • Utiliza el patrón PRG (Post-Redirect-Get) para evitar el reenvío de formularios.

Dominar la diferencia entre Servlet y JSP es un paso fundamental en la carrera de un desarrollador Java. Estas tecnologías no solo sientan las bases para comprender frameworks más complejos, sino que también permiten crear aplicaciones web completas con una arquitectura limpia y comprensible.

Inicia con proyectos simples, añadiendo gradualmente nuevos componentes, y pronto notarás un crecimiento en tus habilidades de desarrollo web con Java.

Recuerda que en la programación no existen soluciones universales, y el conocimiento de las tecnologías fundamentales te proporciona un conjunto de herramientas potente para resolver cualquier desafío.

Categorizado en:

Java,