Preface

This tutorial explains how Thymeleaf can be integrated with the Spring Framework, especially (but not only) Spring MVC.

Note that Thymeleaf has integrations for both versions 3.x and 4.x of the Spring Framework, provided by two separate libraries called thymeleaf-spring3 and thymeleaf-spring4. These libraries are packaged in separate .jar files (thymeleaf-spring3-{version}.jar and thymeleaf-spring4-{version}.jar) and need to be added to your classpath in order to use Thymeleaf’s Spring integrations in your application.

The code samples and example application in this tutorial make use of Spring 4.x and its corresponding Thymeleaf integrations, but the contents of this text are valid also for Spring 3.x. If your application uses Spring 3.x, all you have to do is replace the org.thymeleaf.spring4 package with org.thymeleaf.spring3 in the code samples.

1 Integrating Thymeleaf with Spring

Thymeleaf offers a set of Spring integrations that allow you to use it as a full-featured substitute for JSP in Spring MVC applications.

These integrations will allow you to:

Make the mapped methods in your Spring MVC @Controller objects forward to templates managed by Thymeleaf, exactly like you do with JSPs.

Create forms in your templates that are completely integrated with your form-backing beans and result bindings, including the use of property editors, conversion services and validation error handling.

Note that in order to fully understand this tutorial, you should have first gone through the “Using Thymeleaf” tutorial, which explains the Standard Dialect in depth.

2 The SpringStandard Dialect

In order to achieve an easier and better integration, Thymeleaf provides a dialect which specifically implements all the needed features for it to work correctly with Spring.

This specific dialect is based on the Thymeleaf Standard Dialect and is implemented in a class called org.thymeleaf.spring4.dialect.SpringStandardDialect, which in fact extends from org.thymeleaf.standard.StandardDialect.

Besides all the features already present in the Standard Dialect – and therefore inherited –, the SpringStandard Dialect introduces the following specific features:

Use Spring Expression Language (Spring EL) as a variable expression language, instead of OGNL. Consequently, all ${...} and *{...} expressions will be evaluated by Spring’s Expression Language engine.

New attributes for form processing: th:field, th:errors and th:errorclass, besides a new implementation of th:object that allows it to be used for form command selection.

An expression object and method, #themes.code(...), which is equivalent to the spring:theme JSP custom tag.

An expression object and method, #mvc.uri(...), which is equivalent to the spring:mvcUrl(...) JSP custom function (only in Spring 4.1+).

New DTDs for validation, including these new attributes, as well as new corresponding DOCTYPE translation rules.

Note that you shouldn’t use this dialect directly in a normal TemplateEngine object as a part of its configuration. Instead, you should instance a new template engine class that performs all the required configuration steps: org.thymeleaf.spring4.SpringTemplateEngine.

3 Views and View Resolvers

3.1 Views and View Resolvers in Spring MVC

There are two interfaces in Spring MVC that conform the core of its templating system:

org.springframework.web.servlet.View

org.springframework.web.servlet.ViewResolver

Views model pages in our applications and allow us to modify and predefine their behaviour by defining them as beans. Views are in charge of rendering the actual HTML interface, usually by the execution of some template engine like JSP (or Thymeleaf).

ViewResolvers are the objects in charge of obtaining View objects for a specific operation and locale. Typically, controllers ask ViewResolvers to forward to a view with a specific name (a String returned by the controller method), and then all the view resolvers in the application execute in ordered chain until one of them is able to resolve that view, in which case a View object is returned and control is passed to it for the renderization of HTML.

