Trong bài viết này, mình hướng dẫn các bạn cách integrate Thymeleaf với Jakarta EE Servlet để thay thế việc sử dụng JSP trong các ứng dụng Jakarta EE Servlet các bạn nhé!
Đầu tiên, mình sẽ tạo mới một Jakarta EE Servlet Maven project để làm ví dụ:
Thymeleaf dependency như sau:
1 2 3 4 5 |
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.1.0.M1</version> </dependency> |
Mình sẽ tạo mới một Servlet để handle request từ người dùng trước:
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); } } |
Trong phương thức doGet() của IndexServlet trên, chúng ta sẽ sử dụng đối tượng TemplateEngine của Thymeleaf để handle response của request.
Để làm được điều này, đầu tiên, chúng ta cần có đối tượng TemplateEngine trước. Mà để có đối tượng TemplateEngine, chúng ta cần có đối tượng của interface ITemplateResolver cấu hình location của các tập tin template.
Có rất nhiều implementation của interface ITemplateResolver giúp chúng ta có thể cấu hình location đến các tập tin template theo nhiều cách khác nhau:
Mình sẽ sử dụng WebApplicationTemplateResolver cho ví dụ của bài viết này:
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; } |
Giờ thì các bạn có thể khởi tạo đối tượng TemplateEngine rồi:
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; } |
Các bạn cần phải đăng ký đối tượng TemplateEngine của Thymeleaf với ServletContext ngay khi ứng dụng chạy lên trước. Chúng ta sẽ sử dụng ServletContextListener để làm điều này như sau:
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 } } |
Như các bạn thấy, chúng ta sẽ lưu đối tượng TemplateEngine này vào ServletContext để mỗi khi có request tới ứng dụng, chúng ta sẽ lấy nó ra và handle response cho request đó.
Giờ mình sẽ modify servlet ở trên để lấy và sử dụng TemplateEngine, như sau:
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()); } } |
Phương thức process() của đối tượng TemplateEngine sẽ handle response cho ứng dụng. Tham số của phương thức này bao gồm template name, đối tượng WebContext được build từ request và response của servlet hiện tại, đối tượng Writer từ response của servlet hiện tại để write response tới người dùng.
Nội dung của tập tin home.html trong thư mục src/main/webapp/WEB-INF/templates như sau:
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> |
Xoá tập tin web.xml trong thư mục src/main/webapp/WEB-INF/ (vì chúng ta đang sử dụng annotation @WebServlet) và tập tin index.jsp trong thư mục src/main/webapp/, sau đó chạy ứng dụng, các bạn sẽ thấy kết quả như sau: