SpringMVC之请求与响应
简单入门
概述
SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中。
SpringMVC 已经成为目前最主流的MVC框架之一,并且随着Spring3.0 的发布,全面超越 Struts2,成为最优 秀的 MVC 框架。它通过一套注解,让一个简单的 Java 类成为处理请求的控制器,而无须实现任何接口。同时 它还支持 RESTful 编程风格的请求。
快速入门
SpringMVC基本逻辑:客户端发起请求,服务器接收请求,执行逻辑并进行视图跳转
Spring MVC中的前端控制器就是 DispatcherServlet,它继承了HttpServlet这个抽象类。前端控制器是Spring MVC的集中访问点,主要职责是调度、流程控制。为了给整个Web项目配置一个前端控制器,需要在web.xml文件中进行如下配置:
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载spring-mvc.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置映射地址-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 默认参数,所有请求都会通过此servlet-->
<url-pattern>/</url-pattern>
</servlet-mapping>
基本步骤
- 导入SpringMVC相关坐标
- 在web.xml配置SpringMVC核心控制器DispathcerServlet
- 创建Controller类和视图页面
- 使用注解配置Controller类中业务方法的映射地址
- 配置SpringMVC核心文件:spring-mvc.xml
- 客户端发起请求
详细步骤
- 导入相关坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.5.RELEASE</version>
</dependency>
- 配置核心控制器
<!--配置SpringMVC的前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 加载spring-mvc.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置映射地址-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!-- 默认参数,所有请求都会通过此servlet-->
<url-pattern>/</url-pattern>
</servlet-mapping>
- 创建Controller类和业务方法并配置注解
// 放到spring容器
@Controller
// 与方法的请求映射组合到一起进行访问,以此区分各个模块
@RequestMapping("/user")
public class UserController {
// 请求地址 http://localhost:8080/user/quick
// 请求映射,建立请求URL和处理请求方法之间的关系
// method属性:指定请求方式
// params属性:指定限制请求参数的条件,支持简单表达式,要求请求参数的key和value必须和配置的一模一样
@RequestMapping(value = "/quick", method = RequestMethod.GET)
public String save(){
System.out.println("Controller save running....");
// return要跳转的视图,加/表示从根目录下开始访问,不然相对路径会从http://localhost:8080/user/quick下开始访问
// forward:转发,redirect:重定向
return "forward:/success";
}
}
- 创建视图页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success!</h1>
</body>
</html>
- 创建spring-mvc.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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- Controller组件扫描-->
<context:component-scan base-package="com.itheima.controller"/>
- 客户端测试
访问测试地址:http://localhost:8080/user/quick
控制台打印:Controller save running....
页面显示:Success!
过程解析
客户端,即浏览器发起访问请求到Tomcat引擎,Tomcat接收客户端请求,解析请求资源地址;创建代表请求的req对象以及代表响应的resp对象;调用目标资源;获得resp中的内容,组装成http响应返回客户端。
XML配置解析
视图解析
<!--配置内部资源视图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀-->
<property name="prefix" value="/jsp/"/>
<!-- 后缀-->
<property name="suffix" value=".jsp"/>
</bean>
配置视图解析器之后,在Controller类中的return跳转就不需要加入要跳转的页面后缀以及页面的上一级目录(前缀)。
请求与响应
以下代码示例均在Controller类中,同时所有配置文件遵循文章上面配置文件。Controller类在SpringMVC中负责处理由DispatcherServlet分发的请求,把用户请求的数据经过业务处理层处理之后封装成一个Model,然会把该Model返回给对应的View进行展示。
数据响应
页面跳转
- 直接返回字符串
此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。
@RequestMapping("/quick")
public String quickMethod() {
return "index";
}
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".jsp"/>
请求地址:localhost:3306/quick
转发资源地址:/WEB-INF/views/index.jsp
返回带有前缀的字符串:
转发:forward:/WEB-INF/views/index.jsp
重定向:redirect:/index.jsp
- 通过ModelAndView对象返回
// 返回ModelAndView
@RequestMapping(value = "/quick2")
public ModelAndView save2() {
/*
* Model:封装数据
* View:展示数据
* */
ModelAndView modelAndView = new ModelAndView();
// 设置模型数据放到request域中
modelAndView.addObject("username", "itcast");
// 设置视图名称
modelAndView.setViewName("success");
return modelAndView;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>Success!</h1>
<h1>${username}</h1>
</body>
</html>
直接返回ModelAndView对象,Model用于封装数据,其中的key需要与视图中的值一致,View用于设置视图名称,用于返回到指定视图(视图即jsp页面),View参数可以设置forward或redirect属性。
也可以直接在方法里直接定义ModelAndView形参,省略掉新建对象的步骤,代码如下所示。
@RequestMapping(value = "/quick3")
public ModelAndView save3(ModelAndView modelAndView) {
modelAndView.addObject("username", "itheima");
modelAndView.setViewName("success");
return modelAndView;
}
也可以返回String字符串,指定要跳转到的视图,同时在形参里定义Model,设置模型数据,代码如下所示。
@RequestMapping(value = "/quick4")
public String save4(Model model) {
model.addAttribute("username", "it4");
return "success";
}
也可以直接设置request,返回String字符串,此方法不常用,代码如下所示。
// 不常用
@RequestMapping(value = "/quick5")
public String save5(HttpServletRequest request) {
request.setAttribute("username", "it5");
return "success";
}
回写数据
- 返回普通字符串
// 回写普通字符串
@RequestMapping(value = "/quick6")
public void save6(HttpServletResponse response) throws Exception{
response.getWriter().print("hello world");
}
通过普通的response直接将字符串返回到控制台,也可以通过@ResponseBody注解回写字符串,该注解告知spring-mvc框架,该方法不进行视图跳转,直接进行数据响应,在控制台直接打印字符串。同时需要在spring-mvc.xml中打开spring-mvc的注解驱动
<!-- mvc的注解驱动-->
<mvc:annotation-driven/>
// 使用注解回写普通字符串,告知spring-mvc框架,该方法不进行视图跳转,直接进行数据响应
@ResponseBody
@RequestMapping(value = "/quick7")
public String save7() throws Exception{
return "hello world";
}
- 返回对象
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数, 指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中使用mvc的注解驱动代替json的复杂配置。
// 使用json转换工具返回对象或集合
@ResponseBody
@RequestMapping(value = "/quick10")
public User save10() {
User user = new User();
user.setUsername("lisi");
user.setAge(30);
return user;
}
使用json转换工具返回对象或集合时,可以直接new一个实体类对象,同时使用实体类对象中的set方法传入参数并直接返回对象。
- 返回json字符串
// 返回json格式字符串
@ResponseBody
@RequestMapping(value = "/quick8")
public String save8() throws Exception {
return "{\"username\":\"zhangsan\",\"age\":18}";
}
json字符串也可以使用jackson返回json格式字符串,但是这里不会用到,所以不会赘述,以后会单独在关于json里面详解。
数据请求
获得请求参数
客户端请求参数的格式是:name=value&name=value......
客户端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
- 基本类型参数
// 获得基本类型参数
@ResponseBody
@RequestMapping(value = "/quick11")
public void save11(String username, int age) throws IOException{
System.out.println(username);
System.out.println(age);
}
客户端请求地址:localhost://3306/user/quick11?username=lisi&age=14
控制台打印:lisi \n14
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
当请求的参数名称与Controller的业务方法参数名称不一致时,可以使用SpringMVC注解来解决这个问题,也称参数绑定,代码如下所示。
// 获得基本类型参数
// 参数绑定注解:@requestParam:当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@requestParam注解显示绑定
@ResponseBody
@RequestMapping(value = "/quick16")
// 使用@RequestParam将请求的name绑定到username
// required属性:是否必须包括请求的参数,默认为true,提交时如果没有此参数则报错
// defaultValue属性:当没有指定请求参数时,则使用指定的默认值赋值
public void save16(@RequestParam(value = "name", required = false, defaultValue = "tom") String username) throws IOException{
System.out.println(username);
}
- POJO类型参数
// 获得POJO类型参数
@ResponseBody
@RequestMapping(value = "/quick12")
public void save12(User user) throws IOException {
System.out.println(user );
}
客户端请求地址:localhost://3306/user/quick11?username=lisi&age=14
控制台打印:User{username='lisi', age=18}
Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。在这里业务方法中的参数名称是一个POJO类,请求时要请求类中包含的所有属性,服务器获取的是POJO类型的对象。
- 数组类型参数
// 获得数组类型参数
@ResponseBody
@RequestMapping(value = "/quick13")
public void save13(String[] strs) throws IOException {
System.out.println(Arrays.asList(strs));
}
客户端请求地址:localhost:8080/user/quick13?strs=111&strs=222&strs=333
控制台打印:[111, 222, 333]
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
- 集合类型参数
// 获得集合类型参数
// 获得集合参数时,要将集合参数包装到一个POJO中
@ResponseBody
@RequestMapping(value = "/quick14")
// 使用VO创建存放User的List,利用表单提交List
public void save14(VO vo) throws IOException {
System.out.println(vo);
}
package com.itheima.domain;
import java.util.List;
public class VO {
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "VO{" +
"userList=" + userList +
'}';
}
}
定义一个VO类,类中有实体类User的List集合与对应的get、set方法,业务方法会使用VO类进行请求数据的获取。同时因为在浏览器发起请求过于复杂,所以借助一个form表单来提交数据。
<%--
Created by IntelliJ IDEA.
User: zzc
Date: 2021/12/16
Time: 20:18
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/user/quick14" method="post">
<%-- 表明是第几个User对象的username,age--%>
<%-- userList里第一个对象的username--%>
<input type="text" name="userList[0].username"><br/>
<input type="text" name="userList[0].age"><br/>
<input type="text" name="userList[1].username"><br/>
<input type="text" name="userList[1].age"><br/>
<input type="submit">
</form>
</body>
</html>
请求数据乱码问题
<!-- 配置全局过滤的filter解决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>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
数据乱码问题只需要在web.xml中配置全局过滤的filter即可解决。
获得Servlet相关API
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的对象如下:
- HttpServletRequest
- HttpServletResponse
- HttpSession
// 获得Servlet相关API
@ResponseBody
@RequestMapping(value = "/quick19")
public void save19(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException{
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
获得请求头
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name) @RequestHeader注解的属性如下:
- value:请求头的名称
- required:是否必须携带此请求头
// 获得请求头:user_agent
@ResponseBody
@RequestMapping(value = "/quick20")
public void save20(@RequestHeader(value = "User-Agent", required = false) String user_agent) throws IOException{
System.out.println(user_agent);
}
使用@CookieValue可以获得指定Cookie的值,@CookieValue注解的属性如下:
- value:指定cookie的名称
- required:是否必须携带此cookie
// 获得请求头
@ResponseBody
@RequestMapping(value = "/quick21")
public void save21(@CookieValue(value = "JSESSIONID") String jsessionId) throws IOException{
System.out.println(jsessionId);
}

浙公网安备 33010602011771号