Note that not all pages in our applications have to be defined as Views, but only those which behaviour we wish to be non-standard or configured in a specific way (for example, by wiring some special beans to it. If a ViewResolver is asked a view that has no corresponding bean —which is the common case—, a new View object is created ad hoc and returned.

A typical configuration for a JSP+JSTL ViewResolver in a Spring MVC application looks like this:

The templateEngine parameter is, of course, the SpringTemplateEngine object we defined in the previous chapter. The other two (order and viewNames) are both optional, and have the same meaning as in the JSP ViewResolver we saw before.

Note that we do not need prefix or suffix parameters, because these are already specified at the Template Resolver (which in turn is passed to the Template Engine).

And what if we wanted to define a View bean and add some static variables to it? Easy:

Note that this Spring-based resource resolver will never be used by default. It will just be an option available for applications to configure in addition to the other template resolver implementations offered by the Thymeleaf core.

5 Spring Thyme Seed Starter Manager

5.1 The Concept

At Thymeleaf we’re huge fans of thyme, and every spring we prepare our seed starting kits with good soil and our favourite seeds, place them under the Spanish sun and patiently wait for our new plants to grow.

But this year we got fed up with sticking labels to the seed starter containers for knowing which seed was in each cell of the container, so we decided to prepare an application using Spring MVC and Thymeleaf to help us catalogue our starters: The Spring Thyme SeedStarter Manager.

STSM front page

In a similar way to the Good Thymes Virtual Grocery application we developed in the Using Thymeleaf tutorial, the STSM will allow us to exemplify the most important aspects of the integration of Thymeleaf as a template engine for Spring MVC.

5.2 Business Layer

We will need a very simple business layer for our application. First of all, let’s have a look at our model entities:

STSM model

A couple of very simple service classes will provide the required business methods. Like:

5.3 Spring MVC configuration

Next we need to set up the Spring MVC configuration for the application, which will include not only the standard Spring MVC artifacts like resource handling or annotation scanning, but also the creation of the Template Engine and View Resolver instances.

5.4 The Controller

Of course, we will also need a controller for our application. As the STSM will only contain one web page with a list of seed starters and a form for adding new ones, we will write only one controller class for all the server interactions:

That conversion service allowed us to register two Spring formatters, implementations of the org.springframework.format.Formatter interface. For more information on how the Spring conversion infrastructure works, see the docs at spring.io.

Let’s have a look at the DateFormatter, which formats dates according to a format string present at the date.format message key of our Messages.properties:

We will learn more on how these formatters affect the way our data is displayed later on.

6 Listing Seed Starter Data

The first thing that our /WEB-INF/templates/seedstartermng.html page will show is a listing with the seed starters currently stored. For this we will need some externalized messages and also some expression evaluation on model attributes. Like this:

In the first column of the table listing we will show the date when the seed starter was prepared. But we will show it formatted in the way we defined in our DateFormatter. In order to do that we will use the double-bracket syntax, which will automatically apply the Spring Conversion Service.

<tdth:text="${{sb.datePlanted}}">13/01/2011</td>

Next is showing whether the seed starter container is covered or not, by transforming the value of the boolean covered bean property into an internationalized “yes” or “no” with a conditional expression:

<tdth:text="${sb.covered}? #{bool.true} : #{bool.false}">yes</td>

Now we have to show the type of seed starter container. Type is a java enum with two values (WOOD and PLASTIC), and that’s why we defined two properties in our Messages file called seedstarter.type.WOOD and seedstarter.type.PLASTIC.

But in order to obtain the internationalized names of the types, we will need to add the seedstarter.type. prefix to the enum value by means of an expression, which result we will then use as the message key:

<tdth:text="#{${'seedstarter.type.' + sb.type}}">Wireframe</td>

The most difficult part of this listing is the features column. In it we want to display all the features of our container —that come in the form of an array of Feature enums—, separated by commas. Like “Electric Heating, Turf”.

Note that this is particularly difficult because these enum values also need to be externalized, as we did with Types. The flow is then:

Prepend the corresponding prefix to all the elements of the features array.

Obtain the externalized messages corresponding to all the keys from step 1.

Join all the messages obtained in step 2, using a comma as a delimiter.

7 Creating a Form

7.1 Handling the command object

Command object is the name Spring MVC gives to form-backing beans, this is, to objects that model a form’s fields and provide getter and setter methods that will be used by the framework for establishing and obtaining the values input by the user at the browser side.

Thymeleaf requires you to specify the command object by using a th:object attribute in your <form> tag:

This is consistent with other uses of th:object, but in fact this specific scenario adds some limitations in order to correctly integrate with Spring MVC’s infrastructure:

Values for th:object attributes in form tags must be variable expressions (${...}</code>) specifying only the name of a model attribute, without property navigation. This means that an expression like <code>${seedStarter} is valid, but ${seedStarter.data} would not be.

Once inside the <form> tag, no other th:object attribute can be specified. This is consistent with the fact that HTML forms cannot be nested.

7.2 Inputs

Let’s see now how to add an input to our form:

<inputtype="text"th:field="*{datePlanted}"/>

As you can see, we are introducing a new attribute here: th:field. This is a very important feature for Spring MVC integration because it does all the heavy work of binding your input with a property in the form-backing bean. You can see it as an equivalent of the path attribute in a tag from Spring MVC’s JSP tag library.

The th:field attribute behaves differently depending on whether it is attached to an <input>, <select> or <textarea> tag (and also depending on the specific type of <input> tag). In this case (input[type=text]), the above line of code is similar to:

…but in fact it is a little bit more than that, because th:field will also apply the registered Spring Conversion Service, including the DateFormatter we saw before (even if the field expression is not double-bracketed). Thanks to this, the date will be shown correctly formatted.

Values for th:field attributes must be selection expressions (*{...}), which makes sense given the fact that they will be evaluated on the form-backing bean and not on the context variables (or model attributes in Spring MVC jargon).

Contrary to the ones in th:object, these expressions can include property navigation (in fact any expression allowed for the path attribute of a <form:input> JSP tag will be allowed here).

Note there’s some fine stuff here besides the checkbox itself, like an externalized label and also the use of the #ids.next('covered') function for obtaining the value that will be applied to the id attribute of the checkbox input.

Why do we need this dynamic generation of an id attribute for this field? Because checkboxes are potentially multi-valued, and thus their id values will always be suffixed a sequence number (by internally using the #ids.seq(...) function) in order to ensure that each of the checkbox inputs for the same property has a different id value.

We can see this more easily if we look at such a multi-valued checkbox field:

We can see here how a sequence suffix is added to each input’s id attribute, and how the #ids.prev(...) function allows us to retrieve the last sequence value generated for a specific input id.

Don’t worry about those hidden inputs with name="_features": they are automatically added in order to avoid problems with browsers not sending unchecked checkbox values to the server upon form submission.

Also note that if our features property contained some selected values in our form-backing bean, th:fieldwould have taken care of that and would have added a checked="checked" attribute to the corresponding input tags.

7.4 Radio Button fields

Radio button fields are specified in a similar way to non-boolean (multi-valued) checkboxes —except that they are not multivalued, of course:

7.5 Dropdown/List selectors

Select fields have two parts: the <select> tag and its nested <option> tags. When creating this kind of field, only the <select> tag has to include a th:field attribute, but the th:value attributes in the nested <option> tags will be very important because they will provide the means of knowing which is the currently selected option (in a similar way to non-boolean checkboxes and radio buttons).

At this point, understanding this piece of code is quite easy. Just notice how attribute precedence allows us to set the th:each attribute in the <option> tag itself.

7.6 Dynamic fields

Thanks to the advanced form-field binding capabilities in Spring MVC, we can use complex Spring EL expressions to bind dynamic form fields to our form-backing bean. This will allow us to create new Row objects in our SeedStarter bean, and to add those rows’ fields to our form at user request.

In order to do this, we will need a couple of new mapped methods in our controller, which will add or remove a row from our SeedStarter depending on the existence of specific request parameters:

If you recall from the “Using Thymeleaf” tutorial, that __${...}__ syntax is a preprocessing expression, which is an inner expression that is evaluated before actually evaluating the whole expression. But why that way of specifying the row index? Wouldn’t it be enough with:

<selectth:field="*{rows[rowStat.index].variety}">
...
</select>

…well, actually, no. The problem is that Spring EL does not evaluate variables inside array index brackets, so when executing the above expression we would obtain an error telling us that rows[rowStat.index] (instead of rows[0], rows[1], etc) is not a valid position in the rows collection. That’s why preprocessing is needed here.

Let’s have a look at a fragment of the resulting HTML after pressing “Add Row” a couple of times:

Simplifying error-based CSS styling: th:errorclass

The example we saw above, setting a CSS class to a form input if that field has errors, is so common that Thymeleaf offers a specific attribute for doing exacly that: th:errorclass.

Applied to a form field tag (input, select, textarea…), it will read the name of the field to be examined from any existing name or th:field attributes in the same tag, and then append the specified CSS class to the tag if such field has any associated errors:

9 It’s still a Prototype!

Our application is ready now. But let’s have a second look at the .html page we created…

One of the nicest consequences of working with Thymeleaf is that after all this functionality we have added to our HTML, we can still use it as a prototype (we say it is a Natural Template). Let’s open seedstartermng.html directly in our browser without executing our application:

STSM natural templating

There it is! It’s not a working application, it’s not real data… but it is a perfectly valid prototype made up of perfectly displayable HTML code. Try to do that with JSP!

10 The Conversion Service

10.1 Configuration

As explained before, Thymeleaf can make use of a Conversion Service registered at the Application Context. Let’s see again what it looks like:

10.1 Double-bracket syntax

The Conversion Service can be easily applied in order to convert/format any object into String. This is done by means of the double-bracket syntax:

For variable expressions: ${{...}}

For selection expressions: *{{...}}

So, for example, given an Integer-to-String converter that adds commas as a thousands separator, this:

<pth:text="${val}">...</p><pth:text="${{val}}">...</p>

…should result in:

<p>1234567890</p><p>1,234,567,890</p>

10.2 Use in forms

We saw before that every th:field attribute will always apply the conversion service, so this:

<inputtype="text"th:field="*{datePlanted}"/>

…is actually equivalent to:

<inputtype="text"th:field="*{{datePlanted}}"/>

Note that this is the only scenario in which the Conversion Service is applied in expressions using single-bracket syntax.

10.3 #conversions utility object

The #conversions expression utility object allows the manual execution of the Conversion Service wherever needed:

<pth:text="${'Val: ' + #conversions.convert(val,'String')}">...</p>

Syntax for this utility object:

#conversions.convert(Object,Class): converts the object to the specified class.

#conversions.convert(Object,String): same as above, but specifying the target class as a String (note the java.lang. package can be ommitted).

11 Rendering Template Fragments

Thymeleaf offers the possibility to render only part of a template as the result of its execution: a fragment.

This can be a useful componentization tool. For example, it can be used at controllers that execute on AJAX calls, which might return markup fragments of a page that is already loaded at the browser (for updating a select, enabling/disabling buttons…).

Fragmentary rendering can be achieved by using Thymeleaf’s fragment specs: objects implementing the org.thymeleaf.fragment.IFragmentSpec interface.

The most common of these implementations is org.thymeleaf.standard.fragment.StandardDOMSelectorFragmentSpec, which allows specifying a fragment using a DOM Selector exactly like the ones used at th:include or th:replace.

11.1 Specifying fragments in view beans

View beans are beans of the org.thymeleaf.spring4.view.ThymeleafView class declared at the application context. They allow the specification of fragments like this:

12 Advanced Integration Features

12.1 Integration with RequestDataValueProcessor

Thymeleaf now seamlessly integrates with Spring’s RequestDataValueProcessor interface. This interface allows the interception of link URLs, form URLs and form field values before they are written to the markup result, as well as transparently adding hidden form fields that enable security features like e.g. protection agains CSRF (Cross-Site Request Forgery).

An implementation of RequestDataValueProcessor can be easily configured at the Application Context:

th:href and th:src call RequestDataValueProcessor.processUrl(...) before rendering the URL.

th:action calls RequestDataValueProcessor.processAction(...) before rendering the form’s action attribute, and additionally it detects when this attribute is being applied on a <form> tag —which should be the only place, anyway—, and in such case calls RequestDataValueProcessor.getExtraHiddenFields(...) and adds the returned hidden fields just before the closing </form> tag.

th:value calls RequestDataValueProcessor.processFormFieldValue(...) for rendering the value it refers to, unless there is a th:field present in the same tag (in which case th:field will take care).

th:field calls RequestDataValueProcessor.processFormFieldValue(...) for rendering the value of the field it applies to (or the tag body if it is a <textarea>).

Note this feature will only be available for Spring versions 3.1 and newer.

12.1 Building URIs to controllers

Since version 4.1, Spring allows the possibility to build links to annotated controllers directly from views, without the need to know the URIs these controllers are mapped to.

In Thymeleaf, this can be achieved by means of the #mvc.url(...) expression method, which allows the specification of controller methods by the capital letters of the controller class they are in, followed by the name of the method itself. This is equivalent to JSP’s spring:mvcUrl(...) custom function.

You can read more about this mechanism at http://docs.spring.io/spring-framework/docs/4.1.2.RELEASE/spring-framework-reference/html/mvc.html#mvc-links-to-controllers-from-views

13 Spring WebFlow integration

13.1 Basic configuration

The thymeleaf-spring4 integration package includes integration with Spring WebFlow 2.3.x.

WebFlow includes some AJAX capabilities for rendering fragments of the displayed page when specific events (transitions) are triggered, and in order to enable Thymeleaf to attend these AJAX requests, we will have to use a different ViewResolver implementation, configured like this: