Spring Boot + Thymeleaf + Maven Example
June 01, 2017
This page will walk through Spring Boot + Thymeleaf + Maven example. We will provide here how to use Spring Boot using Thymeleaf with internationalization (i18n), form validation and logging. If Spring Boot scans Thymeleaf library in classpath, it will automatically configures Thymeleaf. We can change the default Thymeleaf configurations using application.properties
. Thymeleaf is a server-side template engine that can process XML, HTML etc. Thymeleaf can access a class fields, message properties from i18n messages files. We can bind our class fields with HTML form elements using Thymeleaf. We can iterate our java collections using Thymeleaf. In our example we will perform form validation and dispplay i18n messages using Thymeleaf. We will also provide how to use Thymeleaf higher version using Maven if Spring Boot starter is using lower version of Thymeleaf. We will also use CSS files with our Thymeleaf view. Now let us discuss the complete example step by step.
Contents
- Technologies Used
- Integrate Thymeleaf 3 with Spring Boot
- Internationalization (i18n)
- Validation
- Logging
- Complete Example
- 1. Project Structure using Eclipse
- 2. Maven File
- 3. Create Domain and Service
- 4. Create Controller
- 5. Create application.properties
- 6. Create Message Properties for Internationalization
- 7. Create Thymeleaf Templates
- 8. Create CSS File
- 9. Spring Boot Main Class
- Run Application
- References
- Download Source Code
Technologies Used
Find the technologies being used in our application.1. Java 8
2. Spring Boot 1.5.3.RELEASE
3. Thymeleaf 3.0
4. Maven 3.3
5. Eclipse Mars
Integrate Thymeleaf 3 with Spring Boot
To integrate Thymeleaf with Spring Boot, we need to use following Spring Boot starter in our build tool such as Maven and Gradle. Suppose we are using Maven, then we will use following Maven dependency.<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
At the time of writing this article, the latest version of Thymeleaf starter is
1.5.3.RELEASE
and it uses Thymeleaf 2.1 version. If we want to use higher version of Thymeleaf for example Thymeleaf 3.0 then we need to configure following properties in pom.xml
.
1. thymeleaf.version
2. thymeleaf-layout-dialect.version
Suppose we are using Maven then the above properties will be configured as follows.
<properties> <thymeleaf.version>3.0.6.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> <java.version>1.8</java.version> </properties>
Using Thymeleaf
If Spring Boot scans Thymeleaf library in its classpath then we are ready to work with Thymeleaf. Spring Boot provides properties for Thymeleaf that will be configured inapplication.properties
or application.yml
to change the configurations of Thymeleaf with Spring Boot. We are listing some of them here.
spring.thymeleaf.mode: Template mode that will be applied on templates. Default is
HTML 5
.
spring.thymeleaf.prefix: This is the value that will be prepended with view name to build the URL. Default value is
classpath:/templates/
.
spring.thymeleaf.suffix: This is the value that will be appended with view name to build the URL. Default value is
.html
.
With the default Spring Boot and Thymeleaf configuration we can keep our Thymeleaf files with html extension at following location.
src\main\resources\templates
Creating <form> using Thymeleaf
Suppose we have a method in controller class.
@Controller @RequestMapping("app") public class UserController { @GetMapping("create-user") public ModelAndView createUserView() { ModelAndView mav = new ModelAndView(); mav.setViewName("user-creation"); mav.addObject("user", new User()); mav.addObject("allProfiles", getProfiles()); return mav; } --- }
/app/create-user
URL, the Thymeleaf file named as user-creation.html
will open and there we will create a form to create user. Now look into the code snippet given below.
mav.addObject("user", new User());
user
will be used in <form> with Thymeleaf th:object
attribute.
<form action="#" th:action="@{/app/create-user}" th:object="${user}" method="POST">
th:action
is assigned with action URL that will be called on form submit. On form submit, the URL /app/create-user
will access following methods in controller class.
@Controller @RequestMapping("app") public class UserController { --- @PostMapping("create-user") public ModelAndView createUser(@Valid User user, BindingResult result) { --- } }
public class User { private String userId; private String userName; private String gender; private Boolean married; private String profile; }
th:field
attribute. Suppose we want to create an input text box to get user id. The field userId
of User
class will be bound to input text box using Thymeleaf th:field
as given below.
<input type="text" th:field="*{userId}" />
User
class fields. Find the input box for userName
field of User
class.
<input type="text" th:field="*{userName}" />
gender
field of User
class.
<input type="radio" th:field="*{gender}" value="Male"/> <input type="radio" th:field="*{gender}" value="Female"/>
married
field of User
class.
<input type="checkbox" th:field="*{married}" />
profile
field of User
class.
<select th:field="*{profile}"> <option th:each="profile : ${allProfiles}" th:value="${profile}" th:text="${profile}">Profile</option> </select>
th:each
will iterate the collection. Thymeleaf th:value
attribute assigns value. allProfiles
is the attribute name that will be added in ModelAndView
for every request.
mav.addObject("allProfiles", getProfiles());
getProfiles()
method will return the list of all profiles.
private List<String> getProfiles() { List<String> list = new ArrayList<>(); list.add("Developer"); list.add("Manager"); list.add("Director"); return list; }
th:text="${...}"
syntax that can be used to access a value from a java object. If we want to display a report of all items in a table, we write code as follows.
<table> <tr th:each="user : ${allUsers}"> <td th:text="${user.userId}">Id</td> <td th:text="${user.userName}">Title</td> </tr> </table>
th:href="@{..}"
syntax for loading CSS or to create a link to redirect a URL.
Internationalization (i18n)
When we use Spring Boot starter, our Spring Boot application is ready to use internationalization (i18n). We need to create properties file in following location.src\main\resources\messages.properties src\main\resources\messages_en.properties
en
then Spring application will pick messages_en.properties
file. The default i18n file will be messages.properties
.
We can change default spring message configurations using spring properties in
application.properties
file. For example,
spring.messages.basename: It configures comma separated base names for message files. Default is
messages
.
spring.messages.encoding: It configures message bundle encoding. Default is UTF-8
To display i18n messages, we need to use Thymeleaf
th:text="#{...}"
syntax. Suppose we want to display i18n message for our page title and for that we have a key defined in our message properties file as given below.
app.title= Spring Boot + Thymeleaf
<title th:text="#{app.title}"> Title </title>
#locale
context using which we can display locale specific details such as language, country as follows.
<p th:text="${#locale.language}"> language</p> <p th:text="${#locale.country}"> country</p>
Validation
To validate a field we can use Hibernate validator. When we use Spring Boot starter, the Hibernate validator library is configured automatically in classpath. Now we will use validator annotations in our class that will bind the form. The validator annotations are such as@NotNull
, @Size
etc from javax.validation
library.
public class User { @NotNull @Size(min=3, max=10) private String userId; @NotNull @Size(min=5, max=20) private String userName; @NotNull private String gender; private Boolean married; private String profile; }
@Valid
annotation from javax.validation
library in our Spring controller method argument that will be called on form submit. We also need the instance of Spring BindingResult
to know whether form is validated or not.
@PostMapping("create-user") public ModelAndView createUser(@Valid User user, BindingResult result) { ModelAndView mav = new ModelAndView(); if(result.hasErrors()) { logger.info("Validation errors while submitting form."); mav.setViewName("user-creation"); mav.addObject("user", user); mav.addObject("allProfiles", getProfiles()); return mav; } userService.addUser(user); mav.addObject("allUsers", userService.getAllUserArticles()); mav.setViewName("user-info"); logger.info("Form submitted successfully."); return mav; }
result.hasErrors()
will return true. If validation errors are there in the form then we should call the same Thymeleaf view again and if no validation errors then perform business logic and redirect to success view.
Now in Thymeleaf view we need to display validation error messages. To check if there is validation errors for a given field name, Thymeleaf provides
#fields.hasErrors(...)
that returns boolean values.
<label th:if="${#fields.hasErrors('userId')}" th:errors="*{userId}" th:class="'error'">Id Error</label>
th:errors
will display default validation error message of Spring on the basis of Hibernate validator annotation that we have used in our class fields bound with the form elements. If we want to use i18n message for validation errors, then use th:text="#{...}
.
<label th:if="${#fields.hasErrors('userId')}" th:text="#{error.user.id}" th:class="'error'">Id Error</label>
error.user.id
is the i18n properties defined in messages_en.properties
file.
Logging
In Spring Boot application Logback dependency is resolved automatically. Every Spring Boot starter resolvesspring-boot-starter-logging
itself. To change the default configuration of logging, we need to configure logging properties in application.properties
file. Find some of the those properties.
logging.level.*: It is used as prefix with package name to set log level.
logging.file: It configures a log file name to log message in file.
logging.path: It only configures path for log file. Spring boot creates a log file with name
spring.log
.
Suppose our class package is
com.concretepage
then log level can be defined in application.properties
file using logging.level.*
as follows.
logging.level.com.concretepage= INFO
com.concretepage
package or its sub package, the log level INFO
will be applied. We can use org.slf4j.Logger
as follows.
1. Instantiate
org.slf4j.Logger
at class level.
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
logger.info("Validation errors while submitting form.");
Complete Example
Find the complete example of Spring boot with Thymeleaf. We will create our application with internalization (i18n), form validation and logging. Find the complete code.1. Project Structure using Eclipse
Find the demo project structure using eclipse.
2. Maven File
pom.xml<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.concretepage</groupId> <artifactId>spring-boot-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-demo</name> <description>Spring Boot Demo Project</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> </parent> <properties> <thymeleaf.version>3.0.6.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring-boot-starter-parent: Parent POM for dependency management.
spring-boot-starter-web: Starter for building web, REST applications. It uses tomcat server as default embedded server.
spring-boot-starter-thymeleaf : Used in Thymeleaf integration with Spring Framework.
spring-boot-devtools : It provides developer tools. These tools are helpful in application development mode. One of the features of developer tool is automatic restart of the server for any change in code.
3. Create Domain and Service
User.javapackage com.concretepage.domain; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; public class User { @NotNull @Size(min=3, max=10) private String userId; @NotNull @Size(min=5, max=20) private String userName; @NotNull private String gender; private Boolean married; private String profile; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Boolean getMarried() { return married; } public void setMarried(Boolean married) { this.married = married; } public String getProfile() { return profile; } public void setProfile(String profile) { this.profile = profile; } }
UserService.java
package com.concretepage.service; import java.util.ArrayList; import java.util.List; import org.springframework.stereotype.Service; import com.concretepage.domain.User; @Service public class UserService { private List<User> allUsers = new ArrayList<>(); public List<User> getAllUserArticles(){ return allUsers; } public void addUser(User user) { allUsers.add(user); } }
4. Create Controller
UserController.javapackage com.concretepage.controller; import java.util.ArrayList; import java.util.List; import javax.validation.Valid; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.ModelAndView; import com.concretepage.domain.User; import com.concretepage.service.UserService; @Controller @RequestMapping("app") public class UserController { private static final Logger logger = LoggerFactory.getLogger(UserController.class); @Autowired private UserService userService; @GetMapping("create-user") public ModelAndView createUserView() { ModelAndView mav = new ModelAndView(); mav.setViewName("user-creation"); mav.addObject("user", new User()); mav.addObject("allProfiles", getProfiles()); return mav; } @PostMapping("create-user") public ModelAndView createUser(@Valid User user, BindingResult result) { ModelAndView mav = new ModelAndView(); if(result.hasErrors()) { logger.info("Validation errors while submitting form."); mav.setViewName("user-creation"); mav.addObject("user", user); mav.addObject("allProfiles", getProfiles()); return mav; } userService.addUser(user); mav.addObject("allUsers", userService.getAllUserArticles()); mav.setViewName("user-info"); logger.info("Form submitted successfully."); return mav; } private List<String> getProfiles() { List<String> list = new ArrayList<>(); list.add("Developer"); list.add("Manager"); list.add("Director"); return list; } }
5. Create application.properties
application.propertieslogging.level.root= WARN logging.level.org.springframework.web= ERROR logging.level.com.concretepage= INFO
6. Create Message Properties for Internationalization
Find the message properties foren
locale.
messages_en.properties
app.title= Spring Boot + Thymeleaf app.create-user= Create User app.user-details= User Details user.id= Id user.name= Name user.gender= Gender user.married= Married user.profile= Profile user.gender.male= Male user.gender.female= Female user.form.submit= Submit user.form.reset= Reset user.add-more-users= Add More Users error.user.id= Id must be between 3 to 10 characters. error.user.name= User name must be between 5 to 20 characters. error.user.gender= Select gender.
7. Create Thymeleaf Templates
When we access the application, following page will open.user-creation.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="#{app.title}"> Title </title> <link rel="stylesheet" th:href="@{/css/styles.css}"/> </head> <body> <h1 th:text="#{app.create-user}">Create</h1> <form action="#" th:action="@{/app/create-user}" th:object="${user}" method="POST"> <table> <tr><td th:text="#{user.id}"/></td> <td> <input type="text" th:field="*{userId}" /> <label th:if="${#fields.hasErrors('userId')}" th:text="#{error.user.id}" th:class="'error'">Id Error</label> </td> </tr> <tr><td th:text="#{user.name}"></td> <td> <input type="text" th:field="*{userName}" /> <label th:if="${#fields.hasErrors('userName')}" th:text="#{error.user.name}" th:class="'error'">Name Error</label> </td> </tr> <tr><td th:text="#{user.gender}"></td> <td> <input type="radio" th:field="*{gender}" value="Male"/><label th:text="#{user.gender.male}">M</label> <input type="radio" th:field="*{gender}" value="Female"/><label th:text="#{user.gender.female}">F</label> <label th:if="${#fields.hasErrors('gender')}" th:text="#{error.user.gender}" th:class="'error'">Gender Error</label> </td> </tr> <tr><td th:text="#{user.married}+ '?'"></td> <td> <input type="checkbox" th:field="*{married}" /> </td> </tr> <tr><td th:text="#{user.profile}"></td> <td> <select th:field="*{profile}"> <option th:each="profile : ${allProfiles}" th:value="${profile}" th:text="${profile}">Profile</option> </select> </td> </tr> <tr><td colspan="2"> <input type="submit" th:value="#{user.form.submit}"/> <input type="reset" th:value="#{user.form.reset}"/> </td> </tr> </table> </form> </body> </html>
user-info.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title th:text="#{app.title}"> Title </title> <link rel="stylesheet" th:href="@{/css/styles.css}"/> </head> <body> <h1 th:text="#{app.user-details}">Details</h1> <table> <tr> <th th:text="#{user.id}"/></th> <th th:text="#{user.name}"></th> <th th:text="#{user.gender}"></th> <th th:text="#{user.married}+ '?'"></th> <th th:text="#{user.profile}"></th> </tr> <tr th:each="user : ${allUsers}"> <td th:text="${user.userId}">Id</td> <td th:text="${user.userName}">Title</td> <td th:text="${user.gender}">Gender</td> <td th:text="${user.married}">Married</td> <td th:text="${user.profile}">Profile</td> </tr> </table> <a href="#" th:href="@{/app/create-user}" th:text="#{user.add-more-users}">Add User</a> </body> </html>
8. Create CSS File
styles.css.error{ color: red; font-size: 15px; } .user{ color: blue; font-size: 15px; } table { border-collapse: collapse; } table, th, td { border: 1px solid black; }
9. Spring Boot Main Class
Find Spring boot Main class to run the application.MyApplication.java
package com.concretepage; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } }
Run Application
We can run our application in following ways.1. Using Eclipse: Download the project source code using the download link given at the end of page. Import the project into eclipse. Using command prompt, go to the root folder of the project and run.
mvn clean eclipse:eclipse
MyApplication
by clicking Run as -> Java Application. Tomcat server will be started.
2. Using Maven Command: Download the project source code. Go to the root folder of the project using command prompt and run the command.
mvn spring-boot:run
3. Using Executable JAR: Using command prompt, go to the root folder of the project and run the command.
mvn clean package
target
folder. Run this JAR as
java -jar target/spring-boot-demo-0.0.1-SNAPSHOT.jar
Now we are ready to test the application. Access the URL as given below.
http://localhost:8080/app/create-user


I am done now. Happy Spring Boot learning!
References
Spring Boot Reference GuideUsing Thymeleaf