Skip to content

Servlets and JSP — Complete Guide

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Servlets and JSP. We cover key concepts, practical examples, and best practices to help you master this topic.

The Foundation of Java Web Development

Before modern frameworks like Spring Boot and Jakarta Faces, Java web applications were built with Servlets and JavaServer Pages (JSP). Servlets handle the request-response lifecycle on the server side, processing HTTP requests and generating responses. JSP provides a templating engine that mixes HTML with Java code to create dynamic web pages. Together, they formed the Model-View-Controller (MVC) pattern for Java web applications.

Although many developers now use higher-level frameworks, understanding Servlets and JSP is valuable because every Java web framework ultimately runs on top of the Servlet API. The Servlet container (Tomcat, Jetty, Undertow) handles HTTP connections, threading, and request routing. Knowledge of the request lifecycle, session management, filters, and listeners translates directly to Spring MVC and other frameworks.

flowchart TB
    subgraph Container[Servlet Container - Tomcat]
        direction TB
        Client[Web Browser] --> Connector
        Connector --> Engine
        Engine --> Host[Virtual Host]
        Host --> Context[Web Application]
        Context --> Filter1 --> Filter2 --> Servlet
        Servlet --> JSP[JSP Engine]
        Context --> Listener[Context Listener]
    end
    Servlet --> JDBC[(Database)]
    JSP --> HTML[HTML Response]
    HTML --> Client

Setting Up a Servlet Application

Project Structure

my-webapp/
  src/
    main/
      java/
        com/example/
          HelloServlet.java
          UserController.java
      webapp/
        WEB-INF/
          web.xml
        index.jsp
        user-list.jsp
  pom.xml

Maven Dependencies

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.0.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>jakarta.servlet.jsp</groupId>
    <artifactId>jakarta.servlet.jsp-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

Writing Your First Servlet

Servlets extend HttpServlet and override doGet(), doPost(), or other HTTP method handlers.

package com.example;

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
import java.io.*;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    
    @Override
    public void init() throws ServletException {
        System.out.println("HelloServlet initialized");
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();
        
        String name = req.getParameter("name");
        if (name == null || name.isEmpty()) {
            name = "World";
        }
        
        out.println("<html><body>");
        out.println("<h1>Hello, " + name + "!</h1>");
        out.println("<p>Request method: " + req.getMethod() + "</p>");
        out.println("<p>User agent: " + req.getHeader("User-Agent") + "</p>");
        out.println("</body></html>");
    }
    
    @Override
    public void destroy() {
        System.out.println("HelloServlet destroyed");
    }
}

web.xml Configuration (Alternative to Annotations)

<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
         version="6.0">
    
    <servlet>
        <servlet-name>hello</servlet-name>
        <servlet-class>com.example.HelloServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>hello</servlet-name>
        <url-pattern>/hello</url-pattern>
    </servlet-mapping>
    
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Handling Form Data with doPost

@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        String username = req.getParameter("username");
        String email = req.getParameter("email");
        String password = req.getParameter("password");
        
        ValidationResult validation = validate(username, email, password);
        
        if (validation.hasErrors()) {
            req.setAttribute("errors", validation.getErrors());
            req.setAttribute("submitted", Map.of("username", username, "email", email));
            req.getRequestDispatcher("/WEB-INF/register.jsp")
                .forward(req, resp);
        } else {
            // Save to database...
            req.getSession().setAttribute("message", "Registration successful!");
            resp.sendRedirect("/myapp/profile");
        }
    }
}

JavaServer Pages (JSP)

JSP pages mix HTML with JSP tags and expressions to create dynamic content.

Basic JSP Page

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>User List</title>
</head>
<body>
    <h1>Users</h1>
    
    <c:if test="${not empty message}">
        <div class="alert">${message}</div>
    </c:if>
    
    <table border="1">
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Email</th>
        </tr>
        <c:forEach items="${users}" var="user">
            <tr>
                <td>${user.id}</td>
                <td>${user.name}</td>
                <td>${user.email}</td>
            </tr>
        </c:forEach>
    </table>
    
    <%@ include file="footer.jsp" %>
</body>
</html>

JSP Declarations, Scriptlets, and Expressions

<%-- Declaration --%>
<%!
    private int counter = 0;
    
    private String formatDate(Date date) {
        return new java.text.SimpleDateFormat("yyyy-MM-dd")
            .format(date);
    }
%>

<%-- Scriptlet (raw Java code - avoid in practice) --%>
<%
    counter++;
    List<String> items = (List<String>) request.getAttribute("items");
    for (String item : items) {
%>
    <li><%= item %></li>  <%-- Expression --%>
<%
    }
%>

<%-- Current counter: <%= counter %> --%>

In modern practice, avoid scriptlets and use JSTL tags and EL expressions instead.

Session Management

HTTP is stateless. Sessions allow the server to maintain state across multiple requests.

@WebServlet("/login")
public class LoginServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        
        User user = authenticate(username, password);
        
        if (user != null) {
            HttpSession session = req.getSession();
            session.setAttribute("user", user);
            session.setMaxInactiveInterval(3600); // 1 hour
            
            Cookie cookie = new Cookie("sessionId", session.getId());
            cookie.setHttpOnly(true);
            cookie.setSecure(true);
            cookie.setMaxAge(3600);
            resp.addCookie(cookie);
            
            resp.sendRedirect("/myapp/dashboard");
        } else {
            req.setAttribute("error", "Invalid credentials");
            req.getRequestDispatcher("/login.jsp").forward(req, resp);
        }
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        HttpSession session = req.getSession(false);
        if (session != null) {
            session.invalidate();
        }
        resp.sendRedirect("/myapp/login.jsp");
    }
}

Filters and Listeners

Servlet Filter

Filters intercept requests before they reach a servlet and responses before they reach the client.

@WebFilter("/*")
public class LoggingFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest req = (HttpServletRequest) request;
        long start = System.currentTimeMillis();
        
        chain.doFilter(request, response);
        
        long duration = System.currentTimeMillis() - start;
        System.out.printf("%s %s (%d ms)%n",
            req.getMethod(), req.getRequestURI(), duration);
    }
}

Context Listener

@WebListener
public class AppContextListener implements ServletContextListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext ctx = sce.getServletContext();
        DataSource ds = createDataSource();
        ctx.setAttribute("dataSource", ds);
        System.out.println("Application started");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Application stopped");
    }
}

Common Mistakes

1. Thread Safety Issues

Servlets are singletons shared across all requests. Instance variables are not thread-safe.

// Unsafe: shared mutable state
public class UnsafeServlet extends HttpServlet {
    private int counter = 0; // Not thread-safe!
    
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        counter++;
        // ...
    }
}

// Safe: store state in local variables or use synchronization
public class SafeServlet extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
        int localCounter = incrementCounter(); // Use thread-safe mechanism
    }
}

2. Writing Too Much Java Code in JSP

JSP files should contain presentation logic only. Business logic belongs in servlets or service classes (MVC pattern).

3. Not Handling Character Encoding

// Set encoding before reading/writing
req.setCharacterEncoding("UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");

4. Exposing JSP Files Directly

Place JSP files under WEB-INF/ so they cannot be accessed directly via URL. Route all requests through servlets.

5. Creating Sessions for Every Request

req.getSession() creates a session if one does not exist. Use req.getSession(false) to avoid unnecessary session creation for static resources.

6. Forgetting to Configure Servlets

Ensure your @WebServlet annotation or web.xml mapping covers the correct URL pattern. Common mistake: mapping /user instead of /user/*.

Practice Questions

  1. What is the lifecycle of a servlet? When are init(), service(), and destroy() called?
  2. What is the difference between forward() and sendRedirect()?
  3. How does the Servlet container handle multiple concurrent requests to the same servlet?
  4. What is the purpose of the WEB-INF directory?
  5. How do you pass data from a servlet to a JSP page?

Challenge: Build a complete MVC web application with user registration, login/logout, and a protected dashboard. Use servlets as controllers, JSP as views, and a service/DAO layer for business logic and data access. Implement a Filter for authentication checking.

FAQ

What is the difference between Tomcat, Jetty, and Undertow?

Tomcat is the most popular and feature-rich. Jetty is lightweight and embeddable. Undertow is designed for high performance and is used by WildFly. All implement the Servlet specification.

How does session tracking work without cookies?

The servlet container can rewrite URLs to include the session ID if cookies are disabled. This is called URL rewriting and is handled via encodeURL() and encodeRedirectURL().

What is the difference between Servlet 5 and Servlet 6?

Servlet 5 moved from javax.servlet to jakarta.servlet namespace. Servlet 6 requires Jakarta EE 10 and supports the Servlet 6.0 specification with HTTP/2 enhancements.

Can Servlets handle WebSocket connections?

Yes. The Servlet API includes support for WebSocket endpoints since Servlet 4.0 (Java EE 8). You can use @WebSocket or extend javax.websocket.Endpoint.

What is the difference between include directive and include action in JSP?

@include file='...' is a compile-time include that inlines the file content. <jsp:include page='...' /> is a runtime include that executes the page separately and includes its output.

Mini Project: Blog Engine with Servlets and JSP

Build a simple blog engine that supports:

  • User registration and login with session management
  • Creating, editing, and deleting blog posts (CRUD)
  • Viewing a list of posts with pagination
  • Adding comments to posts
  • Admin role that can moderate comments

Use Layered Architecture: Servlets (controller), Service classes (business logic), DAO classes (JDBC data access). Use JSP with JSTL for all views.

What's Next

Servlets and JSP are the foundation, but modern Java web development is dominated by frameworks. In the next lesson, you will learn Spring Boot Basics, which simplifies web application development with auto-configuration and production-ready features.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro