Spring中多ViewResolver实践

1. 概述

在Spring中,提供View Resolver以使用模型中可用的数据来解析视图,而无需紧密绑定到View技术,无论是JSP,Velocity,JSON 还是 Thymeleaf ,Spring都可以根据需要轻松灵活地配置一个或多个View Resolvers

2. Spring MVC 请求处理流程

在我们继续了解多个View Resolvers如何实现之前,我们看看 SpringMVC 是如何处理请求的流程:

  1. 传入请求来自web.xmlDispatcherServlet 并最终到达 Controller
  2. Controller与应用程序层交互并准备Model
  3. Controller返回ModelAndView,包含模型和视图名称。
  4. ViewResolver提供视图名称和实际视图之间的映射。
  5. View接口决定了展示View的技术。
  6. 然后将视图与模型数据一起呈现到浏览器上。

3.实施

让我们从pom依赖开始:

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
<!-- 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>

web.xml中没有太大的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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>

这是 mvc-dispatcher-servlet,它定义了多个View Resolvers

mvc-dispatcher-servlet.xml内容如下所示:

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
<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>

请注意,配置的两个View Resolvers是InternalResourceViewResolverBeanNameViewResolver。我们还使用View Resolver 的order属性设置了优先级。因此 BeanNameViewResolver 具有更高的优先级。这意味着当返回ModelAndView对象时, BeanNameViewResolver将使用返回的视图名称检查可用的bean视图。如果找到匹配的bean视图,则呈现它。如果没有,则下一个View Resolver即 InternalResourceViewResolver尝试解析,同样检查具有ModelAndView返回的视图名称的JSP 。如果找到,则呈现视图。但如果没有,并且层次结构中没有更多View Resolvers,则抛出适当的异常。

继续实现,我们先定义一个 Employee 实体类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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;
}
}

接下来是 EmployeeController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@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;
}
}

然后写一个JSP(employeeDetails.jsp):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%@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. 运行验证

当我们运行应用程序时,我们看到如下的界面:

upload successful

请注意,我们没有任何名为employeeDetails的 bean,而是与实际的JSP视图文件employeeDetails.jsp匹配。因此,视图将作为JSP解析。

现在让我们将视图名称设置为 jsonView 返回(我们之前的 XML 配置中有一个 jsonView
Bean),修改 Controller 中的视图名称:

1
modelAndView.setViewName("jsonView");

BeanNameViewResolver 优先级更高,所以这次查找到名为 jsonView 的Bean视图并完成解析,而不是呈现JSP视图,相同的URI现在返回一个JSON

upload successful