JSP - JavaServer Pages
In the Java ecosystem, when talking about building web applications, JSP (JavaServer Pages) is inevitable. JSP is a server-side technology that allows embedding Java code into HTML, XML, or other markup languages to create dynamic web pages. Its main goal is to help separate the presentation layer from business logic, although how effective this separation is depends greatly on how you use JSP.
Compared to servlets, JSP provides a more comfortable environment for frontend developers or designers, because it mostly resembles HTML and dynamic parts are added with special tags or minimal Java code. However, understanding what happens behind the scenes is crucial for any serious Java web developer.
What Happens Behind the Scenes of JSP: The Lifecycle
When a JSP page receives a request for the first time or when the web application starts (depending on configuration), it goes through a lifecycle. Understanding this process is critical to grasp how JSP works and potential pitfalls:
- Translation Phase: The JSP container (e.g., Jasper inside Tomcat or Jetty) reads the
.jsp
file and converts it into an equivalent Java Servlet source code file (.java
). During this phase, all JSP elements (scriptlets, expressions, directives, actions) are transformed into appropriate Java code. Static HTML/markup is converted into statements likeout.println()
. - Compilation Phase: The JSP container compiles the generated
.java
file into Java bytecode (.class
) using the standard Java compiler (javac
). Any syntax errors will surface at this stage. - Loading Phase: The compiled
.class
file is loaded into the JVM (Java Virtual Machine). - Instantiation Phase: An instance (object) is created from the loaded class. Typically, only one instance is created per JSP page (though different configurations are possible).
- Initialization Phase: As in the Servlet specification, the container calls the
jspInit()
method. This method is called only once, right after the instance is created, and can be used for tasks like opening database connections or other one-time setups. You can override it using the JSP declaration element. - Request Processing Phase: For each client request, the container creates a new thread and calls the
_jspService()
method on that thread. All your scriptlet code, expressions, and HTML in the JSP file are placed inside this method. This method accepts therequest
andresponse
objects as parameters.- ⚠️ Important: Since the
_jspService()
method runs in a multithreaded environment, be careful with thread safety when using instance variables (those created with declarations). Local variables (created inside scriptlets) are per-request and thus thread-safe.
- ⚠️ Important: Since the
- Destruction Phase: When the web application stops or the JSP container is shut down, the container calls the
jspDestroy()
method. This is also called only once and can be used to close resources opened injspInit()
(e.g., database connections).
➡️ Key Takeaway: Every JSP file is actually a Servlet running behind the scenes! Understanding this is fundamental to comprehending JSP behavior and its relationship with the Servlet context.
JSP Syntax
A JSP page consists of a mix of static markup (HTML, XML) and dynamic JSP elements. The JSP container processes these elements and sends the result to the client.
Scripting Elements (The "Old" Way — Use with Caution!)
These elements allow embedding raw Java code directly into JSP pages. In modern JSP practices (with EL and JSTL), it is recommended to avoid them as much as possible because they mix presentation with logic and make the code unreadable. Still, it’s important to know them:
-
Declarations (
<%! ... %>
):- Used to declare variables (member variables) and methods at the generated servlet class level.
- Variables declared here are shared across all requests and are not thread-safe! Use with great caution.
- More commonly used for declaring methods (e.g., helper methods or overriding
jspInit
/jspDestroy
).
example/Declarations.jsp -
Scriptlets (
<% ... %>
):- Java code blocks that will be placed inside the
_jspService()
method of the generated servlet. - Any amount of Java code (variable declarations, loops, conditionals, method calls) can be written here.
- Local variables declared here are recreated for each request and are thread-safe.
- ⚠️ Main Problem: Mixing HTML markup with Java code (spaghetti code) severely reduces readability and maintainability. Avoid this!
example/Scriptlet.jsp - Java code blocks that will be placed inside the
-
Expressions (
<%= ... %>
):- Evaluates a Java expression, converts it to a String, and writes it directly to the response output stream (like
out.print(...)
). - Do not put a semicolon (
;
) at the end of the code block. - More concise and readable than using
out.println()
in scriptlets.
example/Expressions.jsp - Evaluates a Java expression, converts it to a String, and writes it directly to the response output stream (like
Directives (<%@ ... %>
)
Directives provide instructions to the JSP container during the translation phase on how to process the page. They do not produce output at runtime.
-
page
Directive: The most commonly used directive; it sets various page-level attributes. A page may contain multiplepage
directives, but some attributes (e.g.,language
) can be set only once. Key attributes:import="package.Class, package.*, ..."
: Imports Java classes. Multiple imports are comma-separated. Packages likejava.lang.*
,javax.servlet.*
,javax.servlet.http.*
, andjavax.servlet.jsp.*
are imported automatically.contentType="mimeType; charset=encoding"
: Sets the MIME type and character encoding of the response sent (e.g.,"text/html; charset=UTF-8"
). Proper encoding (UTF-8) is essential for Azerbaijani characters.session="true|false"
: Determines whether this page participates in HTTP sessions. Default istrue
. If no session is needed, settingfalse
can improve performance. Whenfalse
, the implicit objectsession
is unavailable.errorPage="relativeURL"
: Specifies the page to which the request will be forwarded when an uncaught exception occurs on the page.isErrorPage="true|false"
: Indicates whether this page is an error page that receives errors forwarded from another page. Default isfalse
. Whentrue
, the implicit objectexception
is available.buffer="none|sizekb"
: Sets the buffer size for the output stream (out
object). Default is usually8kb
.none
disables buffering (may impact performance negatively).autoFlush="true|false"
: Determines whether the buffer is automatically flushed to the client when full. Default istrue
. Iffalse
and the buffer fills, an exception will occur.language="java"
: Specifies the scripting language used. Currently only"java"
is supported and is the default.
directives/page-directive.jsp -
include
Directive: Statically includes the contents (text, HTML, JSP code) of another file into the current JSP page during the translation phase. The included file is processed as if it were part of the main file. The path must be relative.- Difference: Unlike the
<jsp:include>
action, this directive includes the file only once before compilation. If the included file changes, the main JSP file must be recompiled. - Typically used with
.jspf
(JSP Fragment) files for static or reusable code parts like headers and footers.
directives/include-directive.jsp - Difference: Unlike the
-
taglib
Directive: Includes a custom tag library (including JSTL) in the page. Theprefix
attribute defines the namespace used when referencing the tags, anduri
indicates the location of the Tag Library Descriptor (TLD) file or a known URI.directives/taglib-directives.jsp (We’ll cover JSTL and custom tags in detail later.)
JSP Implicit Objects
For each JSP page (more precisely, for its _jspService
method), the JSP container automatically makes a number of objects available. These objects can be used directly within scriptlets and expressions.
request
(javax.servlet.http.HttpServletRequest
): Represents the current HTTP request. Used to access parameters, headers, cookies, attributes, and the session.response
(javax.servlet.http.HttpServletResponse
): Represents the current HTTP response. Used to set headers, add cookies, and redirect. Althoughresponse.getWriter()
can be used to write directly to the output stream, theout
object is usually more convenient.out
(javax.servlet.jsp.JspWriter
): A buffered writer object used to write content to the response body. Methods includeprintln()
,print()
, andnewLine()
.session
(javax.servlet.http.HttpSession
): Represents the session object for the current client. Used to store user data across requests (setAttribute
,getAttribute
). Available only when<%@ page session="true" %>
(default).application
(javax.servlet.ServletContext
): Represents the context for the entire web application. Used to store data shared across all users and requests (application scope attributes), initialization parameters, and to access resources.config
(javax.servlet.ServletConfig
): Stores configuration information for the current JSP page (generated servlet), such as init parameters defined inweb.xml
.pageContext
(javax.servlet.jsp.PageContext
): A central object that provides programmatic access to all other implicit objects and various scopes (page, request, session, application). It also holds methods for attribute management, request forwarding, and including. Heavily used within custom tag handlers.page
(java.lang.Object
): Represents the instance of the current JSP page (this
reference). Rarely used directly.exception
(java.lang.Throwable
): Available only on error pages (<%@ page isErrorPage="true" %>
) and holds the exception that occurred on the original page.
JSP Standard Actions (<jsp:...>
)
Standard actions provide XML-based alternatives to scripting elements. They are more readable, better separate presentation logic, and are managed by the container.
-
<jsp:useBean>
: Used to manage JavaBeans components. Either finds an existing bean in a specific scope or creates a new one.id
: Variable name used to reference the bean within the JSP page.class
: Fully-qualified class name of the bean (if a new instance should be created).scope
: The scope in which the bean is stored and searched. Values:page
(default),request
,session
,application
. The container first searches for the bean byid
in the specified scope; if not found, it creates a new one (if theclass
attribute is provided) and stores it in that scope.
actions/useBean.jsp -
<jsp:setProperty>
: Used to set the properties of a bean found or created with<jsp:useBean>
.name
: Theid
of the target bean as defined in<jsp:useBean>
.property
: The name of the property to set. The bean must have a matching setter (e.g.,setPropertyName()
).property="*"
: Special value. Attempts to automatically set all matching properties by mapping request parameter names to bean property names (type conversion is automatic). Very powerful, but use with caution.value
: A fixed value (String or expression) to set for the property.param
: The request parameter from which to get the value for the property. Ifparam
andvalue
are not provided, the value is taken from a request parameter with the same name as theproperty
.
actions/setProperty.jsp -
<jsp:getProperty>
: Retrieves a bean property and writes it to the output (calls the getter). Rarely used, since EL (Expression Language) is more convenient.name
: The beanid
.property
: The name of the property to retrieve.
actions/getProperty.jsp -
<jsp:include>
: Dynamically includes another resource (JSP, Servlet, HTML) into the current page at request time. The included resource is processed separately and its output is appended to the main page’s output.page
: The relative URL of the resource to include. Can be a runtime expression.flush="true|false"
: Indicates whether to flush the main page’s buffer before including. Default isfalse
, buttrue
may be more stable in some containers.- Difference: Unlike the
<%@ include ... %>
directive, this action runs on every request and includes the latest version of the resource, providing more dynamism. - Parameters can be passed via
<jsp:param>
.
actions/include.jsp -
<jsp:forward>
: Forwards the current request to another resource (JSP, Servlet, HTML). The forwarding occurs entirely on the server-side; the client browser is unaware (URL does not change). The current page’s output buffer is cleared, and execution stops where the forward occurs.page
: The relative URL of the resource to forward to. Can be a runtime expression.- Parameters can be added using
<jsp:param>
(appended to the original request parameters). - Primarily used in MVC (Model-View-Controller) architectures to forward from the Controller (Servlet) to the View (JSP).
actions/forward.jsp
<jsp:param>
Used within <jsp:include>
and <jsp:forward>
actions to add request parameters to the included or forwarded resource.
name
: Parameter name.value
: Parameter value.
Expression Language (EL): Simplifying JSP
To avoid the mess created by scriptlets, Expression Language (EL) was introduced with JSP 2.0. EL is designed to provide easy and readable access from JSP pages to JavaBeans properties, collections, implicit objects, and request parameters. The syntax is ${expression}
. EL is the foundation of modern JSP development.
-
Basic Syntax:
${...}
-
JavaBeans Property Access:
${beanName.propertyName}
(Under the hood, callsgetBeanName().getPropertyName()
). -
Collection/Array Access:
${myList[0]}
,${myMap.keyName}
,${myMap['key-with-hyphen']}
. -
Implicit Objects (for EL):
pageScope
,requestScope
,sessionScope
,applicationScope
: Directly access attributes in the respective scope (e.g.,${requestScope.user}
). If the scope is not specified (e.g.,${user}
), EL automatically searches the scopes in the order: page, request, session, application.param
: Access request parameters (e.g.,${param.userId}
). Equivalent torequest.getParameter()
.paramValues
: Retrieve multiple request parameters with the same name as a String array (e.g.,${paramValues.interests[0]}
). Equivalent torequest.getParameterValues()
.header
: Access HTTP request headers (e.g.,${header['User-Agent']}
).headerValues
: Retrieve multiple headers with the same name as a String array.cookie
: Access cookies (e.g.,${cookie.userPref.value}
). Returns the cookie object with the given name.initParam
: Access context initialization parameters defined in the deployment descriptor (web.xml
) (e.g.,${initParam.adminEmail}
). Equivalent toservletContext.getInitParameter()
.pageContext
: Access thePageContext
object itself (e.g.,${pageContext.request.contextPath}
to get the context path).
-
Operators:
- Arithmetic:
+
, , ,/
(ordiv
),%
(ormod
). - Logical:
&&
(orand
),||
(oror
),!
(ornot
). - Relational:
==
(oreq
),!=
(orne
),<
(orlt
),>
(orgt
),<=
(orle
),>=
(orge
). empty
: Checks whether a value isnull
, an emptyString
, an emptycollection
, or an emptymap
(e.g.,${empty param.search}
).- Conditional:
A ? B : C
(e.g.,${user.isAdmin ? 'Admin' : 'User'}
).
- Arithmetic:
-
Examples:
views/el-examples.jsp -
Ignore EL: Although rarely needed, you can disable EL processing on a page with the directive
<%@ page isELIgnored="true" %>
.
Servlet and JSP Integration: The MVC Dance (Detailed Explanation)
As mentioned earlier, a JSP is compiled into a Servlet. In real-world applications, however, they typically work together within the Model-View-Controller (MVC) architecture:
- Model: Objects that hold the application’s data and business logic (JavaBeans, POJOs, Entities).
- View: The component that presents data to the user (typically JSP + EL + JSTL). The View should contain as little logic as possible.
- Controller: The component that receives incoming requests, processes user input, updates the Model, and selects and forwards the appropriate View to display the result (typically a Servlet).
Most Common Model: Servlet as Controller, JSP as View
This is the standard and most recommended approach:
-
Request Arrives: The client sends a request to a URL (e.g.,
/users?action=list
). -
Controller (Servlet) Processes: The Deployment Descriptor (
web.xml
) or the@WebServlet
annotation routes this URL pattern to the appropriate servlet.- The servlet accepts the request (
doGet
,doPost
methods). - Reads request parameters (
request.getParameter("action")
). - Invokes necessary business logic (e.g., obtains the list of users from
UserService
). - Places the retrieved data (Model) into the request scope as an attribute:
request.setAttribute("userList", listOfUsers);
.
- The servlet accepts the request (
-
Controller Forwards to the View: The servlet forwards the request to the JSP page that will display the data using
RequestDispatcher
:controller/UsersServlet.java -
View (JSP) Displays the Data: The JSP page displays the data in the request scope using EL and JSTL:
WEB-INF/jsp/userList.jsp
➡️ JSPs Under /WEB-INF/
: Note that the JSP file (/WEB-INF/jsp/userList.jsp
) is located under the /WEB-INF
directory. This is an important security practice. Resources under /WEB-INF
cannot be accessed directly via a URL from the client browser. They can only be reached via server-side forwarding. This prevents users from bypassing the controller and directly accessing the view, preserving the MVC flow.
Less Common Model: Including a Servlet Inside JSP
With the <jsp:include page="/path/to/servlet" />
action, a JSP page can include the output of another servlet into its content.
- Main JSP: Renders the main part of the page.
<jsp:include>
: At a certain point, calls<jsp:include page="/dynamicFragmentServlet" />
.- Included Servlet: Receives the request (with the same
request
andresponse
objects as the main JSP), executes its logic, and writes the result to its ownresponse.getWriter()
. - Result is Combined: The output of the included servlet is appended to the output of the main JSP at that point.
Use Cases: This approach is less common. It is mostly used to have certain dynamic parts of a page (e.g., a personalized ad block, dynamic navigation menu) managed by separate servlets and included into various JSPs. However, it is usually considered cleaner for the controller to prepare all data and pass it to a single JSP. Sometimes JSP Fragments (.jspf
) and custom tags are better alternatives.
Forward vs Redirect (From Servlet to JSP)
forward
(RequestDispatcher.forward()
):- Happens entirely on the server-side.
- The browser URL does not change.
- The original
request
object (and its attributes) is forwarded to the new resource. - Faster (no additional network roundtrip).
- Commonly used to pass data from the controller to the view.
redirect
(response.sendRedirect()
):- The server sends a
302
status code and a new URL to the browser. - The browser sends a completely new request to the new URL.
- The original
request
objects and attributes are lost. To pass data, use session scope or URL parameters (less efficient). - The URL changes in the browser.
- Post-Redirect-Get (PRG) Pattern: The main use case is to prevent the resubmission of the same POST request on browser refresh after a POST request (e.g., form submission). The controller processes the POST and then redirects to a results page with a GET request.
- The server sends a
Advanced Topics and Best Practices
Error Handling
- Use the directives
<%@ page errorPage="error.jsp" %>
and<%@ page isErrorPage="true" %>
for page-level error handling. Use theexception
object on the error page. - Define global error pages in
web.xml
with<error-page>
elements for HTTP status codes (404, 500) or exception types. This is a more centralized approach.
Security
- Avoid Scriptlets: The golden rule! Use EL and JSTL.
- XSS Prevention: Always escape user-supplied or dynamic data using
<c:out>
or equivalent escaping mechanisms. - Input Validation: Implement both client-side (JavaScript) and (more importantly) server-side (Servlet/Service layer) validation. Never rely solely on client-side validation.
- /WEB-INF/: Place all JSP views under
/WEB-INF
. - CSRF (Cross-Site Request Forgery) Prevention: Add unique tokens to forms and validate them on the server side.
Performance
- JSP Compilation: Understand that JSPs are converted to servlets on the first request (or when precompiled). This first request may be slightly slower.
- Session Management: If a page does not use the session, disable it with
<%@ page session="false" %>
. This removes unnecessary session creation overhead. include
Directive vs Action: For static content (headers, footers), the<%@ include file="..." %>
directive is more performant than the<jsp:include>
action, as it runs only once during the translation phase.
JSP Fragments (.jspf)
Files with the .jspf
extension are usually not full JSP pages; they are intended to be included in other JSPs using the <%@ include ... %>
directive. This is useful for reusable static markup or JSP code blocks.
JSP and Modern Web Development
It must be admitted that with the rise of modern Single Page Applications (SPA) (React, Angular, Vue) and microservices architectures, the use of traditional server-rendered JSP has somewhat declined. Often the backend (e.g., Spring Boot, Jakarta EE REST) provides JSON APIs, and the frontend is a separate JavaScript application that consumes these APIs.
However, JSP is still used in many existing Jakarta EE applications, in some new projects (especially when server-side rendering is preferred), and alongside alternative template engines like Thymeleaf.
Conclusion
JSP is an important part of Java web development. Understanding its lifecycle, various elements (scripting, directives, actions), implicit objects, and especially modern approaches like Expression Language (EL) and JSP Standard Tag Library (JSTL) is critical to building effective and maintainable web applications. Proper integration with servlets (especially within the MVC pattern) and security practices like placing views under /WEB-INF/
should not be overlooked.
Avoid scriptlets and focus on EL/JSTL to achieve a cleaner, more readable, and maintainable presentation layer.