I have commented in the past on some of the differences between OGNL and SpEL - see Thymeleaf vs. SpEL vs. OGNL.

Some of the additional functionality you get with SpEL may be compelling enough that you want to use SpEL - but maybe you don’t want to use it in a Spring framework application.

You can do this - but how much work it will be depends on how your application is using Thymeleaf.

First of all you need the Spring Thymeleaf JAR. Assuming Maven, this is:

1
2
3
4
5
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.7.2</version>
</dependency>

Note that you cannot include only the relevant version of the thymeleaf-spring dependency - for example:

1
2
3
4
5
6
<!-- THIS IS NOT SUFFICIENT -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf-spring5</artifactId>
    <version>3.1.0.RELEASE</version>
</dependency>

But by including the spring-boot-starter-thymeleaf dependency your project will pull not only the above thymeleaf-spring JAR but also several other required Spring JARs.

Then, you need to tell your Thymeleaf installation to use the Spring dialect of Thymeleaf, as opposed to the standard dialect.

This requires you to have an explicit Thymeleaf engine configuration (as opposed to a configuration provided by default).

In my case, the code for my Thymeleaf engine looks like this:

 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
package org.northcoder.javalin5;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.TemplateSpec;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.dialect.SpringStandardDialect;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;

public class MyTemplateEngine {

    public static TemplateEngine configureEngine() {
        // Use the Thymeleaf classloader resolver:
        ClassLoaderTemplateResolver templateResolver
                = new ClassLoaderTemplateResolver(Thread
                        .currentThread().getContextClassLoader());
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setPrefix("/thymeleaf/");
        templateResolver.setSuffix(".html");
        templateResolver.setCacheTTLMs(3600000L); // one hour
        templateResolver.setCacheable(true);
        TemplateEngine templateEngine = new TemplateEngine();
        templateEngine.setTemplateResolver(templateResolver);

        templateEngine.setDialect(new SpringStandardDialect());

        return templateEngine;
    }

}

Things to note:

  1. We have the following import:
1
import org.thymeleaf.spring5.dialect.SpringStandardDialect;
  1. We use it as follows:
1
templateEngine.setDialect(new SpringStandardDialect());

One final step may be needed, depending on how your project integrates the standard Thymeleaf dialect. If you do not already have a custom implementation of a template engine, then you may be relying on a web framework which handles this for you automatically (e.g. via a default configuration).

In my case, I am using Javalin, and I tell it to use my template engine as follows:

1
2
3
4
5
Javalin.create(config -> {
    JavalinThymeleaf.init(MyTemplateEngine.configureEngine());
})
      .get("/test", TEST)
      .start(7070);

You may need to adjust the above approach (and your template engine code) based on whatever framework you are using.

As an example of what you can do with the dialect, see the following - just a few of the features you can now use:

 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
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>Spring Thymeleaf Dialect Test</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
    </head>

    <body>

        <!-- safe navigation operator -->
        <div th:text="${foo?.bar}"></div>

        <!-- inline lists -->
        <select>
            <option th:each="opt : ${ {1, 2, 3} }"
                    th:text="${opt}" th:value="${opt}">
            </option>
        </select>

        <!-- Spring converters (this one is ASCII decimal code to char) -->
        <div th:each="num : ${#numbers.sequence(97, 122)}">
            <div th:text="${#conversions.convert(num, 'java.lang.Character')}"></div>
        </div>

        <!-- collection selection -->
        <table th:with="min=${minAge}">
            <tr th:each="person : ${people.?[age > __${min}__]}">
                <td><span th:text="${person.age}"></span></td>
            </tr>
        </table>

        <!-- correctly fails with SpEL (but, oddly, gives "0" with OGNL)
        <div th:text="${null - null}"></div>
        -->

    </body>
</html>

There is much more information in the official SpEL documentation here.