一,概述
SpringMVC是基于Servlet封装的⽤于实现MVC控制的框架,实现前端和服务端的交互。
SpringMVC优势
•严格遵守了MVC分层思想
•采⽤了松耦合、插件式结构;相⽐较于我们封装的BaseServlet以及其他的⼀些MVC框架来说更灵 活、更具扩展性
•SpringMVC是基于Spring的扩展、提供了⼀套完善的MVC注解
•SpringMVC在数据绑定、视图解析都提供了多种处理⽅式,可灵活配置
•SpringMVC对RESTful URL设计⽅法提供了良好的⽀持
SpringMVC本质⼯作
•接收并解析请求
•处理请求
•数据渲染、响应请求
二,框架部署
1.基于Maven创建⼀个web⼯程
•SpringMVC是⼀个web框架,应⽤在web⼯程中(还需要添加spring框架)
2.添加SpringMVC依赖
•spring-context
•spring-aspects
•spring-jdbc
•spring-test
•spring-web
•spring-webmv
<properties>
<spring.version>5.2.13.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</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>
</dependencies>
3.创建SpringMVC配置⽂件
•在resources⽬录下创建名为 spring-servlet.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: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">
<!--IoC采⽤注解配置-->
<context:annotation-config/>
<context:component-scan base-package="com.renzhe"/>
<!-- 声明MVC使⽤注解驱动 -->
<mvc:annotation-driven/>
</beans>
4.在web.xml中配置SpringMVC的前端控制器
SpringMVC提供了⼀个名为DispatcherServlet的类(SpringMVC前端控制器),⽤于拦截⽤⼾请求 交由SpringMVC处理
<?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_3_1.xsd"
version="3.1">
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass>
<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框架使用
1.创建控制器
•创建⼀个名为 com.qfedu.controllers 的包(包需要在Spring注解扫描的范围内)
•创建⼀个类(⽆需做任何的继承和实现)
•在类上添加 @Controller 注解声明此类为SpringMVC的控制器
•在类上添加 @RequestMapping("url") 声明此控制器类的请求url(可以省略)
@Controller
@RequestMapping("/book")
public class BookController {
}
2.在控制器类中定义处理请求的方法
•在⼀个控制器类中可以定于多个⽅法处理不同的请求
•在每个⽅法上添加 @RequestMapping("url") ⽤于声明当前⽅法请求的url
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public void addBook(){
System.out.println("---book akdd");
}
@RequestMapping("/list")
public void listBooks(){
System.out.println("---book list");
}
}
3.静态资源配置
静态资源:就是项⽬中的HTML、 css、 js、图⽚、字体等
1. /* 和 / 的区别
•/* 拦截所有的HTTP请求,包括.jsp的请求,都做为控制器类的请求路径来处理
•/ 拦截所有的HTTP请求,但不包括.jsp的请求,不会放⾏静态资源的请求(html/css/js/图⽚)
2.静态资源放⾏配置
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/imgs/**" location="/imgs/"/>
<mvc:resources mapping="/pages/**" location="/pages/"/>
4.控制器接收前端提交的数据
1.@RequestParam 接收请求⾏传值
•表单提交
•URL提交
•$.ajax()请求的url传值
•$.post()/$.get()中的{}传值
前端:
<form action="book/add" method="post">
<p>图书名称:<input type="text" name="name"/></p>
<p>图书作者:<input type="text" name="author"/></p>
<p>图书价格:<input type="text" name="price"/></p>
<p><input type="submit" value="提交"/></p>
</form>
后端:
@RequestMapping("/add")
public void addBook(@RequestParam("name") String a,
@RequestParam("author") String b,
@RequestParam("price") double c){
System.out.println("---book add");
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
//注意 如果控制器⽅法中接收数据的参数名与请求⾏传值的key⼀致,则@RequestParam注解可省略Java
@RequestMapping("/add")
public void addBook(String name,String author, double price){
System.out.println("---book add");
System.out.println(name);
System.out.println(author);
System.out.println(price);
}
//注意 如果控制器⽅法中接收数据的参数名与请求⾏传值的key⼀致,则@RequestParam注解可省略Java
2.@RequestHeader接收请求头传值
前端:
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function(){
$.ajax({
url:"book/list",
type:"post",
headers:{
token:"wahahaawahaha"
},
success:function(res){
console.log(res);
}
});
});
</script>
后端:
@RequestMapping("/list")
public void listBooks(@RequestHeader("token") String token){
System.out.println("---book list");
}
3.@RequestBody接收请求体传值
前端:
<input type="button" value="ajax提交" id="btn1"/>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$("#btn1").click(function(){
var obj = {};
obj.bookName = "Python";
obj.bookAuthor="杰哥";
obj.bookPrice = 2.22;
var s = JSON.stringify(obj); //将对象转换成JSON格式
$.ajax({
url:"book/update",
type:"post",
contentType:"application/json",
data:s, //如果data的值为json格式字符串, contentType必须设置为"application/json"
success:function(res){
console.log(res);
}
});
});
</script>
需要依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
后端:
@RequestMapping("/update")
public void update(@RequestBody Book book){
System.out.println("---book update");
System.out.println(book);
}
5.控制器响应前端请求
同步和异步请求的主要区别:
- 响应方式:同步请求需要等待服务器返回响应后才能继续执行,而异步请求可以在发送请求后立即执行后续操作。
- 线程阻塞:同步请求会阻塞客户端的线程,而异步请求不会阻塞客户端的线程,可以提高并发性能。
- 响应处理:同步请求通过获取服务器返回的响应来处理结果,而异步请求可以通过回调函数或事件处理程序来处理服务器返回的响应。
1.控制器响应同步请求
同步请求: form、超链接 处理同步请求的⽅法的返回类型定义为String或者ModelAndView,以实现⻚⾯的跳转
•返回类型为String
转发
@RequestMapping("/add")
public String addBook(String name, String author, double price){
System.out.println("---book add");
return "/tips.jsp";
}
重定向
//重定向
TypeScript
@RequestMapping("/add")
public String addBook(String name, String author, double price){
System.out.println("---book add");
return "redirect:/tips.jsp";
}
•返回类型为 ModelAndView
转发
//转发
TypeScript
@RequestMapping("/add")
public ModelAndView addBook(String name, String author, double price){
System.out.println("---book add");
ModelAndView modelAndView = new ModelAndView("/tips.jsp");
return modelAndView;
}
重定向
@RequestMapping("/add")
public ModelAndView addBook(String name, String author, double price){
System.out.println("---book add");
ModelAndView modelAndView = new ModelAndView("redirect:/tips.jsp");
return modelAndView;
}
2.控制器响应异步请求
异步请求: ajax请求
使⽤response中的输出流进⾏响应
•控制器⽅法的返回类型为 void
•控制器⽅法添加 HttpServletResponse response 参数
•在⽅法中通过response获取输出流,使⽤流响应ajax请求
@RequestMapping("/update")
public void update(@RequestBody Book book, HttpServletResponse response)
throws IOException {
System.out.println("---book update");
System.out.println(book);
//使⽤ObjectMapper将对象转换成JSON格式字符串
String s = new ObjectMapper().writeValueAsString(book);
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter out = response.getWriter();
out.println(s);
out.flush();
out.close();
}
直接在控制器⽅法返回响应的对象
•控制器⽅法的返回类型设置为响应给ajax请求的对象类型
•在控制器⽅法前添加 @ResponseBody 注解,将返回的对象转换成JSON响应给ajax请求
•如果⼀个控制器类中的所有⽅法都是响应ajax请求,则可以直接在控制器类前添加
@RequestMapping("/update")
@ResponseBody
public List<Book> update() {
System.out.println("---book update");
List<Book> books = new ArrayList<Book>();
books.add(new Book(1,"Java","⽼张",2.22));
books.add(new Book(2,"C++","⽼李",3.22));
return books;
}
3.控制器响应同步请求的数据传递
对于同步请求的转发响应,我们可以传递参数到转发的⻚⾯
•返回类型为String:
//1.在控制器⽅法中定义⼀个Model类型的参数
//2.在return⻚⾯之前,向model中添加键值对,添加的键值对就会被传递到转发的⻚⾯
@RequestMapping("/add")
public String addBook(String name, String author, double price,Model model){
model.addAttribute("key1","value1");
model.addAttribute("book",new Book(1,"Java","⽼张",2.22));
return "/tips.jsp";
}
//除了使⽤Model对象传值外,还可以直接使⽤HttpServletRequest对象
@RequestMapping("/add")
public String addBook(String name, String author, double
price,HttpServletRequest request){
request.setAttribute("key1","value1");
request.setAttribute("book",new Book(1,"Java","⽼张",2.22));
return "/tips.jsp";
}
•返回类型ModelAndView:
@RequestMapping("/add2")
public ModelAndView addBook2(String name, String author, double price){
ModelAndView modelAndView = new ModelAndView("/tips.jsp");
modelAndView.addObject("key1","value1");
modelAndView.addObject("book",new Book(1,"Java","⽼张",2.22));
return modelAndView;
}
6. 解决中文乱码问题
1.前端页面
//JSP⻚⾯:
<%@ page contentType="text/html;charset=UTF-8" language="java"
pageEncoding="UTF-8" %>
//HTML⻚⾯:
<meta charset="UTF-8">
2.服务器编码
•tomcat/conf/server.xml
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" URIEncoding="UTF-8"/>
3.设置SpringMVC的编码⽅式
•在web.xml中配置SpringMVC编码过滤器的编码⽅式
<filter>
<filter-name>EncodingFilter</filter-name>
<filterclass>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>
四,SpringMVC的请求处理流程
1.请求处理流程
① 前端发送请求被前端控制器DispatcherServlet拦截
② 前端控制器调⽤处理器映射器HandlerMapping对请求URL进⾏解析,解析之后返回调⽤给前端控制器
③ 前端控制器调⽤处理器适配器处理调⽤链
④ 处理器适配器基于反射通过适配器设计模式完成处理器(控制器)的调⽤处理⽤⼾请求
⑤ 处理器适配器将控制器返回的视图和数据信息封装成ModelAndView响应给前端控制器
⑥ 前端控制器调⽤视图解析器ViewResolver对ModelAndView进⾏解析,将解析结果(视图资源和 数据)响应给前端控制器
⑦ 前端控制器调⽤视图view组件将数据进⾏渲染,将渲染结果(静态视图)响应给前端控制器
⑧ 前端控制器响应⽤⼾请求
2.SpringMVC的核⼼组件
•DispatcherServlet 前端控制器、总控制器
◦ 作⽤:接收请求,协同各组件⼯作、响应请求
•HandlerMapping 处理器映射
◦ 作⽤:负责根据⽤⼾请求的URL找到对应的Handler
◦ 可配置 SpringMVC提供多个处理器映射的实现,可以根据需要进⾏配置
•HandlerAdapter 处理器适配器
◦ 作⽤:按照处理器映射器解析的⽤⼾请求的调⽤链,通过适配器模式完成Handler的调⽤ •Handler 处理器/控制器
◦ 由⼯程师根据业务的需求进⾏开发
◦ 作⽤:处理请求 •ModelAndView 视图模型
◦ 作⽤:⽤于封装处理器返回的数据以及相应的视图
◦ ModelAndView = Model + View
•ViewResolver 视图解析器
◦ 作⽤:对ModelAndView进⾏解析
◦ 可配置 SpringMVC提供多个视图解析器的实现,可以根据需要进⾏配置
•View 视图
◦ 作⽤:完成数据渲染
3.处理器映射器
不同的处理器映射器对URL处理的⽅式也不相同,使⽤对应的处理器映射器之后我们的前端请求规则 也需要发⽣相应的变化
SpringMVC提供的处理器映射器:
•BeanNameUrlHandlerMapping 根据控制器的ID访问控制器
•SimpleUrlHandlerMapping 根据控制器配置的URL访问(默认)
配置处理器映射器:
•在SpringMVC的配置⽂件中通过bean标签声明处理器映射器
•配置BeanNameUrlHandlerMapping
<bean
class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
</bean>
•配置SimpleUrlHandlerMapping
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/aaa">bookController</prop>
<prop key="/bbb">studentController</prop>
</props>
</property>
</bean>
4.视图解析器
spring提供了多个视图解析器:
◦ UrlBasedViewResolver
◦ InternalResourceViewResolver
•UrlBasedViewResolver 需要依赖jstl
◦ 添加JSTL的依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
◦ 配置视图解析器
<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
•InternalResourceViewResolver
<bean
id="viewResolver"class="org.springframework.web.servlet.view.InternalResour
ceViewResolver">
<property name="prefix" value="/"/>
<property name="suffix" value=".jsp"/>
</bean>
五,⽇期格式处理
如果前端需要输⼊⽇期数据,在控制器中转换成Date对象, SpringMVC要求前端输⼊的⽇期格式必 须 为 yyyy/MM/dd
如果甲⽅要求⽇期格式必须为指定的格式,⽽这个指定格式SpringMVC不接受
1.创建⾃定义⽇期转换器
public class MyDateConverter implements Converter<String, Date> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM⽉dd⽇");
public Date convert(String s) {
Date date = null;
try {
date = sdf.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
}
2.配置⾃定义转换器
<mvc:annotation-driven conversion-service="converterFactory"/>
<bean id="converterFactory"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.renzhe.utils.MyDateConverter"/>
</set>
</property>
</bean>
六,文件上传下载
1.SpringMVC框架部署
•基于Maven创建web⼯程
•添加SpringMVC所需的依赖
◦ Spring:context aspects jdbc test web webmvc jackson
•创建SpringMVC配置⽂件
•在web.xml中配置SpringMVC的前端控制器
•配置springmvc静态资源处理策略
2.文件上传
1.前端提交文件
•表单提⽰⽅式必须为post
•表单enctype属性设置为 multipart/form-data
<form action="book/add" method="post" enctype="multipart/form-data">
<p>图书名称:<input type="text" name="bookName"/></p>
<p>图书作者:<input type="text" name="bookAuthor"/></p>
<p>图书价格:<input type="text" name="bookPrice"/></p>
<p>图书封⾯:<input type="file" name="imgFile"/></p>
<p><input type="submit" value="提交"/></p>
</form>
2.控制器接收数据和文件
SpringMVC处理上传⽂件需要借助于CommonsMultipartResolver⽂件解析器
•添加依赖: commons-io 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>
•在spring-servlet.xml中配置⽂件解析器
<bean
id="multipartResolver"class="org.springframework.web.multipart.commons.Commons
MultipartResolver">
<property name="maxUploadSize" value="10240000"/
<property name="maxInMemorySize" value="102400"/>
<property name="defaultEncoding" value="utf-8"/>
</bean>
•控制器接收⽂件
◦ 在处理⽂件上传的⽅法中定义⼀个MultiPartFile类型的对象,就可以接受图⽚了
@Controller
@RequestMapping("/book")
public class BookController {
@RequestMapping("/add")
public String addBook(Book book, MultipartFile imgFile,
HttpServletRequestrequest) throws IOException {
System.out.println("--------------add");
//imgFile就表⽰上传的图⽚
//1.截取上传⽂件的后缀名,⽣成新的⽂件名
String originalFilename = imgFile.getOriginalFilename();
String ext = originalFilename.substring(originalFilename.lastIndexOf(".") );
String fileName = System.currentTimeMillis()+ext;
//2.获取imgs⽬录在服务器的路径
String dir = request.getServletContext().getRealPath("imgs");
String savePath = dir+"/"+fileName;
//3.保存⽂件
imgFile.transferTo( new File(savePath));
//4.将图⽚的访问路径设置到book对象
book.setBookImg("imgs/"+fileName);
//5.调⽤service保存book到数据库
return "/tips.jsp";
}
}
3.文件下载
1.显示文件列表
•list.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<!-- 最新版本的 Bootstrap 核⼼ CSS ⽂件 -->
<link rel="stylesheet"href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"integrity="sha384-
BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
<!-- 可选的 Bootstrap 主题⽂件(⼀般不⽤引⼊) -->
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-them
e.min.css"
integrity="sha384-
rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp"
crossorigin="anonymous">
<!-- 最新的 Bootstrap 核⼼ JavaScript ⽂件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"crossorigin="anonymous">
</script>
<body>
<h4>⽂件列表</h4>
<div class="row" id="container"></div>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
<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='#'
class='btn btn-primary' role='button'>下载</a></p></div> </div>
</div>";
$("#container").append(htmlStr);
}
},"json");
</script>
</body>
</html>
•BookController
@RequestMapping("/list")
@ResponseBody
public String[] listImgs(HttpServletRequest request){
//从imgs⽬录下获取所有的图⽚信息
String dir = request.getServletContext().getRealPath("imgs");
File imgDir = new File(dir);
String[] fileNames = imgDir.list();
return fileNames;
}
2.实现⽂件下载
•list.jsp
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?
fname="+fn+"' class='btn btn-primary' role='button'>下载</a></p></div> </div>
</div>";
•BookController
@RequestMapping("/download")
public void downloadImg(String fname, HttpServletRequest request,
HttpServletResponse response) throws Exception {
//从imgs⽬录找到当前⽂件
String dir = request.getServletContext().getRealPath("imgs");
String filePath = dir+"/"+fname;
FileInputStream fileInputStream = new FileInputStream(filePath);
response.setContentType("application/exe");
response.addHeader("Content-Disposition","attachment;filename="+fname);
IOUtils.copy(fileInputStream, response.getOutputStream());
}
七,统⼀异常处理
1. HTTP异常状态统⼀处理
HTTP Status 404
•创建⼀个⽤于进⾏异常提⽰的⻚⾯: 404.jsp
•在web.xml中进⾏配置:
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>
2.Java代码异常的统⼀处理
1.基于Servlet-api的处理
•建异常提⽰⻚⾯: err.jsp
•在web.xml中进⾏配置
<error-page>
<exception-type>java.lang.NumberFormatException</exception-type>
<location>/err.jsp</location>
</error-page>
2.SpringMVC处理
•使⽤异常处理类进⾏统⼀处理
@ControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(NullPointerException.class)
public String nullHandler(){
return "/err1.jsp";
}
@ExceptionHandler(NumberFormatException.class)
public String formatHandler(){
return "/err2.jsp";
}
}