Java EE MVC Thymeleaf
last modified July 13, 2020
Java EE MVC Thymeleaf tutorial shows how to use integrame Thymeleaf template engine with Java EE MVC web framework. We use Tomcat web server.
Thymeleaf
Thymeleaf is a modern server-side Java template engine for both web and standalone environments.
In web applications Thymeleaf aims to be a complete substitute for JSP. It is a natural template engine: template files can be directly opened in browsers and that still display correctly as web pages.
Java EE MVC
Java MVC is a specification (JSR-371) for a new Java action-based web framework. It is an alternative to the traditional component-based JSF. The MVC API is layered on top of JAX-RS and integrates with existing Java EE technologies like CDI and Bean Validation. Eclipse Ozark is an implementation of Java MVC. It currently contains support for RESTEasy, Jersey, and Apache CXF.
Java MVC Thymeleaf example
In the following web application, we create a simple Java MVC application. It displays data with Thymeleaf.
$ tree . ├── nb-configuration.xml ├── pom.xml └── src ├── main │ ├── java │ │ └── com │ │ └── zetcode │ │ ├── config │ │ │ └── ApplicationConfig.java │ │ ├── controller │ │ │ ├── CityController.java │ │ │ └── HelloController.java │ │ └── model │ │ ├── City.java │ │ └── Message.java │ ├── resources │ └── webapp │ ├── index.html │ ├── META-INF │ │ └── context.xml │ └── WEB-INF │ ├── beans.xml │ └── views │ ├── cities.html │ └── hello.html └── test └── java
This is the project structure.
<?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.zetcode</groupId> <artifactId>JavaMVCThymeleaf</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <name>JavaMVCThymeleaf</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.mvc-spec.ozark</groupId> <artifactId>ozark-resteasy</artifactId> <version>1.0.0-m03</version> </dependency> <dependency> <groupId>javax.mvc</groupId> <artifactId>javax.mvc-api</artifactId> <version>1.0-pr</version> </dependency> <dependency> <groupId>org.mvc-spec.ozark</groupId> <artifactId>ozark-core</artifactId> <version>1.0.0-m03</version> </dependency> <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>org.jboss.weld.servlet</groupId> <artifactId>weld-servlet-shaded</artifactId> <version>3.0.4.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.2.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-servlet-initializer</artifactId> <version>3.5.1.Final</version> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-cdi</artifactId> <version>3.5.1.Final</version> </dependency> <dependency> <groupId>org.mvc-spec.ozark.ext</groupId> <artifactId>ozark-thymeleaf</artifactId> <version>1.0.0-m03</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>2.3</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </build> </project>
This is the Maven POM file. It contains all the neccessary libraries to build our application, including Java MVC framework, Weld container, and Thymeleaf engine.
<?xml version="1.0" encoding="UTF-8"?> <Context path="/JavaMVCThymeleaf"> <Resource name="BeanManager" auth="Container" type="javax.enterprise.inject.spi.BeanManager" factory="org.jboss.weld.resources.ManagerObjectFactory" /> </Context>
In the Tomcat context.xml
file, we define the context path
and register Weld's BeanManager
factory.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd" bean-discovery-mode="all"> </beans>
In the WEB-INF
directory, we have an empty beans.xml
file. It is a deployment descriptor for CDI. It can be used for configuring interceptors,
decorators, and other things. Even if we do no configurations, we need to add
an empty beans.xml
for registering CDI.
package com.zetcode.config; import javax.ws.rs.ApplicationPath; import javax.ws.rs.core.Application; @ApplicationPath("mvc") public class ApplicationConfig extends Application { }
ApplicationConfig
is the application configuration class. It is possible to
omit the web.xml
file since Servlet 3.0. In JAX-RS, we create
a configuration class that extends the abstract Application
and use the @ApplicationPath
annotation. The Application
defines the components of a JAX-RS application and supplies additional meta-data.
Here we register resource classes, providers, or properties the application needs.
package com.zetcode.model; import java.util.Objects; public class City { private Long id; private String name; private int population; public City(Long id, String name, int population) { this.id = id; this.name = name; this.population = population; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getPopulation() { return population; } public void setPopulation(int population) { this.population = population; } @Override public int hashCode() { int hash = 5; hash = 97 * hash + Objects.hashCode(this.id); hash = 97 * hash + Objects.hashCode(this.name); hash = 97 * hash + this.population; return hash; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final City other = (City) obj; if (this.population != other.population) { return false; } if (!Objects.equals(this.name, other.name)) { return false; } if (!Objects.equals(this.id, other.id)) { return false; } return true; } @Override public String toString() { return "City{" + "id=" + id + ", name=" + name + ", population=" + population + '}'; } }
The City
bean holds data of a city object. It
has three attributes: id
, name
, and
population
.
package com.zetcode.model; import javax.enterprise.context.RequestScoped; @RequestScoped public class Message { private String text; public String getText() { return text; } public void setText(String text) { this.text = text; } }
This is a simple model which contains a message.
@RequestScoped public class Message {
An object which is defined as @RequestScoped
is created once
for every request and is shared by all the beans that inject it throughout a request.
package com.zetcode.controller; import com.zetcode.model.Message; import javax.inject.Inject; import javax.mvc.Models; import javax.mvc.annotation.Controller; import javax.mvc.annotation.View; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path("hello") @Controller public class HelloController { @Inject private Message message; @Inject private Models models; @GET @Produces("text/html") @View("hello.html") public void hello() { message.setText("Today is a sunny day"); models.put("message", message); } }
The HelloController
returns a simple message to the client.
@Path("hello") @Controller public class HelloController {
The @Controller
annotation defines a controller and
the @Path
annotation the route to the controller.
@GET @Produces("text/html") @View("hello.html") public void hello() {
The @GET
annotation defines the method for which the
hello
method is called. The @Produces
annotation defines the media type of the response. The @View
annotation defines the view name. It is the name of the Thymeleaf
template file.
message.setText("Today is a sunny day"); models.put("message", message);
We set the message and place the message bean to the models object. The model contains the data to be displayed by the views.
package com.zetcode.controller; import com.zetcode.model.City; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import javax.mvc.Models; import javax.mvc.annotation.Controller; import javax.mvc.annotation.View; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path("cities") @Controller public class CityController { @Inject private Models models; @GET @Produces("text/html") @View("cities.html") public void getCities() { List<City> cities = new ArrayList<>(); cities.add(new City(1L, "Bratislava", 432000)); cities.add(new City(2L, "Budapest", 1759000)); cities.add(new City(3L, "Prague", 1280000)); cities.add(new City(4L, "Los Angeles", 1748000)); cities.add(new City(5L, "New York", 3971000)); models.put("cities", cities); } }
The CityController
returns a list of cities.
<html> <head> <title>Message</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <p th:text="${message.text}">Message text</span> </body> </html>
The hello.html
is the first Thymeleaf template. The templates
are located in the default WEB-INF/views
directory.
<p th:text="${message.text}">Message text</span>
The text from the bean is displayed with the Thymeleaf
th:text="${message.text}"
directive.
<!DOCTYPE html> <html> <head> <title>Cities</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> </head> <body> <h2>Cities</h2> <ul> <li th:each="c : ${cities}" th:text="${c.name + ': ' + c.population}">city</li> </ul> </body> </html>
In the second view file, we show the list of cities in an HTML list
with Thymeleaf's th:each
directive.
<!DOCTYPE html> <html> <head> <title>Home Page</title> <meta charset="UTF-8"> </head> <body> <p> <a href="mvc/hello">Get message</a> </p> <p> <a href="mvc/cities">Get cities</a> </p> </body> </html>
This is the home page. It contains two links to execute two actions on the server.
In this tutorial we have shown how to integrate Thymeleaf into Java EE MVC framework.