Spring MVC使用篇(八)—— 处理器(Handler)方法的返回值
1、演示项目环境搭建
1.1 演示项目工程结构
在IDEA中创建Web项目,具体项目工程结构如图所示:
controller:存放控制器(Controller)类。
model:存放JavaBean模型类。
config:存放Spring MVC核心配置文件等各种配置文件。
jsp:存放JSP页面文件。
jsp/user:存放该演示项目有关user模型的所有JSP文件。
lib:存放各种依赖的jar包。
1.2 演示项目依赖的基础jar包
该演示项目是在“Spring MVC使用篇”系列文章之前的项目工程的基础上搭建的,因此依赖的基础jar包于第二篇文章中已经介绍过了(点击链接即可查看:Spring MVC使用篇(二)—— 环境搭建),这里便不再赘述。
1.3 配置web.xml
此次出去在web.xml配置文件中配置Spring MVC的前端控制器(DispatcherServlet)外,还需要针对解决中文乱码问题设置过滤器。由于之前已经详细讲解,这里只贴出配置文件代码,详细请查看该系列第二篇文章(点击链接即可查看:Spring MVC使用篇(二)—— 环境搭建)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/config/springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
<!--post中文乱码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<!--该配置表示,名为CharacterEncodingFilter的过滤器对所有请求进行过滤,然后该过滤器会
以encoding指定的编码格式对请求数据进行统一编码-->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
1.4 配置Spring MVC核心配置文件
在config文件夹下创建Spring MVC的核心配置文件“springmvc.xml”,并在配置文件中配置基于注解方式的处理器映射器和适配器、使用扫描配置和配置视图解析器。由于之前已经详细讲解,这里只贴出配置文件代码,详细请查看该系列第二篇文章(点击链接即可查看:Spring MVC使用篇(二)—— 环境搭建)具体代码如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!--配置基于注解的处理器适配器与处理器映射器-->
<mvc:annotation-driven/>
<!--使用扫描配置,对某一个包下面的所有类进行扫描,
找出所有使用@Controller注解的Handler控制器类-->
<context:component-scan base-package="com.ccff.controller"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2、返回ModelAndView
若处理器方法处理完后,需要跳转到其他资源,且又要在跳转的资源间传递数据,此时处理器方法返回ModelAndView比较好。当然,若要返回ModelAndView,则处理器方法中需要定义ModelAndView对象。
例如要实现一个简单的处理用户注册请求的业务逻辑。在用户注册页面中要求用户输入用户编号、用户名和密码信息。当用户填写完毕后点击“用户注册”按钮后将请求和数据提交到后台进行处理,并返回处理结果(这里为了演示方便,处理用户注册数据的方式为直接显示用户注册时填写的全部信息)。
第一步,在“WEB-INF/jsp/user”下创建一个名为“login.jsp”的JSP页面,页面中通过一个表单收集用户输入的全部注册信息,并提交给“doLogin.action”进行注册业务逻辑的处理。login.jsp页面的代码如下所示:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!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=UTF-8">
<title>用户注册</title>
</head>
<body>
<h3>用户注册</h3>
<form action="doLogin.action" method="post">
<table width="300px;" border=1>
<tr>
<td>用户编号:</td>
<td><input type="text" name="userId" /></td>
</tr>
<tr>
<td>用户名:</td>
<td><input type="text" name="username" /></td>
</tr>
<tr>
<td>密 码:</td>
<td><input type="password" name="password" /></td>
</tr>
</table>
<br/>
<input type="submit" value="用户注册" />
</form>
</body>
</html>
第二步,在controller文件夹下创建名为“UserController”的Controller类,该类用来实现所有的业务逻辑。首先创建login方法用来向用户显示注册页面(login.jsp)。然后创建doLogin方法用来处理前台页面通过表单传递过来的数据。Controller的具体代码如下所示:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public ModelAndView login(){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("user/login");
return modelAndView;
}
@RequestMapping("/doLogin")
public ModelAndView doLogin(User user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user",user);
modelAndView.addObject("username",user.getUsername());
modelAndView.addObject("userId",user.getUserId());
modelAndView.addObject("password",user.getPassword());
modelAndView.setViewName("user/doLogin");
return modelAndView;
}
}
第三步,由Controller中的代码可知,在Controller中的doLogin方法的形参采用的是接收引用类型的变量,这里便涉及到了在Spring MVC中关于各种类型参数的绑定问题(详细请查看该系列第七篇文章:Spring MVC使用篇(七)—— 参数绑定)和User这个JavaBean模型,具体代码如下所示:
package com.ccff.model;
public class User {
private int userId;
private String username;
private String password;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
第四步,在“WEB-INF/jsp/user”下创建名为“doLogin.jsp”的JSP页面用来显示处理用户注册信息后的结果页面。具体代码如下所示:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!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=UTF-8">
<title>处理用户注册信息</title>
</head>
<body>
<h3>使用user参数显示用户注册信息</h3>
<table width="300px;" border=1>
<tr>
<td>用户编号:</td>
<td>${user.userId}</td>
</tr>
<tr>
<td>用户名:</td>
<td>${user.username}</td>
</tr>
<tr>
<td>密 码:</td>
<td>${user.password}</td>
</tr>
</table>
<br/>
<hr/>
<h3>使用userId、username和password参数显示用户注册信息</h3>
<table width="300px;" border=1>
<tr>
<td>用户编号:</td>
<td>${userId}</td>
</tr>
<tr>
<td>用户名:</td>
<td>${username}</td>
</tr>
<tr>
<td>密 码:</td>
<td>${password}</td>
</tr>
</table>
</body>
</html>
第五步,将项目部署到Tomcat服务器上后,通过浏览器访问请求URL:http://localhost:8080/demo/user/login.action 即可得到login.jsp所展示出来的页面,具体结果如下图所示:
当用户输入全部信息点击用户注册按钮后,表单会把用户所有的数据自动转换为与Controller中doLogin方法中形参所对应的User类型变量,并提交给doLogin方法处理,处理后返回doLogin.jsp页面,具体结果如下图所示:
注意:在使用ModelAndView时,若该方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的Ajax异步响应),此时若返回ModelAndView对象,则将总是有一部分多余:要么Model多余,要么View多余。此时返回ModelAndView对象将不再合适。
3、返回String
处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器可以将其转换为物理视图地址。
3.1 返回内部资源逻辑视图名
3.1.1 配置视图解析器
若要跳转的资源为内部资源,则视图解析器可以使用InternalResourceViewResolver内部资源视图解析器(在之前的配置中我们默认配置的视图解析器即为该视图解析器)。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的prefix、suffix相结合,即可形成要访问的URI。
视图解析器的配置如下:
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
这里将Controller中的login方法修改为返回值为String类型,修改后的Controller如下:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "user/login";
}
@RequestMapping("/doLogin")
public ModelAndView doLogin(User user){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user",user);
modelAndView.addObject("username",user.getUsername());
modelAndView.addObject("userId",user.getUserId());
modelAndView.addObject("password",user.getPassword());
modelAndView.setViewName("user/doLogin");
return modelAndView;
}
}
再次将项目部署到Tomcat服务器上后,在浏览器内访问请求URL:http://localhost:8080/demo/user/login.action ,仍然能够显示用户注册页面,说明修改正确,结果如下图所示:
当然,也可以直接返回资源的物理视图名称(如该演示项目中的“/WEB-INF/jsp/user/login.jsp”)。不过,此时就不需要再在视图解析器中再配置前缀与后缀了。而且,需要特别注意的是直接返回的资源的物理名称的请求路径书写问题(详细请查看该系列文章:Spring MVC使用篇(五)—— 请求路径问题)。
3.1.2 使用HttpServletRequest对象携带数据跳转
由于Controller中的doLogin方法需要向跳转的页面传递数据,因此该方法的形参中除去接收绑定的User对象外,还需要有能够传递数据的HttpServletRequest对象。通过调用HttpServletRequest对象中的setAttribute方法实现数据的传递。修改后的Controller代码如下:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "user/login";
}
@RequestMapping("/doLogin")
public String doLogin(User user, HttpServletRequest request){
request.setAttribute("user",user);
request.setAttribute("username",user.getUsername());
request.setAttribute("userId",user.getUserId());
request.setAttribute("password",user.getPassword());
return "user/doLogin";
}
}
将修改后的项目重新部署到Tomcat服务器上后,在浏览器输入请求URL:http://localhost:8080/demo/user/login.action 后输入用户注册信息后点击“用户注册”按钮将表单中的数据传递给Controller中的doLogin方法进行处理,处理后显示doLogin.jsp页面,若如下图一样能够显示处理用户输入的注册数据,则证明修改正确。
3.1.3 使用Model对象携带数据跳转
除了使用HttpServletRequest对象传递数据外,还可以使用Model对象传递数据。Model对象就是ModelAndView对象中的Model,它只负责传递数据,通过Model对象的addAttribute方法即可实现。修改后的Controller代码如下所示:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "user/login";
}
@RequestMapping("/doLogin")
public String doLogin(User user, Model model){
model.addAttribute("user",user);
model.addAttribute("username",user.getUsername());
model.addAttribute("userId",user.getUserId());
model.addAttribute("password",user.getPassword());
return "user/doLogin";
}
}
将修改后的项目重新部署到Tomcat服务器上后,在浏览器输入请求URL:http://localhost:8080/demo/user/login.action 后输入用户注册信息后点击“用户注册”按钮将表单中的数据传递给Controller中的doLogin方法进行处理,处理后显示doLogin.jsp页面,若如下图一样能够显示处理用户输入的注册数据,则证明修改正确。
3.2 返回View对象名
若要跳转的资源为外部资源,则视图解析器可以使用BeanNameViewResolver,然后在配置文件中再定义一些外部视图资源View对象,此时处理器方法返回的字符串就是要跳转资源视图View的名称。当然,这些视图View对象也可以是内部资源视图View对象。
第一步,为了能够实现外部资源的跳转,在Spring MVC的核心配置文件springmvc.xml中添加BeanNameViewResolver视图解析器,并设置BeanNameViewResolver视图解析器与默认的InternalResourceViewResolver视图解析器的优先级(关于视图解析器的详细讲解,请参考该系列的第四篇文章:Spring MVC使用篇(四)—— 视图解析器)。修改后的springmvc.xml文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd ">
<!--基于注解的方式配置处理器映射器和适配器方法一-->
<!--配置基于注解的处理器适配器与处理器映射器-->
<mvc:annotation-driven/>
<!--使用扫描配置,对某一个包下面的所有类进行扫描,
找出所有使用@Controller注解的Handler控制器类-->
<context:component-scan base-package="com.ccff.controller"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
<property name="order" value="2"/>
</bean>
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="1"/>
</bean>
<!--定义一个内部资源-->
<bean id="doLoginInternal" class="org.springframework.web.servlet.view.InternalResourceView">
<property name="url" value="/WEB-INF/jsp/user/doLogin.jsp"/>
</bean>
<!--定义一个外部资源-->
<bean id="baidu" class="org.springframework.web.servlet.view.RedirectView">
<property name="url" value="https://www.baidu.com/"/>
</bean>
</beans>
为了测试内部资源视图的跳转,将Controller中的doLogin方法修改如下:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "user/login";
}
@RequestMapping("/doLogin")
public String doLogin(User user, Model model){
model.addAttribute("user",user);
model.addAttribute("username",user.getUsername());
model.addAttribute("userId",user.getUserId());
model.addAttribute("password",user.getPassword());
return "doLoginInternal";
}
}
将修改后的项目重新部署到Tomcat服务器上后,在浏览器输入请求URL:http://localhost:8080/demo/user/login.action 后输入用户注册信息后点击“用户注册”按钮将表单中的数据传递给Controller中的doLogin方法进行处理,处理后显示doLogin.jsp页面。
由于在springmvc.xml配置文件中设置的BeanNameViewResolver视图解析器的优先级最高(Order值最小),因此默认会首先尝试采用BeanNameViewResolver视图解析器解析,得到如下结果,即说明修改正确。
接下来为了测试外部资源视图的跳转,将Controller中的doLogin方法修改如下:
package com.ccff.controller;
import com.ccff.model.User;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(){
return "user/login";
}
@RequestMapping("/doLogin")
public String doLogin(User user){
return "baidu";
}
}
将修改后的项目重新部署到Tomcat服务器上后,在浏览器输入请求URL:http://localhost:8080/demo/user/login.action 后输入用户注册信息后点击“用户注册”按钮将表单中的数据传递给Controller中的doLogin方法进行处理,处理后直接跳转到外部资源(百度首页)。
由于在springmvc.xml配置文件中设置的BeanNameViewResolver视图解析器的优先级最高(Order值最小),因此默认会首先尝试采用BeanNameViewResolver视图解析器解析。BeanNameViewResolver通过返回的视图名(“baidu”)找到在springm.xml文件中配置的外部资源,能够直接跳转到百度首页即说明修改正确。