Configure multiple View Resolvers in Spring
1. Introduction
In Spring, the View Resolver is provided to resolve the view with the data available in the model, without tightly binding to a View technology, be it JSP, Velocity or Thymeleaf. Spring makes it easy and flexible to configure one or multiple View Resolvers, as per the need would be.
2. Spring MVC application flow
Before we proceed with understanding how multiple View Resolvers serve the purpose, lets take a quick recap of the Spring MVC application flow.
- Incoming request comes through web.xml, dispatcher servlet and hits the controller.
- Controller interacts with the application layers and prepares the model.
- Controller returns the ModelAndView, with model and the view name.
- The ViewResolver provides a mapping between view names and actual views.
- The View interface addresses the request of a view to respective View technology.
- The view is then rendered onto the browser along with the model data.
3. Implementation
Let’s start with the pom dependencies.
pom.xml
<!-- Spring 3 dependencies --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Jackson JSON Mapper --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>${jackson.version}</version> </dependency> <!-- JSTL Dependency --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency>
Not much change in the web.xml.
web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>MultipleViewResolversExample</display-name> <servlet> <servlet-name>mvc-dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>mvc-dispatcher</servlet-name> <url-pattern>/API/*</url-pattern> </servlet-mapping> </web-app>
Here is the mvc-dispatcher-servlet, which has multiple View Resolvers defined.
mvc-dispatcher-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> <mvc:annotation-driven /> <context:component-scan base-package="com.jcombat.controller" /> <!-- Bean View Resolver --> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"> <property name="order" value="0" /> </bean> <!-- JSP View Resolver --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix"> <value>/WEB-INF/</value> </property> <property name="suffix"> <value>.jsp</value> </property> <property name="order" value="1" /> </bean> <bean name="jsonView" class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" /> </beans>
Note that the two View Resolvers configured are InternalResourceViewResolver and BeanNameViewResolver. We have also set the priorities using the order property of the View Resolver. So BeanNameViewResolver has a higher priority. This means that when the ModelAndView object is returned, the BeanNameViewResolver checks for the available bean views with the view name that is returned. If the matching bean view is found, it is rendered. If not, the next View Resolver i.e. InternalResourceViewResolver, comes into the picture, and similarly checks for the JSPs with the view name that is returned with ModelAndView. If it is found, the view is rendered. But if not, and there is no more View Resolvers down the hierarchy, an appropriate exception is thrown.
Moving ahead with the implemetation, we have the same Employee entity class, as we have been using recently.
Employee.java
package com.jcombat.bean; public class Employee { private String empId; private String name; public String getEmpId() { return empId; } public void setEmpId(String empId) { this.empId = empId; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Let’s now write down the EmployeeController.
EmployeeController.java
package com.jcombat.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import com.jcombat.bean.Employee; @Controller @RequestMapping(value = "/Employee") public class EmployeeController { @RequestMapping(value = "/{name}/{empId}", method = RequestMethod.GET) public ModelAndView process( @PathVariable("name") String name, @PathVariable("empId") String empId) { ModelAndView modelAndView = new ModelAndView(); Employee employee = new Employee(); employee.setEmpId(empId); employee.setName(name); modelAndView.setViewName("employeeDetails"); modelAndView.addObject("employee", employee); return modelAndView; } }
We now make sure the JSP with the view name – employeeDetails, exists.
employeeDetails.jsp
<%@page contentType="text/html" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>Via JSP View Resolver</title> </head> <body> <!-- Retrieve the model data through JSTL --> <p>${employee.empId}</p> <p>${employee.name}</p> </body> </html>
4. Running the application
When we run the application, below is what we see.
Note that we don’t have any bean view with name employeeDetails, but rather matches with the actual JSP view file employeeDetails.jsp. Hence, the view gets resolved as JSP.
Now let’s return the view name as jsonView, modifying the below statement in the controller method.
modelAndView.setViewName("jsonView");
BeanNameViewResolver being higher in priority, this time finds the bean view with the name jsonView, and instead of rendering the JSP view, the same URI now returns a JSON.
5. Download the source code
Reference: | Configure multiple View Resolvers in Spring from our JCG partner Abhimanyu Prasad at the jCombat blog. |
Hi Abhimanyu, I can get your attention to ActFramework (http://actframework.org)? Although I’ve just released the first version to maven central repository, it has been put into comercial usage for 1 years. I suppose most feature SpringBoot provided in a better and simpler way. Take for the Multiple view as an example, the developer doesn’t need to configure anything, just put the different resource folder root that matches the view engine id. Here is the sample app demonstrate using multiple view engine in an ACT application: https://github.com/actframework/act-demo-apps/tree/master/views There are some other links you might feel interesting: 1. TechEmpower(https://www.techempower.com/benchmarks/) ACT test bed… Read more »