[JAVA] Switch from JSP + JSTL to Thymeleaf

It is the 7th day of Assoview Advent Calendar 2019.

This is Azuma, a brown mouse from the backend development team. This time, I will introduce the correspondence table when replacing the JSP + JSTL function that has been used for a long time in Java Web applications with Thymeleaf.

Transfer from JSP + JSTL to Thymeleaf

JSP used to be the de facto method for creating Java Web applications, but now that Spring Boot has adopted Thymeleaf, there are cases where the presentation is replaced from JSP to Thymeleaf with the replacement of existing applications [^ 2].

This time, I will introduce how to replace the function used in JSTL of the JSP + tag library used in the old Java Web application with Thymeleaf, along with the function correspondence table.

[^ 2]: Originally, HTML was output as a character string by a Servlet, but JSP makes it possible to write Java code by regarding HTML as one template. However, the operation speed of JSP is not good, the readability deteriorates because the tag is overlaid in the tag, and the JSP code is added to the original HTML, so if the screen design or layout is modified, the HTML and JSP The maintainability was not good, such as correcting the difference.

What is targeted this time

In addition to JSTL tags, we will also introduce things related to how to use JSP and EL expressions.

Namespace Feature overview
c basic operation. For output and temporary storage of values.
fmt Output format formatting
fn Function for output processing

Not applicable

Namespace Feature overview Reason for exclusion
x xml operation(XPath and XSLT) Do not manipulate XML or elements within Thymeleaf
sql Database operation Do not refer to database from Thymeleaf[^1]

[^ 1]: There is a method to refer to and output the data source from the Spring management bean, especially the class with @Service and @Repository, using Thymeleaf-Spring plugin. However, proper transaction management and resource allocation should be performed, and cached contents should be returned as needed.

Core (namespace: c)

The core tag is a frequently used tag such as value output and conditional branching.

