Java Servlet API Guide Essentials | Generated by AI

Home 2025.09

Here’s a practical, end-to-end guide to the Java Servlet API in the classic javax.servlet.* namespace (Servlet 2.x–4.x era). I’ll also note what changed after the move to jakarta.servlet.* so you’re future-proof.

1) What is a Servlet?

A Servlet is a Java class that runs inside a Servlet container (Tomcat, Jetty, WebSphere, WebLogic, etc.) and handles HTTP requests/responses. The container manages:

2) Core Packages & Key Interfaces (javax.*)

Jakarta switch: starting Servlet 5.0 (Jakarta EE 9), the packages renamed to jakarta.servlet.*. Most APIs are the same; update imports and dependencies when migrating.

3) Servlet Lifecycle & Threading Model

Threading: The container calls service concurrently on the same instance. Rule: Avoid mutable instance fields; if you must, use thread-safe structures or proper synchronization. Prefer locals.

4) Minimal Servlet (annotations)

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import javax.servlet.ServletException;
import java.io.IOException;

@WebServlet(name = "HelloServlet", urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
  @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException {
    resp.setContentType("text/plain;charset=UTF-8");
    resp.getWriter().println("Hello, Servlet!");
  }
}

5) web.xml vs Annotations

Annotations (3.0+) are easiest for simple apps. web.xml still useful for ordering, overrides, or legacy containers.

Minimal web.xml:

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="3.0">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>com.example.HelloServlet</servlet-class>
    <init-param>
      <param-name>greeting</param-name>
      <param-value>Hi</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

6) The HTTP Request/Response Essentials

Reading request data

String q = req.getParameter("q");        // query/form field
Enumeration<String> names = req.getParameterNames();
BufferedReader reader = req.getReader(); // raw body text
ServletInputStream in = req.getInputStream(); // binary body
String header = req.getHeader("X-Token");

Tip: Always set encoding before reading params:

req.setCharacterEncoding("UTF-8");

Writing responses

resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("application/json;charset=UTF-8");
resp.setHeader("Cache-Control", "no-store");
try (PrintWriter out = resp.getWriter()) {
  out.write("{\"ok\":true}");
}

7) doGet vs doPost vs others

8) Sessions & Cookies

HttpSession session = req.getSession(); // creates if absent
session.setAttribute("userId", 123L);
Long userId = (Long) session.getAttribute("userId");
session.invalidate(); // logout

Configure session cookie flags via container or programmatically:

9) Filters (cross-cutting concerns)

Use Filters for logging, auth, CORS, compression, encoding, etc.

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

@WebFilter(urlPatterns = "/*")
public class LoggingFilter implements Filter {
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    long start = System.nanoTime();
    try {
      chain.doFilter(req, res);
    } finally {
      long ms = (System.nanoTime() - start) / 1_000_000;
      req.getServletContext().log("Handled in " + ms + " ms");
    }
  }
}

10) Listeners (app & request hooks)

Common ones:

Example:

@WebListener
public class AppBoot implements javax.servlet.ServletContextListener {
  public void contextInitialized(javax.servlet.ServletContextEvent sce) {
    sce.getServletContext().log("App starting...");
  }
  public void contextDestroyed(javax.servlet.ServletContextEvent sce) {
    sce.getServletContext().log("App stopping...");
  }
}

11) Async & Non-Blocking I/O

Async (Servlet 3.0)

Allows freeing container threads while a backend call runs.

@WebServlet(urlPatterns="/async", asyncSupported=true)
public class AsyncDemo extends HttpServlet {
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
    AsyncContext ctx = req.startAsync();
    ctx.start(() -> {
      try {
        // call slow service...
        ctx.getResponse().getWriter().println("done");
      } catch (Exception e) {
        ctx.complete();
      } finally {
        ctx.complete();
      }
    });
  }
}

Non-blocking (Servlet 3.1)

Register ReadListener/WriteListener on the streams for event-driven I/O. Useful for streaming large bodies without blocking threads.

12) File Uploads (Multipart)

import javax.servlet.annotation.MultipartConfig;

@MultipartConfig(maxFileSize = 10 * 1024 * 1024)
@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
  protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    Part file = req.getPart("file");
    String filename = file.getSubmittedFileName();
    try (InputStream is = file.getInputStream()) {
      // save...
    }
    resp.getWriter().println("Uploaded " + filename);
  }
}

Make sure the client sends Content-Type: multipart/form-data.

13) Dispatching & Templating

14) Character Encoding & i18n

A tiny encoding filter prevents mojibake:

@WebFilter("/*")
public class Utf8Filter implements Filter {
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {
    req.setCharacterEncoding("UTF-8");
    res.setCharacterEncoding("UTF-8");
    chain.doFilter(req, res);
  }
}

Use Locale from HttpServletRequest#getLocale() and resource bundles for i18n.

15) Security Basics

16) Error Handling

<error-page>
  <error-code>404</error-code>
  <location>/WEB-INF/errors/404.jsp</location>
</error-page>
<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/WEB-INF/errors/500.jsp</location>
</error-page>

17) Logging & Observability

18) Packaging & Deployment

WAR layout:

myapp/
  WEB-INF/
    web.xml
    classes/            # compiled .class files
    lib/                # third-party jars
  index.html
  static/...

Build with Maven/Gradle, produce a WAR, deploy to the container’s webapps (Tomcat) or via admin console. For embedded style, use Jetty or Tomcat embedded with a main() bootstrapping the server.

19) Testing Servlets

20) Performance Tips

21) Common Pitfalls

22) From javax.servlet.* to jakarta.servlet.*

If/when you upgrade to Jakarta EE 9+:

23) Minimal REST-style Example

@WebServlet(name="UsersApi", urlPatterns="/api/users/*")
public class UsersApi extends HttpServlet {
  @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("application/json;charset=UTF-8");
    String id = pathParam(req, 1); // e.g. /api/users/42
    if (id == null) {
      resp.getWriter().write("[{\"id\":1,\"name\":\"A\"}]");
    } else {
      resp.getWriter().write("{\"id\":"+id+",\"name\":\"A\"}");
    }
  }
  private String pathParam(HttpServletRequest req, int idx) {
    String path = req.getPathInfo();        // "/42"
    if (path == null) return null;
    String[] parts = path.split("/");
    return parts.length > idx ? parts[idx] : null;
  }
}

24) Build Dependencies (classic javax.servlet)

Provided scope in Maven because the container supplies the API at runtime:

<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>4.0.1</version>
  <scope>provided</scope>
</dependency>

For Jakarta:

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

If you tell me your target container (Tomcat/Jetty/Liberty), Java version, and whether you want synchronous, async, or non-blocking I/O, I can sketch a production-ready skeleton tailored to your stack (logging, CORS, security headers, and a clean Maven/Gradle layout) — no tables, just tight code and steps.


Back Donate