一、Spring MVC概述
Spring MVC 是由Spring官方提供的基于MVC设计理念的web框架。
Spring MVC 是基于Servlet封装的用于实现MVC控制的框架,实现前端和服务端的交互。
1.1 Spring MVC优势
1.严格遵守了MVC分层思想
2.采用了松耦合、插件式结构,更具扩展性。
3.SpringMVC是基于Spring的扩展、提供了一套完善的MVC注解
4.SpringMVC对RESful URL设计方法提供了良好的支持
1.2 SpringMVC本质工作
1.接收并解析请求
2.处理请求
3.数据渲染、响应请求
二、SpringMVC框架部署
2.1 基于Maven创建一个web工程
2.2 添加SpringMVC依赖
spring-context
spring-aspects
spring-jdbc
spring-web
spring-webmvc
spring-junit
<properties>
<spring.version>5.2.13.RELEASE</spring.version>
</properties>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
</dependencies>
2.3 创建SpringMVC配置文件
1.在resource目录下创建 名为 spring-servlet.xml文件
2.添加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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd" >
<context:annotation-config/>
<context:component-scan base-package="com.lxr."/>
<!-- 声明MVC使用注解驱动-->
<mvc:annotation-driven/>
</beans>
2.4 在web.xml中配置SpringMVC的前端控制器
SpringMVC提供了一个名为DispatcherServlet的类(SpringMVC中央处理器,前端控制器),用于拦截用户请求交由SpringMVC处理
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>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
三、SpringMVC框架使用
在SpringMVC中,我们把接收用户请求,处理用户请求的类称之为Controller(控制器)
3.1 控制器
3.1.1 创建控制器类
就是接收请求的类
1.创建一个名为com.lxr.controllers的包(需要在Spring注解扫描的范围内)
2.创建一个类(无需做任何的继承和实现)
3.在类上添加@Controller
注解声明此类为SpringMVC的控制器
4.在类上添加@RequestMapping("url")
声明此控制器类的请求url
@Controller
@RequestMapping("/book")
public class BookController {
}
3.1.2 在控制器类中定义处理请求的方法
1.在一个控制器类中可以定义多个方法处理不同的请求
2.在每个方法上添加@RequestMapping("url")
用于声明当前方法请求的url
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public void add(){
System.out.println("book add");
}
@RequestMapping("/list")
public void list(){
System.out.println("book list");
}
}
3.1.3 访问
http://localhost:8080/springmvc_demo2/book/add
http://localhost:8080/springmvc_demo2/book/list
3.2 前端提交数据到控制器
3.2.1 创建前端页面
book-add.jsp 表单action属性设置为控制类的url和对应方法的组合路径
<%--
Created by IntelliJ IDEA.
User: lixingrui
Date: 2022/1/6
Time: 17:04
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>
<h3>添加图书</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text"/></p>
<p>图书作者:<input type="text"/></p>
<p>图书价格:<input type="text"/></p>
<p><input type="submit" value="提交"/></p>
</form>
</body>
</html>
3.2.2 /*和/的区别
/* 表示拦截所有的http请求,包括jsp的请求,都作为控制器类的请求路径来处理
/ 表示拦截所有的http请求,不包括jsp的请求,但不会放行静态资源请求(html,css,js,图片)
3.2.3 静态资源放行配置
在springMVC的配置文件,添加如下静态资源放行的配置:
<!-- 配置静态资源放行 -->
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/pages/**" location="/pages/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/imgs/**" location="/imgs/"/>
3.2.4 前端页面提交数据
1.表单提交:输入框需要提供name属性,SpringMVC控制器是通过name属性取值的
<body>
<h3>添加图书</h3>
<form action="book/add" method="post">
<p>图书名称:<input type="text" name="bookName"/></p>
<p>图书作者:<input type="text" name="bookAuthor"/></p>
<p>图书价格:<input type="text" name="bookPrice"/></p>
<p><input type="submit" value="提交"/></p>
</form>
</body>
2.超链接(url)提交:<a href="book/add?bookName=Java">
3.异步提交(AJAX)提交:请求行,请求头,请求体都可以用来传值
3.3 控制器接收前端提交的数据
3.3.1 请求行传值
表单提交
url提交
$.ajax()请求的url传值
$.post()/$.get中的{}传值
@RequestParam注解用于接收请求行传递的数据
前端代码:
<form action="book/add" method="post">
<p>图书名称:<input type="text" name="bookName"/></p>
<p>图书作者:<input type="text" name="bookAuthor"/></p>
<p>图书价格:<input type="text" name="bookPrice"/></p>
<p><input type="submit" value="提交"/></p>
</form>
@RequestParam注解用于接收请求行传递的数据
@RequestMapping("/add")
public void add(@RequestParam("bookName") String a,@RequestParam("bookAuthor") String b,@RequestParam("bookPrice") double c){
System.out.println("book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
注意如果控制器方法中接收数据的参数名与请求行传递的key一致,则注释可以省略
public void add(String bookName,String bookAuthor,double bookPrice){}
3.3.2 请求头传值
ajax封装请求头数据
$.ajax({
...
headers:{
},
...
})
3.3.3 请求体传值
ajax封装请求体数据
$.ajax({
...
data:{
},
...
})
3.4 控制器响应前端请求
3.4.1 控制器响应同步请求
同步请求:form,超链接
1.处理同步请求的方法的返回类型定义为String或者ModelAndView,以实现页面的跳转
2.返回类型为String时,直接return页面
请求跳转(转发)
@RequestMapping("/add")
public String add(@RequestParam("bookName") String a, @RequestParam("bookAuthor") String b, @RequestParam("bookPrice") double c){
System.out.println("book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
return "/tips.jsp";
}
重定向:
@RequestMapping("/add")
public String add(@RequestParam("bookName") String a, @RequestParam("bookAuthor") String b, @RequestParam("bookPrice") double c){
System.out.println("book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
return "redirect:/tips.jsp";
}
3.返回类型为ModelAndView时,创建ModelAndView对象,返回ModelAndView对象
请求跳转
@RequestMapping("/add")
public ModelAndView add(@RequestParam("bookName") String a, @RequestParam("bookAuthor") String b, @RequestParam("bookPrice") double c){
System.out.println("book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
ModelAndView modelAndView = new ModelAndView("/tips.jsp");
return modelAndView;
}
重定向
@RequestMapping("/add")
public ModelAndView add(@RequestParam("bookName") String a, @RequestParam("bookAuthor") String b, @RequestParam("bookPrice") double c){
System.out.println("book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
ModelAndView modelAndView = new ModelAndView("redirect:/tips.jsp");
return modelAndView;
}
3.5 控制器传递数据
对于同步请求的转发响应,我们可以传递参数到转发的页面
3.5.1 返回类型为String
1.在控制器方法中定义一个Model类型的参数
2.在return页面之前,想model中添加键值对,添加的键值对就会被传递到转发的页面
@RequestMapping("/add")
public String add(String bookName, String bookAuthor, double bookPrice, Model model){
System.out.println("book add");
model.addAttribute("key1","values1");
model.addAttribute("key2","values2");
return "/tips.jsp";
}
除了使用Model对象,还可以直接使用HttpServletRequest对象
@RequestMapping("/add")
public String add(String bookName, String bookAuthor, double bookPrice, HttpServletRequest request){
System.out.println("book add");
request.setAttribute("key1","values1");
request.setAttribute("key2","values2");
return "/tips.jsp";
}
3.5.2 返回类型为ModelAndView
@RequestMapping("/add")
public ModelAndView add(String bookName, String bookAuthor, double bookPrice){
System.out.println("book add");
ModelAndView modelAndView = new ModelAndView("/tips.jsp");
modelAndView.addObject("key1","value1");
modelAndView.addObject("key2","value2");
return modelAndView;
}
3.6 解决中文乱码问题
3.6.1 前端编码
1.JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
2.HTML页面
<meta charset="UTF-8">
3.6.2 服务器编码
tomcat/conf/server.xml
URIEncoding="UTF-8"
3.6.3 设置SpringMVC的编码方式
在web.xml中配置SrpingMVC编码过滤器的编码方式
<filter>
<filter-name>EncodingFilter</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>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.7 在控制器方法中使用对象接收数据
@RequestMapping("/add")
//表单提交的多个数据,在控制器方法中,可以使用对象接收,
//但是提交的数据的key必须与对象的属性名一致
public String addBook(Book book){
return "/tips.jsp";
}
四、SpringMVC的请求处理流程(原理)
4.1 SpringMVC通过前端控制器(DispatcherServlet)拦截并处理用户请求的
4.2 SpringMVC的核心组件
4.2.1 DispatcherServlet 前端控制器、总控制器
由SpringMVC提供,无需工程师开发
作用:接收请求,协同各个组件工作、响应请求
4.2.2 HandlerMapping 处理器映射
由SpringMVC提供
作用:负责根据用户请求的url找到对应的Handler
可配置 SpringMVC提供多个处理器映射的实现,可以根据需要进行配置
4.2.3 HanlerAdapter 处理器适配器
作用:按照处理器映射器解析的用户请求的调用链,通过适配器模式完成Handeler的调用
4.2.4 Hanlder 处理器、控制器
由工程师根据业务的需求进行开发
作用:处理请求
4.2.5 ModelAndView 视图模型
作用:用于封装处理器返回的数据以及相应的视图
ModelAndViex=Model + View
4.2.6 ViewResolve 视图解析器
作用:对ModelAndView进行解析
可配置 SpringMVC提供多个视图解析器的实现,可以根据需要进行配置
4.2.7 View 视图
作用:完成数据渲染
4.3 处理器映射器
不同的处理器映射器对URL处理的方式也不同,使用对应的处理器映射器之后我们的前端请求规则也许呀发生相应的变化
SpringMVC提供的处理器映射器:
BeanNameUrlHandlerMapping 根据控制器的ID来访问控制器
SimpleUrlHandlerMapping 根据控制器配置的URL返回(默认)
4.3.1 配置处理器映射器
在SpringMVC的配置文件中通过bean标签声明处理器映射器
1.配置 BeanNameUrlHandlerMapping
在spring-servlet.xml中配置
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
4.4 视图解析器
五、日期格式处理
5.1 日期格式处理
如果前端需要输入日期数据,在控制器中转换成Date对象,SpringMVC要求前端输入的日期格式必须为
yyyy/MM/DD
输入日期:2022/01/07
5.2 自定义日期转换器
5.2.1 创建自定义日期转换器
//1.创建一个类实现Converter接口,泛型指定从什么类型转换为什么类型
//2.实现convert转换方法
public class MyDateConverter implements Converter<String, Date> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
@Override
public Date convert(String s) {
Date date = null;
try {
date = sdf.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
5.2.2 配置自定义转换器
<mvc:annotation-driven conversion-service="conversionFactory"/>
<bean id="conversionFactory" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.lxr.utils.MyDateConverter"/>
</set>
</property>
</bean>
六、文件上传
6.1 SpringMVC框架部署
6.2 文件上传
添加图书,同时提交图书的封面
6.2.1 前端提交文件
1.表单提交方式必须为post
2.表单enctype属性设置为multipart/form-data
6.2.2 控制器接收数据和文件
SpringMVC处理上传文件需要借助于CommonsMutipartResolver文件解析器
6.2.2.1添加依赖
commons-is
commons-fileupload
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
6.2.2.2 在spring-servlet.xml中配置文件解析器
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--设置文件大小 字节-->
<property name="maxUploadSize" value="1024000"></property>
<property name="maxInMemorySize" value="102400"></property>
<property name="defaultEncoding" value="utf-8"></property>
</bean>
6.2.2.3 控制器接收文件
在处理文件上传的方法中,定义一个MultiPartFile类型的对象,就可以接收图像
package com.lxr.controller;
import com.lxr.beans.Book;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public String addBook(Book book, MultipartFile imgFile, HttpServletRequest request) throws IOException {
//1.截取上传文件的后缀名,生产新的文件名
String originalFilename = imgFile.getOriginalFilename();
String ext = originalFilename.substring(originalFilename.lastIndexOf("."));//.jpg
String fileName = System.currentTimeMillis() + ext;
String dir = request.getServletContext().getRealPath("imgs");
System.out.println(dir);
String savePath = dir + "\\" + fileName;
System.out.println(savePath);
//3.保存文件
imgFile.transferTo(new File(savePath));
//4.将图片的访问路径设置到book对象
book.setBookImg("imgs\\" + fileName);
System.out.println(book.getBookName() + " " +book.getBookImg());
//5.调用service保存book到数据库
return "/tips.jsp";
}
}
6.2.3 显示文件列表
@RequestMapping("/list")
@ResponseBody
public String[] listImgs(HttpServletRequest request){
//1.从imgs目录下获取所有的图片信息
String dir = request.getServletContext().getRealPath("imgs");
File imgDir = new File(dir);
String[] fileName = imgDir.list();
return fileName;
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<!-- 新 Bootstrap4 核心 CSS 文件 -->
<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.1.0/css/bootstrap.min.css" rel="external nofollow" target="_blank" >
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js" rel="external nofollow" ></script>
<!-- popper.min.js 用于弹窗、提示、下拉菜单 -->
<script src="https://cdn.bootcss.com/popper.js/1.12.5/umd/popper.min.js" rel="external nofollow" ></script>
<!-- 最新的 Bootstrap4 核心 JavaScript 文件 -->
<script src="https://cdn.bootcss.com/bootstrap/4.1.0/js/bootstrap.min.js" rel="external nofollow" ></script>
</head>
<body>
<h4>图片列表</h4>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<div class="row" id="container"></div>
<script type="text/javascript">
$.get("book/list",function (res){
for(var i = 0;i < res.length;i++){
var fn = res[i];
var htmlStr = "<div class='col-lg-2 col-md-3 col-sm-4 col-xs-6'><div class='thumbnail'><img src='imgs\\"+fn+"' alt='...'> <div class='caption'> <p><a href='book/download?fileName="+fn+"' class='btn btn-primary' role='button'>下载</a></p></div></div></div>";
$("#container").append(htmlStr);
}
},"json");
</script>
</body>
</html>
6.3 文件下载
@RequestMapping("/download")
public void downloadImg(String fileName, HttpServletRequest request, HttpServletResponse response) throws Exception {
//从imgs目录找到当前文件
String dir = request.getServletContext().getRealPath("imgs");
String filePath = dir + "\\" + fileName;
FileInputStream fileInputStream = new FileInputStream(filePath);
response.setContentType("application/pdf");
response.addHeader("Content-Disposition","attachment;filename=" + fileName);
IOUtils.copy(fileInputStream,response.getOutputStream());
}
七、统一异常处理
在我们的应用系统运行的过程中、可能由于运行环境、用户操作、资源不足等各方面原因引起系统异常,导致系统出现异常(HTTP状态异常,Java代码异常 Exception);如果系统出现异常,这些异常将会通过浏览器反馈给用户
7.1 HTTP异常状态统一处理
HTTP status 404
1.创建一个用于异常提示的页面:404.jsp
2.在web.xml进行配置
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
7.2 Java代码异常的统一处理
7.2.1 基于Servlet-api的处理
<error-page>
<exception-type>java.lang.NumberFormatException</exception-type>
<location>/err.jsp</location>
</error-page>
7.2.2 基于SpringMVC
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(NullPointerException.class)
//异常处理器
public String nullHandle(){
return "/err.jsp"
}
...
}
八、拦截器
8.1 拦截器介绍
SpringMVC提供的拦截器就类似于Servlet-api中的过滤器,可以对控制器的请求进行拦截实现相关的预处理和后处理。
过滤器
是Servlet规范的一部分,所有的web项目都可以使用
过滤器在web.xml配置(可以使用注解),能够拦截所有web请求
拦截器
是SpringMVC框架的实现,只有在SpringMVC框架中才能使用
拦截器在springMVC配置文件进行配置,不会拦截SpringMVC放行的资源
8.2 拦截器配置
1.创建拦截器MyInterceptor1
public class MyInterceptor1 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("---------预处理");
Enumeration<String> keys = request.getParameterNames();
while(keys.hasMoreElements()){
String key = keys.nextElement();
if("bookId".equals(key)){
return true;
}
}
response.setStatus(400);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("tips","这是通过拦截器后处理添加的数据")
System.out.println("----------后处理");
}
}
2.配置拦截器 springmvc-servlet
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/book/query"/>
<mvc:mapping path=""/>
<!--可以调用多个拦截对象-->
<mvc:mapping path="/student/**"/>
拦截所有
<mvc:exclude-mapping path="/student/add"/>
除了student/add
<!---->
<bean class="com.lxr.utils.MyInterceptor1"/>
<!-- 当前拦截器要拦截 book/query -->
</mvc:interceptor>
</mvc:interceptors>
8.3 拦截器链
将多个拦截器按照一定的顺序,构成一个执行链
配置顺序 拦截器1 拦截器2
执行顺序 预处理1 预处理2 后处理2 后处理1