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.
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.
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 |
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.
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.
<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.
<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 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>
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>
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>
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 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.
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>
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
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>
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.
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>
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.
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 |
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.
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 |
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.
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