JSTL Thymeleaf Functional overview
<c:out> th:value、th:Provides an attribute with the same name that matches the attribute name output in HTML, such as text. Output the value passed to the template in HTML
<c:set> th:with Store the value in a temporary variable of the template
<c:remove> (No corresponding attribute) Remove values from temporary variables and Servlet attributes
<c:if> th:if Describe the conditional expression of the conditional branch.
<c:choose> th:switch Subsequent<c:when>Or th:List the conditions in the case element.
<c:when> th:case <c:choose>And th:Child element of switch
<c:forEach> th:each Loop and output repeatedly. Define temporary variables such as loop elements and rule variables here
<c:forToken> #strings.arraySplit etc. Split a string with a delimiter
<c:url> ${#uris.**} Generate a URL. Also serves as URL encoding.
<c:import> th:insert, th:replace, th:include Capture and display external resources
<c:redirect> (No corresponding function) Redirect
<c:catch> (No corresponding function) Handling of exceptions that occur during output
<c:param> (Provided in URL notation) Generate request parameters for other JSTL tags

Now, I will introduce each function and usage example while comparing them.

th: text: Output to tag text (= element value)

Output to element value

<div>message</div>

To display this, let's compare the case of passing by name: message and value: message.

thymeleaf JSP/JSTL
<div th:text="${message}">text</div> <div><c:out value="${message}"/></div>

In Thymeleaf, the text characters written in the element value of the div are output as the contents of th: text. In JSP, it is displayed even if it is not output via <c: out>, but it always goes through JSTL's <c: out> to sanitize the output value.

Output to attribute value

<input type="text" value="message" />

In order to display this, the way to write when passing by name: message, value: message,

thymeleaf JSP/JSTL
<input type="text" value="text" th:value="${message}"/> <input type="text" value="<c:out value="${message}"/>" />

The value is rewritten as ** Specify the same attribute name as the attribute you want to output **, which is also a feature of Thymeleaf. On the other hand, in JSP / JSPL, it is a little difficult to read because it is a description that outputs the value inside the attribute value of the tag and further encloses it in the tag.

String concatenation

String concatenation is also possible within the attributes in Thymeleaf. When writing the character string directly, enclose it in single quotes.

<div th:text="'this is,' + ${message} + 'is'">text</div>

The output result of this is

<div>This is a message</div>

th: with: Store value in a temporary variable

When you are outputting HTML, you can use the output value repeatedly, or you can reassign the value to change the output content.

thymeleaf JSP/JSTL
<div th:with="another='${original}' +is">text</div> <div><c:set value="${original}is" var="another"/></div>

Temporary variables defined in Thymeleaf templates can be referenced by the elements that define the temporary variables and their child elements. To Thymeleaf Name: message, Value: When passed as a message

<div th:with="anotherMessage='this is,' + ${message} + 'is'" th:text="${anotherMessage}">text</div>

The output result of this is

<div>This is a message</div>

Define multiple temporary variables

If you want to define multiple temporary variables, use commas to connect them. Of course, character string processing and calculation are possible even if multiple definitions are described.

<div th:with="i=3, anotherMessage=${message} + 'is'">
	<span th:text="${anotherMessage}">text</span>
	<span th:text="${i}">text</span>
</div>

The output result of this is

<div>
	<span>Is a message</span>
	<span>3</span>
</div>

Define the same variable name

What if I store them in the same variable name?

<div th:with="message=${message} + 'is'">
	<span th:text="${message}">Text 1</span>
</div>
<div th:text="${message}">Text 2</div>

In this case, ** message assignment is performed only within the ** th: with element.

<div>
	<span>Is a message</span>
</div>
<div>message</div>

The value temporarily defined in th: with of Thymeleaf is ** valid only for that element and its child elements **. On the other hand, JSP + JSTL retains the stored contents and the same contents up to the output of text 2. This is a big difference. Therefore, in JSP, the value is deleted with <c: remove> and the subsequent processing is continued.

When dealing with temporary variables in Thymeleaf, use it in combination with subsequent attributes such as conditional branching and loop processing such as th: if and th: each.

Output child element & process subsequent thymeleaf attribute when th: if condition is met

Output the contents of the child element or execute the tag of the child element only when certain conditions are met. This is the same for JSP / JSTL and Thymeleaf, but Thymeleaf outputs the elements that define th: if. Also, the thymeleaf attribute (th: 〇〇) in the same element is executed.

thymeleaf JSP/JSTL
<div th:if="${original == 'message'}> <div><c:if test="${original == 'message'}"/></div>

You can use almost the same method and method of writing conditional expressions in attribute values.

Basic system of conditional expression

To Thymeleaf Name: message, Value: When passed as a message

<div th:if="${message == 'message'}">
	<span th:text="${message}">text</span>
</div>

This output result is

<div>
	<span>message</span>
</div>

Use Java code for conditional expressions

Various Java code can be written in the conditional expression of th: if. The most used method is to call a method of Java class, and it is possible to incorporate it in a conditional expression or output a display-only value.

<div th:if="${message.startsWith('Messe')}">
	<span th:text="${message}">text</span>
</div>

Since the variable message was a message with String, startsWith () is true, so this output result is

<div>
	<span>message</span>
</div>

It will be.

For the output when the condition is not met, for example, the following determines whether the length of the character string of the variable message is 256 or more.

<div th:if="${message.length() >= 256}">
	<span th:text="${message}">text</span>
</div>

If the condition of this if statement is not satisfied, the element and child elements will not be output in their entirety. In other words, this

element is not output.

Branch with multiple conditions th: switch th: case

The switch to case are the same as the Java switch statement, and if one condition is not met, the other condition is verified. If even one of the conditions is not met, the condition is represented by th: case =" * ".

<div th:switch="${message}">
	<span th:case="message" th:text="${message}"></span>
	<span th:case="*">There was no corresponding message</span>
</div>

If you pass this Thymeleaf template with name: message and value: message, it matches the first th: case, so the output is as follows.

<div>
	<span>message</span>
	
</div>

Elements that are not output will be blank lines. If the value of message is other than message

<div>
	
	<span>There was no corresponding message</span>
</div>

In addition to strings, th: case can be numeric or enumerated (enum).

<div th:switch="${price}">
	<span th:case="100" th:text="${price}">price</span>
	<span th:case="200" th:text="${price}">price</span>
	<span th:case="300" th:text="${price}">price</span>
	<span th:case="*" th:text="There is no applicable price"></span>
</div>

For this template, if the price value is 200,

<div>

	<span>200</spam>


</div>

Is displayed, for example, if the price value is 150, the trailing th: case tag is executed.

<div>



	<span>There is no applicable price</spam>
</div>

When using an enum value

If the following enums are defined

public enum Direction {
	North, South, East , West ;

	public String getValue() {
		return name();
	}
}

The verification method in Thymeleaf can be described as follows.

<div th:switch="${direction.getValue()}">
	<span th:case="North" th:text="North"></span>
	<span th:case="East" th:text="east"></span>
	<span th:case="South" th:text="South"></span>
	<span th:case="West" th:text="West"></span>
	<span th:case="*" th:text="Not applicable"></span>
</div>

If you specify the name direction and value: Direction.East for this template, the contents of <span th: case =" East "th: text =" East "> </ span> will be output.

Repeated output th: each

There is a difference in the description method between JSTL and Thymeleaf in the method of repeatedly outputting elements such as table output using <table> and list box using <select> and <option>. ..

JSTL Thymeleaf Overview
<c:forEach> th:each Declare repeated output

To output a list box using JSTL, enclose the element you want to output repeatedly with <c: forEach>.

<select>
  <c:forEach var="result" items="${list}">
    <option value="<c:out value="${result.value}" />"><c:out value="${result.label}" /></option>
  </c:forEach>
</select>

Thymeleaf describes th: each directly to the element you want to output repeatedly. It can be described without changing the tag hierarchy.

<select>
  <option th:each="result : ${list}" th:value="${result.value}" th:inline="text">[[${result.label}]]</option>
</select>

Format (namespace: fmt)

A function that defines the output format.

JSTL Thymeleaf Overview of tag function
<fmt:formatNumber> ${#numbers.formatInteger(..)} Format for numbers and amounts
<fmt:formatDate> ${#dates.format(..)} Date format
<fmt:parseNumber> ※${#conversions.convert(object, targetClass)} Convert strings to numbers
<fmt:parseDate> ※${#conversions.convert(object, targetClass)} Convert string to date
<fmt:setBundle> (None) Set resource bundle
<fmt:bundle> (None) Get resource bundle
<fmt:message> #{messageId} Output the specified message from the resource bundle
<fmt:requestEncoding> (None) Change the character encoding of the request.
<fmt:setLocale> (None) Set the locale. The locale after this declaration is changed.
<fmt:setTimeZone> (None) Set the time zone. The time zone after this declaration is switched.
<fmt:timeZone> (None) Returns the time zone.

Besides defining the value format, fmt can switch locales and timezones within the same JSP file. This provides the ability to change the variable type during template output and switch the language and property file to be output to the message, but it is better to switch the JSP by locale than to switch within one file. It is easy to maintain, and you will often adopt a method that changes the screen layout for each language.

In contrast, Thymeleaf offers a variety of format conversions for strings, amounts, and dates. Here are some of them.

Thymeleaf Overview
${#dates.format(date, 'dd/MMM/yyyy HH:mm')} Output in a dated format
${#numbers.formatInteger(num,3)} Specify the minimum number of display digits to display
${#numbers.formatPercent(num)} Percentage notation
${#numbers.sequence(from,to)} Consecutive numbers

In Thymeleaf, you can use the convertions function that converts the variable type during template output to convert it to another type, but the template only outputs the type value and inadvertently converts it in the template. You should avoid writing the process to do it because it will reduce the readability.

Function (namespace: fn)

Functions that operate on strings. In Thymeleaf, if the variable is a String type, it will be executed if you write the method as it is, but all the functions provided in JSTL are provided in the # string function.

JSTL Thymeleaf Overview of tag function
<fn:contains> ${#strings.contains(name,'ez')} True if the specified string is included
<fn:containsIgnoreCase> ${#strings.containsIgnoreCase(name,'ez')} True if it contains the specified string, ignoring case
<fn:indexOf> ${#strings.indexOf(name,frag)} Returns the position of the specified string
<fn:startsWith> ${#strings.startsWith(name,'Don')} True if started from the specified string
<fn:endsWith> ${#strings.endsWith(name,endingFragment)} True if the specified string is at the end
<fn:trim> ${#strings.trim(str)} Remove the half-width space before and after the character string
<fn:join> ${#strings.arrayJoin(namesArray,',')}Or${#strings.listJoin(namesList,',')} Combine strings
<fn:replace> ${#strings.replace(name,'las','ler')} Replace string
<fn:split> ${#strings.arraySplit(namesStr,',')}Or${#strings.listSplit(namesStr,',')} Split by specified characters
<fn:length> ${#strings.length(str)} Returns the length of the string
<fn:substring> ${#strings.substring(name,3,5)} Cut with the specified character string
<fn:substringAfter> ${#strings.substringAfter(name,prefix)} Cuts the character string after the specified position
<fn:substringBefore> ${#strings.substringBefore(name,suffix)} Cuts the character string up to the specified position
<fn:lowerCase> ${#strings.toLowerCase(name)} Make it lowercase
<fn:upperCase> ${#strings.toUpperCase(name)} Capitalize
<fn:escapeXml> ${#strings.escapeXml(str)} HTML escape
(None) ${#strings.equals(first, second)} True if the strings are equal
(None) ${#strings.equalsIgnoreCase(first, second)} True if the strings are equal, ignoring case
(None) ${#strings.randomAlphanumeric(count)} Returns an alphanumeric random string with the specified number of characters

Implicit objects and scope of JSP

JSP has the scope of the attribute of the Servlet [^ 3] + the area unique to JSP, but you can also refer to it by specifying this "scope" in Thymeleaf.

Implicit object of JSP Description in Thymeleaf
request #request
session #session
application #servletContext
page (None)
config (None)

There is a separate acquisition method for Thymeleaf settings, which is referenced in #execInfo.

[^ 3]: The scope defines the lifespan when storing variables. Request attribute, session attribute, application attribute (or Servlet context attribute), JSP has four more page context attributes. Spring MVC has a separate flash attribute.

Arithmetic / verification formula

You can use almost the same with Thymeleaf.

JSP / EL Thymeleaf Outline of processing
+ + sum
- - difference
* * product
/ div / div Xu
% mod % mod remainder
== eq == eq Equivalent
!= ne != ne Not equal
< lt < lt Left side<right side
> gt > gt Left side>right side
<= le <= le Left side<=right side
>= ge >= ge Left side>=right side
& and & Logical AND(AND)
pipe or | Logical sum(OR)
! not ! not denial(NOT)
empty (None) Sky

Exception handling

In JSP, if there is any run-time error during template output, you can declare the setting to transition to the error-only page in the JSP directive, or try ~ catch in the scriptlet (although it is not recommended) and throw an exception. I was able to catch and implement exception handling.

Exception handling cannot be implemented in Thymeleaf. Since only the response for the error is output, exception handling is implemented in the class that calls the Thymeleaf template.

in conclusion

As mentioned above, it was a bit of a rush, but I hope it helps to replace the implementation of JSP + JSTL with Thymeleaf.

Recommended Posts

Switch from JSP + JSTL to Thymeleaf
Switch from slim3-gen to slim3-gen-jsr269
Switch from Eclipse to VS Code
Reintroduction to JSTL
[Java] How to switch from open jdk to oracle jdk
Pass Form class from Controller to Thymeleaf by Spring-Boot
Changes from Java 8 to Java 11
Sum from Java_1 to 100
Migrate from JUnit 4 to JUnit 5
Introduction to Ratpack (9) --Thymeleaf
Swipe to switch screens
From Java to Ruby !!
Moved from iBATIS to MyBatis3
Migration from Cobol to JAVA
Moving from AWS to PaizaCloud
New features from Java7 to Java8
Migrating from vargrant to docker
Connect from Java to PostgreSQL
Convert from ○ months to ○ years ○ months
JSP error display from Servlet
Rewriting from applet to application
Change from SQLite3 to PostgreSQL
From Ineffective Java to Effective Java
How to migrate from JUnit4 to JUnit5