JSP, JSTL & the View Layer

JSP Scripting Elements

18 min Lesson 2 of 13

JSP Scripting Elements

JavaServer Pages were designed as a way to embed dynamic output directly inside HTML. To do that, the original specification defined three scripting elements that let you write Java inside a .jsp file: scriptlets, expressions, and declarations. Every JSP developer needs to recognise these constructs — both to maintain legacy code and to understand exactly why modern JSP deliberately avoids them.

How JSP Compilation Works

Before diving into the syntax it helps to understand what the servlet container actually does with a JSP file. When a request arrives for a .jsp resource, the container (Tomcat, WildFly, etc.) translates it into a plain Java source file that extends HttpJspBase, compiles that file to bytecode, and then serves subsequent requests from the compiled servlet — exactly like a hand-written HttpServlet. The scripting elements are literal Java code that is spliced into the generated class. This translation model is why scripting elements are both powerful and dangerous.

Scriptlets: <% ... %>

A scriptlet places arbitrary Java statements inside the generated _jspService() method. The syntax is <% javaCode; %>.

<%@ page import="java.util.List" %> <!DOCTYPE html> <html> <body> <% // Scriptlet: runs inside _jspService() List<String> names = (List<String>) request.getAttribute("names"); if (names == null || names.isEmpty()) { %> <p>No names found.</p> <% } else { for (String name : names) { %> <p><%= name %></p> <% } } %> </body> </html>

The container weaves the HTML fragments between the scriptlet blocks into out.write("...") calls. The result compiles and runs, but the generated source is a mess of interleaved Java and string-writing calls that is nearly impossible to test or maintain.

Scriptlets break the separation of concerns. Business logic leaks into the view, the HTML and Java become tightly coupled, and unit testing becomes impossible because the code only runs inside a live servlet container. This is the core reason the industry moved away from scriptlets.

Expressions: <%= ... %>

An expression element evaluates a single Java expression and writes its toString() value directly into the response. There is no semicolon inside an expression tag.

<p>Welcome, <%= request.getAttribute("username") %>!</p> <p>Today is <%= new java.time.LocalDate.now() %></p> <p>Items in cart: <%= ((java.util.List<?>) session.getAttribute("cart")).size() %></p>

The container translates <%= expr %> into out.print(expr);. Note that if the expression evaluates to null, the string "null" is written to the output — there is no null safety.

Expressions vs scriptlets: An expression is a shorthand for a one-line out.print() scriptlet. You cannot use statements (assignments, loops, conditionals) inside an expression tag — only a single value-producing expression.

Declarations: <%! ... %>

A declaration places code at the class level of the generated servlet, outside _jspService(). You can declare instance variables and methods here.

<%! // Declared as an instance variable of the generated servlet class private static final org.slf4j.Logger LOG = org.slf4j.LoggerFactory.getLogger("MyPage"); private String formatPrice(double amount) { return String.format("$%.2f", amount); } %> <!-- Use the declared method from an expression --> <p>Price: <%= formatPrice(49.99) %></p>
Instance variables in declarations are shared across all concurrent requests. A JSP-generated servlet is a singleton — one instance serves all threads. Any mutable state in a declaration is a data race waiting to happen. This is subtle and a common source of hard-to-reproduce bugs in legacy JSP applications.

The Page Directive

Almost every JSP file starts with a page directive, which configures settings for the generated servlet. It is not a scripting element, but it always appears alongside them.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" import="java.util.List, java.util.Map" errorPage="/WEB-INF/views/error.jsp" isErrorPage="false" session="true" %>

Key attributes: import adds import statements to the generated class; contentType sets the response Content-Type header; errorPage redirects uncaught exceptions to a dedicated error page.

Why Modern JSP Avoids Scriptlets

The Java EE community reached a broad consensus — enshrined in Sun's Core J2EE Patterns and later the JSP 2.0 specification guidance — that scriptlets should not appear in production JSP files. The reasons are concrete:

  • Untestable code. Logic inside a scriptlet can only be exercised by deploying the page and making an HTTP request. There is no way to write a unit test for a scriptlet block.
  • Designer-hostile syntax. HTML/CSS designers and front-end engineers cannot work with JSP files littered with Java. Tools like IDEs and static analysers struggle with the mixed syntax.
  • Maintenance cost. Interleaved Java and HTML multiplies the cognitive load of every change. Studies of legacy codebases consistently show JSP scriptlets as a maintenance hotspot.
  • Better alternatives exist. The Expression Language (EL) and JSTL — covered in the next lessons — provide declarative, designer-friendly replacements for every common scriptlet pattern (iteration, conditionals, formatting). Thymeleaf and FreeMarker go further by being valid HTML even before template processing.
The Sun/Oracle JSP 2.0 specification itself says: "We recommend that scriptlet usage be avoided." You can enforce this at the container level by adding <scripting-invalid>true</scripting-invalid> to the <jsp-property-group> in your web.xml. Doing so turns any remaining scriptlet in the project into a compile-time error — a useful guard rail when onboarding a legacy codebase.
<!-- web.xml: disable scriptlets project-wide --> <jsp-config> <jsp-property-group> <url-pattern>*.jsp</url-pattern> <scripting-invalid>true</scripting-invalid> <el-ignored>false</el-ignored> </jsp-property-group> </jsp-config>

Quick Reference: The Three Scripting Elements

  • <% statements; %>Scriptlet: arbitrary Java statements, placed in _jspService(). Avoid in new code.
  • <%= expression %>Expression: a single Java expression whose value is printed to the response. Superseded by EL ${expression}.
  • <%! declaration %>Declaration: class-level members (fields, methods). Risky due to shared mutable state; rarely needed.

Summary

Scriptlets, expressions, and declarations let you embed Java directly inside HTML — which is exactly what makes them problematic. They tangle view concerns with logic, produce untestable code, and require understanding the translation model (generated servlet class, _jspService() method) to reason about their behaviour. Modern JSP development replaces all three with EL and JSTL, which deliver the same dynamic output without mixing languages. In the next lesson you will explore the implicit objects that are always available in every JSP — understanding them rounds out the picture before we move to EL and JSTL.