SpringMVC框架
一、概述
1.1 MVC三层架构:
表现层(Web层):负责接收客户端请求,并向客户端响应结果;
业务层(Service层):负责业务逻辑处理,和项目需求息息相关;
持久层(Dao层):负责和数据库交互,对数据库表进行增删改查。
1.2 定义
Spring MVC是Spring提供的一个实现了Web MVC设计模式的轻量级Web框架。Spring MVC提供了对MVC模式的全面支持,它可以将表现层解耦,同时Spring MVC是基于请求-响应处理模型的请求驱动框架,简化了表现层的实现。
Spring MVC作用于三层架构中的表现层,用于接收客户端的请求并进行响应。
Spring MVC中包含了控制器和视图,控制器接收到客户端的请求后对请求数据进行解析和封装,接着将请求交给业务层处理。业务层会对请求进行处理,最后将处理结果返回给表现层。表现层接收到业务层的处理结果后,再由视图对处理结果进行渲染,渲染完成后响应给客户端。
1.3 Spring MVC工作原理
(1)用户通过浏览器向服务器发送请求,请求会被Spring MVC的前端控制器DispatcherServlet拦截。
(2)DispatcherServlet拦截到请求后,会调用HandlerMapping(处理器映射器)。
(3)处理器映射器根据请求URL找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet会通过返回信息选择合适的HandlerAdapter(处理器适配器)。
(5) HandlerAdapter会调用并执行Handler(处理器),这里的处理器指的就是程序中编写的Controller类,也被称之为后端控制器。
(6)Controller执行完成后,会返回一个ModelAndView对象,该对象中会包含视图名或包含模型和视图名。
(7)HandlerAdapter将ModelAndView对象返回给DispatcherServlet。
(8)前端控制器请求视图解析器根据逻辑视图名解析真正的视图。(9)ViewResolver解析后,会向DispatcherServlet中返回具体的View(视图)。
(10)DispatcherServlet对View进行渲染(即将模型数据填充至视图中)。
(11)前端控制器向用户响应结果。
二、Spring MVC的实现
2.1 前端控制器DispatcherServlet
DispatcherServlet是Spring MVC的核心类,也是Spring MVC的流程控制中心,也称为Spring MVC的前端控制器,它可以拦截客户端的请求。拦截客户端请求之后,DispatcherServlet会根据具体规则将请求交给其他组件处理。所有请求都要经过DispatcherServlet进行转发处理,这样就降低了Spring MVC组件之间的耦合性。
- 代码实现(web.xml)
<?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>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置初始化参数 -->
<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>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 细节解释
-
前端控制器实质上是一个Servlet类,即
org.springframework.web.servlet.DispatcherServlet
-
初始化参数是指在DispatcherServlet类加载时,加载spring-mvc核心配置文件,若没有指定,应用程序会在WEB-INF中找DispatcherServlet-servlet.xml的配置文件。
-
<load-on-startup>
中未0或正整数时表示项目启动时才加载此Servlet,此值越小Servlet越早被加载。若此值为负数或不设置,则Servlet在请求时才会被加载。 -
servlet-mapping
中指定url-pattern
为/,表面所有的请求均会被拦截到此Servlet中
2.2 spring-mvc.xml核心配置
- 当使用注解开发时,此配置需要开启对处理器的扫描
<context:component-scan base-package="com.tyut.controller"/>
- 同时需要配置视图处理器
下面视图处理器实际上将访问视图的路径均改为
/WEB-INF/pages/视图逻辑名.jsp
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!--配置Spring MVC要扫描的包-->
<context:component-scan base-package="com.tyut.controller"/>
<!-- 配置视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2.3 适配器类(@Controller注解)
Spring MVC框架提供了@Controller注解。使用@Controller注解,只需要将@Controller注解标注在普通Java类上,然后通过Spring的扫描机制找到标注了该注解的Java类,该Java类就成为了Spring MVC的处理器类。
@Controller
public class UserController {
@RequestMapping("/helloSpring")
public String sayHello() {
System.out.println("访问到helloSpring页面");
return "hello";
}
}
//在spring-mvc.xml中配置了spring可以扫描的Controller,同时可定位到`/WEB-INF/pages/hello.jsp`
2.4 适配器(@RequestMapping注解)
@RequestMapping注解用于建立请求URL和Handler(处理器)之间的映射关系,该注解可以标注在方法上和类上。
- 适配器标注的位置
- 标注到类上
当@RequestMapping注解标注在类上时,@RequestMapping的value属性值相当于本处理器类的命名空间,即访问该处理器类下的任意处理器都需要带上这个命名空间。
URL = 项目访问路径 + 处理器类的映射路径 + 处理器的映射路径
- 标注到方法上
当@RequestMapping注解标注在方法上时,该方法就成了一个可以处理客户端请求的Handler(处理器),它会在Spring MVC接收到对应的URL请求时被执行。
URL = 项目访问路径 + 处理方法的映射路径
- @RequestMapping注解属性
- 属性介绍
属性名 | 类型 | 描述 |
---|---|---|
name | String | 可选属性,用于为映射地址指定别名。 |
value | String[] | 可选属性,也是默认属性,用于指定请求的URL。 1.当value属性为唯一属性时,此value可以省略不写。 2.映射单个的请求URL,也可以将多个请求映射到一个方法上:@RequestMapping(value = {“/addUser”,“/deleteUser”}) |
method | RequestMethod[] | 可选属性,用于指定该方法可以处理哪种类型的请求方式,method若不匹配则不能访问:RequestMethod.GET。 |
params | String[] | 可选属性,用于指定客户端请求中参数的值必须包含哪些参数的值,才可以通过其标注的方法处理。 |
headers | String[] | 可选属性,用于指定客户端请求中,必须包含哪些header的值,才可以通过其标注的方法处理。 |
consumes | String[] | 可选属性,用于指定处理请求的提交内容类型(Content-type),如text/html,application/json。 |
produces | String[] | 可选属性,用于指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回。 |
- 请求映射方式
基于请求方式的URL路径映射
@GetMapping:匹配GET方式的请求。
@PostMapping:匹配POST方式的请求。
@PutMapping:匹配PUT方式的请求。
@DeleteMapping:匹配DELETE方式的请求。
@PatchMapping:匹配PATCH方式的请求。
基于Ant风格的URL路径映射(通配符风格)
?匹配任何单字符;
*匹配0或者任意数量的字符;
**匹配0或者多级目录。
//最长匹配原则:当映射路径中同时使用多个通配符时,会有通配符冲突的情况。当多个通配符冲突时,如果一个请求路径同时满足两个或多个Ant风格的映射路径匹配规则,那么请求路径最终会匹配满足规则字符最多的路径。
基于REST风格的URL路径映射
RESTful就是把请求参数变成请求路径
http://.../findUserById?id=1
=》
http://.../user/id/1
RESTful风格在HTTP请求中,通过GET 、POST 、PUT和DELETE 4个动词对应四种基本请求操作,具体如下所示。
-
GET用于获取资源
-
POST用于新建资源
-
PUT用于更新资源
-
DELETE用于删除资源
使用RESTful风格的优势在于路径的书写比较简便,并且通过地址无法得知做的是何种操作,可以隐藏资源的访问行为。
三、SpringMVC数据绑定与响应
3.1 数据绑定
**1.定义:**Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑定。
2.数据绑定的原理
(1)Spring MVC将ServletRequest对象传递给DataBinder。
(2)将处理器方法的形参对象传递给DataBinder。
(3)DataBinder调用ConversionService组件进行数据类型转换、数据格式化等工作,并将ServletRequest对象中的消息填充到参数对象中。
(4)调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验。
(5)校验完成后会生成数据绑定结果BindingResult对象,Spring MVC会将BindingResult对象中的内容赋给处理方法的相应参数。
3.2 简单数据绑定
简单数据是指请求中的参数不是基于列表或多层级的数据,参数直接和服务器端的形参或形参属性进行绑定。
- 默认类型的数据绑定
默认类型 | 说明 |
---|---|
HttpServletRequest | 获取请求信息 |
HttpServletResponse | 处理响应信息 |
HttpSession | 获取session中存放的对象 |
Model/ModelMap | 可以设置model数据,model数据会填充到request域。 |
案例:
@RequestMapping("/getUserId")
public void getUserId(HttpServletRequest request) {
String id = request.getParameter("id");
System.out.println("用户的id为:" + id);
}
- 简单数据类型的绑定
- 定义:简单数据类型的绑定,就是指Java中基本类型(如int、double、String等)的数据绑定。
- 要求:只需客户端请求参数的名称和处理器的形参名称一致即可,请求参数会自动映射并匹配到处理器的形参完成数据绑定。
- 案例:
@RequestMapping("/getUserName")
public void getUserName(String username) {
System.out.println(username);
}
- @RequestParam注解
作用:处理客户端参数与处理器形参名称不一致
属性:
属性 | 说明 |
---|---|
value | name属性的别名,这里指参数的名称,即入参的请求参数名称,如value="name"表示请求的参数中,名称为name的参数的值将传入。如果当前@RequestParam注解只使用vaule属性,则可以省略value属性名,如@RequestParam(“name”) |
name | 绑定的请求参数名称 |
required | 用于指定参数是否必须,默认是true,表示请求中一定要有相应的参数 |
default Value | 形参的默认值,表示如果请求中没有同名参数时的默认值 |
案例:
@RequestMapping("/getUserName")
public void getUserName(@RequestParam(value = "name", defaultValue = "xyx") String username) {
System.out.println(username);
}
- @PathVariable注解
作用:当请求的映射方式是REST风格时,,Spring MVC提供了@PathVariable注解,通过 @PathVariable注解可以将URL中占位符参数绑定到处理器的形参中。
属性:
属性 | 说明 |
---|---|
value | 用于指定URL中占位符名称。 |
required | 是否必须提供占位符,默认值为true |
案例:
@RequestMapping("/getUserName/{name}")
public void getUserNameByREST(@PathVariable(value = "name") String username) {
System.out.println(username);
}
如果请求路径中占位符的参数名称和方法形参名称一致,那么@PathVariable注解的value属性可以省略。
- POJO绑定
- 定义:将所有关联的请求参数封装在一个POJO中,然后在方法中直接使用该POJO作为形参来完成数据绑定
案例:
@RequestMapping("/login")
public void login(User user) {
System.out.println("用户名为" + user.getUsername());
System.out.println("密码为" + user.getPassword());
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<form method="post" action="${pageContext.request.contextPath}/login">
用户名:<input name="username" type="text"><br>
密 码:<input name="password" type="password"><br>
<input name="submit" type="submit" value="提交"><br>
</form>
</body>
</html>
- 解决请求参数的中文乱码
原因:由于html或者jsp中指定了请求的编码为utf-8,而Tomcat的解码会以ISO-8859-1解码,所以会出现乱码。
解决方法(POST请求):在前端控制器中(web.xml)添加如下过滤器
<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>
解决方法(GET请求):Tomcat 8后无需配置
String username = new String(user.getUsername().getBytes(“ISO8859-1”),“UTF-8”);,
3.3复杂数据绑定
在开发基于 Spring MVC 的 Web 应用程序时,数据绑定和页面跳转是非常重要的功能。本篇博客将通过一个简单的示例,介绍如何在 Spring MVC 中进行简单和复杂的数据绑定,以及如何在控制器中进行页面跳转。
1. 简单数据绑定
Spring MVC 支持多种方式的数据绑定,最常见的包括:
- 默认数据类型绑定:Spring 自动根据请求参数与方法参数类型进行匹配。
- 简单数据类型绑定:支持直接将表单参数绑定到方法参数。
- 简单POJO的绑定:支持将表单提交的数据绑定到 POJO 对象。
1.1 默认数据类型的绑定
java@RequestMapping("/getUserId")
public void getUserId(HttpServletRequest request) {
String id = request.getParameter("id");
System.out.println(id);
}
在上面的代码中,getUserId
方法通过 HttpServletRequest
获取请求中的参数。
1.2 简单数据类型绑定
java@RequestMapping("/getUserIdAndName1")
public void getUserIdAndName1(String username, int id) {
System.out.println("id为" + id + "的用户名为:" + username);
}
Spring 会自动将请求参数绑定到方法的参数 username
和 id
上。
1.3 简单数据类型与请求参数不一致
java@RequestMapping("/getUserIdAndName2")
public void getUserIdAndName2(@RequestParam(value = "name", defaultValue = "xyx") String username, int id) {
System.out.println("id为" + id + "的用户名为:" + username);
}
通过 @RequestParam
注解,可以将请求中的 name
参数绑定到 username
上,如果请求中没有该参数,则使用默认值 "xyx"
。
1.4 REST 风格的路径变量绑定
java@RequestMapping("/getUserIdAndName3/{name}")
public void getUserIdAndName3(@PathVariable("name") String username) {
System.out.println("用户名为:" + username);
}
使用 @PathVariable
注解,可以将 URL 中的变量 name
绑定到方法的参数 username
上。
2. 复杂数据绑定
Spring MVC 还支持复杂的数据绑定,包括数组、集合、以及复杂的 POJO 对象。
2.1 数组绑定
java@RequestMapping("/getUserArray")
public void getUserArray(String[] nameList) {
for (String name : nameList) {
System.out.println(name);
}
}
通过简单地定义一个 String[]
类型的参数,可以自动绑定请求中的数组数据。
2.2 集合绑定
java@RequestMapping("/getUserList")
public void getUserList(@RequestParam("name") List<String> nameList) {
for (String name : nameList) {
System.out.println(name);
}
}
使用 @RequestParam
可以将请求参数绑定到 List
集合中,Spring 会根据请求的多个参数自动填充集合。
2.3 复杂POJO绑定
java@RequestMapping("/showOrder")
public void showOrder(User user) {
List<Order> orderList = user.getOrderList();
List<String> address = user.getAddress();
for (int i = 0; i < orderList.size(); i++) {
System.out.println("订单号为:" + orderList.get(i).getOrderId() + ",派送地址为:" + address.get(i));
}
}
Spring 会自动将请求中的数据绑定到 User
对象中的 orderList
和 address
字段。
3. 页面跳转
Spring MVC 提供了多种方式进行页面跳转,常见的方式包括:
- 返回值为
void
:在没有视图解析器配置时,跳转到请求路径名对应的视图。 - 返回值为
String
:通过视图解析器配置,跳转到特定的页面。 - 返回值为
ModelAndView
:可以同时设置模型数据和视图。
3.1 返回 void
类型
java@RequestMapping("/register")
public void register1(User user) {
System.out.println("用户名为:" + user.getUsername());
System.out.println("密码为:" + user.getPassword());
}
如果视图解析器配置正确,Spring 会根据请求路径来解析视图名称。如果没有配置视图解析器,会报 500
错误。
3.2 返回 String
类型
java@RequestMapping("/register")
public String register2(User user) {
System.out.println("用户名为:" + user.getUsername());
System.out.println("密码为:" + user.getPassword());
return "login"; // 跳转到 login.jsp 页面
}
如果配置了视图解析器,返回值 "login"
会被解析为 login.jsp
页面。
3.3 请求转发
java@RequestMapping("/register")
public String register3(User user) {
System.out.println("用户名为:" + user.getUsername());
System.out.println("密码为:" + user.getPassword());
return "forward:Jsp/login.jsp"; // 请求转发到 login.jsp 页面
}
forward:
前缀表示请求转发,Spring 会将请求转发到指定的 JSP 页面。
3.4 重定向
java@RequestMapping("/login")
public String login(User user) {
System.out.println("用户名为:" + user.getUsername());
System.out.println("密码为:" + user.getPassword());
return "redirect:https://cat.chatavx.com/#/home"; // 重定向到外部页面
}
redirect:
前缀表示重定向,Spring 会将请求重定向到指定的 URL。
4. 携带数据到页面
4.1 使用 HttpServletRequest
携带数据
java@RequestMapping("/showPageByRequest")
public String showPageByRequest(HttpServletRequest request) {
request.setAttribute("username", "解耀轩");
return "showPage";
}
通过 HttpServletRequest
,可以将数据添加到请求范围,然后在 JSP 页面中使用该数据。
4.2 使用 Model
携带数据
java@RequestMapping("/showPageByModel")
public String showPageByModel(Model model) {
model.addAttribute("username", "杨浩文");
return "showPage";
}
通过 Model
对象可以将数据添加到模型中,并且在视图页面中访问。
4.3 使用 ModelAndView
携带数据
java@RequestMapping("/showPageByModelAndView")
public ModelAndView showPage() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("username", "太原理工大学");
modelAndView.setViewName("showPage");
return modelAndView;
}
ModelAndView
可以同时设置数据和视图,常用于需要返回数据和视图的场景。
pojo层
package com.tyut.pojo;
import java.util.Map;
public class Order {
private int orderId;
private Map<String, String> orderMap;
public Map<String, String> getOrderMap() {
return orderMap;
}
public void setOrderMap(Map<String, String> orderMap) {
this.orderMap = orderMap;
}
public int getOrderId() {
return orderId;
}
public void setOrderId(int orderId) {
this.orderId = orderId;
}
}
package com.tyut.pojo;
import java.util.List;
public class User {
private String username;
private String password;
private List<Order> orderList;
private List<String> address;
public List<Order> getOrderList() {
return orderList;
}
public void setOrderList(List<Order> orderList) {
this.orderList = orderList;
}
public List<String> getAddress() {
return address;
}
public void setAddress(List<String> address) {
this.address = address;
}
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;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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">
<!-- 配置Spring MVC要扫描的包 -->
<context:component-scan base-package="com.tyut.controller"/>
<!-- 开启视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
<?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">
<!-- 配置DispatcherServlet -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 设置初始化参数,使得可以在此Servlet加载的时候便可以假造到spring-mvc配置 -->
<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>
<!--设置DispatcherServlet要拦截的路径-->
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 解决中文乱码 -->
<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-app>