In this tutorial, I show you how to integrate Thymeleaf with Jakarta EE Servlet to replace using JSP in Jakarta EE Servlet applications.
First, I will create a new Jakarta EE Servlet Maven project as an example:
Thymeleaf dependency is as follows:
1 2 3 4 5 |
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.1.0.M1</version> </dependency> |
I will create a new Servlet to handle the request from user:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.huongdanjava.jakartaee.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; @WebServlet("/") public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { super.doGet(req, resp); } } |
In the doGet() method of the above IndexServlet, we will use Thymeleaf’s TemplateEngine object to handle the request’s response.
To do this, first, we need to have the TemplateEngine object. But to have the TemplateEngine object, we need the object of the ITemplateResolver interface to configure the location of the template files.
There are many implementations of the ITemplateResolver interface that make it possible to configure the location to template files in different ways:
I will use WebApplicationTemplateResolver for the example of this tutorial:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
private WebApplicationTemplateResolver templateResolver(IWebApplication application) { WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(application); // HTML is the default mode, but we will set it anyway for better understanding of code templateResolver.setTemplateMode(TemplateMode.HTML); // This will convert "home" to "/WEB-INF/templates/home.html" templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); // Set template cache TTL to 1 hour. If not set, entries would live in cache until expelled by LRU templateResolver.setCacheTTLMs(Long.valueOf(3600000L)); // Cache is set to true by default. Set to false if you want templates to be automatically updated when modified. templateResolver.setCacheable(true); return templateResolver; } |
Now you can instantiate the TemplateEngine object:
1 2 3 4 5 6 7 8 |
private ITemplateEngine templateEngine(IWebApplication application) { TemplateEngine templateEngine = new TemplateEngine(); WebApplicationTemplateResolver templateResolver = templateResolver(application); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } |
You need to register Thymeleaf’s TemplateEngine object with the ServletContext as soon as the application is up and running. We will use the ServletContextListener to do this as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package com.huongdanjava.jakartaee.servlet; import jakarta.servlet.ServletContextEvent; import jakarta.servlet.ServletContextListener; import jakarta.servlet.annotation.WebListener; import org.thymeleaf.ITemplateEngine; import org.thymeleaf.TemplateEngine; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.WebApplicationTemplateResolver; import org.thymeleaf.web.IWebApplication; import org.thymeleaf.web.servlet.JakartaServletWebApplication; @WebListener public class ThymeleafConfiguration implements ServletContextListener { public static final String TEMPLATE_ENGINE_ATTR = "com.huongdanjava.thymeleaf.TemplateEngineInstance"; private ITemplateEngine templateEngine; private JakartaServletWebApplication application; @Override public void contextInitialized(ServletContextEvent sce) { this.application = JakartaServletWebApplication.buildApplication(sce.getServletContext()); this.templateEngine = templateEngine(this.application); sce.getServletContext().setAttribute(TEMPLATE_ENGINE_ATTR, templateEngine); } private ITemplateEngine templateEngine(IWebApplication application) { TemplateEngine templateEngine = new TemplateEngine(); WebApplicationTemplateResolver templateResolver = templateResolver(application); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } private WebApplicationTemplateResolver templateResolver(IWebApplication application) { WebApplicationTemplateResolver templateResolver = new WebApplicationTemplateResolver(application); // HTML is the default mode, but we will set it anyway for better understanding of code templateResolver.setTemplateMode(TemplateMode.HTML); // This will convert "home" to "/WEB-INF/templates/home.html" templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); // Set template cache TTL to 1 hour. If not set, entries would live in cache until expelled by LRU templateResolver.setCacheTTLMs(Long.valueOf(3600000L)); // Cache is set to true by default. Set to false if you want templates to be automatically updated when modified. templateResolver.setCacheable(true); return templateResolver; } @Override public void contextDestroyed(ServletContextEvent sce) { // NOP } } |
As you can see, we will save this TemplateEngine object to the ServletContext so that every time a request comes to the application, we will retrieve it and handle the response for that request.
Now I will modify the above servlet to get and use the TemplateEngine, as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
package com.huongdanjava.jakartaee.servlet; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import org.thymeleaf.TemplateEngine; import org.thymeleaf.context.WebContext; import org.thymeleaf.web.IWebExchange; import org.thymeleaf.web.servlet.JakartaServletWebApplication; @WebServlet("/") public class IndexServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { TemplateEngine templateEngine = (TemplateEngine) getServletContext().getAttribute( ThymeleafConfiguration.TEMPLATE_ENGINE_ATTR); IWebExchange webExchange = JakartaServletWebApplication.buildApplication(getServletContext()) .buildExchange(req, resp); WebContext context = new WebContext(webExchange); context.setVariable("name", "Huong Dan Java"); templateEngine.process("home", context, resp.getWriter()); } } |
The process() method of the TemplateEngine object will handle the response for the application. The parameters of this method include the template name, the WebContext object built from the request and response of the current servlet, the Writer object from the response of the current servlet to write the response to the user.
The contents of the home.html file in the src/main/webapp/WEB-INF/templates directory are as follows:
1 2 3 4 5 6 7 8 9 10 11 12 |
<html xmlns:th="http://www.thymeleaf.org"> <head> <title>Home</title> </head> <body> <h1> Hello world </h1> <P> from <label th:text="${name}"></label>! </P> </body> </html> |
Delete the web.xml file in the src/main/webapp/WEB-INF/ directory (because we are using the @WebServlet annotation) and the index.jsp file in the src/main/webapp/ directory, then run the application, you will see the following result:
Kyle
Can you post a link to the source code?