Use multiple ViewResolvers in Spring Boot
View more Tutorials:
Normally, in a Spring application, you usually need to use only one technology for the View layer, which can be a Thymeleaf, JSP or FreeMarker, etc. However, you can also use multiple technologies for the View layer at the same time. In this post, I am going to guide you for creating a such application.
OK, We will create an application that uses all of the 3 technologies such as Thymeleaf, JSP & FreeMarker for the View layer.



To be able to use the JSP, Thymeleaf, FreeMarker, you need to have the following libraries:
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- FreeMarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSP -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
The full content of the pom.xml 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>org.o7planning</groupId>
<artifactId>SpringBootMultiViewResolver</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBootMultiViewResolver</name>
<description>Spring Boot + Multi ViewResolver</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- FreeMarker -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JSP -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<!-- JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
When developing a Spring Boot project, you usually use only one technology for the View layer (JSP, Thymeleaf, ..), the Spring Boot will automatically configure a ViewResolver for you to work with that technology. However, in case you use multiple technologies for the View layer, you have to manually configure all necessary ViewResolvers.
This is an illustration of the flow of a Spring application if you use a ViewResolver.

In case, you use multiple technologies for the View layer, a lot of ViewResolvers take part in the flow of the application. The ViewResolvers are arranged in order of priority (0, 1, 2, ..). If ViewResolver (0) does not find the required "View Name" , the ViewResolver (1) will be used, ...

Configure Thymeleaf ViewResolver.
ThymeleafViewResolverConfig.java
package org.o7planning.sbmultiviewresolver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
@Configuration
public class ThymeleafViewResolverConfig {
@Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(thymeleafTemplateEngine());
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setOrder(0);
// Important!!
// th_page1.html, th_page2.html, ...
viewResolver.setViewNames(new String[] { "th_*" });
return viewResolver;
}
// Thymeleaf template engine with Spring integration
@Bean
public SpringTemplateEngine thymeleafTemplateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(thymeleafTemplateResolver());
templateEngine.setEnableSpringELCompiler(true);
return templateEngine;
}
@Bean
public SpringResourceTemplateResolver springResourceTemplateResolver() {
return new SpringResourceTemplateResolver();
}
// Thymeleaf template resolver serving HTML 5
@Bean
public ITemplateResolver thymeleafTemplateResolver() {
ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
templateResolver.setPrefix("templates/");
templateResolver.setCacheable(false);
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode(TemplateMode.HTML);
templateResolver.setCharacterEncoding("UTF-8");
return templateResolver;
}
}
In this application, we configure the Thymeleaf ViewResolver with the highest priority (order = 0).
Note: The Thymeleaf ViewResolver will throw an exception when it does not find the required "View Name" (A necessary html file). It is contrary to your wish that the ViewResolver with succeeding priority will be used. Therefore, you need to set up rules for the "View Names" to be served by the Thymeleaf ViewResolver.// Important!! // th_page1.html, th_page2.html, ... viewResolver.setViewNames(new String[] { "th_*" });
Configure FreeMarker ViewResolver.
FreeMarkerViewResolverConfig.java
package org.o7planning.sbmultiviewresolver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver;
@Configuration
public class FreeMarkerViewResolverConfig {
@Bean(name = "viewResolver")
public ViewResolver getViewResolver() {
FreeMarkerViewResolver viewResolver = new FreeMarkerViewResolver();
viewResolver.setCache(true);
viewResolver.setPrefix("/freemarker/");
viewResolver.setSuffix(".ftl");
viewResolver.setOrder(1);
return viewResolver;
}
@Bean(name = "freemarkerConfig")
public FreeMarkerConfigurer getFreemarkerConfig() {
FreeMarkerConfigurer config = new FreeMarkerConfigurer();
// Folder containing FreeMarker templates.
// 1 - "/WEB-INF/views/"
// 2 - "classpath:/templates"
config.setTemplateLoaderPath("classpath:/templates");
return config;
}
}
The operating principle of the FreeMarker ViewResolver:

Configure JSP ViewResolver.
Note: The JSP ViewResolver has to be established with the lowest priority. This is commented in the doucments of Spring.
JspViewResolverConfig.java
package org.o7planning.sbmultiviewresolver.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
public class JspViewResolverConfig {
@Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
viewResolver.setContentType("text/html");
// Make sure > Thymeleaf order & FreeMarker order.
viewResolver.setOrder(1000);
return viewResolver;
}
}
The operating principles of the JSP ViewResolver:

MainController.java
package org.o7planning.sbmultiviewresolver.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MainController {
@RequestMapping(value = { "/testJsp" }, method = RequestMethod.GET)
public String testJspView() {
return "testJsp";
}
@RequestMapping(value = { "/testThymeleaf" }, method = RequestMethod.GET)
public String testThymeleafView() {
return "th_page1";
}
@RequestMapping(value = { "/testFreeMarker" }, method = RequestMethod.GET)
public String testFreeMarkerView() {
return "testFreeMarker";
}
}

th_page1.html (Thymeleaf)
<!DOCTYPE html>
<html lang="en">
<head>
<title>Thymeleaf</title>
</head>
<body>
<h2>Thymeleaf Page</h2>
<p>templates/th_page1.html</p>
</body>
</html>
freemaker/testFreeMarker.ftl (FreeMarker)
<!DOCTYPE html>
<html lang="en">
<head>
<title>FreeMarker</title>
</head>
<body>
<h2>FreeMarker Page</h2>
<p>templates/freemarker/testFreeMarker.ftl</p>
</body>
</html>

testJsp.jsp
<!DOCTYPE html>
<html lang="en">
<head>
<title>JSP</title>
</head>
<body>
<h2>JSP Page</h2>
<p>WEB-INF/jsp/testJsp.jsp</p>
</body>
</html>