Combining Servlets and JSP in the Core Guide to Java Web Technologies

In modern Java web development, Servlets and JSP (JavaServer Pages) are the two core components for building dynamic websites. While both run on the server side and handle HTTP requests, their design philosophies differ significantly: Servlets focus on processing business logic, whereas JSP is dedicated to rendering and presenting the view layer. Understanding how these two work together is key to implementing the "separation of concerns" principle and forms the foundation for writing maintainable, high-performance web applications.
In traditional MVC (Model-View-Controller) architecture, the Servlet acts as the controller. Its primary responsibility is to receive client HTTP requests, parse parameters, invoke the business logic layer to process data, and finally decide what content to return to the browser. When an HTML response needs to be generated, a Servlet can directly output strings using response.getWriter(), or it can forward (Forward) to a JSP page for rendering.

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 1. Process business logic: retrieve user information
        String userId = request.getParameter("id");
        List<User> users = userService.findById(userId);

        // 2. Prepare data into request attributes for use by JSP
        request.setAttribute("users", users);

        // 3. Forward to the JSP page for rendering (recommended approach)
        getServletContext().getRequestDispatcher("/WEB-INF/views/user.jsp").forward(request, response);
    }
}

The code above demonstrates how a Servlet acts as an entry point; after processing business logic, it encapsulates data and passes it to the JSP. Using the RequestDispatcher's forward() method is better than direct redirection for maintaining URL consistency and does not lose request context (such as Session).

JSP Focuses on View Rendering and Page Layout

JSP is essentially a Servlet but is designed to allow developers to embed Java code within HTML, XML, or other markup languages. This enables developers to build interfaces like static web pages while leveraging Java objects to drive data display. The core advantage of JSP lies in decoupling business logic from the presentation layer, making page structures clearer.

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>User List</title></head>
<body>
<h2>User Information</h2>
<ul>
    <%-- Iterate over the user list passed from Servlet --%>
    <c:forEach items="${users}" var="user">
        <li>${user.name} - ${user.email}</li>
    </c:forEach>
</ul>
</body>
</html>

In JSP, developers primarily use EL (Expression Language) or JSTL (JavaServer Standard Tag Library) tags to access data passed from the Servlet. This approach not only improves readability but also avoids excessive String concatenation operations, enhancing code maintainability.

Best Practices: Avoid Direct Output of Java Objects

In early development practices, a common mistake was serializing complete Java objects into HTML strings within the Servlet and writing them directly to the response stream (the so-called "JSP Hell" pattern). While feasible, this leads to tight coupling between view and logic; if page styles or layouts need modification, changes must be made deep in business code.

The correct collaboration flow should be: Servlet is responsible only for data cleaning, transformation, and transfer; JSP focuses entirely on how to display these data to the user. If HTML output must occur within a Servlet (e.g., in legacy projects where JSP was not yet prevalent), use StringWriter or builder patterns to avoid character encoding issues directly manipulating streams.

Performance Optimization and Thread Safety Considerations

Although modern frameworks like Spring Boot largely encapsulate these details, understanding the underlying mechanisms remains important. JSP is compiled into a Servlet on the first request; subsequent requests reuse this instance. This means JSP pages can share the same HttpServlet object (Singleton pattern), thereby saving memory resources to some extent. However, since web containers create independent context environments for each thread, developers must still avoid storing mutable state variables in JSPs to prevent potential thread safety issues.

Furthermore, loops and conditional statements within large dynamic content rendering in JSP can consume considerable CPU time. If complex calculations or formatting involving large amounts of data are involved, it is recommended to migrate such logic back to the Servlet layer for processing before passing results to the page, thereby reducing view layer burden and improving response speed.