JSP, JSTL & the View Layer

Introduction to JSP

18 min Lesson 1 of 13

Introduction to JSP

JavaServer Pages (JSP) is a server-side technology that lets you embed Java logic directly inside HTML markup. The result is a template file that the server compiles into a full Servlet, executes, and streams back to the browser as ordinary HTML. Understanding why JSP was invented — and exactly what happens between a .jsp file and the HTTP response — is the conceptual foundation for everything else in this tutorial.

The Problem JSP Was Designed to Solve

Before JSP existed, dynamic web content in Java meant writing a Servlet: a plain Java class that responds to HTTP requests. Servlets work perfectly well, but generating HTML from inside a Java method is painful:

// Pure Servlet — generating HTML the hard way protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html;charset=UTF-8"); PrintWriter out = resp.getWriter(); out.println("<!DOCTYPE html>"); out.println("<html><head><title>Products</title></head><body>"); out.println("<h1>Product List</h1>"); out.println("<ul>"); for (String name : productService.findAll()) { out.println("<li>" + name + "</li>"); // XSS risk here too } out.println("</ul></body></html>"); }

Every HTML change requires a recompile and redeploy. A designer who understands HTML but not Java cannot touch the file safely. Strings are hard to read, tooling cannot syntax-highlight the markup, and the code is tightly coupled to presentation details that change far more often than business logic.

JSP inverts the relationship: you write HTML first, and drop small islands of Java in where dynamic content is needed.

JSP vs Servlets — Not Competitors, Partners

JSP and Servlets solve different halves of the same problem. The mature pattern (MVC, covered fully in Lesson 7) assigns each its proper role:

  • Servlet — handles the request, validates input, calls services/repositories, places results into request attributes, then forwards to a JSP.
  • JSP — reads those attributes and renders HTML. It contains no business logic, no database calls, and ideally no raw Java scriptlets.
The rule of thumb: if a JSP page would break if you removed every Java expression from it and replaced each with a placeholder string, but the HTML structure still makes sense, you have the separation right. JSP is a view technology, not a controller.

How a JSP File Becomes a Servlet

When a request for products.jsp arrives, the Jakarta EE container (Tomcat, WildFly, GlassFish, etc.) runs it through a well-defined pipeline. Understanding this pipeline demystifies error messages and tells you when recompilation is triggered.

  1. Translation phase — The container reads the .jsp source and generates a Java source file. That file is a class that extends jakarta.servlet.http.HttpJspBase, which in turn extends HttpServlet. Your HTML markup becomes a series of out.write("<p>..."); calls; your embedded Java expressions become direct output statements.
  2. Compilation phase — The generated Java source is compiled to a .class file by the container's built-in Java compiler (javac). If your embedded Java is syntactically wrong, you see a compiler error here, often with a line number that points into the generated source rather than your original JSP — which is why JSP error messages can be confusing at first.
  3. Loading & initialisation — The compiled class is loaded into the JVM and instantiated like any other Servlet. jspInit() is called once.
  4. Request handling — For every subsequent request, _jspService(HttpServletRequest, HttpServletResponse) is called on the existing instance. This method contains all the generated output code.
  5. Caching — The container caches the compiled class. On the next request it checks whether the .jsp source is newer than the compiled class; if not, it skips translation and compilation entirely. This is why most containers need to be told when to reload in production (or you set development="false" in Tomcat's conf/web.xml to disable the timestamp check altogether for performance).
<!-- products.jsp (what you write) --> <%@ page contentType="text/html;charset=UTF-8" %> <!DOCTYPE html> <html> <head><title>Products</title></head> <body> <h1>Product List</h1> <ul> <% for (String p : (java.util.List<String>) request.getAttribute("products")) { %> <li><%= p %></li> <% } %> </ul> </body> </html>

The container translates that page into something structurally equivalent to:

// Simplified generated servlet (pseudocode — real output is verbose) public final class products_jsp extends HttpJspBase { public void _jspService(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.setContentType("text/html;charset=UTF-8"); JspWriter out = pageContext.getOut(); out.write("<!DOCTYPE html>\n<html>\n<head>..."); out.write("<h1>Product List</h1>\n<ul>\n"); for (String p : (java.util.List<String>) request.getAttribute("products")) { out.write("<li>"); out.print(p); // <%= p %> becomes out.print(...) out.write("</li>\n"); } out.write("</ul>\n</body>\n</html>"); } }
Find the generated source on disk. In Tomcat, generated Java files land in $CATALINA_HOME/work/Catalina/localhost/<app>/. Reading the generated file when you get a cryptic error is often the fastest way to understand what went wrong — the line numbers in the stack trace refer to that generated file.

The JSP Lifecycle at a Glance

Because a JSP is ultimately a Servlet, its lifecycle mirrors the Servlet lifecycle:

  • jspInit() — called once after the class is loaded. Override this in a JSP declaration block to set up resources (rarely needed).
  • _jspService() — called for every request. This is what you are effectively writing when you author the JSP body. You cannot override it directly — the container generates it.
  • jspDestroy() — called once when the container is shutting down or the JSP is being reloaded. Clean up any resources initialised in jspInit().
JSP is not thread-safe by default. Because a single instance handles concurrent requests, any instance variable you declare in a JSP declaration is shared across all requests. Never store request-specific data in instance variables — use request attributes, local variables inside _jspService(), or session attributes. This is the same pitfall as with plain Servlets.

Where JSP Fits Today

Modern Jakarta EE and Spring applications often replace JSP with Thymeleaf, FreeMarker, or a JavaScript front end. However, JSP remains the canonical view technology taught in the Jakarta EE specification, and vast amounts of enterprise Java code still use it. Understanding JSP deeply also gives you an accurate mental model of how any server-side template engine works: all of them ultimately produce a stream of characters to a Writer, using some form of expression evaluation to insert dynamic values. The concepts you learn here transfer directly.

Summary

JSP exists because writing HTML inside Java Servlet code is error-prone and unmaintainable. A JSP file is HTML with embedded Java fragments; the container translates and compiles it into a full Servlet behind the scenes. The translation-compilation-caching pipeline explains why JSP error messages sometimes reference generated source files, and why a page does not always reflect a change until the container recompiles it. JSP and Servlets are partners: Servlets handle control flow and data access, JSP handles rendering. In the next lesson you will explore the three JSP scripting elements — declarations, scriptlets, and expressions — that let you embed Java inside that HTML markup.