DEV Community

Salad Lam
Salad Lam

Posted on

Spring MVC support using more than 1 template engine

Notice

I wrote this article and was originally published on Qiita on 16 September 2019.


Example

In this example, extra FreeMarker template engine is added into Spring MVC project using Thymeleaf template engine. A 'hello world' page is displayed using this new added engine.

First get a copy of my notice board example application. Add following line into pom.xml to include FreeMarker template engine into project.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
Enter fullscreen mode Exit fullscreen mode

Add a FreeMarker template, as 'resources/templates/public/freemarker_test.ftl'.

<html>
<head>
  <title>${title}</title>
</head>
<body>
  <div>${body}</div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Add a new function into info.saladlam.example.spring.noticeboard.controller.PublicController controller.

public class PublicController {
    // ...
    @GetMapping("/freemarker_test")

    public String freemarkerTest(Model model) {
        model.addAttribute("title", "FreeMarker test page");
        model.addAttribute("body", "Hello world!");

        return "public/freemarker_test";
    }
}
Enter fullscreen mode Exit fullscreen mode

Start application and in browser to open http://localhost:8080/freemarker_test. As result 'Hello world!' is show.

# What happen behind?

When application started. Following org.springframework.web.servlet.ViewResolver bean is created by Spring Boot.

  • org.thymeleaf.spring5.view.ThymeleafViewResolver
  • org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver

org.springframework.web.servlet.DispatcherServlet instance is created. During initialization, method initViewResolvers() is called.

        if (this.detectAllViewResolvers) {
            // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
            Map<String, ViewResolver> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList<>(matchingBeans.values());
                // We keep ViewResolvers in sorted order.
                AnnotationAwareOrderComparator.sort(this.viewResolvers);
            }
        }
        // ...
Enter fullscreen mode Exit fullscreen mode

Code listed above shows that all bean with ViewResolver interface is provided by BeanFactory.

When method freemarkerTest() in PublicController being called, string 'public/freemarker_test' is return. This string will pass into org.thymeleaf.spring5.view.ThymeleafViewResolver. Since no Thymeleaf template withi this name is defined, so null is returned. Then pass into org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver and instance with org.springframework.web.servlet.View interface is returned.

Top comments (0)