├── .gitignore ├── README.md ├── SpringMVC_Annotaion ├── pom.xml └── src │ └── main │ ├── java │ └── top │ │ └── mowang │ │ ├── config │ │ ├── SpringConfig.java │ │ ├── WebConfig.java │ │ └── WebInit.java │ │ ├── controller │ │ └── TestController.java │ │ └── interceptor │ │ └── Interceptor.java │ └── webapp │ └── WEB-INF │ └── templates │ ├── error.html │ ├── hello.html │ └── index.html ├── SpringMVC_HelloWorld ├── pom.xml └── src │ └── main │ ├── java │ └── top │ │ └── mowang │ │ └── controller │ │ └── HelloController.java │ ├── resources │ └── springMVC.xml │ └── webapp │ └── WEB-INF │ ├── templates │ ├── index.html │ └── target.html │ └── web.xml ├── SpringMVC_MVC ├── pom.xml └── src │ └── main │ ├── java │ └── top │ │ └── mowang │ │ ├── controller │ │ ├── ExceptionController.java │ │ ├── FileUpAndDownController.java │ │ ├── HttpController.java │ │ ├── InterceptorController.java │ │ ├── RestfulController.java │ │ ├── ScopeController.java │ │ ├── TestController.java │ │ └── ViewController.java │ │ ├── dao │ │ └── UserDao.java │ │ ├── interceptors │ │ └── FistInterceptor.java │ │ └── pojo │ │ └── User.java │ ├── resources │ └── springMVC.xml │ └── webapp │ ├── WEB-INF │ ├── templates │ │ ├── error.html │ │ ├── file.html │ │ ├── index.html │ │ ├── success.html │ │ └── user_update.html │ └── web.xml │ └── static │ └── img │ └── dongman.jpeg ├── SpringMVC_Request ├── pom.xml └── src │ └── main │ ├── java │ └── top │ │ └── mowang │ │ ├── controller │ │ ├── IndexController.java │ │ ├── ParamController.java │ │ └── RequestMappingController.java │ │ └── pojo │ │ └── User.java │ ├── resources │ └── springMVC.xml │ └── webapp │ └── WEB-INF │ ├── templates │ ├── index.html │ ├── target.html │ └── test.html │ └── web.xml ├── docs ├── .nojekyll ├── README.md ├── _coverpage.md └── index.html └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | *.log 4 | 5 | *.ctxt 6 | 7 | *.jar 8 | *.war 9 | *.nar 10 | *.ear 11 | *.zip 12 | *.tar.gz 13 | *.rar 14 | 15 | hs_err_pid* 16 | 17 | .classpath 18 | .project 19 | .settings 20 | .idea 21 | *.iml 22 | target 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spring-MVC-Demo 2 | 3 | [![](https://res.mowangblog.top/img/2021/10/website-blog-lightgrey)](https://mowangblog.top) [![](https://res.mowangblog.top/img/2021/10/bilibili-video-orange)](https://www.bilibili.com/video/BV1Ry4y1574R) 4 | 5 | 📓Spring MVC 框架使用演示,点击在线文档查看详细笔记 6 | 7 | [![](https://img.shields.io/badge/在线-文档-yellowgreen)](https://mowangblog.github.io/SpringMVC-Demo/#/) 8 | 9 | ## 目录 10 | 11 | - [一、SpringMVC简介](#%E4%B8%80springmvc%E7%AE%80%E4%BB%8B) 12 | - [1、什么是MVC](#1%E4%BB%80%E4%B9%88%E6%98%AFmvc) 13 | - [2、什么是SpringMVC](#2%E4%BB%80%E4%B9%88%E6%98%AFspringmvc) 14 | - [3、SpringMVC的特点](#3springmvc%E7%9A%84%E7%89%B9%E7%82%B9) 15 | - [二、HelloWorld](#%E4%BA%8Chelloworld) 16 | - [1、开发环境](#1%E5%BC%80%E5%8F%91%E7%8E%AF%E5%A2%83) 17 | - [2、创建maven工程](#2%E5%88%9B%E5%BB%BAmaven%E5%B7%A5%E7%A8%8B) 18 | - [a>添加web模块](#a%E6%B7%BB%E5%8A%A0web%E6%A8%A1%E5%9D%97) 19 | - [b>打包方式:war](#b%E6%89%93%E5%8C%85%E6%96%B9%E5%BC%8Fwar) 20 | - [c>引入依赖](#c%E5%BC%95%E5%85%A5%E4%BE%9D%E8%B5%96) 21 | - [3、配置web.xml](#3%E9%85%8D%E7%BD%AEwebxml) 22 | - [a>默认配置方式](#a%E9%BB%98%E8%AE%A4%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F) 23 | - [b>扩展配置方式](#b%E6%89%A9%E5%B1%95%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F) 24 | - [4、创建请求控制器](#4%E5%88%9B%E5%BB%BA%E8%AF%B7%E6%B1%82%E6%8E%A7%E5%88%B6%E5%99%A8) 25 | - [5、创建springMVC的配置文件](#5%E5%88%9B%E5%BB%BAspringmvc%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 26 | - [6、测试HelloWorld](#6%E6%B5%8B%E8%AF%95helloworld) 27 | - [a>实现对首页的访问](#a%E5%AE%9E%E7%8E%B0%E5%AF%B9%E9%A6%96%E9%A1%B5%E7%9A%84%E8%AE%BF%E9%97%AE) 28 | - [b>通过超链接跳转到指定页面](#b%E9%80%9A%E8%BF%87%E8%B6%85%E9%93%BE%E6%8E%A5%E8%B7%B3%E8%BD%AC%E5%88%B0%E6%8C%87%E5%AE%9A%E9%A1%B5%E9%9D%A2) 29 | - [7、总结](#7%E6%80%BB%E7%BB%93) 30 | - [三、@RequestMapping注解](#%E4%B8%89requestmapping%E6%B3%A8%E8%A7%A3) 31 | - [1、@RequestMapping注解的功能](#1requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84%E5%8A%9F%E8%83%BD) 32 | - [2、@RequestMapping注解的位置](#2requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84%E4%BD%8D%E7%BD%AE) 33 | - [3、@RequestMapping注解的value属性](#3requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84value%E5%B1%9E%E6%80%A7) 34 | - [4、@RequestMapping注解的method属性](#4requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84method%E5%B1%9E%E6%80%A7) 35 | - [5、@RequestMapping注解的params属性(了解)](#5requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84params%E5%B1%9E%E6%80%A7%E4%BA%86%E8%A7%A3) 36 | - [6、@RequestMapping注解的headers属性(了解)](#6requestmapping%E6%B3%A8%E8%A7%A3%E7%9A%84headers%E5%B1%9E%E6%80%A7%E4%BA%86%E8%A7%A3) 37 | - [7、SpringMVC支持ant风格的路径](#7springmvc%E6%94%AF%E6%8C%81ant%E9%A3%8E%E6%A0%BC%E7%9A%84%E8%B7%AF%E5%BE%84) 38 | - [8、SpringMVC支持路径中的占位符(重点)](#8springmvc%E6%94%AF%E6%8C%81%E8%B7%AF%E5%BE%84%E4%B8%AD%E7%9A%84%E5%8D%A0%E4%BD%8D%E7%AC%A6%E9%87%8D%E7%82%B9) 39 | - [四、SpringMVC获取请求参数](#%E5%9B%9Bspringmvc%E8%8E%B7%E5%8F%96%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) 40 | - [1、通过ServletAPI获取](#1%E9%80%9A%E8%BF%87servletapi%E8%8E%B7%E5%8F%96) 41 | - [2、通过控制器方法的形参获取请求参数](#2%E9%80%9A%E8%BF%87%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95%E7%9A%84%E5%BD%A2%E5%8F%82%E8%8E%B7%E5%8F%96%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) 42 | - [3、@RequestParam](#3requestparam) 43 | - [4、@RequestHeader](#4requestheader) 44 | - [5、@CookieValue](#5cookievalue) 45 | - [6、通过POJO获取请求参数](#6%E9%80%9A%E8%BF%87pojo%E8%8E%B7%E5%8F%96%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0) 46 | - [7、解决获取请求参数的乱码问题](#7%E8%A7%A3%E5%86%B3%E8%8E%B7%E5%8F%96%E8%AF%B7%E6%B1%82%E5%8F%82%E6%95%B0%E7%9A%84%E4%B9%B1%E7%A0%81%E9%97%AE%E9%A2%98) 47 | - [五、域对象共享数据](#%E4%BA%94%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 48 | - [1、使用ServletAPI向request域对象共享数据](#1%E4%BD%BF%E7%94%A8servletapi%E5%90%91request%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 49 | - [2、使用ModelAndView向request域对象共享数据](#2%E4%BD%BF%E7%94%A8modelandview%E5%90%91request%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 50 | - [3、使用Model向request域对象共享数据](#3%E4%BD%BF%E7%94%A8model%E5%90%91request%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 51 | - [4、使用map向request域对象共享数据](#4%E4%BD%BF%E7%94%A8map%E5%90%91request%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 52 | - [5、使用ModelMap向request域对象共享数据](#5%E4%BD%BF%E7%94%A8modelmap%E5%90%91request%E5%9F%9F%E5%AF%B9%E8%B1%A1%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 53 | - [6、Model、ModelMap、Map的关系](#6modelmodelmapmap%E7%9A%84%E5%85%B3%E7%B3%BB) 54 | - [7、向session域共享数据](#7%E5%90%91session%E5%9F%9F%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 55 | - [8、向application域共享数据](#8%E5%90%91application%E5%9F%9F%E5%85%B1%E4%BA%AB%E6%95%B0%E6%8D%AE) 56 | - [六、SpringMVC的视图](#%E5%85%ADspringmvc%E7%9A%84%E8%A7%86%E5%9B%BE) 57 | - [1、ThymeleafView](#1thymeleafview) 58 | - [2、转发视图](#2%E8%BD%AC%E5%8F%91%E8%A7%86%E5%9B%BE) 59 | - [3、重定向视图](#3%E9%87%8D%E5%AE%9A%E5%90%91%E8%A7%86%E5%9B%BE) 60 | - [4、视图控制器view-controller](#4%E8%A7%86%E5%9B%BE%E6%8E%A7%E5%88%B6%E5%99%A8view-controller) 61 | - [七、RESTful](#%E4%B8%83restful) 62 | - [1、RESTful简介](#1restful%E7%AE%80%E4%BB%8B) 63 | - [a>资源](#a%E8%B5%84%E6%BA%90) 64 | - [b>资源的表述](#b%E8%B5%84%E6%BA%90%E7%9A%84%E8%A1%A8%E8%BF%B0) 65 | - [c>状态转移](#c%E7%8A%B6%E6%80%81%E8%BD%AC%E7%A7%BB) 66 | - [2、RESTful的实现](#2restful%E7%9A%84%E5%AE%9E%E7%8E%B0) 67 | - [3、HiddenHttpMethodFilter](#3hiddenhttpmethodfilter) 68 | - [八、RESTful案例](#%E5%85%ABrestful%E6%A1%88%E4%BE%8B) 69 | - [1、准备工作](#1%E5%87%86%E5%A4%87%E5%B7%A5%E4%BD%9C) 70 | - [2、功能清单](#2%E5%8A%9F%E8%83%BD%E6%B8%85%E5%8D%95) 71 | - [3、具体功能:访问首页](#3%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E8%AE%BF%E9%97%AE%E9%A6%96%E9%A1%B5) 72 | - [a>配置view-controller](#a%E9%85%8D%E7%BD%AEview-controller) 73 | - [b>创建页面](#b%E5%88%9B%E5%BB%BA%E9%A1%B5%E9%9D%A2) 74 | - [4、具体功能:查询所有员工数据](#4%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E6%9F%A5%E8%AF%A2%E6%89%80%E6%9C%89%E5%91%98%E5%B7%A5%E6%95%B0%E6%8D%AE) 75 | - [a>控制器方法](#a%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95) 76 | - [b>创建employee_list.html](#b%E5%88%9B%E5%BB%BAemployee_listhtml) 77 | - [5、具体功能:删除](#5%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E5%88%A0%E9%99%A4) 78 | - [a>创建处理delete请求方式的表单](#a%E5%88%9B%E5%BB%BA%E5%A4%84%E7%90%86delete%E8%AF%B7%E6%B1%82%E6%96%B9%E5%BC%8F%E7%9A%84%E8%A1%A8%E5%8D%95) 79 | - [b>删除超链接绑定点击事件](#b%E5%88%A0%E9%99%A4%E8%B6%85%E9%93%BE%E6%8E%A5%E7%BB%91%E5%AE%9A%E7%82%B9%E5%87%BB%E4%BA%8B%E4%BB%B6) 80 | - [c>控制器方法](#c%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95) 81 | - [6、具体功能:跳转到添加数据页面](#6%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E8%B7%B3%E8%BD%AC%E5%88%B0%E6%B7%BB%E5%8A%A0%E6%95%B0%E6%8D%AE%E9%A1%B5%E9%9D%A2) 82 | - [a>配置view-controller](#a%E9%85%8D%E7%BD%AEview-controller-1) 83 | - [b>创建employee_add.html](#b%E5%88%9B%E5%BB%BAemployee_addhtml) 84 | - [7、具体功能:执行保存](#7%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E6%89%A7%E8%A1%8C%E4%BF%9D%E5%AD%98) 85 | - [a>控制器方法](#a%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95-1) 86 | - [8、具体功能:跳转到更新数据页面](#8%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E8%B7%B3%E8%BD%AC%E5%88%B0%E6%9B%B4%E6%96%B0%E6%95%B0%E6%8D%AE%E9%A1%B5%E9%9D%A2) 87 | - [a>修改超链接](#a%E4%BF%AE%E6%94%B9%E8%B6%85%E9%93%BE%E6%8E%A5) 88 | - [b>控制器方法](#b%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95) 89 | - [c>创建employee_update.html](#c%E5%88%9B%E5%BB%BAemployee_updatehtml) 90 | - [9、具体功能:执行更新](#9%E5%85%B7%E4%BD%93%E5%8A%9F%E8%83%BD%E6%89%A7%E8%A1%8C%E6%9B%B4%E6%96%B0) 91 | - [a>控制器方法](#a%E6%8E%A7%E5%88%B6%E5%99%A8%E6%96%B9%E6%B3%95-2) 92 | - [八、HttpMessageConverter](#%E5%85%ABhttpmessageconverter) 93 | - [1、@RequestBody](#1requestbody) 94 | - [2、RequestEntity](#2requestentity) 95 | - [3、@ResponseBody](#3responsebody) 96 | - [4、SpringMVC处理json](#4springmvc%E5%A4%84%E7%90%86json) 97 | - [5、SpringMVC处理ajax](#5springmvc%E5%A4%84%E7%90%86ajax) 98 | - [6、@RestController注解](#6restcontroller%E6%B3%A8%E8%A7%A3) 99 | - [7、ResponseEntity](#7responseentity) 100 | - [九、文件上传和下载](#%E4%B9%9D%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E5%92%8C%E4%B8%8B%E8%BD%BD) 101 | - [1、文件下载](#1%E6%96%87%E4%BB%B6%E4%B8%8B%E8%BD%BD) 102 | - [2、文件上传](#2%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0) 103 | - [十、拦截器](#%E5%8D%81%E6%8B%A6%E6%88%AA%E5%99%A8) 104 | - [1、拦截器的配置](#1%E6%8B%A6%E6%88%AA%E5%99%A8%E7%9A%84%E9%85%8D%E7%BD%AE) 105 | - [2、拦截器的三个抽象方法](#2%E6%8B%A6%E6%88%AA%E5%99%A8%E7%9A%84%E4%B8%89%E4%B8%AA%E6%8A%BD%E8%B1%A1%E6%96%B9%E6%B3%95) 106 | - [3、多个拦截器的执行顺序](#3%E5%A4%9A%E4%B8%AA%E6%8B%A6%E6%88%AA%E5%99%A8%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F) 107 | - [十一、异常处理器](#%E5%8D%81%E4%B8%80%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86%E5%99%A8) 108 | - [1、基于配置的异常处理](#1%E5%9F%BA%E4%BA%8E%E9%85%8D%E7%BD%AE%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86) 109 | - [2、基于注解的异常处理](#2%E5%9F%BA%E4%BA%8E%E6%B3%A8%E8%A7%A3%E7%9A%84%E5%BC%82%E5%B8%B8%E5%A4%84%E7%90%86) 110 | - [十二、注解配置SpringMVC](#%E5%8D%81%E4%BA%8C%E6%B3%A8%E8%A7%A3%E9%85%8D%E7%BD%AEspringmvc) 111 | - [1、创建初始化类,代替web.xml](#1%E5%88%9B%E5%BB%BA%E5%88%9D%E5%A7%8B%E5%8C%96%E7%B1%BB%E4%BB%A3%E6%9B%BFwebxml) 112 | - [2、创建SpringConfig配置类,代替spring的配置文件](#2%E5%88%9B%E5%BB%BAspringconfig%E9%85%8D%E7%BD%AE%E7%B1%BB%E4%BB%A3%E6%9B%BFspring%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 113 | - [3、创建WebConfig配置类,代替SpringMVC的配置文件](#3%E5%88%9B%E5%BB%BAwebconfig%E9%85%8D%E7%BD%AE%E7%B1%BB%E4%BB%A3%E6%9B%BFspringmvc%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6) 114 | - [4、测试功能](#4%E6%B5%8B%E8%AF%95%E5%8A%9F%E8%83%BD) 115 | - [十三、SpringMVC执行流程](#%E5%8D%81%E4%B8%89springmvc%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B) 116 | - [1、SpringMVC常用组件](#1springmvc%E5%B8%B8%E7%94%A8%E7%BB%84%E4%BB%B6) 117 | - [2、DispatcherServlet初始化过程](#2dispatcherservlet%E5%88%9D%E5%A7%8B%E5%8C%96%E8%BF%87%E7%A8%8B) 118 | - [a>初始化WebApplicationContext](#a%E5%88%9D%E5%A7%8B%E5%8C%96webapplicationcontext) 119 | - [b>创建WebApplicationContext](#b%E5%88%9B%E5%BB%BAwebapplicationcontext) 120 | - [c>DispatcherServlet初始化策略](#cdispatcherservlet%E5%88%9D%E5%A7%8B%E5%8C%96%E7%AD%96%E7%95%A5) 121 | - [3、DispatcherServlet调用组件处理请求](#3dispatcherservlet%E8%B0%83%E7%94%A8%E7%BB%84%E4%BB%B6%E5%A4%84%E7%90%86%E8%AF%B7%E6%B1%82) 122 | - [a>processRequest()](#aprocessrequest) 123 | - [b>doService()](#bdoservice) 124 | - [c>doDispatch()](#cdodispatch) 125 | - [d>processDispatchResult()](#dprocessdispatchresult) 126 | - [4、SpringMVC的执行流程](#4springmvc%E7%9A%84%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B) 127 | 128 | # 一、SpringMVC简介 129 | 130 | ### 1、什么是MVC 131 | 132 | MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 133 | 134 | M:Model,模型层,指工程中的JavaBean,作用是处理数据 135 | 136 | JavaBean分为两类: 137 | 138 | - 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等 139 | - 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。 140 | 141 | V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据 142 | 143 | C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器 144 | 145 | MVC的工作流程: 146 | 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器 147 | 148 | ### 2、什么是SpringMVC 149 | 150 | SpringMVC是Spring的一个后续产品,是Spring的一个子项目 151 | 152 | SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的**首选方案**。 153 | 154 | > 注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet 155 | 156 | ### 3、SpringMVC的特点 157 | 158 | - **Spring 家族原生产品**,与 IOC 容器等基础设施无缝对接 159 | - **基于原生的Servlet**,通过了功能强大的**前端控制器DispatcherServlet**,对请求和响应进行统一处理 160 | - 表述层各细分领域需要解决的问题**全方位覆盖**,提供**全面解决方案** 161 | - **代码清新简洁**,大幅度提升开发效率 162 | - 内部组件化程度高,可插拔式组件**即插即用**,想要什么功能配置相应组件即可 163 | - **性能卓著**,尤其适合现代大型、超大型互联网项目要求 164 | 165 | # 二、HelloWorld 166 | 167 | ### 1、开发环境 168 | 169 | IDE:idea 2019.2 170 | 171 | 构建工具:maven3.5.4 172 | 173 | 服务器:tomcat7 174 | 175 | Spring版本:5.3.1 176 | 177 | ### 2、创建maven工程 178 | 179 | ##### a>添加web模块 180 | 181 | ##### b>打包方式:war 182 | 183 | ##### c>引入依赖 184 | 185 | ```xml 186 | 187 | 188 | 189 | org.springframework 190 | spring-webmvc 191 | 5.3.1 192 | 193 | 194 | 195 | 196 | ch.qos.logback 197 | logback-classic 198 | 1.2.3 199 | 200 | 201 | 202 | 203 | javax.servlet 204 | javax.servlet-api 205 | 3.1.0 206 | provided 207 | 208 | 209 | 210 | 211 | org.thymeleaf 212 | thymeleaf-spring5 213 | 3.0.12.RELEASE 214 | 215 | 216 | 123456789101112131415161718192021222324252627282930 217 | ``` 218 | 219 | 注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。 220 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/836b26dcd15140e486cb3a566abcc2b0.png) 221 | 222 | ### 3、配置web.xml 223 | 224 | 注册SpringMVC的前端控制器DispatcherServlet 225 | 226 | ##### a>默认配置方式 227 | 228 | 此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml 229 | 230 | ```xml 231 | 232 | 233 | springMVC 234 | org.springframework.web.servlet.DispatcherServlet 235 | 236 | 237 | springMVC 238 | 243 | / 244 | 245 | 1234567891011121314 246 | ``` 247 | 248 | ##### b>扩展配置方式 249 | 250 | 可通过init-param标签设置SpringMVC配置文件的位置和名称,通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间 251 | 252 | ```xml 253 | 254 | 255 | springMVC 256 | org.springframework.web.servlet.DispatcherServlet 257 | 258 | 259 | 260 | contextConfigLocation 261 | 262 | classpath:springMVC.xml 263 | 264 | 269 | 1 270 | 271 | 272 | springMVC 273 | 278 | / 279 | 280 | 123456789101112131415161718192021222324252627 281 | ``` 282 | 283 | > 注: 284 | > 285 | > 标签中使用/和/*的区别: 286 | > 287 | > /所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求 288 | > 289 | > 因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面 290 | > 291 | > /*则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*的写法 292 | 293 | ### 4、创建请求控制器 294 | 295 | 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器 296 | 297 | 请求控制器中每一个处理请求的方法成为控制器方法 298 | 299 | 因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在 300 | 301 | ```java 302 | @Controller 303 | public class HelloController { 304 | 305 | } 306 | 1234 307 | ``` 308 | 309 | ### 5、创建springMVC的配置文件 310 | 311 | ```xml 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | text/html 353 | application/json 354 | 355 | 356 | 357 | 358 | 359 | 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 360 | ``` 361 | 362 | ### 6、测试HelloWorld 363 | 364 | ##### a>实现对首页的访问 365 | 366 | 在请求控制器中创建处理请求的方法 367 | 368 | ```java 369 | // @RequestMapping注解:处理请求和控制器方法之间的映射关系 370 | // @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径 371 | // localhost:8080/springMVC/ 372 | @RequestMapping("/") 373 | public String index() { 374 | //设置视图名称 375 | return "index"; 376 | } 377 | 12345678 378 | ``` 379 | 380 | ##### b>通过超链接跳转到指定页面 381 | 382 | 在主页index.html中设置超链接 383 | 384 | ```html 385 | 386 | 387 | 388 | 389 | 首页 390 | 391 | 392 |

首页

393 | HelloWorld
394 | 395 | 396 | 1234567891011 397 | ``` 398 | 399 | 在请求控制器中创建处理请求的方法 400 | 401 | ```java 402 | @RequestMapping("/hello") 403 | public String HelloWorld() { 404 | return "target"; 405 | } 406 | 1234 407 | ``` 408 | 409 | ### 7、总结 410 | 411 | 浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面 412 | 413 | # 三、@RequestMapping注解 414 | 415 | ### 1、@RequestMapping注解的功能 416 | 417 | 从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。 418 | 419 | SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。 420 | 421 | ### 2、@RequestMapping注解的位置 422 | 423 | @RequestMapping标识一个类:设置映射请求的请求路径的初始信息 424 | 425 | @RequestMapping标识一个方法:设置映射请求请求路径的具体信息 426 | 427 | ```java 428 | @Controller 429 | @RequestMapping("/test") 430 | public class RequestMappingController { 431 | 432 | //此时请求映射所映射的请求的请求路径为:/test/testRequestMapping 433 | @RequestMapping("/testRequestMapping") 434 | public String testRequestMapping(){ 435 | return "success"; 436 | } 437 | 438 | } 439 | 1234567891011 440 | ``` 441 | 442 | ### 3、@RequestMapping注解的value属性 443 | 444 | @RequestMapping注解的value属性通过请求的请求地址匹配请求映射 445 | 446 | @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求 447 | 448 | @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射 449 | 450 | ```html 451 | 测试@RequestMapping的value属性-->/testRequestMapping
452 | 测试@RequestMapping的value属性-->/test
453 | 12 454 | @RequestMapping( 455 | value = {"/testRequestMapping", "/test"} 456 | ) 457 | public String testRequestMapping(){ 458 | return "success"; 459 | } 460 | 123456 461 | ``` 462 | 463 | ### 4、@RequestMapping注解的method属性 464 | 465 | @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射 466 | 467 | @RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求 468 | 469 | 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported 470 | 471 | ```html 472 | 测试@RequestMapping的value属性-->/test
473 |
474 | 475 |
476 | 1234 477 | @RequestMapping( 478 | value = {"/testRequestMapping", "/test"}, 479 | method = {RequestMethod.GET, RequestMethod.POST} 480 | ) 481 | public String testRequestMapping(){ 482 | return "success"; 483 | } 484 | 1234567 485 | ``` 486 | 487 | > 注: 488 | > 489 | > 1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解 490 | > 491 | > 处理get请求的映射–>@GetMapping 492 | > 493 | > 处理post请求的映射–>@PostMapping 494 | > 495 | > 处理put请求的映射–>@PutMapping 496 | > 497 | > 处理delete请求的映射–>@DeleteMapping 498 | > 499 | > 2、常用的请求方式有get,post,put,delete 500 | > 501 | > 但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理 502 | > 503 | > 若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到 504 | 505 | ### 5、@RequestMapping注解的params属性(了解) 506 | 507 | @RequestMapping注解的params属性通过请求的请求参数匹配请求映射 508 | 509 | @RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系 510 | 511 | “param”:要求请求映射所匹配的请求必须携带param请求参数 512 | 513 | “!param”:要求请求映射所匹配的请求必须不能携带param请求参数 514 | 515 | “param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value 516 | 517 | “param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value 518 | 519 | ```html 520 | 测试@RequestMapping的params属性-->/test
521 | 1 522 | @RequestMapping( 523 | value = {"/testRequestMapping", "/test"} 524 | ,method = {RequestMethod.GET, RequestMethod.POST} 525 | ,params = {"username","password!=123456"} 526 | ) 527 | public String testRequestMapping(){ 528 | return "success"; 529 | } 530 | 12345678 531 | ``` 532 | 533 | > 注: 534 | > 535 | > 若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions “username, password!=123456” not met for actual request parameters: username={admin}, password={123456} 536 | 537 | ### 6、@RequestMapping注解的headers属性(了解) 538 | 539 | @RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射 540 | 541 | @RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系 542 | 543 | “header”:要求请求映射所匹配的请求必须携带header请求头信息 544 | 545 | “!header”:要求请求映射所匹配的请求必须不能携带header请求头信息 546 | 547 | “header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value 548 | 549 | “header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value 550 | 551 | 若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到 552 | 553 | ### 7、SpringMVC支持ant风格的路径 554 | 555 | ?:表示任意的单个字符 556 | 557 | *:表示任意的0个或多个字符 558 | 559 | **:表示任意的一层或多层目录 560 | 561 | 注意:在使用**时,只能使用/**/xxx的方式 562 | 563 | ### 8、SpringMVC支持路径中的占位符(重点) 564 | 565 | 原始方式:/deleteUser?id=1 566 | 567 | rest方式:/deleteUser/1 568 | 569 | SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参 570 | 571 | ```html 572 | 测试路径中的占位符-->/testRest
573 | 1 574 | @RequestMapping("/testRest/{id}/{username}") 575 | public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){ 576 | System.out.println("id:"+id+",username:"+username); 577 | return "success"; 578 | } 579 | //最终输出的内容为-->id:1,username:admin 580 | 123456 581 | ``` 582 | 583 | # 四、SpringMVC获取请求参数 584 | 585 | ### 1、通过ServletAPI获取 586 | 587 | 将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象 588 | 589 | ```java 590 | @RequestMapping("/testParam") 591 | public String testParam(HttpServletRequest request){ 592 | String username = request.getParameter("username"); 593 | String password = request.getParameter("password"); 594 | System.out.println("username:"+username+",password:"+password); 595 | return "success"; 596 | } 597 | 1234567 598 | ``` 599 | 600 | ### 2、通过控制器方法的形参获取请求参数 601 | 602 | 在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参 603 | 604 | ```html 605 | 测试获取请求参数-->/testParam
606 | 1 607 | @RequestMapping("/testParam") 608 | public String testParam(String username, String password){ 609 | System.out.println("username:"+username+",password:"+password); 610 | return "success"; 611 | } 612 | 12345 613 | ``` 614 | 615 | > 注: 616 | > 617 | > 若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数 618 | > 619 | > 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据 620 | > 621 | > 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果 622 | 623 | ### 3、@RequestParam 624 | 625 | @RequestParam是将请求参数和控制器方法的形参创建映射关系 626 | 627 | @RequestParam注解一共有三个属性: 628 | 629 | value:指定为形参赋值的请求参数的参数名 630 | 631 | required:设置是否必须传输此请求参数,默认值为true 632 | 633 | 若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null 634 | 635 | defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值 636 | 637 | ### 4、@RequestHeader 638 | 639 | @RequestHeader是将请求头信息和控制器方法的形参创建映射关系 640 | 641 | @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam 642 | 643 | ### 5、@CookieValue 644 | 645 | @CookieValue是将cookie数据和控制器方法的形参创建映射关系 646 | 647 | @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam 648 | 649 | ### 6、通过POJO获取请求参数 650 | 651 | 可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值 652 | 653 | ```html 654 |
655 | 用户名:
656 | 密码:
657 | 性别:
658 | 年龄:
659 | 邮箱:
660 | 661 |
662 | 12345678 663 | @RequestMapping("/testpojo") 664 | public String testPOJO(User user){ 665 | System.out.println(user); 666 | return "success"; 667 | } 668 | //最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'} 669 | 123456 670 | ``` 671 | 672 | ### 7、解决获取请求参数的乱码问题 673 | 674 | 解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册 675 | 676 | ```xml 677 | 678 | 679 | CharacterEncodingFilter 680 | org.springframework.web.filter.CharacterEncodingFilter 681 | 682 | encoding 683 | UTF-8 684 | 685 | 686 | forceResponseEncoding 687 | true 688 | 689 | 690 | 691 | CharacterEncodingFilter 692 | /* 693 | 694 | 1234567891011121314151617 695 | ``` 696 | 697 | > 注: 698 | > 699 | > SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效 700 | 701 | # 五、域对象共享数据 702 | 703 | ### 1、使用ServletAPI向request域对象共享数据 704 | 705 | ```java 706 | @RequestMapping("/testServletAPI") 707 | public String testServletAPI(HttpServletRequest request){ 708 | request.setAttribute("testScope", "hello,servletAPI"); 709 | return "success"; 710 | } 711 | 12345 712 | ``` 713 | 714 | ### 2、使用ModelAndView向request域对象共享数据 715 | 716 | ```java 717 | @RequestMapping("/testModelAndView") 718 | public ModelAndView testModelAndView(){ 719 | /** 720 | * ModelAndView有Model和View的功能 721 | * Model主要用于向请求域共享数据 722 | * View主要用于设置视图,实现页面跳转 723 | */ 724 | ModelAndView mav = new ModelAndView(); 725 | //向请求域共享数据 726 | mav.addObject("testScope", "hello,ModelAndView"); 727 | //设置视图,实现页面跳转 728 | mav.setViewName("success"); 729 | return mav; 730 | } 731 | 1234567891011121314 732 | ``` 733 | 734 | ### 3、使用Model向request域对象共享数据 735 | 736 | ```java 737 | @RequestMapping("/testModel") 738 | public String testModel(Model model){ 739 | model.addAttribute("testScope", "hello,Model"); 740 | return "success"; 741 | } 742 | 12345 743 | ``` 744 | 745 | ### 4、使用map向request域对象共享数据 746 | 747 | ```java 748 | @RequestMapping("/testMap") 749 | public String testMap(Map map){ 750 | map.put("testScope", "hello,Map"); 751 | return "success"; 752 | } 753 | 12345 754 | ``` 755 | 756 | ### 5、使用ModelMap向request域对象共享数据 757 | 758 | ```java 759 | @RequestMapping("/testModelMap") 760 | public String testModelMap(ModelMap modelMap){ 761 | modelMap.addAttribute("testScope", "hello,ModelMap"); 762 | return "success"; 763 | } 764 | 12345 765 | ``` 766 | 767 | ### 6、Model、ModelMap、Map的关系 768 | 769 | Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的 770 | 771 | ``` 772 | public interface Model{} 773 | public class ModelMap extends LinkedHashMap {} 774 | public class ExtendedModelMap extends ModelMap implements Model {} 775 | public class BindingAwareModelMap extends ExtendedModelMap {} 776 | 1234 777 | ``` 778 | 779 | ### 7、向session域共享数据 780 | 781 | ```java 782 | @RequestMapping("/testSession") 783 | public String testSession(HttpSession session){ 784 | session.setAttribute("testSessionScope", "hello,session"); 785 | return "success"; 786 | } 787 | 12345 788 | ``` 789 | 790 | ### 8、向application域共享数据 791 | 792 | ```java 793 | @RequestMapping("/testApplication") 794 | public String testApplication(HttpSession session){ 795 | ServletContext application = session.getServletContext(); 796 | application.setAttribute("testApplicationScope", "hello,application"); 797 | return "success"; 798 | } 799 | 123456 800 | ``` 801 | 802 | # 六、SpringMVC的视图 803 | 804 | SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户 805 | 806 | SpringMVC视图的种类很多,默认有转发视图和重定向视图 807 | 808 | 当工程引入jstl的依赖,转发视图会自动转换为JstlView 809 | 810 | 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView 811 | 812 | ### 1、ThymeleafView 813 | 814 | 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转 815 | 816 | ```java 817 | @RequestMapping("/testHello") 818 | public String testHello(){ 819 | return "hello"; 820 | } 821 | 1234 822 | ``` 823 | 824 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/781f6b299e6b41a8b006866ecbcb76ba.png) 825 | 826 | ### 2、转发视图 827 | 828 | SpringMVC中默认的转发视图是InternalResourceView 829 | 830 | SpringMVC中创建转发视图的情况: 831 | 832 | 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转 833 | 834 | 例如"forward:/",“forward:/employee” 835 | 836 | ```java 837 | @RequestMapping("/testForward") 838 | public String testForward(){ 839 | return "forward:/testHello"; 840 | } 841 | 1234 842 | ``` 843 | 844 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/71526c269bbb447b8701d906b2859965.png) 845 | 846 | ### 3、重定向视图 847 | 848 | SpringMVC中默认的重定向视图是RedirectView 849 | 850 | 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转 851 | 852 | 例如"redirect:/",“redirect:/employee” 853 | 854 | ```java 855 | @RequestMapping("/testRedirect") 856 | public String testRedirect(){ 857 | return "redirect:/testHello"; 858 | } 859 | 1234 860 | ``` 861 | 862 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/ec5b5371ea804cafb27e2751231df362.png) 863 | 864 | > 注: 865 | > 866 | > 重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径 867 | 868 | ### 4、视图控制器view-controller 869 | 870 | 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示 871 | 872 | ```xml 873 | 877 | 878 | 12345 879 | ``` 880 | 881 | > 注: 882 | > 883 | > 当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签: 884 | > 885 | > 886 | 887 | # 七、RESTful 888 | 889 | ### 1、RESTful简介 890 | 891 | REST:**Re**presentational **S**tate **T**ransfer,表现层资源状态转移。 892 | 893 | ##### a>资源 894 | 895 | 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。 896 | 897 | ##### b>资源的表述 898 | 899 | 资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。 900 | 901 | ##### c>状态转移 902 | 903 | 状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。 904 | 905 | ### 2、RESTful的实现 906 | 907 | 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。 908 | 909 | 它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。 910 | 911 | REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。 912 | 913 | | 操作 | 传统方式 | REST风格 | 914 | | -------- | ---------------- | ---------------------- | 915 | | 查询操作 | getUserById?id=1 | user/1–>get请求方式 | 916 | | 保存操作 | saveUser | user–>post请求方式 | 917 | | 删除操作 | deleteUser?id=1 | user/1–>delete请求方式 | 918 | | 更新操作 | updateUser | user–>put请求方式 | 919 | 920 | ### 3、HiddenHttpMethodFilter 921 | 922 | 由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢? 923 | 924 | SpringMVC 提供了 **HiddenHttpMethodFilter** 帮助我们**将 POST 请求转换为 DELETE 或 PUT 请求** 925 | 926 | **HiddenHttpMethodFilter** 处理put和delete请求的条件: 927 | 928 | a>当前请求的请求方式必须为post 929 | 930 | b>当前请求必须传输请求参数_method 931 | 932 | 满足以上条件,**HiddenHttpMethodFilter** 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式 933 | 934 | 在web.xml中注册**HiddenHttpMethodFilter** 935 | 936 | ```xml 937 | 938 | HiddenHttpMethodFilter 939 | org.springframework.web.filter.HiddenHttpMethodFilter 940 | 941 | 942 | HiddenHttpMethodFilter 943 | /* 944 | 945 | 12345678 946 | ``` 947 | 948 | > 注: 949 | > 950 | > 目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和HiddenHttpMethodFilter 951 | > 952 | > 在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter 953 | > 954 | > 原因: 955 | > 956 | > - 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的 957 | > 958 | > - request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作 959 | > 960 | > - 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作: 961 | > 962 | > - ``` 963 | > String paramValue = request.getParameter(this.methodParam); 964 | > 1 965 | > ``` 966 | 967 | # 八、RESTful案例 968 | 969 | ### 1、准备工作 970 | 971 | 和传统 CRUD 一样,实现对员工信息的增删改查。 972 | 973 | - 搭建环境 974 | 975 | - 准备实体类 976 | 977 | ```java 978 | package com.atguigu.mvc.bean; 979 | 980 | public class Employee { 981 | 982 | private Integer id; 983 | private String lastName; 984 | 985 | private String email; 986 | //1 male, 0 female 987 | private Integer gender; 988 | 989 | public Integer getId() { 990 | return id; 991 | } 992 | 993 | public void setId(Integer id) { 994 | this.id = id; 995 | } 996 | 997 | public String getLastName() { 998 | return lastName; 999 | } 1000 | 1001 | public void setLastName(String lastName) { 1002 | this.lastName = lastName; 1003 | } 1004 | 1005 | public String getEmail() { 1006 | return email; 1007 | } 1008 | 1009 | public void setEmail(String email) { 1010 | this.email = email; 1011 | } 1012 | 1013 | public Integer getGender() { 1014 | return gender; 1015 | } 1016 | 1017 | public void setGender(Integer gender) { 1018 | this.gender = gender; 1019 | } 1020 | 1021 | public Employee(Integer id, String lastName, String email, Integer gender) { 1022 | super(); 1023 | this.id = id; 1024 | this.lastName = lastName; 1025 | this.email = email; 1026 | this.gender = gender; 1027 | } 1028 | 1029 | public Employee() { 1030 | } 1031 | } 1032 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 1033 | ``` 1034 | 1035 | - 准备dao模拟数据 1036 | 1037 | ```java 1038 | package com.atguigu.mvc.dao; 1039 | 1040 | import java.util.Collection; 1041 | import java.util.HashMap; 1042 | import java.util.Map; 1043 | 1044 | import com.atguigu.mvc.bean.Employee; 1045 | import org.springframework.stereotype.Repository; 1046 | 1047 | 1048 | @Repository 1049 | public class EmployeeDao { 1050 | 1051 | private static Map employees = null; 1052 | 1053 | static{ 1054 | employees = new HashMap(); 1055 | 1056 | employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1)); 1057 | employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1)); 1058 | employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0)); 1059 | employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0)); 1060 | employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1)); 1061 | } 1062 | 1063 | private static Integer initId = 1006; 1064 | 1065 | public void save(Employee employee){ 1066 | if(employee.getId() == null){ 1067 | employee.setId(initId++); 1068 | } 1069 | employees.put(employee.getId(), employee); 1070 | } 1071 | 1072 | public Collection getAll(){ 1073 | return employees.values(); 1074 | } 1075 | 1076 | public Employee get(Integer id){ 1077 | return employees.get(id); 1078 | } 1079 | 1080 | public void delete(Integer id){ 1081 | employees.remove(id); 1082 | } 1083 | } 1084 | 12345678910111213141516171819202122232425262728293031323334353637383940414243444546 1085 | ``` 1086 | 1087 | ### 2、功能清单 1088 | 1089 | | 功能 | URL 地址 | 请求方式 | 1090 | | ------------------- | ----------- | -------- | 1091 | | 访问首页√ | / | GET | 1092 | | 查询全部数据√ | /employee | GET | 1093 | | 删除√ | /employee/2 | DELETE | 1094 | | 跳转到添加数据页面√ | /toAdd | GET | 1095 | | 执行保存√ | /employee | POST | 1096 | | 跳转到更新数据页面√ | /employee/2 | GET | 1097 | | 执行更新√ | /employee | PUT | 1098 | 1099 | ### 3、具体功能:访问首页 1100 | 1101 | ##### a>配置view-controller 1102 | 1103 | ```xml 1104 | 1105 | 1 1106 | ``` 1107 | 1108 | ##### b>创建页面 1109 | 1110 | ```html 1111 | 1112 | 1113 | 1114 | 1115 | Title 1116 | 1117 | 1118 |

首页

1119 | 访问员工信息 1120 | 1121 | 1122 | 1234567891011 1123 | ``` 1124 | 1125 | ### 4、具体功能:查询所有员工数据 1126 | 1127 | ##### a>控制器方法 1128 | 1129 | ```java 1130 | @RequestMapping(value = "/employee", method = RequestMethod.GET) 1131 | public String getEmployeeList(Model model){ 1132 | Collection employeeList = employeeDao.getAll(); 1133 | model.addAttribute("employeeList", employeeList); 1134 | return "employee_list"; 1135 | } 1136 | 123456 1137 | ``` 1138 | 1139 | ##### b>创建employee_list.html 1140 | 1141 | ```html 1142 | 1143 | 1144 | 1145 | 1146 | Employee Info 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1171 | 1172 |
Employee Info
idlastNameemailgenderoptions(add)
1168 | delete 1169 | update 1170 |
1173 | 1174 | 1175 | 123456789101112131415161718192021222324252627282930313233 1176 | ``` 1177 | 1178 | ### 5、具体功能:删除 1179 | 1180 | ##### a>创建处理delete请求方式的表单 1181 | 1182 | ```html 1183 | 1184 |
1185 | 1186 | 1187 |
1188 | 12345 1189 | ``` 1190 | 1191 | ##### b>删除超链接绑定点击事件 1192 | 1193 | 引入vue.js 1194 | 1195 | ```html 1196 | 1197 | 1 1198 | ``` 1199 | 1200 | 删除超链接 1201 | 1202 | ```html 1203 | delete 1204 | 1 1205 | ``` 1206 | 1207 | 通过vue处理点击事件 1208 | 1209 | ```html 1210 | 1228 | 123456789101112131415161718 1229 | ``` 1230 | 1231 | ##### c>控制器方法 1232 | 1233 | ```java 1234 | @RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE) 1235 | public String deleteEmployee(@PathVariable("id") Integer id){ 1236 | employeeDao.delete(id); 1237 | return "redirect:/employee"; 1238 | } 1239 | 12345 1240 | ``` 1241 | 1242 | ### 6、具体功能:跳转到添加数据页面 1243 | 1244 | ##### a>配置view-controller 1245 | 1246 | ```xml 1247 | 1248 | 1 1249 | ``` 1250 | 1251 | ##### b>创建employee_add.html 1252 | 1253 | ```html 1254 | 1255 | 1256 | 1257 | 1258 | Add Employee 1259 | 1260 | 1261 | 1262 |
1263 | lastName:
1264 | email:
1265 | gender:male 1266 | female
1267 |
1268 |
1269 | 1270 | 1271 | 1272 | 123456789101112131415161718 1273 | ``` 1274 | 1275 | ### 7、具体功能:执行保存 1276 | 1277 | ##### a>控制器方法 1278 | 1279 | ```java 1280 | @RequestMapping(value = "/employee", method = RequestMethod.POST) 1281 | public String addEmployee(Employee employee){ 1282 | employeeDao.save(employee); 1283 | return "redirect:/employee"; 1284 | } 1285 | 12345 1286 | ``` 1287 | 1288 | ### 8、具体功能:跳转到更新数据页面 1289 | 1290 | ##### a>修改超链接 1291 | 1292 | ```html 1293 | update 1294 | 1 1295 | ``` 1296 | 1297 | ##### b>控制器方法 1298 | 1299 | ```java 1300 | @RequestMapping(value = "/employee/{id}", method = RequestMethod.GET) 1301 | public String getEmployeeById(@PathVariable("id") Integer id, Model model){ 1302 | Employee employee = employeeDao.get(id); 1303 | model.addAttribute("employee", employee); 1304 | return "employee_update"; 1305 | } 1306 | 123456 1307 | ``` 1308 | 1309 | ##### c>创建employee_update.html 1310 | 1311 | ```html 1312 | 1313 | 1314 | 1315 | 1316 | Update Employee 1317 | 1318 | 1319 | 1320 |
1321 | 1322 | 1323 | lastName:
1324 | email:
1325 | 1329 | gender:male 1330 | female
1331 |
1332 |
1333 | 1334 | 1335 | 1336 | 123456789101112131415161718192021222324 1337 | ``` 1338 | 1339 | ### 9、具体功能:执行更新 1340 | 1341 | ##### a>控制器方法 1342 | 1343 | ```java 1344 | @RequestMapping(value = "/employee", method = RequestMethod.PUT) 1345 | public String updateEmployee(Employee employee){ 1346 | employeeDao.save(employee); 1347 | return "redirect:/employee"; 1348 | } 1349 | 12345 1350 | ``` 1351 | 1352 | # 八、HttpMessageConverter 1353 | 1354 | HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文 1355 | 1356 | HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity, 1357 | 1358 | ResponseEntity 1359 | 1360 | ### 1、@RequestBody 1361 | 1362 | @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值 1363 | 1364 | ```html 1365 |
1366 | 用户名:
1367 | 密码:
1368 | 1369 |
1370 | 12345 1371 | @RequestMapping("/testRequestBody") 1372 | public String testRequestBody(@RequestBody String requestBody){ 1373 | System.out.println("requestBody:"+requestBody); 1374 | return "success"; 1375 | } 1376 | 12345 1377 | ``` 1378 | 1379 | 输出结果: 1380 | 1381 | requestBody:username=admin&password=123456 1382 | 1383 | ### 2、RequestEntity 1384 | 1385 | RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息 1386 | 1387 | ```java 1388 | @RequestMapping("/testRequestEntity") 1389 | public String testRequestEntity(RequestEntity requestEntity){ 1390 | System.out.println("requestHeader:"+requestEntity.getHeaders()); 1391 | System.out.println("requestBody:"+requestEntity.getBody()); 1392 | return "success"; 1393 | } 1394 | 123456 1395 | ``` 1396 | 1397 | 输出结果: 1398 | requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:"" Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google Chrome”;v=“90"”, sec-ch-ua-mobile:"?0", upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”] 1399 | requestBody:username=admin&password=123 1400 | 1401 | ### 3、@ResponseBody 1402 | 1403 | @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器 1404 | 1405 | ```java 1406 | @RequestMapping("/testResponseBody") 1407 | @ResponseBody 1408 | public String testResponseBody(){ 1409 | return "success"; 1410 | } 1411 | 12345 1412 | ``` 1413 | 1414 | 结果:浏览器页面显示success 1415 | 1416 | ### 4、SpringMVC处理json 1417 | 1418 | @ResponseBody处理json的步骤: 1419 | 1420 | a>导入jackson的依赖 1421 | 1422 | ```xml 1423 | 1424 | com.fasterxml.jackson.core 1425 | jackson-databind 1426 | 2.12.1 1427 | 1428 | 12345 1429 | ``` 1430 | 1431 | b>在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串 1432 | 1433 | ``` 1434 | 1435 | 1 1436 | ``` 1437 | 1438 | c>在处理器方法上使用@ResponseBody注解进行标识 1439 | 1440 | d>将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串 1441 | 1442 | ```java 1443 | @RequestMapping("/testResponseUser") 1444 | @ResponseBody 1445 | public User testResponseUser(){ 1446 | return new User(1001,"admin","123456",23,"男"); 1447 | } 1448 | 12345 1449 | ``` 1450 | 1451 | 浏览器的页面中展示的结果: 1452 | 1453 | {“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”} 1454 | 1455 | ### 5、SpringMVC处理ajax 1456 | 1457 | a>请求超链接: 1458 | 1459 | ```html 1460 |
1461 | testAjax
1462 |
1463 | 123 1464 | ``` 1465 | 1466 | b>通过vue和axios处理点击事件: 1467 | 1468 | ```html 1469 | 1470 | 1471 | 1491 | 12345678910111213141516171819202122 1492 | ``` 1493 | 1494 | c>控制器方法: 1495 | 1496 | ```java 1497 | @RequestMapping("/testAjax") 1498 | @ResponseBody 1499 | public String testAjax(String username, String password){ 1500 | System.out.println("username:"+username+",password:"+password); 1501 | return "hello,ajax"; 1502 | } 1503 | 123456 1504 | ``` 1505 | 1506 | ### 6、@RestController注解 1507 | 1508 | @RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解 1509 | 1510 | ### 7、ResponseEntity 1511 | 1512 | ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文 1513 | 1514 | # 九、文件上传和下载 1515 | 1516 | ### 1、文件下载 1517 | 1518 | 使用ResponseEntity实现下载文件的功能 1519 | 1520 | ```java 1521 | @RequestMapping("/testDown") 1522 | public ResponseEntity testResponseEntity(HttpSession session) throws IOException { 1523 | //获取ServletContext对象 1524 | ServletContext servletContext = session.getServletContext(); 1525 | //获取服务器中文件的真实路径 1526 | String realPath = servletContext.getRealPath("/static/img/1.jpg"); 1527 | //创建输入流 1528 | InputStream is = new FileInputStream(realPath); 1529 | //创建字节数组 1530 | byte[] bytes = new byte[is.available()]; 1531 | //将流读到字节数组中 1532 | is.read(bytes); 1533 | //创建HttpHeaders对象设置响应头信息 1534 | MultiValueMap headers = new HttpHeaders(); 1535 | //设置要下载方式以及下载文件的名字 1536 | headers.add("Content-Disposition", "attachment;filename=1.jpg"); 1537 | //设置响应状态码 1538 | HttpStatus statusCode = HttpStatus.OK; 1539 | //创建ResponseEntity对象 1540 | ResponseEntity responseEntity = new ResponseEntity<>(bytes, headers, statusCode); 1541 | //关闭输入流 1542 | is.close(); 1543 | return responseEntity; 1544 | } 1545 | 123456789101112131415161718192021222324 1546 | ``` 1547 | 1548 | ### 2、文件上传 1549 | 1550 | 文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data” 1551 | 1552 | SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息 1553 | 1554 | 上传步骤: 1555 | 1556 | a>添加依赖: 1557 | 1558 | ```xml 1559 | 1560 | 1561 | commons-fileupload 1562 | commons-fileupload 1563 | 1.3.1 1564 | 1565 | 123456 1566 | ``` 1567 | 1568 | b>在SpringMVC的配置文件中添加配置: 1569 | 1570 | ```xml 1571 | 1572 | 1573 | 12 1574 | ``` 1575 | 1576 | c>控制器方法: 1577 | 1578 | ```java 1579 | @RequestMapping("/testUp") 1580 | public String testUp(MultipartFile photo, HttpSession session) throws IOException { 1581 | //获取上传的文件的文件名 1582 | String fileName = photo.getOriginalFilename(); 1583 | //处理文件重名问题 1584 | String hzName = fileName.substring(fileName.lastIndexOf(".")); 1585 | fileName = UUID.randomUUID().toString() + hzName; 1586 | //获取服务器中photo目录的路径 1587 | ServletContext servletContext = session.getServletContext(); 1588 | String photoPath = servletContext.getRealPath("photo"); 1589 | File file = new File(photoPath); 1590 | if(!file.exists()){ 1591 | file.mkdir(); 1592 | } 1593 | String finalPath = photoPath + File.separator + fileName; 1594 | //实现上传功能 1595 | photo.transferTo(new File(finalPath)); 1596 | return "success"; 1597 | } 1598 | 12345678910111213141516171819 1599 | ``` 1600 | 1601 | # 十、拦截器 1602 | 1603 | ### 1、拦截器的配置 1604 | 1605 | SpringMVC中的拦截器用于拦截控制器方法的执行 1606 | 1607 | SpringMVC中的拦截器需要实现HandlerInterceptor 1608 | 1609 | SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置: 1610 | 1611 | ```xml 1612 | 1613 | 1614 | 1615 | 1616 | 1617 | 1618 | 1619 | 1620 | 1623 | 1234567891011 1624 | ``` 1625 | 1626 | ### 2、拦截器的三个抽象方法 1627 | 1628 | SpringMVC中的拦截器有三个抽象方法: 1629 | 1630 | preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法 1631 | 1632 | postHandle:控制器方法执行之后执行postHandle() 1633 | 1634 | afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation() 1635 | 1636 | ### 3、多个拦截器的执行顺序 1637 | 1638 | a>若每个拦截器的preHandle()都返回true 1639 | 1640 | 此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关: 1641 | 1642 | preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行 1643 | 1644 | b>若某个拦截器的preHandle()返回了false 1645 | 1646 | preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行 1647 | 1648 | # 十一、异常处理器 1649 | 1650 | ### 1、基于配置的异常处理 1651 | 1652 | SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver 1653 | 1654 | HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver 1655 | 1656 | SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式: 1657 | 1658 | ```xml 1659 | 1660 | 1661 | 1662 | 1666 | error 1667 | 1668 | 1669 | 1672 | 1673 | 1674 | 123456789101112131415 1675 | ``` 1676 | 1677 | ### 2、基于注解的异常处理 1678 | 1679 | ```java 1680 | //@ControllerAdvice将当前类标识为异常处理的组件 1681 | @ControllerAdvice 1682 | public class ExceptionController { 1683 | 1684 | //@ExceptionHandler用于设置所标识方法处理的异常 1685 | @ExceptionHandler(ArithmeticException.class) 1686 | //ex表示当前请求处理中出现的异常对象 1687 | public String handleArithmeticException(Exception ex, Model model){ 1688 | model.addAttribute("ex", ex); 1689 | return "error"; 1690 | } 1691 | 1692 | } 1693 | 12345678910111213 1694 | ``` 1695 | 1696 | # 十二、注解配置SpringMVC 1697 | 1698 | 使用配置类和注解代替web.xml和SpringMVC配置文件的功能 1699 | 1700 | ### 1、创建初始化类,代替web.xml 1701 | 1702 | 在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。 1703 | Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。 1704 | 1705 | ```java 1706 | public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { 1707 | 1708 | /** 1709 | * 指定spring的配置类 1710 | * @return 1711 | */ 1712 | @Override 1713 | protected Class[] getRootConfigClasses() { 1714 | return new Class[]{SpringConfig.class}; 1715 | } 1716 | 1717 | /** 1718 | * 指定SpringMVC的配置类 1719 | * @return 1720 | */ 1721 | @Override 1722 | protected Class[] getServletConfigClasses() { 1723 | return new Class[]{WebConfig.class}; 1724 | } 1725 | 1726 | /** 1727 | * 指定DispatcherServlet的映射规则,即url-pattern 1728 | * @return 1729 | */ 1730 | @Override 1731 | protected String[] getServletMappings() { 1732 | return new String[]{"/"}; 1733 | } 1734 | 1735 | /** 1736 | * 添加过滤器 1737 | * @return 1738 | */ 1739 | @Override 1740 | protected Filter[] getServletFilters() { 1741 | CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); 1742 | encodingFilter.setEncoding("UTF-8"); 1743 | encodingFilter.setForceRequestEncoding(true); 1744 | HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); 1745 | return new Filter[]{encodingFilter, hiddenHttpMethodFilter}; 1746 | } 1747 | } 1748 | 123456789101112131415161718192021222324252627282930313233343536373839404142 1749 | ``` 1750 | 1751 | ### 2、创建SpringConfig配置类,代替spring的配置文件 1752 | 1753 | ```java 1754 | @Configuration 1755 | public class SpringConfig { 1756 | //ssm整合之后,spring的配置信息写在此类中 1757 | } 1758 | 1234 1759 | ``` 1760 | 1761 | ### 3、创建WebConfig配置类,代替SpringMVC的配置文件 1762 | 1763 | ```java 1764 | @Configuration 1765 | //扫描组件 1766 | @ComponentScan("com.atguigu.mvc.controller") 1767 | //开启MVC注解驱动 1768 | @EnableWebMvc 1769 | public class WebConfig implements WebMvcConfigurer { 1770 | 1771 | //使用默认的servlet处理静态资源 1772 | @Override 1773 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 1774 | configurer.enable(); 1775 | } 1776 | 1777 | //配置文件上传解析器 1778 | @Bean 1779 | public CommonsMultipartResolver multipartResolver(){ 1780 | return new CommonsMultipartResolver(); 1781 | } 1782 | 1783 | //配置拦截器 1784 | @Override 1785 | public void addInterceptors(InterceptorRegistry registry) { 1786 | FirstInterceptor firstInterceptor = new FirstInterceptor(); 1787 | registry.addInterceptor(firstInterceptor).addPathPatterns("/**"); 1788 | } 1789 | 1790 | //配置视图控制 1791 | 1792 | /*@Override 1793 | public void addViewControllers(ViewControllerRegistry registry) { 1794 | registry.addViewController("/").setViewName("index"); 1795 | }*/ 1796 | 1797 | //配置异常映射 1798 | /*@Override 1799 | public void configureHandlerExceptionResolvers(List resolvers) { 1800 | SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); 1801 | Properties prop = new Properties(); 1802 | prop.setProperty("java.lang.ArithmeticException", "error"); 1803 | //设置异常映射 1804 | exceptionResolver.setExceptionMappings(prop); 1805 | //设置共享异常信息的键 1806 | exceptionResolver.setExceptionAttribute("ex"); 1807 | resolvers.add(exceptionResolver); 1808 | }*/ 1809 | 1810 | //配置生成模板解析器 1811 | @Bean 1812 | public ITemplateResolver templateResolver() { 1813 | WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); 1814 | // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得 1815 | ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( 1816 | webApplicationContext.getServletContext()); 1817 | templateResolver.setPrefix("/WEB-INF/templates/"); 1818 | templateResolver.setSuffix(".html"); 1819 | templateResolver.setCharacterEncoding("UTF-8"); 1820 | templateResolver.setTemplateMode(TemplateMode.HTML); 1821 | return templateResolver; 1822 | } 1823 | 1824 | //生成模板引擎并为模板引擎注入模板解析器 1825 | @Bean 1826 | public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { 1827 | SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 1828 | templateEngine.setTemplateResolver(templateResolver); 1829 | return templateEngine; 1830 | } 1831 | 1832 | //生成视图解析器并未解析器注入模板引擎 1833 | @Bean 1834 | public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { 1835 | ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); 1836 | viewResolver.setCharacterEncoding("UTF-8"); 1837 | viewResolver.setTemplateEngine(templateEngine); 1838 | return viewResolver; 1839 | } 1840 | 1841 | 1842 | } 1843 | 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 1844 | ``` 1845 | 1846 | ### 4、测试功能 1847 | 1848 | ```java 1849 | @RequestMapping("/") 1850 | public String index(){ 1851 | return "index"; 1852 | } 1853 | 1234 1854 | ``` 1855 | 1856 | # 十三、SpringMVC执行流程 1857 | 1858 | ### 1、SpringMVC常用组件 1859 | 1860 | - DispatcherServlet:**前端控制器**,不需要工程师开发,由框架提供 1861 | 1862 | 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求 1863 | 1864 | - HandlerMapping:**处理器映射器**,不需要工程师开发,由框架提供 1865 | 1866 | 作用:根据请求的url、method等信息查找Handler,即控制器方法 1867 | 1868 | - Handler:**处理器**,需要工程师开发 1869 | 1870 | 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理 1871 | 1872 | - HandlerAdapter:**处理器适配器**,不需要工程师开发,由框架提供 1873 | 1874 | 作用:通过HandlerAdapter对处理器(控制器方法)进行执行 1875 | 1876 | - ViewResolver:**视图解析器**,不需要工程师开发,由框架提供 1877 | 1878 | 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView 1879 | 1880 | - View:**视图** 1881 | 1882 | 作用:将模型数据通过页面展示给用户 1883 | 1884 | ### 2、DispatcherServlet初始化过程 1885 | 1886 | DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。 1887 | 1888 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kphToPhs-1627992919015)(img/img005.png)] 1889 | 1890 | ##### a>初始化WebApplicationContext 1891 | 1892 | 所在类:org.springframework.web.servlet.FrameworkServlet 1893 | 1894 | ```java 1895 | protected WebApplicationContext initWebApplicationContext() { 1896 | WebApplicationContext rootContext = 1897 | WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 1898 | WebApplicationContext wac = null; 1899 | 1900 | if (this.webApplicationContext != null) { 1901 | // A context instance was injected at construction time -> use it 1902 | wac = this.webApplicationContext; 1903 | if (wac instanceof ConfigurableWebApplicationContext) { 1904 | ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 1905 | if (!cwac.isActive()) { 1906 | // The context has not yet been refreshed -> provide services such as 1907 | // setting the parent context, setting the application context id, etc 1908 | if (cwac.getParent() == null) { 1909 | // The context instance was injected without an explicit parent -> set 1910 | // the root application context (if any; may be null) as the parent 1911 | cwac.setParent(rootContext); 1912 | } 1913 | configureAndRefreshWebApplicationContext(cwac); 1914 | } 1915 | } 1916 | } 1917 | if (wac == null) { 1918 | // No context instance was injected at construction time -> see if one 1919 | // has been registered in the servlet context. If one exists, it is assumed 1920 | // that the parent context (if any) has already been set and that the 1921 | // user has performed any initialization such as setting the context id 1922 | wac = findWebApplicationContext(); 1923 | } 1924 | if (wac == null) { 1925 | // No context instance is defined for this servlet -> create a local one 1926 | // 创建WebApplicationContext 1927 | wac = createWebApplicationContext(rootContext); 1928 | } 1929 | 1930 | if (!this.refreshEventReceived) { 1931 | // Either the context is not a ConfigurableApplicationContext with refresh 1932 | // support or the context injected at construction time had already been 1933 | // refreshed -> trigger initial onRefresh manually here. 1934 | synchronized (this.onRefreshMonitor) { 1935 | // 刷新WebApplicationContext 1936 | onRefresh(wac); 1937 | } 1938 | } 1939 | 1940 | if (this.publishContext) { 1941 | // Publish the context as a servlet context attribute. 1942 | // 将IOC容器在应用域共享 1943 | String attrName = getServletContextAttributeName(); 1944 | getServletContext().setAttribute(attrName, wac); 1945 | } 1946 | 1947 | return wac; 1948 | } 1949 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 1950 | ``` 1951 | 1952 | ##### b>创建WebApplicationContext 1953 | 1954 | 所在类:org.springframework.web.servlet.FrameworkServlet 1955 | 1956 | ```java 1957 | protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { 1958 | Class contextClass = getContextClass(); 1959 | if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 1960 | throw new ApplicationContextException( 1961 | "Fatal initialization error in servlet with name '" + getServletName() + 1962 | "': custom WebApplicationContext class [" + contextClass.getName() + 1963 | "] is not of type ConfigurableWebApplicationContext"); 1964 | } 1965 | // 通过反射创建 IOC 容器对象 1966 | ConfigurableWebApplicationContext wac = 1967 | (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 1968 | 1969 | wac.setEnvironment(getEnvironment()); 1970 | // 设置父容器 1971 | wac.setParent(parent); 1972 | String configLocation = getContextConfigLocation(); 1973 | if (configLocation != null) { 1974 | wac.setConfigLocation(configLocation); 1975 | } 1976 | configureAndRefreshWebApplicationContext(wac); 1977 | 1978 | return wac; 1979 | } 1980 | 1234567891011121314151617181920212223 1981 | ``` 1982 | 1983 | ##### c>DispatcherServlet初始化策略 1984 | 1985 | FrameworkServlet创建WebApplicationContext后,刷新容器,调用onRefresh(wac),此方法在DispatcherServlet中进行了重写,调用了initStrategies(context)方法,初始化策略,即初始化DispatcherServlet的各个组件 1986 | 1987 | 所在类:org.springframework.web.servlet.DispatcherServlet 1988 | 1989 | ```java 1990 | protected void initStrategies(ApplicationContext context) { 1991 | initMultipartResolver(context); 1992 | initLocaleResolver(context); 1993 | initThemeResolver(context); 1994 | initHandlerMappings(context); 1995 | initHandlerAdapters(context); 1996 | initHandlerExceptionResolvers(context); 1997 | initRequestToViewNameTranslator(context); 1998 | initViewResolvers(context); 1999 | initFlashMapManager(context); 2000 | } 2001 | 1234567891011 2002 | ``` 2003 | 2004 | ### 3、DispatcherServlet调用组件处理请求 2005 | 2006 | ##### a>processRequest() 2007 | 2008 | FrameworkServlet重写HttpServlet中的service()和doXxx(),这些方法中调用了processRequest(request, response) 2009 | 2010 | 所在类:org.springframework.web.servlet.FrameworkServlet 2011 | 2012 | ```java 2013 | protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 2014 | throws ServletException, IOException { 2015 | 2016 | long startTime = System.currentTimeMillis(); 2017 | Throwable failureCause = null; 2018 | 2019 | LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 2020 | LocaleContext localeContext = buildLocaleContext(request); 2021 | 2022 | RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 2023 | ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 2024 | 2025 | WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 2026 | asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 2027 | 2028 | initContextHolders(request, localeContext, requestAttributes); 2029 | 2030 | try { 2031 | // 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写 2032 | doService(request, response); 2033 | } 2034 | catch (ServletException | IOException ex) { 2035 | failureCause = ex; 2036 | throw ex; 2037 | } 2038 | catch (Throwable ex) { 2039 | failureCause = ex; 2040 | throw new NestedServletException("Request processing failed", ex); 2041 | } 2042 | 2043 | finally { 2044 | resetContextHolders(request, previousLocaleContext, previousAttributes); 2045 | if (requestAttributes != null) { 2046 | requestAttributes.requestCompleted(); 2047 | } 2048 | logResult(request, response, failureCause, asyncManager); 2049 | publishRequestHandledEvent(request, response, startTime, failureCause); 2050 | } 2051 | } 2052 | 123456789101112131415161718192021222324252627282930313233343536373839 2053 | ``` 2054 | 2055 | ##### b>doService() 2056 | 2057 | 所在类:org.springframework.web.servlet.DispatcherServlet 2058 | 2059 | ```java 2060 | @Override 2061 | protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 2062 | logRequest(request); 2063 | 2064 | // Keep a snapshot of the request attributes in case of an include, 2065 | // to be able to restore the original attributes after the include. 2066 | Map attributesSnapshot = null; 2067 | if (WebUtils.isIncludeRequest(request)) { 2068 | attributesSnapshot = new HashMap<>(); 2069 | Enumeration attrNames = request.getAttributeNames(); 2070 | while (attrNames.hasMoreElements()) { 2071 | String attrName = (String) attrNames.nextElement(); 2072 | if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { 2073 | attributesSnapshot.put(attrName, request.getAttribute(attrName)); 2074 | } 2075 | } 2076 | } 2077 | 2078 | // Make framework objects available to handlers and view objects. 2079 | request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 2080 | request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 2081 | request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 2082 | request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 2083 | 2084 | if (this.flashMapManager != null) { 2085 | FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 2086 | if (inputFlashMap != null) { 2087 | request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 2088 | } 2089 | request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 2090 | request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 2091 | } 2092 | 2093 | RequestPath requestPath = null; 2094 | if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) { 2095 | requestPath = ServletRequestPathUtils.parseAndCache(request); 2096 | } 2097 | 2098 | try { 2099 | // 处理请求和响应 2100 | doDispatch(request, response); 2101 | } 2102 | finally { 2103 | if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 2104 | // Restore the original attribute snapshot, in case of an include. 2105 | if (attributesSnapshot != null) { 2106 | restoreAttributesAfterInclude(request, attributesSnapshot); 2107 | } 2108 | } 2109 | if (requestPath != null) { 2110 | ServletRequestPathUtils.clearParsedRequestPath(request); 2111 | } 2112 | } 2113 | } 2114 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 2115 | ``` 2116 | 2117 | ##### c>doDispatch() 2118 | 2119 | 所在类:org.springframework.web.servlet.DispatcherServlet 2120 | 2121 | ```java 2122 | protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2123 | HttpServletRequest processedRequest = request; 2124 | HandlerExecutionChain mappedHandler = null; 2125 | boolean multipartRequestParsed = false; 2126 | 2127 | WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 2128 | 2129 | try { 2130 | ModelAndView mv = null; 2131 | Exception dispatchException = null; 2132 | 2133 | try { 2134 | processedRequest = checkMultipart(request); 2135 | multipartRequestParsed = (processedRequest != request); 2136 | 2137 | // Determine handler for the current request. 2138 | /* 2139 | mappedHandler:调用链 2140 | 包含handler、interceptorList、interceptorIndex 2141 | handler:浏览器发送的请求所匹配的控制器方法 2142 | interceptorList:处理控制器方法的所有拦截器集合 2143 | interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行 2144 | */ 2145 | mappedHandler = getHandler(processedRequest); 2146 | if (mappedHandler == null) { 2147 | noHandlerFound(processedRequest, response); 2148 | return; 2149 | } 2150 | 2151 | // Determine handler adapter for the current request. 2152 | // 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法 2153 | HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 2154 | 2155 | // Process last-modified header, if supported by the handler. 2156 | String method = request.getMethod(); 2157 | boolean isGet = "GET".equals(method); 2158 | if (isGet || "HEAD".equals(method)) { 2159 | long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 2160 | if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 2161 | return; 2162 | } 2163 | } 2164 | 2165 | // 调用拦截器的preHandle() 2166 | if (!mappedHandler.applyPreHandle(processedRequest, response)) { 2167 | return; 2168 | } 2169 | 2170 | // Actually invoke the handler. 2171 | // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象 2172 | mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 2173 | 2174 | if (asyncManager.isConcurrentHandlingStarted()) { 2175 | return; 2176 | } 2177 | 2178 | applyDefaultViewName(processedRequest, mv); 2179 | // 调用拦截器的postHandle() 2180 | mappedHandler.applyPostHandle(processedRequest, response, mv); 2181 | } 2182 | catch (Exception ex) { 2183 | dispatchException = ex; 2184 | } 2185 | catch (Throwable err) { 2186 | // As of 4.3, we're processing Errors thrown from handler methods as well, 2187 | // making them available for @ExceptionHandler methods and other scenarios. 2188 | dispatchException = new NestedServletException("Handler dispatch failed", err); 2189 | } 2190 | // 后续处理:处理模型数据和渲染视图 2191 | processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 2192 | } 2193 | catch (Exception ex) { 2194 | triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 2195 | } 2196 | catch (Throwable err) { 2197 | triggerAfterCompletion(processedRequest, response, mappedHandler, 2198 | new NestedServletException("Handler processing failed", err)); 2199 | } 2200 | finally { 2201 | if (asyncManager.isConcurrentHandlingStarted()) { 2202 | // Instead of postHandle and afterCompletion 2203 | if (mappedHandler != null) { 2204 | mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 2205 | } 2206 | } 2207 | else { 2208 | // Clean up any resources used by a multipart request. 2209 | if (multipartRequestParsed) { 2210 | cleanupMultipart(processedRequest); 2211 | } 2212 | } 2213 | } 2214 | } 2215 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 2216 | ``` 2217 | 2218 | ##### d>processDispatchResult() 2219 | 2220 | ```java 2221 | private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 2222 | @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, 2223 | @Nullable Exception exception) throws Exception { 2224 | 2225 | boolean errorView = false; 2226 | 2227 | if (exception != null) { 2228 | if (exception instanceof ModelAndViewDefiningException) { 2229 | logger.debug("ModelAndViewDefiningException encountered", exception); 2230 | mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 2231 | } 2232 | else { 2233 | Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 2234 | mv = processHandlerException(request, response, handler, exception); 2235 | errorView = (mv != null); 2236 | } 2237 | } 2238 | 2239 | // Did the handler return a view to render? 2240 | if (mv != null && !mv.wasCleared()) { 2241 | // 处理模型数据和渲染视图 2242 | render(mv, request, response); 2243 | if (errorView) { 2244 | WebUtils.clearErrorRequestAttributes(request); 2245 | } 2246 | } 2247 | else { 2248 | if (logger.isTraceEnabled()) { 2249 | logger.trace("No view rendering, null ModelAndView returned."); 2250 | } 2251 | } 2252 | 2253 | if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 2254 | // Concurrent handling started during a forward 2255 | return; 2256 | } 2257 | 2258 | if (mappedHandler != null) { 2259 | // Exception (if any) is already handled.. 2260 | // 调用拦截器的afterCompletion() 2261 | mappedHandler.triggerAfterCompletion(request, response, null); 2262 | } 2263 | } 2264 | 12345678910111213141516171819202122232425262728293031323334353637383940414243 2265 | ``` 2266 | 2267 | ### 4、SpringMVC的执行流程 2268 | 2269 | 1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。 2270 | 2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射: 2271 | 2272 | a) 不存在 2273 | 2274 | i. 再判断是否配置了mvc:default-servlet-handler 2275 | 2276 | ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误 2277 | 2278 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/8df6a30e77184eb0bc1547dae5838f65.png) 2279 | 2280 | iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误 2281 | 2282 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/67d25736b3b74dfe9145acedb0e8656d.png) 2283 | 2284 | b) 存在则执行下面的流程 2285 | 2286 | 1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。 2287 | 2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 2288 | 3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】 2289 | 4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: 2290 | 2291 | a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 2292 | 2293 | b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 2294 | 2295 | c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 2296 | 2297 | d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 2298 | 2299 | 1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。 2300 | 2. 此时将开始执行拦截器的postHandle(…)方法【逆向】。 2301 | 3. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。 2302 | 4. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。 2303 | 5. 将渲染结果返回给客户端。 2304 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | top.mowang 8 | SpringMVC_Annotaion 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | 13 | 8 14 | 8 15 | 16 | 17 | 18 | 19 | org.springframework 20 | spring-webmvc 21 | 5.3.1 22 | 23 | 24 | 25 | 26 | ch.qos.logback 27 | logback-classic 28 | 1.2.3 29 | 30 | 31 | 32 | 33 | javax.servlet 34 | javax.servlet-api 35 | 3.1.0 36 | provided 37 | 38 | 39 | 40 | 41 | org.thymeleaf 42 | thymeleaf-spring5 43 | 3.0.12.RELEASE 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/java/top/mowang/config/SpringConfig.java: -------------------------------------------------------------------------------- 1 | package top.mowang.config; 2 | 3 | /** 4 | * Spring-MVC-Demo 5 | * 6 | * @author : Xuan Li 7 | * @website : https://mowangblog.top 8 | * @date : 2021/10/09 22:29 9 | **/ 10 | public class SpringConfig { 11 | } 12 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/java/top/mowang/config/WebConfig.java: -------------------------------------------------------------------------------- 1 | package top.mowang.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.ComponentScan; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.web.context.ContextLoader; 7 | import org.springframework.web.context.WebApplicationContext; 8 | import org.springframework.web.multipart.commons.CommonsMultipartResolver; 9 | import org.springframework.web.servlet.HandlerExceptionResolver; 10 | import org.springframework.web.servlet.ViewResolver; 11 | import org.springframework.web.servlet.config.annotation.*; 12 | import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; 13 | import org.thymeleaf.spring5.SpringTemplateEngine; 14 | import org.thymeleaf.spring5.view.ThymeleafViewResolver; 15 | import org.thymeleaf.templatemode.TemplateMode; 16 | import org.thymeleaf.templateresolver.ITemplateResolver; 17 | import org.thymeleaf.templateresolver.ServletContextTemplateResolver; 18 | import top.mowang.interceptor.Interceptor; 19 | 20 | import java.util.List; 21 | import java.util.Properties; 22 | 23 | /** 24 | * Spring-MVC-Demo 25 | * mvc配置文件 26 | * @author : Xuan Li 27 | * @website : https://mowangblog.top 28 | * @date : 2021/10/09 22:29 29 | **/ 30 | @Configuration 31 | @ComponentScan("top.mowang") 32 | @EnableWebMvc 33 | @SuppressWarnings("all") 34 | public class WebConfig implements WebMvcConfigurer { 35 | 36 | //使用默认的servlet处理静态资源 37 | @Override 38 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 39 | configurer.enable(); 40 | } 41 | 42 | //配置文件上传解析器 43 | @Bean 44 | public CommonsMultipartResolver multipartResolver(){ 45 | return new CommonsMultipartResolver(); 46 | } 47 | 48 | //配置拦截器 49 | @Override 50 | public void addInterceptors(InterceptorRegistry registry) { 51 | Interceptor firstInterceptor = new Interceptor(); 52 | registry.addInterceptor(firstInterceptor).addPathPatterns("/**").excludePathPatterns("/"); 53 | } 54 | 55 | //配置视图控制 56 | 57 | @Override 58 | public void addViewControllers(ViewControllerRegistry registry) { 59 | registry.addViewController("/hello").setViewName("hello"); 60 | } 61 | 62 | //配置异常映射 63 | @Override 64 | public void configureHandlerExceptionResolvers(List resolvers) { 65 | SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); 66 | Properties prop = new Properties(); 67 | prop.setProperty("java.lang.ArithmeticException", "error"); 68 | //设置异常映射 69 | exceptionResolver.setExceptionMappings(prop); 70 | //设置共享异常信息的键 71 | exceptionResolver.setExceptionAttribute("ex"); 72 | resolvers.add(exceptionResolver); 73 | } 74 | 75 | /** 76 | * 配置生成模板解析器 77 | * @description: 配置生成模板解析器 78 | * @author: Xuan Li 79 | * @date: 2021/10/9 22:38 80 | */ 81 | @Bean 82 | public ITemplateResolver templateResolver() { 83 | WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); 84 | // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得 85 | ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( 86 | webApplicationContext.getServletContext()); 87 | templateResolver.setPrefix("/WEB-INF/templates/"); 88 | templateResolver.setSuffix(".html"); 89 | templateResolver.setCharacterEncoding("UTF-8"); 90 | templateResolver.setTemplateMode(TemplateMode.HTML); 91 | return templateResolver; 92 | } 93 | 94 | //生成模板引擎并为模板引擎注入模板解析器 95 | @Bean 96 | public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { 97 | SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 98 | templateEngine.setTemplateResolver(templateResolver); 99 | return templateEngine; 100 | } 101 | 102 | //生成视图解析器并未解析器注入模板引擎 103 | @Bean 104 | public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { 105 | ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); 106 | viewResolver.setCharacterEncoding("UTF-8"); 107 | viewResolver.setTemplateEngine(templateEngine); 108 | return viewResolver; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/java/top/mowang/config/WebInit.java: -------------------------------------------------------------------------------- 1 | package top.mowang.config; 2 | 3 | import org.springframework.web.filter.CharacterEncodingFilter; 4 | import org.springframework.web.filter.HiddenHttpMethodFilter; 5 | import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; 6 | 7 | import javax.servlet.Filter; 8 | 9 | /** 10 | * Spring-MVC-Demo 11 | * web工程的初始化类,代替web.xml 12 | * @author : Xuan Li 13 | * @website : https://mowangblog.top 14 | * @date : 2021/10/09 22:26 15 | **/ 16 | public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { 17 | 18 | /** 19 | * @description: 指定spring的配置类 20 | * @author: Xuan Li 21 | * @date: 2021/10/9 22:27 22 | */ 23 | @Override 24 | protected Class[] getRootConfigClasses() { 25 | return new Class[]{SpringConfig.class}; 26 | } 27 | 28 | /** 29 | * @description: 指定springMVC的配置类 30 | * @author: Xuan Li 31 | * @date: 2021/10/9 22:27 32 | */ 33 | @Override 34 | protected Class[] getServletConfigClasses() { 35 | return new Class[]{WebConfig.class}; 36 | } 37 | 38 | /** 39 | * @description: 指定dispatcherServlet的映射规则,url-pattern 40 | * @author: Xuan Li 41 | * @date: 2021/10/9 22:27 42 | */ 43 | @Override 44 | protected String[] getServletMappings() { 45 | return new String[]{"/"}; 46 | } 47 | 48 | /** 49 | * @description: 注册过滤器 50 | * @author: Xuan Li 51 | * @date: 2021/10/9 22:33 52 | */ 53 | @Override 54 | protected Filter[] getServletFilters() { 55 | CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); 56 | characterEncodingFilter.setEncoding("UTF-8"); 57 | characterEncodingFilter.setForceResponseEncoding(true); 58 | HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); 59 | return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter}; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/java/top/mowang/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * 9 | * @author : Xuan Li 10 | * @website : https://mowangblog.top 11 | * @date : 2021/10/09 22:43 12 | **/ 13 | @Controller 14 | public class TestController { 15 | @RequestMapping("/") 16 | public String index(){ 17 | return "index"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/java/top/mowang/interceptor/Interceptor.java: -------------------------------------------------------------------------------- 1 | package top.mowang.interceptor; 2 | 3 | import org.springframework.web.servlet.HandlerInterceptor; 4 | import org.springframework.web.servlet.ModelAndView; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * Spring-MVC-Demo 11 | * 12 | * @author : Xuan Li 13 | * @website : https://mowangblog.top 14 | * @date : 2021/10/09 23:33 15 | **/ 16 | public class Interceptor implements HandlerInterceptor { 17 | @Override 18 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 19 | System.out.println("preHandle"); 20 | return true; 21 | } 22 | 23 | @Override 24 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 25 | System.out.println("postHandle"); 26 | } 27 | 28 | @Override 29 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 30 | System.out.println("afterCompletion"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/webapp/WEB-INF/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

出现错误

9 |

10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/webapp/WEB-INF/templates/hello.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

Hello

9 | 10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_Annotaion/src/main/webapp/WEB-INF/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

注解首页

9 | 10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 4.0.0 7 | top.mowang 8 | SpringMVC_HelloWorld 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | 13 | 8 14 | 8 15 | 16 | 17 | 18 | 19 | 20 | org.springframework 21 | spring-webmvc 22 | 5.3.1 23 | 24 | 25 | 26 | 27 | ch.qos.logback 28 | logback-classic 29 | 1.2.3 30 | 31 | 32 | 33 | 34 | javax.servlet 35 | javax.servlet-api 36 | 3.1.0 37 | provided 38 | 39 | 40 | 41 | 42 | org.thymeleaf 43 | thymeleaf-spring5 44 | 3.0.12.RELEASE 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/src/main/java/top/mowang/controller/HelloController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * hello mvc 9 | * 10 | * @author : Xuan Li 11 | * @website : https://mowangblg.top 12 | * @date : 2021/10/06 19:51 13 | **/ 14 | @Controller 15 | @SuppressWarnings("all") 16 | public class HelloController { 17 | 18 | @RequestMapping("/") 19 | public String index(){ 20 | return "index"; 21 | } 22 | 23 | @RequestMapping("/target") 24 | public String target(){ 25 | return "target"; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/src/main/resources/springMVC.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/src/main/webapp/WEB-INF/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 首页 6 | 7 | 8 |

首页

9 | 访问target.html 10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/src/main/webapp/WEB-INF/templates/target.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

目标页面

9 | 10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_HelloWorld/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | SpringMVC 9 | org.springframework.web.servlet.DispatcherServlet 10 | 11 | 12 | contextConfigLocation 13 | classpath:springMVC.xml 14 | 15 | 16 | 1 17 | 18 | 19 | 20 | SpringMVC 21 | / 22 | 23 | -------------------------------------------------------------------------------- /SpringMVC_MVC/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | top.mowang 8 | SpringMVC_MVC 9 | 1.0-SNAPSHOT 10 | war 11 | 12 | 13 | 8 14 | 8 15 | 16 | 17 | 18 | 19 | 20 | org.springframework 21 | spring-webmvc 22 | 5.3.1 23 | 24 | 25 | 26 | 27 | ch.qos.logback 28 | logback-classic 29 | 1.2.3 30 | 31 | 32 | 33 | 34 | javax.servlet 35 | javax.servlet-api 36 | 3.1.0 37 | provided 38 | 39 | 40 | 41 | 42 | org.thymeleaf 43 | thymeleaf-spring5 44 | 3.0.12.RELEASE 45 | 46 | 47 | 48 | 49 | com.fasterxml.jackson.core 50 | jackson-databind 51 | 2.12.1 52 | 53 | 54 | 55 | 56 | commons-fileupload 57 | commons-fileupload 58 | 1.3.1 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/ExceptionController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.web.bind.annotation.ControllerAdvice; 6 | import org.springframework.web.bind.annotation.ExceptionHandler; 7 | 8 | /** 9 | * Spring-MVC-Demo 10 | * 11 | * @author : Xuan Li 12 | * @website : https://mowangblog.top 13 | * @date : 2021/10/09 22:13 14 | **/ 15 | @ControllerAdvice 16 | public class ExceptionController { 17 | @ExceptionHandler(value = {ArithmeticException.class}) 18 | public String testException(Exception ex, Model model){ 19 | model.addAttribute("ex",ex); 20 | return "error"; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/FileUpAndDownController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.http.HttpHeaders; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.util.MultiValueMap; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.ResponseBody; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | import javax.servlet.ServletContext; 13 | import javax.servlet.http.HttpSession; 14 | import java.io.*; 15 | import java.util.UUID; 16 | 17 | /** 18 | * Spring-MVC-Demo 19 | * 20 | * @author : Xuan Li 21 | * @website : https://mowangblog.top 22 | * @date : 2021/10/08 23:35 23 | **/ 24 | @Controller 25 | public class FileUpAndDownController { 26 | 27 | @RequestMapping("/file") 28 | public String file() { 29 | return "file"; 30 | } 31 | 32 | @RequestMapping("/testDown") 33 | public ResponseEntity testResponseEntity(HttpSession session) throws IOException { 34 | //获取ServletContext对象 35 | ServletContext servletContext = session.getServletContext(); 36 | //获取服务器中文件的真实路径 37 | String realPath = servletContext.getRealPath("/static/img/dongman.jpeg"); 38 | //创建输入流 39 | InputStream is = new FileInputStream(realPath); 40 | //创建字节数组 41 | byte[] bytes = new byte[is.available()]; 42 | //将流读到字节数组中 43 | is.read(bytes); 44 | //创建HttpHeaders对象设置响应头信息 45 | MultiValueMap headers = new HttpHeaders(); 46 | //设置要下载方式以及下载文件的名字 47 | headers.add("Content-Disposition", "attachment;filename=dongman.jpeg"); 48 | //设置响应状态码 49 | HttpStatus statusCode = HttpStatus.OK; 50 | //创建ResponseEntity对象 51 | ResponseEntity responseEntity = new ResponseEntity<>(bytes, headers, statusCode); 52 | //关闭输入流 53 | is.close(); 54 | return responseEntity; 55 | } 56 | 57 | @RequestMapping("/testUp") 58 | @ResponseBody 59 | public String testUp(MultipartFile photo, HttpSession session) throws IOException { 60 | //获取上传的文件的文件名 61 | String filename = photo.getOriginalFilename(); 62 | //处理文件重名问题 63 | String suffixName = filename.substring(filename.lastIndexOf('.')); 64 | String uuid = UUID.randomUUID().toString().replaceAll("-",""); 65 | filename = uuid+suffixName; 66 | //获取服务器中photo目录的路径 67 | ServletContext servletContext = session.getServletContext(); 68 | String realPath = servletContext.getRealPath("photo"); 69 | File file = new File(realPath); 70 | if (!file.exists()) { 71 | file.mkdir(); 72 | } 73 | //实现上传功能 74 | String finalPath = realPath + File.separator + filename; 75 | photo.transferTo(new File(finalPath)); 76 | return "success"; 77 | } 78 | 79 | 80 | } 81 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/HttpController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.http.RequestEntity; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.web.bind.annotation.RequestBody; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import top.mowang.pojo.User; 9 | 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpServletResponse; 12 | import java.io.IOException; 13 | 14 | /** 15 | * Spring-MVC-Demo 16 | * 17 | * @author : Xuan Li 18 | * @website : https://mowangblog.top 19 | * @date : 2021/10/08 23:01 20 | **/ 21 | @Controller 22 | public class HttpController { 23 | 24 | @RequestMapping("/testRequestBody") 25 | public String testRequestBody(@RequestBody String body){ 26 | System.out.println(body); 27 | return "success"; 28 | } 29 | 30 | @RequestMapping("/testRequestEntity") 31 | public String testRequestEntity(RequestEntity body){ 32 | System.out.println(body.getHeaders()); 33 | System.out.println(body.getBody()); 34 | return "success"; 35 | } 36 | 37 | @RequestMapping("/testResponse") 38 | public void testResponse(HttpServletResponse httpServletResponse) throws IOException { 39 | httpServletResponse.getWriter().write("hello response"); 40 | } 41 | 42 | @RequestMapping("/testResponseBody") 43 | @ResponseBody 44 | public String testResponseBody() { 45 | return "hello testResponseBody"; 46 | } 47 | 48 | @RequestMapping("/testResponseUser") 49 | @ResponseBody 50 | public User testResponseUser() { 51 | return new User(1001, "李煊", "1111","aa@163.com", 1); 52 | } 53 | 54 | @RequestMapping("/testAjax") 55 | @ResponseBody 56 | public String testAjax(String username, String password){ 57 | System.out.println("username:"+username+",password:"+password); 58 | return "hello,ajax"; 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/InterceptorController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.web.bind.annotation.RequestMapping; 4 | import org.springframework.web.bind.annotation.RestController; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * 9 | * @author : Xuan Li 10 | * @website : https://mowangblog.top 11 | * @date : 2021/10/09 21:44 12 | **/ 13 | @RestController 14 | public class InterceptorController { 15 | 16 | @RequestMapping("testInterceptor") 17 | public String testInterceptor() { 18 | 19 | return "success"; 20 | } 21 | 22 | @RequestMapping("testExceptionHandler") 23 | public String testExceptionHandler() { 24 | int i = 1 / 0; 25 | return "success"; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/RestfulController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Controller; 5 | import org.springframework.ui.Model; 6 | import org.springframework.web.bind.annotation.PathVariable; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.RequestMethod; 9 | import top.mowang.dao.UserDao; 10 | import top.mowang.pojo.User; 11 | 12 | import java.util.Collection; 13 | 14 | /** 15 | * Spring-MVC-Demo 16 | * 17 | * @author : Xuan Li 18 | * @website : https://mowangblog.top 19 | * @date : 2021/10/07 23:11 20 | **/ 21 | @Controller 22 | public class RestfulController { 23 | /* 24 | * 使用restful模拟用户的增删改查 25 | * /user GET 查询所有用户信息 26 | * /user/1 GET 根据用户id查询用户信息 27 | * /user post 添加用户信息 28 | * /user/1 Delete 删除用户信息 29 | * /user put 修改用户信息 30 | */ 31 | 32 | 33 | @Autowired 34 | UserDao userDao; 35 | 36 | @RequestMapping(value = "/user",method = RequestMethod.GET) 37 | public String getAllUser(Model model){ 38 | System.out.println("查询所有用户信息"); 39 | Collection all = userDao.getAll(); 40 | model.addAttribute("user",all); 41 | return "success"; 42 | } 43 | 44 | @RequestMapping(value = "/user",method = RequestMethod.POST) 45 | public String saveAllUser(User user){ 46 | System.out.println("保存用户信息"+user); 47 | userDao.save(user); 48 | return "redirect:/user"; 49 | } 50 | 51 | @RequestMapping(value = "/user",method = RequestMethod.PUT) 52 | public String updateAllUser(User user){ 53 | System.out.println("修改用户信息"+user); 54 | userDao.save(user); 55 | return "redirect:/user"; 56 | } 57 | 58 | @RequestMapping(value = "/user/{id}",method = RequestMethod.GET) 59 | public String getUserById(@PathVariable Integer id,Model model){ 60 | System.out.println("根据id"+id+"查询用户信息"); 61 | model.addAttribute("user",userDao.get(id)); 62 | return "user_update"; 63 | } 64 | 65 | @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE) 66 | public String deleteUserById(@PathVariable Integer id){ 67 | System.out.println("根据id"+id+"删除用户信息"); 68 | userDao.delete(id); 69 | return "redirect:/user"; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/ScopeController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.ui.Model; 5 | import org.springframework.ui.ModelMap; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.servlet.DispatcherServlet; 8 | import org.springframework.web.servlet.ModelAndView; 9 | 10 | import javax.servlet.ServletContext; 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpSession; 13 | import java.util.Map; 14 | 15 | /** 16 | * Spring-MVC-Demo 17 | * 18 | * @author : Xuan Li 19 | * @website : https://mowangblog.top 20 | * @date : 2021/10/07 21:10 21 | **/ 22 | @Controller 23 | public class ScopeController { 24 | 25 | @RequestMapping("/testScopeByServlet") 26 | public String testScopeByServlet(HttpServletRequest httpServletRequest){ 27 | httpServletRequest.setAttribute("scope","mowang"); 28 | return "success"; 29 | } 30 | 31 | @RequestMapping("/testModelAndView") 32 | public ModelAndView testModelAndView(){ 33 | ModelAndView modelAndView = new ModelAndView(); 34 | modelAndView.addObject("scope","hello modelAndView"); 35 | modelAndView.setViewName("success"); 36 | return modelAndView; 37 | } 38 | 39 | @RequestMapping("/testModel") 40 | public String testModel(Model model){ 41 | model.addAttribute("scope","hello model"); 42 | System.out.println(model.getClass().getName()); 43 | return "success"; 44 | } 45 | 46 | @RequestMapping("/testMap") 47 | public String testMap(Map map){ 48 | map.put("scope","hello map"); 49 | System.out.println(map.getClass().getName()); 50 | return "success"; 51 | } 52 | 53 | @RequestMapping("/testModelMap") 54 | public String testModelMap(ModelMap model){ 55 | model.addAttribute("scope","hello testModelMap"); 56 | System.out.println(model.getClass().getName()); 57 | return "success"; 58 | } 59 | 60 | @RequestMapping("/testSession") 61 | public String testSession(HttpSession session){ 62 | session.setAttribute("scope","hello testSession"); 63 | return "success"; 64 | } 65 | 66 | @RequestMapping("/testApplication") 67 | public String testApplication(HttpSession session){ 68 | ServletContext servletContext = session.getServletContext(); 69 | servletContext.setAttribute("scope","hello testApplication"); 70 | return "success"; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * 9 | * @author : Xuan Li 10 | * @website : https://mowangblog.top 11 | * @date : 2021/10/07 21:07 12 | **/ 13 | @Controller 14 | public class TestController { 15 | 16 | // @RequestMapping("/") 17 | // public String index(){ 18 | // return "index"; 19 | // } 20 | } 21 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/controller/ViewController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * 9 | * @author : Xuan Li 10 | * @website : https://mowangblog.top 11 | * @date : 2021/10/07 22:23 12 | **/ 13 | @Controller 14 | public class ViewController { 15 | 16 | @RequestMapping("/testView") 17 | public String testView(){ 18 | return "success"; 19 | } 20 | 21 | @RequestMapping("/testForward") 22 | public String testForward(){ 23 | return "forward:/testView"; 24 | } 25 | 26 | @RequestMapping("/testRedirect") 27 | public String testRedirect(){ 28 | return "redirect:https://mowangblog.top"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package top.mowang.dao; 2 | 3 | import org.springframework.stereotype.Repository; 4 | import top.mowang.pojo.User; 5 | 6 | import java.util.Collection; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | /** 11 | * Spring-MVC-Demo 12 | * 13 | * @author : Xuan Li 14 | * @website : https://mowangblog.top 15 | * @date : 2021/10/08 20:19 16 | **/ 17 | @Repository 18 | public class UserDao { 19 | 20 | private static Map users = null; 21 | 22 | static{ 23 | users = new HashMap(); 24 | 25 | users.put(1001, new User(1001, "E-AA", "1111","aa@163.com", 1)); 26 | users.put(1002, new User(1002, "E-BB","1111", "bb@163.com", 1)); 27 | users.put(1003, new User(1003, "E-CC", "1111","cc@163.com", 0)); 28 | users.put(1004, new User(1004, "E-DD","1111", "dd@163.com", 0)); 29 | users.put(1005, new User(1005, "E-EE", "1111","ee@163.com", 1)); 30 | } 31 | 32 | private static Integer initId = 1006; 33 | 34 | public void save(User User){ 35 | if(User.getId() == null){ 36 | User.setId(initId++); 37 | } 38 | users.put(User.getId(), User); 39 | } 40 | 41 | public Collection getAll(){ 42 | return users.values(); 43 | } 44 | 45 | public User get(Integer id){ 46 | return users.get(id); 47 | } 48 | 49 | public void delete(Integer id){ 50 | users.remove(id); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/interceptors/FistInterceptor.java: -------------------------------------------------------------------------------- 1 | package top.mowang.interceptors; 2 | 3 | import org.springframework.stereotype.Component; 4 | import org.springframework.web.servlet.HandlerInterceptor; 5 | import org.springframework.web.servlet.ModelAndView; 6 | 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | /** 11 | * Spring-MVC-Demo 12 | * 13 | * @author : Xuan Li 14 | * @website : https://mowangblog.top 15 | * @date : 2021/10/09 21:47 16 | **/ 17 | @Component 18 | public class FistInterceptor implements HandlerInterceptor { 19 | 20 | @Override 21 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 22 | System.out.println("preHandle"); 23 | return true; 24 | } 25 | 26 | @Override 27 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 28 | System.out.println("postHandle"); 29 | } 30 | 31 | @Override 32 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 33 | System.out.println("afterCompletion"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/java/top/mowang/pojo/User.java: -------------------------------------------------------------------------------- 1 | package top.mowang.pojo; 2 | 3 | /** 4 | * Spring-MVC-Demo 5 | * 实体类 6 | * 7 | * @author : Xuan Li 8 | * @website : https://mowangblog.top 9 | * @date : 2021/10/07 23:15 10 | **/ 11 | public class User { 12 | private Integer id; 13 | private String name; 14 | private String password; 15 | private String email; 16 | private Integer age; 17 | 18 | @Override 19 | public String toString() { 20 | return "User{" + 21 | "id=" + id + 22 | ", name='" + name + '\'' + 23 | ", password='" + password + '\'' + 24 | ", email='" + email + '\'' + 25 | ", age=" + age + 26 | '}'; 27 | } 28 | 29 | public User() { 30 | } 31 | 32 | public User(Integer id, String name, String password, String email, Integer age) { 33 | this.id = id; 34 | this.name = name; 35 | this.password = password; 36 | this.email = email; 37 | this.age = age; 38 | } 39 | 40 | public Integer getId() { 41 | return id; 42 | } 43 | 44 | public void setId(Integer id) { 45 | this.id = id; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | public String getPassword() { 57 | return password; 58 | } 59 | 60 | public void setPassword(String password) { 61 | this.password = password; 62 | } 63 | 64 | public String getEmail() { 65 | return email; 66 | } 67 | 68 | public void setEmail(String email) { 69 | this.email = email; 70 | } 71 | 72 | public Integer getAge() { 73 | return age; 74 | } 75 | 76 | public void setAge(Integer age) { 77 | this.age = age; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/resources/springMVC.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/templates/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

出现错误

9 |

10 | 11 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/templates/file.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 文件上传和下载 6 | 7 | 8 | 下载文件 9 |
10 | 头像:
11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

首页

9 | 通过ServletAPI向request域对象传递对象
10 | 通过ModelAndView向request域对象传递对象
11 | 通过Model向request域对象传递对象
12 | 通过Map向request域对象传递对象
13 | 通过testModelMap向request域对象传递对象
14 | 通过Session向Session域对象传递对象
15 | 通过Application向Application域对象传递对象

16 | 通过thymeleaf跳转
17 | 通过forward跳转
18 | 通过Redirect跳转

19 | 查询所有用户信息
20 | 根据id查询所有用户信息
21 | 22 |

restful

23 | 24 |
25 | 用户名: 26 | 密码: 27 | 邮箱: 28 | 年龄: 29 | 30 |
31 | 32 |
33 | 34 | 用户名: 35 | 密码: 36 | 邮箱: 37 | 年龄: 38 | 39 |
40 | 41 |

RequestBody

42 |
43 | 用户名: 44 | 密码: 45 | 邮箱: 46 | 年龄: 47 | 48 |

49 |

RequestEntity

50 |
51 | 用户名: 52 | 密码: 53 | 邮箱: 54 | 年龄: 55 | 56 |
57 |

testResponse

58 | 通过testResponse对象响应浏览器数据
59 | 通过testResponseBody对象响应浏览器数据
60 | 通过ResponseBody对象响应浏览器JSON,User数据
61 | 62 |

ajax

63 | 66 | 67 |

testInterceptor

68 | 测试拦截器
69 | 测试异常处理
70 | 71 | 72 | 73 | 93 | 94 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/templates/success.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

SUCCESS

9 |

10 |

11 |

12 | 13 | 14 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 44 | 45 |
16 | id 17 | 19 | name 20 | 22 | password 23 | 25 | email 26 | 28 | age 29 | 31 | options 32 |
41 | 删除 42 | 修改 43 |
46 |
47 | 48 |
49 | 50 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/templates/user_update.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |
9 | 10 | 11 | 用户名: 12 | 密码: 13 | 邮箱: 14 | 年龄: 15 | 16 |
17 | 18 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | characterEncodingFilter 10 | org.springframework.web.filter.CharacterEncodingFilter 11 | 12 | encoding 13 | UTF-8 14 | 15 | 16 | forceResponseEncoding 17 | true 18 | 19 | 20 | 21 | characterEncodingFilter 22 | /* 23 | 24 | 25 | 26 | 27 | hiddenHttpMethodFilter 28 | org.springframework.web.filter.HiddenHttpMethodFilter 29 | 30 | 31 | hiddenHttpMethodFilter 32 | /* 33 | 34 | 35 | 36 | 37 | 38 | 39 | dispatcherServlet 40 | org.springframework.web.servlet.DispatcherServlet 41 | 42 | contextConfigLocation 43 | classpath:springMVC.xml 44 | 45 | 46 | 47 | 48 | dispatcherServlet 49 | / 50 | 51 | -------------------------------------------------------------------------------- /SpringMVC_MVC/src/main/webapp/static/img/dongman.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mowangblog/SpringMVC-Demo/30b1af935a02706627dd3f7e6a9cf28533e28dbb/SpringMVC_MVC/src/main/webapp/static/img/dongman.jpeg -------------------------------------------------------------------------------- /SpringMVC_Request/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | top.mowang 7 | SpringMVC_Request 8 | 1.0-SNAPSHOT 9 | war 10 | 11 | 12 | 13 | 8 14 | 8 15 | 16 | 17 | 18 | 19 | 20 | org.springframework 21 | spring-webmvc 22 | 5.3.1 23 | 24 | 25 | 26 | 27 | ch.qos.logback 28 | logback-classic 29 | 1.2.3 30 | 31 | 32 | 33 | 34 | javax.servlet 35 | javax.servlet-api 36 | 3.1.0 37 | provided 38 | 39 | 40 | 41 | 42 | org.thymeleaf 43 | thymeleaf-spring5 44 | 3.0.12.RELEASE 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/java/top/mowang/controller/IndexController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.RequestMapping; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * 主页控制器 9 | * 10 | * @author : Xuan Li 11 | * @website : https://mowangblog.top 12 | * @date : 2021/10/06 21:24 13 | **/ 14 | @Controller 15 | public class IndexController { 16 | @RequestMapping("/") 17 | public String index(){ 18 | return "index"; 19 | } 20 | 21 | @RequestMapping("/test") 22 | public String test(){ 23 | return "test"; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/java/top/mowang/controller/ParamController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.*; 5 | import top.mowang.pojo.User; 6 | 7 | import javax.servlet.http.HttpServlet; 8 | import javax.servlet.http.HttpServletRequest; 9 | import java.util.Arrays; 10 | 11 | /** 12 | * Spring-MVC-Demo 13 | * mvc请求 14 | * 15 | * @author : Xuan Li 16 | * @website : https://mowangblog.top 17 | * @date : 2021/10/06 21:18 18 | **/ 19 | @Controller 20 | @RequestMapping("param") 21 | public class ParamController { 22 | 23 | /** 24 | * @description: servletAPi方式获取参数 25 | * @author: Xuan Li 26 | * @date: 2021/10/6 22:53 27 | */ 28 | @RequestMapping("/servletAPi") 29 | public String servletAPi(HttpServletRequest httpServletRequest){ 30 | String user = httpServletRequest.getParameter("user"); 31 | Object sex = httpServletRequest.getParameter("sex"); 32 | System.out.println(user); 33 | System.out.println(sex); 34 | return "target"; 35 | } 36 | 37 | @RequestMapping("/testParam") 38 | public String testParam(@RequestParam(value = "username",required = false,defaultValue = "李煊") String user, String sex, String[] hobby){ 39 | System.out.println(user); 40 | System.out.println(sex); 41 | System.out.println(Arrays.toString(hobby)); 42 | return "target"; 43 | } 44 | 45 | @RequestMapping("/testHeader") 46 | public String testHeader(@RequestHeader(value = "host") String host,@CookieValue("Idea-c928d5a5") String cookie){ 47 | System.out.println(host); 48 | System.out.println(cookie); 49 | return "target"; 50 | } 51 | 52 | @RequestMapping("/testBean") 53 | public String testHeader(User user){ 54 | System.out.println(user); 55 | return "target"; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/java/top/mowang/controller/RequestMappingController.java: -------------------------------------------------------------------------------- 1 | package top.mowang.controller; 2 | 3 | import org.springframework.stereotype.Controller; 4 | import org.springframework.web.bind.annotation.*; 5 | 6 | /** 7 | * Spring-MVC-Demo 8 | * mvc请求 9 | * 10 | * @author : Xuan Li 11 | * @website : https://mowangblog.top 12 | * @date : 2021/10/06 21:18 13 | **/ 14 | @Controller 15 | @RequestMapping("request") 16 | public class RequestMappingController { 17 | 18 | /** 19 | * value可以设置一个请求对应多个请求地址 20 | * method设置请求方式 21 | * @author: Xuan Li 22 | * @date: 2021/10/6 21:30 23 | */ 24 | @RequestMapping(value = {"/target","test"},method = RequestMethod.POST) 25 | public String target(){ 26 | return "target"; 27 | } 28 | 29 | /** 30 | * @description: GetMapping表示get请求 31 | * @author: Xuan Li 32 | * @date: 2021/10/6 21:46 33 | */ 34 | @GetMapping("testGet") 35 | public String test(){ 36 | return "target"; 37 | } 38 | 39 | /** 40 | * @description: params表示必须携带的参数 !params表示不能携带的参数,headers请求头信息 41 | * @author: Xuan Li 42 | * @date: 2021/10/6 21:53 43 | */ 44 | @GetMapping(value = "testParams",params = "password",headers = "host=localhost:8080") 45 | public String params(){ 46 | return "target"; 47 | } 48 | 49 | /** 50 | * ?匹配任意一个字符 51 | * *匹配0或多个字符 52 | * **表示任意一层或多层目录 53 | * @author: Xuan Li 54 | * @date: 2021/10/6 22:12 55 | */ 56 | @SuppressWarnings("all") 57 | // @GetMapping(value = "testAnt?") 58 | // @GetMapping(value = "testAnt*") 59 | @GetMapping(value = "/**/testAnt") 60 | public String testAnt(){ 61 | return "target"; 62 | } 63 | 64 | /** 65 | * @description: restful风格 66 | * @author: Xuan Li 67 | * @date: 2021/10/6 22:38 68 | */ 69 | @RequestMapping("/testPath/{id}/{name}") 70 | public String testPath(@PathVariable("id")Integer id,@PathVariable String name){ 71 | System.out.println(id+" "+name); 72 | return "target"; 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/java/top/mowang/pojo/User.java: -------------------------------------------------------------------------------- 1 | package top.mowang.pojo; 2 | 3 | /** 4 | * Spring-MVC-Demo 5 | * 实体类 6 | * 7 | * @author : Xuan Li 8 | * @website : https://mowangblog.top 9 | * @date : 2021/10/06 23:15 10 | **/ 11 | public class User { 12 | private Integer id; 13 | private String name; 14 | private String password; 15 | private String email; 16 | private Integer age; 17 | 18 | @Override 19 | public String toString() { 20 | return "User{" + 21 | "id=" + id + 22 | ", name='" + name + '\'' + 23 | ", password='" + password + '\'' + 24 | ", email='" + email + '\'' + 25 | ", age=" + age + 26 | '}'; 27 | } 28 | 29 | public User() { 30 | } 31 | 32 | public User(Integer id, String name, String password, String email, Integer age) { 33 | this.id = id; 34 | this.name = name; 35 | this.password = password; 36 | this.email = email; 37 | this.age = age; 38 | } 39 | 40 | public Integer getId() { 41 | return id; 42 | } 43 | 44 | public void setId(Integer id) { 45 | this.id = id; 46 | } 47 | 48 | public String getName() { 49 | return name; 50 | } 51 | 52 | public void setName(String name) { 53 | this.name = name; 54 | } 55 | 56 | public String getPassword() { 57 | return password; 58 | } 59 | 60 | public void setPassword(String password) { 61 | this.password = password; 62 | } 63 | 64 | public String getEmail() { 65 | return email; 66 | } 67 | 68 | public void setEmail(String email) { 69 | this.email = email; 70 | } 71 | 72 | public Integer getAge() { 73 | return age; 74 | } 75 | 76 | public void setAge(Integer age) { 77 | this.age = age; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/resources/springMVC.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/webapp/WEB-INF/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

首页

9 | 跳转目标页面 10 | 跳转Get目标页面 11 | 跳转Post目标页面 12 | 跳转params目标页面 13 | 跳转params目标页面 14 | restful 15 | 16 |
17 | 18 |
19 | 20 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | 28 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/webapp/WEB-INF/templates/target.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

目标页面

9 | 10 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/webapp/WEB-INF/templates/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

参数测试

9 | servletAPi跳转目标页面 10 | testParam跳转目标页面 11 | testHeader跳转目标页面 12 |
13 | 用户名: 14 | 性别: 15 | 爱好: 16 | a 17 | b 18 | c 19 | 20 |
21 | 22 |
23 | 用户名: 24 | 密码: 25 | 邮箱: 26 | 年龄: 27 | 爱好: 28 | a 29 | b 30 | c 31 | 32 |
33 | 34 | -------------------------------------------------------------------------------- /SpringMVC_Request/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | characterEncodingFilter 11 | org.springframework.web.filter.CharacterEncodingFilter 12 | 13 | encoding 14 | UTF-8 15 | 16 | 17 | forceResponseEncoding 18 | true 19 | 20 | 21 | 22 | 23 | characterEncodingFilter 24 | /* 25 | 26 | 27 | 28 | 29 | dispatcherServlet 30 | org.springframework.web.servlet.DispatcherServlet 31 | 32 | contextConfigLocation 33 | classpath:springMVC.xml 34 | 35 | 36 | 37 | 38 | dispatcherServlet 39 | / 40 | 41 | -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mowangblog/SpringMVC-Demo/30b1af935a02706627dd3f7e6a9cf28533e28dbb/docs/.nojekyll -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Spring-MVC-Demo 2 | 3 | [![](https://res.mowangblog.top/img/2021/10/website-blog-lightgrey)](https://mowangblog.top) [![](https://res.mowangblog.top/img/2021/10/bilibili-video-orange)](https://www.bilibili.com/video/BV1Ry4y1574R) 4 | 5 | 📓Spring MVC 框架使用演示和详细笔记 6 | 7 | # 一、SpringMVC简介 8 | 9 | ### 1、什么是MVC 10 | 11 | MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分 12 | 13 | M:Model,模型层,指工程中的JavaBean,作用是处理数据 14 | 15 | JavaBean分为两类: 16 | 17 | - 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等 18 | - 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。 19 | 20 | V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据 21 | 22 | C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器 23 | 24 | MVC的工作流程: 25 | 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果找到相应的View视图,渲染数据后最终响应给浏览器 26 | 27 | ### 2、什么是SpringMVC 28 | 29 | SpringMVC是Spring的一个后续产品,是Spring的一个子项目 30 | 31 | SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目表述层开发的**首选方案**。 32 | 33 | > 注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台servlet 34 | 35 | ### 3、SpringMVC的特点 36 | 37 | - **Spring 家族原生产品**,与 IOC 容器等基础设施无缝对接 38 | - **基于原生的Servlet**,通过了功能强大的**前端控制器DispatcherServlet**,对请求和响应进行统一处理 39 | - 表述层各细分领域需要解决的问题**全方位覆盖**,提供**全面解决方案** 40 | - **代码清新简洁**,大幅度提升开发效率 41 | - 内部组件化程度高,可插拔式组件**即插即用**,想要什么功能配置相应组件即可 42 | - **性能卓著**,尤其适合现代大型、超大型互联网项目要求 43 | 44 | # 二、HelloWorld 45 | 46 | ### 1、开发环境 47 | 48 | IDE:idea 2019.2 49 | 50 | 构建工具:maven3.5.4 51 | 52 | 服务器:tomcat7 53 | 54 | Spring版本:5.3.1 55 | 56 | ### 2、创建maven工程 57 | 58 | ##### a>添加web模块 59 | 60 | ##### b>打包方式:war 61 | 62 | ##### c>引入依赖 63 | 64 | ```xml 65 | 66 | 67 | 68 | org.springframework 69 | spring-webmvc 70 | 5.3.1 71 | 72 | 73 | 74 | 75 | ch.qos.logback 76 | logback-classic 77 | 1.2.3 78 | 79 | 80 | 81 | 82 | javax.servlet 83 | javax.servlet-api 84 | 3.1.0 85 | provided 86 | 87 | 88 | 89 | 90 | org.thymeleaf 91 | thymeleaf-spring5 92 | 3.0.12.RELEASE 93 | 94 | 95 | 123456789101112131415161718192021222324252627282930 96 | ``` 97 | 98 | 注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。 99 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/836b26dcd15140e486cb3a566abcc2b0.png) 100 | 101 | ### 3、配置web.xml 102 | 103 | 注册SpringMVC的前端控制器DispatcherServlet 104 | 105 | ##### a>默认配置方式 106 | 107 | 此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为-servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml 108 | 109 | ```xml 110 | 111 | 112 | springMVC 113 | org.springframework.web.servlet.DispatcherServlet 114 | 115 | 116 | springMVC 117 | 122 | / 123 | 124 | 1234567891011121314 125 | ``` 126 | 127 | ##### b>扩展配置方式 128 | 129 | 可通过init-param标签设置SpringMVC配置文件的位置和名称,通过load-on-startup标签设置SpringMVC前端控制器DispatcherServlet的初始化时间 130 | 131 | ```xml 132 | 133 | 134 | springMVC 135 | org.springframework.web.servlet.DispatcherServlet 136 | 137 | 138 | 139 | contextConfigLocation 140 | 141 | classpath:springMVC.xml 142 | 143 | 148 | 1 149 | 150 | 151 | springMVC 152 | 157 | / 158 | 159 | 123456789101112131415161718192021222324252627 160 | ``` 161 | 162 | > 注: 163 | > 164 | > 标签中使用/和/*的区别: 165 | > 166 | > /所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/不能匹配.jsp请求路径的请求 167 | > 168 | > 因此就可以避免在访问jsp页面时,该请求被DispatcherServlet处理,从而找不到相应的页面 169 | > 170 | > /*则能够匹配所有请求,例如在使用过滤器时,若需要对所有请求进行过滤,就需要使用/*的写法 171 | 172 | ### 4、创建请求控制器 173 | 174 | 由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器 175 | 176 | 请求控制器中每一个处理请求的方法成为控制器方法 177 | 178 | 因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在 179 | 180 | ```java 181 | @Controller 182 | public class HelloController { 183 | 184 | } 185 | 1234 186 | ``` 187 | 188 | ### 5、创建springMVC的配置文件 189 | 190 | ```xml 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | text/html 232 | application/json 233 | 234 | 235 | 236 | 237 | 238 | 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 239 | ``` 240 | 241 | ### 6、测试HelloWorld 242 | 243 | ##### a>实现对首页的访问 244 | 245 | 在请求控制器中创建处理请求的方法 246 | 247 | ```java 248 | // @RequestMapping注解:处理请求和控制器方法之间的映射关系 249 | // @RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径 250 | // localhost:8080/springMVC/ 251 | @RequestMapping("/") 252 | public String index() { 253 | //设置视图名称 254 | return "index"; 255 | } 256 | 12345678 257 | ``` 258 | 259 | ##### b>通过超链接跳转到指定页面 260 | 261 | 在主页index.html中设置超链接 262 | 263 | ```html 264 | 265 | 266 | 267 | 268 | 首页 269 | 270 | 271 |

首页

272 | HelloWorld
273 | 274 | 275 | 1234567891011 276 | ``` 277 | 278 | 在请求控制器中创建处理请求的方法 279 | 280 | ```java 281 | @RequestMapping("/hello") 282 | public String HelloWorld() { 283 | return "target"; 284 | } 285 | 1234 286 | ``` 287 | 288 | ### 7、总结 289 | 290 | 浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面 291 | 292 | # 三、@RequestMapping注解 293 | 294 | ### 1、@RequestMapping注解的功能 295 | 296 | 从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。 297 | 298 | SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。 299 | 300 | ### 2、@RequestMapping注解的位置 301 | 302 | @RequestMapping标识一个类:设置映射请求的请求路径的初始信息 303 | 304 | @RequestMapping标识一个方法:设置映射请求请求路径的具体信息 305 | 306 | ```java 307 | @Controller 308 | @RequestMapping("/test") 309 | public class RequestMappingController { 310 | 311 | //此时请求映射所映射的请求的请求路径为:/test/testRequestMapping 312 | @RequestMapping("/testRequestMapping") 313 | public String testRequestMapping(){ 314 | return "success"; 315 | } 316 | 317 | } 318 | 1234567891011 319 | ``` 320 | 321 | ### 3、@RequestMapping注解的value属性 322 | 323 | @RequestMapping注解的value属性通过请求的请求地址匹配请求映射 324 | 325 | @RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求 326 | 327 | @RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射 328 | 329 | ```html 330 | 测试@RequestMapping的value属性-->/testRequestMapping
331 | 测试@RequestMapping的value属性-->/test
332 | 12 333 | @RequestMapping( 334 | value = {"/testRequestMapping", "/test"} 335 | ) 336 | public String testRequestMapping(){ 337 | return "success"; 338 | } 339 | 123456 340 | ``` 341 | 342 | ### 4、@RequestMapping注解的method属性 343 | 344 | @RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射 345 | 346 | @RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配多种请求方式的请求 347 | 348 | 若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错405:Request method ‘POST’ not supported 349 | 350 | ```html 351 | 测试@RequestMapping的value属性-->/test
352 |
353 | 354 |
355 | 1234 356 | @RequestMapping( 357 | value = {"/testRequestMapping", "/test"}, 358 | method = {RequestMethod.GET, RequestMethod.POST} 359 | ) 360 | public String testRequestMapping(){ 361 | return "success"; 362 | } 363 | 1234567 364 | ``` 365 | 366 | > 注: 367 | > 368 | > 1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解 369 | > 370 | > 处理get请求的映射–>@GetMapping 371 | > 372 | > 处理post请求的映射–>@PostMapping 373 | > 374 | > 处理put请求的映射–>@PutMapping 375 | > 376 | > 处理delete请求的映射–>@DeleteMapping 377 | > 378 | > 2、常用的请求方式有get,post,put,delete 379 | > 380 | > 但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理 381 | > 382 | > 若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在RESTful部分会讲到 383 | 384 | ### 5、@RequestMapping注解的params属性(了解) 385 | 386 | @RequestMapping注解的params属性通过请求的请求参数匹配请求映射 387 | 388 | @RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数和请求映射的匹配关系 389 | 390 | “param”:要求请求映射所匹配的请求必须携带param请求参数 391 | 392 | “!param”:要求请求映射所匹配的请求必须不能携带param请求参数 393 | 394 | “param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value 395 | 396 | “param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value 397 | 398 | ```html 399 | 测试@RequestMapping的params属性-->/test
400 | 1 401 | @RequestMapping( 402 | value = {"/testRequestMapping", "/test"} 403 | ,method = {RequestMethod.GET, RequestMethod.POST} 404 | ,params = {"username","password!=123456"} 405 | ) 406 | public String testRequestMapping(){ 407 | return "success"; 408 | } 409 | 12345678 410 | ``` 411 | 412 | > 注: 413 | > 414 | > 若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时页面回报错400:Parameter conditions “username, password!=123456” not met for actual request parameters: username={admin}, password={123456} 415 | 416 | ### 6、@RequestMapping注解的headers属性(了解) 417 | 418 | @RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射 419 | 420 | @RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信息和请求映射的匹配关系 421 | 422 | “header”:要求请求映射所匹配的请求必须携带header请求头信息 423 | 424 | “!header”:要求请求映射所匹配的请求必须不能携带header请求头信息 425 | 426 | “header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value 427 | 428 | “header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value 429 | 430 | 若当前请求满足@RequestMapping注解的value和method属性,但是不满足headers属性,此时页面显示404错误,即资源未找到 431 | 432 | ### 7、SpringMVC支持ant风格的路径 433 | 434 | ?:表示任意的单个字符 435 | 436 | *:表示任意的0个或多个字符 437 | 438 | **:表示任意的一层或多层目录 439 | 440 | 注意:在使用**时,只能使用/**/xxx的方式 441 | 442 | ### 8、SpringMVC支持路径中的占位符(重点) 443 | 444 | 原始方式:/deleteUser?id=1 445 | 446 | rest方式:/deleteUser/1 447 | 448 | SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参 449 | 450 | ```html 451 | 测试路径中的占位符-->/testRest
452 | 1 453 | @RequestMapping("/testRest/{id}/{username}") 454 | public String testRest(@PathVariable("id") String id, @PathVariable("username") String username){ 455 | System.out.println("id:"+id+",username:"+username); 456 | return "success"; 457 | } 458 | //最终输出的内容为-->id:1,username:admin 459 | 123456 460 | ``` 461 | 462 | # 四、SpringMVC获取请求参数 463 | 464 | ### 1、通过ServletAPI获取 465 | 466 | 将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象 467 | 468 | ```java 469 | @RequestMapping("/testParam") 470 | public String testParam(HttpServletRequest request){ 471 | String username = request.getParameter("username"); 472 | String password = request.getParameter("password"); 473 | System.out.println("username:"+username+",password:"+password); 474 | return "success"; 475 | } 476 | 1234567 477 | ``` 478 | 479 | ### 2、通过控制器方法的形参获取请求参数 480 | 481 | 在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在DispatcherServlet中就会将请求参数赋值给相应的形参 482 | 483 | ```html 484 | 测试获取请求参数-->/testParam
485 | 1 486 | @RequestMapping("/testParam") 487 | public String testParam(String username, String password){ 488 | System.out.println("username:"+username+",password:"+password); 489 | return "success"; 490 | } 491 | 12345 492 | ``` 493 | 494 | > 注: 495 | > 496 | > 若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数 497 | > 498 | > 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据 499 | > 500 | > 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果 501 | 502 | ### 3、@RequestParam 503 | 504 | @RequestParam是将请求参数和控制器方法的形参创建映射关系 505 | 506 | @RequestParam注解一共有三个属性: 507 | 508 | value:指定为形参赋值的请求参数的参数名 509 | 510 | required:设置是否必须传输此请求参数,默认值为true 511 | 512 | 若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter ‘xxx’ is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null 513 | 514 | defaultValue:不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值 515 | 516 | ### 4、@RequestHeader 517 | 518 | @RequestHeader是将请求头信息和控制器方法的形参创建映射关系 519 | 520 | @RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam 521 | 522 | ### 5、@CookieValue 523 | 524 | @CookieValue是将cookie数据和控制器方法的形参创建映射关系 525 | 526 | @CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam 527 | 528 | ### 6、通过POJO获取请求参数 529 | 530 | 可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值 531 | 532 | ```html 533 |
534 | 用户名:
535 | 密码:
536 | 性别:
537 | 年龄:
538 | 邮箱:
539 | 540 |
541 | 12345678 542 | @RequestMapping("/testpojo") 543 | public String testPOJO(User user){ 544 | System.out.println(user); 545 | return "success"; 546 | } 547 | //最终结果-->User{id=null, username='张三', password='123', age=23, sex='男', email='123@qq.com'} 548 | 123456 549 | ``` 550 | 551 | ### 7、解决获取请求参数的乱码问题 552 | 553 | 解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是必须在web.xml中进行注册 554 | 555 | ```xml 556 | 557 | 558 | CharacterEncodingFilter 559 | org.springframework.web.filter.CharacterEncodingFilter 560 | 561 | encoding 562 | UTF-8 563 | 564 | 565 | forceResponseEncoding 566 | true 567 | 568 | 569 | 570 | CharacterEncodingFilter 571 | /* 572 | 573 | 1234567891011121314151617 574 | ``` 575 | 576 | > 注: 577 | > 578 | > SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效 579 | 580 | # 五、域对象共享数据 581 | 582 | ### 1、使用ServletAPI向request域对象共享数据 583 | 584 | ```java 585 | @RequestMapping("/testServletAPI") 586 | public String testServletAPI(HttpServletRequest request){ 587 | request.setAttribute("testScope", "hello,servletAPI"); 588 | return "success"; 589 | } 590 | 12345 591 | ``` 592 | 593 | ### 2、使用ModelAndView向request域对象共享数据 594 | 595 | ```java 596 | @RequestMapping("/testModelAndView") 597 | public ModelAndView testModelAndView(){ 598 | /** 599 | * ModelAndView有Model和View的功能 600 | * Model主要用于向请求域共享数据 601 | * View主要用于设置视图,实现页面跳转 602 | */ 603 | ModelAndView mav = new ModelAndView(); 604 | //向请求域共享数据 605 | mav.addObject("testScope", "hello,ModelAndView"); 606 | //设置视图,实现页面跳转 607 | mav.setViewName("success"); 608 | return mav; 609 | } 610 | 1234567891011121314 611 | ``` 612 | 613 | ### 3、使用Model向request域对象共享数据 614 | 615 | ```java 616 | @RequestMapping("/testModel") 617 | public String testModel(Model model){ 618 | model.addAttribute("testScope", "hello,Model"); 619 | return "success"; 620 | } 621 | 12345 622 | ``` 623 | 624 | ### 4、使用map向request域对象共享数据 625 | 626 | ```java 627 | @RequestMapping("/testMap") 628 | public String testMap(Map map){ 629 | map.put("testScope", "hello,Map"); 630 | return "success"; 631 | } 632 | 12345 633 | ``` 634 | 635 | ### 5、使用ModelMap向request域对象共享数据 636 | 637 | ```java 638 | @RequestMapping("/testModelMap") 639 | public String testModelMap(ModelMap modelMap){ 640 | modelMap.addAttribute("testScope", "hello,ModelMap"); 641 | return "success"; 642 | } 643 | 12345 644 | ``` 645 | 646 | ### 6、Model、ModelMap、Map的关系 647 | 648 | Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的 649 | 650 | ``` 651 | public interface Model{} 652 | public class ModelMap extends LinkedHashMap {} 653 | public class ExtendedModelMap extends ModelMap implements Model {} 654 | public class BindingAwareModelMap extends ExtendedModelMap {} 655 | 1234 656 | ``` 657 | 658 | ### 7、向session域共享数据 659 | 660 | ```java 661 | @RequestMapping("/testSession") 662 | public String testSession(HttpSession session){ 663 | session.setAttribute("testSessionScope", "hello,session"); 664 | return "success"; 665 | } 666 | 12345 667 | ``` 668 | 669 | ### 8、向application域共享数据 670 | 671 | ```java 672 | @RequestMapping("/testApplication") 673 | public String testApplication(HttpSession session){ 674 | ServletContext application = session.getServletContext(); 675 | application.setAttribute("testApplicationScope", "hello,application"); 676 | return "success"; 677 | } 678 | 123456 679 | ``` 680 | 681 | # 六、SpringMVC的视图 682 | 683 | SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户 684 | 685 | SpringMVC视图的种类很多,默认有转发视图和重定向视图 686 | 687 | 当工程引入jstl的依赖,转发视图会自动转换为JstlView 688 | 689 | 若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView 690 | 691 | ### 1、ThymeleafView 692 | 693 | 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转 694 | 695 | ```java 696 | @RequestMapping("/testHello") 697 | public String testHello(){ 698 | return "hello"; 699 | } 700 | 1234 701 | ``` 702 | 703 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/781f6b299e6b41a8b006866ecbcb76ba.png) 704 | 705 | ### 2、转发视图 706 | 707 | SpringMVC中默认的转发视图是InternalResourceView 708 | 709 | SpringMVC中创建转发视图的情况: 710 | 711 | 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转 712 | 713 | 例如"forward:/",“forward:/employee” 714 | 715 | ```java 716 | @RequestMapping("/testForward") 717 | public String testForward(){ 718 | return "forward:/testHello"; 719 | } 720 | 1234 721 | ``` 722 | 723 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/71526c269bbb447b8701d906b2859965.png) 724 | 725 | ### 3、重定向视图 726 | 727 | SpringMVC中默认的重定向视图是RedirectView 728 | 729 | 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转 730 | 731 | 例如"redirect:/",“redirect:/employee” 732 | 733 | ```java 734 | @RequestMapping("/testRedirect") 735 | public String testRedirect(){ 736 | return "redirect:/testHello"; 737 | } 738 | 1234 739 | ``` 740 | 741 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/ec5b5371ea804cafb27e2751231df362.png) 742 | 743 | > 注: 744 | > 745 | > 重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头,若是则会自动拼接上下文路径 746 | 747 | ### 4、视图控制器view-controller 748 | 749 | 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用view-controller标签进行表示 750 | 751 | ```xml 752 | 756 | 757 | 12345 758 | ``` 759 | 760 | > 注: 761 | > 762 | > 当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签: 763 | > 764 | > 765 | 766 | # 七、RESTful 767 | 768 | ### 1、RESTful简介 769 | 770 | REST:**Re**presentational **S**tate **T**ransfer,表现层资源状态转移。 771 | 772 | ##### a>资源 773 | 774 | 资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。 775 | 776 | ##### b>资源的表述 777 | 778 | 资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。 779 | 780 | ##### c>状态转移 781 | 782 | 状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。 783 | 784 | ### 2、RESTful的实现 785 | 786 | 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。 787 | 788 | 它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。 789 | 790 | REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。 791 | 792 | | 操作 | 传统方式 | REST风格 | 793 | | -------- | ---------------- | ---------------------- | 794 | | 查询操作 | getUserById?id=1 | user/1–>get请求方式 | 795 | | 保存操作 | saveUser | user–>post请求方式 | 796 | | 删除操作 | deleteUser?id=1 | user/1–>delete请求方式 | 797 | | 更新操作 | updateUser | user–>put请求方式 | 798 | 799 | ### 3、HiddenHttpMethodFilter 800 | 801 | 由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢? 802 | 803 | SpringMVC 提供了 **HiddenHttpMethodFilter** 帮助我们**将 POST 请求转换为 DELETE 或 PUT 请求** 804 | 805 | **HiddenHttpMethodFilter** 处理put和delete请求的条件: 806 | 807 | a>当前请求的请求方式必须为post 808 | 809 | b>当前请求必须传输请求参数_method 810 | 811 | 满足以上条件,**HiddenHttpMethodFilter** 过滤器就会将当前请求的请求方式转换为请求参数_method的值,因此请求参数_method的值才是最终的请求方式 812 | 813 | 在web.xml中注册**HiddenHttpMethodFilter** 814 | 815 | ```xml 816 | 817 | HiddenHttpMethodFilter 818 | org.springframework.web.filter.HiddenHttpMethodFilter 819 | 820 | 821 | HiddenHttpMethodFilter 822 | /* 823 | 824 | 12345678 825 | ``` 826 | 827 | > 注: 828 | > 829 | > 目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和HiddenHttpMethodFilter 830 | > 831 | > 在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter 832 | > 833 | > 原因: 834 | > 835 | > - 在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的 836 | > 837 | > - request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作 838 | > 839 | > - 而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作: 840 | > 841 | > - ``` 842 | > String paramValue = request.getParameter(this.methodParam); 843 | > 1 844 | > ``` 845 | 846 | # 八、RESTful案例 847 | 848 | ### 1、准备工作 849 | 850 | 和传统 CRUD 一样,实现对员工信息的增删改查。 851 | 852 | - 搭建环境 853 | 854 | - 准备实体类 855 | 856 | ```java 857 | package com.atguigu.mvc.bean; 858 | 859 | public class Employee { 860 | 861 | private Integer id; 862 | private String lastName; 863 | 864 | private String email; 865 | //1 male, 0 female 866 | private Integer gender; 867 | 868 | public Integer getId() { 869 | return id; 870 | } 871 | 872 | public void setId(Integer id) { 873 | this.id = id; 874 | } 875 | 876 | public String getLastName() { 877 | return lastName; 878 | } 879 | 880 | public void setLastName(String lastName) { 881 | this.lastName = lastName; 882 | } 883 | 884 | public String getEmail() { 885 | return email; 886 | } 887 | 888 | public void setEmail(String email) { 889 | this.email = email; 890 | } 891 | 892 | public Integer getGender() { 893 | return gender; 894 | } 895 | 896 | public void setGender(Integer gender) { 897 | this.gender = gender; 898 | } 899 | 900 | public Employee(Integer id, String lastName, String email, Integer gender) { 901 | super(); 902 | this.id = id; 903 | this.lastName = lastName; 904 | this.email = email; 905 | this.gender = gender; 906 | } 907 | 908 | public Employee() { 909 | } 910 | } 911 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 912 | ``` 913 | 914 | - 准备dao模拟数据 915 | 916 | ```java 917 | package com.atguigu.mvc.dao; 918 | 919 | import java.util.Collection; 920 | import java.util.HashMap; 921 | import java.util.Map; 922 | 923 | import com.atguigu.mvc.bean.Employee; 924 | import org.springframework.stereotype.Repository; 925 | 926 | 927 | @Repository 928 | public class EmployeeDao { 929 | 930 | private static Map employees = null; 931 | 932 | static{ 933 | employees = new HashMap(); 934 | 935 | employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1)); 936 | employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1)); 937 | employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0)); 938 | employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0)); 939 | employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1)); 940 | } 941 | 942 | private static Integer initId = 1006; 943 | 944 | public void save(Employee employee){ 945 | if(employee.getId() == null){ 946 | employee.setId(initId++); 947 | } 948 | employees.put(employee.getId(), employee); 949 | } 950 | 951 | public Collection getAll(){ 952 | return employees.values(); 953 | } 954 | 955 | public Employee get(Integer id){ 956 | return employees.get(id); 957 | } 958 | 959 | public void delete(Integer id){ 960 | employees.remove(id); 961 | } 962 | } 963 | 12345678910111213141516171819202122232425262728293031323334353637383940414243444546 964 | ``` 965 | 966 | ### 2、功能清单 967 | 968 | | 功能 | URL 地址 | 请求方式 | 969 | | ------------------- | ----------- | -------- | 970 | | 访问首页√ | / | GET | 971 | | 查询全部数据√ | /employee | GET | 972 | | 删除√ | /employee/2 | DELETE | 973 | | 跳转到添加数据页面√ | /toAdd | GET | 974 | | 执行保存√ | /employee | POST | 975 | | 跳转到更新数据页面√ | /employee/2 | GET | 976 | | 执行更新√ | /employee | PUT | 977 | 978 | ### 3、具体功能:访问首页 979 | 980 | ##### a>配置view-controller 981 | 982 | ```xml 983 | 984 | 1 985 | ``` 986 | 987 | ##### b>创建页面 988 | 989 | ```html 990 | 991 | 992 | 993 | 994 | Title 995 | 996 | 997 |

首页

998 | 访问员工信息 999 | 1000 | 1001 | 1234567891011 1002 | ``` 1003 | 1004 | ### 4、具体功能:查询所有员工数据 1005 | 1006 | ##### a>控制器方法 1007 | 1008 | ```java 1009 | @RequestMapping(value = "/employee", method = RequestMethod.GET) 1010 | public String getEmployeeList(Model model){ 1011 | Collection employeeList = employeeDao.getAll(); 1012 | model.addAttribute("employeeList", employeeList); 1013 | return "employee_list"; 1014 | } 1015 | 123456 1016 | ``` 1017 | 1018 | ##### b>创建employee_list.html 1019 | 1020 | ```html 1021 | 1022 | 1023 | 1024 | 1025 | Employee Info 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1050 | 1051 |
Employee Info
idlastNameemailgenderoptions(add)
1047 | delete 1048 | update 1049 |
1052 | 1053 | 1054 | 123456789101112131415161718192021222324252627282930313233 1055 | ``` 1056 | 1057 | ### 5、具体功能:删除 1058 | 1059 | ##### a>创建处理delete请求方式的表单 1060 | 1061 | ```html 1062 | 1063 |
1064 | 1065 | 1066 |
1067 | 12345 1068 | ``` 1069 | 1070 | ##### b>删除超链接绑定点击事件 1071 | 1072 | 引入vue.js 1073 | 1074 | ```html 1075 | 1076 | 1 1077 | ``` 1078 | 1079 | 删除超链接 1080 | 1081 | ```html 1082 | delete 1083 | 1 1084 | ``` 1085 | 1086 | 通过vue处理点击事件 1087 | 1088 | ```html 1089 | 1107 | 123456789101112131415161718 1108 | ``` 1109 | 1110 | ##### c>控制器方法 1111 | 1112 | ```java 1113 | @RequestMapping(value = "/employee/{id}", method = RequestMethod.DELETE) 1114 | public String deleteEmployee(@PathVariable("id") Integer id){ 1115 | employeeDao.delete(id); 1116 | return "redirect:/employee"; 1117 | } 1118 | 12345 1119 | ``` 1120 | 1121 | ### 6、具体功能:跳转到添加数据页面 1122 | 1123 | ##### a>配置view-controller 1124 | 1125 | ```xml 1126 | 1127 | 1 1128 | ``` 1129 | 1130 | ##### b>创建employee_add.html 1131 | 1132 | ```html 1133 | 1134 | 1135 | 1136 | 1137 | Add Employee 1138 | 1139 | 1140 | 1141 |
1142 | lastName:
1143 | email:
1144 | gender:male 1145 | female
1146 |
1147 |
1148 | 1149 | 1150 | 1151 | 123456789101112131415161718 1152 | ``` 1153 | 1154 | ### 7、具体功能:执行保存 1155 | 1156 | ##### a>控制器方法 1157 | 1158 | ```java 1159 | @RequestMapping(value = "/employee", method = RequestMethod.POST) 1160 | public String addEmployee(Employee employee){ 1161 | employeeDao.save(employee); 1162 | return "redirect:/employee"; 1163 | } 1164 | 12345 1165 | ``` 1166 | 1167 | ### 8、具体功能:跳转到更新数据页面 1168 | 1169 | ##### a>修改超链接 1170 | 1171 | ```html 1172 | update 1173 | 1 1174 | ``` 1175 | 1176 | ##### b>控制器方法 1177 | 1178 | ```java 1179 | @RequestMapping(value = "/employee/{id}", method = RequestMethod.GET) 1180 | public String getEmployeeById(@PathVariable("id") Integer id, Model model){ 1181 | Employee employee = employeeDao.get(id); 1182 | model.addAttribute("employee", employee); 1183 | return "employee_update"; 1184 | } 1185 | 123456 1186 | ``` 1187 | 1188 | ##### c>创建employee_update.html 1189 | 1190 | ```html 1191 | 1192 | 1193 | 1194 | 1195 | Update Employee 1196 | 1197 | 1198 | 1199 |
1200 | 1201 | 1202 | lastName:
1203 | email:
1204 | 1208 | gender:male 1209 | female
1210 |
1211 |
1212 | 1213 | 1214 | 1215 | 123456789101112131415161718192021222324 1216 | ``` 1217 | 1218 | ### 9、具体功能:执行更新 1219 | 1220 | ##### a>控制器方法 1221 | 1222 | ```java 1223 | @RequestMapping(value = "/employee", method = RequestMethod.PUT) 1224 | public String updateEmployee(Employee employee){ 1225 | employeeDao.save(employee); 1226 | return "redirect:/employee"; 1227 | } 1228 | 12345 1229 | ``` 1230 | 1231 | # 八、HttpMessageConverter 1232 | 1233 | HttpMessageConverter,报文信息转换器,将请求报文转换为Java对象,或将Java对象转换为响应报文 1234 | 1235 | HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity, 1236 | 1237 | ResponseEntity 1238 | 1239 | ### 1、@RequestBody 1240 | 1241 | @RequestBody可以获取请求体,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值 1242 | 1243 | ```html 1244 |
1245 | 用户名:
1246 | 密码:
1247 | 1248 |
1249 | 12345 1250 | @RequestMapping("/testRequestBody") 1251 | public String testRequestBody(@RequestBody String requestBody){ 1252 | System.out.println("requestBody:"+requestBody); 1253 | return "success"; 1254 | } 1255 | 12345 1256 | ``` 1257 | 1258 | 输出结果: 1259 | 1260 | requestBody:username=admin&password=123456 1261 | 1262 | ### 2、RequestEntity 1263 | 1264 | RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息 1265 | 1266 | ```java 1267 | @RequestMapping("/testRequestEntity") 1268 | public String testRequestEntity(RequestEntity requestEntity){ 1269 | System.out.println("requestHeader:"+requestEntity.getHeaders()); 1270 | System.out.println("requestBody:"+requestEntity.getBody()); 1271 | return "success"; 1272 | } 1273 | 123456 1274 | ``` 1275 | 1276 | 输出结果: 1277 | requestHeader:[host:“localhost:8080”, connection:“keep-alive”, content-length:“27”, cache-control:“max-age=0”, sec-ch-ua:"" Not A;Brand";v=“99”, “Chromium”;v=“90”, “Google Chrome”;v=“90"”, sec-ch-ua-mobile:"?0", upgrade-insecure-requests:“1”, origin:“http://localhost:8080”, user-agent:“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36”] 1278 | requestBody:username=admin&password=123 1279 | 1280 | ### 3、@ResponseBody 1281 | 1282 | @ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器 1283 | 1284 | ```java 1285 | @RequestMapping("/testResponseBody") 1286 | @ResponseBody 1287 | public String testResponseBody(){ 1288 | return "success"; 1289 | } 1290 | 12345 1291 | ``` 1292 | 1293 | 结果:浏览器页面显示success 1294 | 1295 | ### 4、SpringMVC处理json 1296 | 1297 | @ResponseBody处理json的步骤: 1298 | 1299 | a>导入jackson的依赖 1300 | 1301 | ```xml 1302 | 1303 | com.fasterxml.jackson.core 1304 | jackson-databind 1305 | 2.12.1 1306 | 1307 | 12345 1308 | ``` 1309 | 1310 | b>在SpringMVC的核心配置文件中开启mvc的注解驱动,此时在HandlerAdaptor中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter,可以将响应到浏览器的Java对象转换为Json格式的字符串 1311 | 1312 | ``` 1313 | 1314 | 1 1315 | ``` 1316 | 1317 | c>在处理器方法上使用@ResponseBody注解进行标识 1318 | 1319 | d>将Java对象直接作为控制器方法的返回值返回,就会自动转换为Json格式的字符串 1320 | 1321 | ```java 1322 | @RequestMapping("/testResponseUser") 1323 | @ResponseBody 1324 | public User testResponseUser(){ 1325 | return new User(1001,"admin","123456",23,"男"); 1326 | } 1327 | 12345 1328 | ``` 1329 | 1330 | 浏览器的页面中展示的结果: 1331 | 1332 | {“id”:1001,“username”:“admin”,“password”:“123456”,“age”:23,“sex”:“男”} 1333 | 1334 | ### 5、SpringMVC处理ajax 1335 | 1336 | a>请求超链接: 1337 | 1338 | ```html 1339 |
1340 | testAjax
1341 |
1342 | 123 1343 | ``` 1344 | 1345 | b>通过vue和axios处理点击事件: 1346 | 1347 | ```html 1348 | 1349 | 1350 | 1370 | 12345678910111213141516171819202122 1371 | ``` 1372 | 1373 | c>控制器方法: 1374 | 1375 | ```java 1376 | @RequestMapping("/testAjax") 1377 | @ResponseBody 1378 | public String testAjax(String username, String password){ 1379 | System.out.println("username:"+username+",password:"+password); 1380 | return "hello,ajax"; 1381 | } 1382 | 123456 1383 | ``` 1384 | 1385 | ### 6、@RestController注解 1386 | 1387 | @RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解 1388 | 1389 | ### 7、ResponseEntity 1390 | 1391 | ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文 1392 | 1393 | # 九、文件上传和下载 1394 | 1395 | ### 1、文件下载 1396 | 1397 | 使用ResponseEntity实现下载文件的功能 1398 | 1399 | ```java 1400 | @RequestMapping("/testDown") 1401 | public ResponseEntity testResponseEntity(HttpSession session) throws IOException { 1402 | //获取ServletContext对象 1403 | ServletContext servletContext = session.getServletContext(); 1404 | //获取服务器中文件的真实路径 1405 | String realPath = servletContext.getRealPath("/static/img/1.jpg"); 1406 | //创建输入流 1407 | InputStream is = new FileInputStream(realPath); 1408 | //创建字节数组 1409 | byte[] bytes = new byte[is.available()]; 1410 | //将流读到字节数组中 1411 | is.read(bytes); 1412 | //创建HttpHeaders对象设置响应头信息 1413 | MultiValueMap headers = new HttpHeaders(); 1414 | //设置要下载方式以及下载文件的名字 1415 | headers.add("Content-Disposition", "attachment;filename=1.jpg"); 1416 | //设置响应状态码 1417 | HttpStatus statusCode = HttpStatus.OK; 1418 | //创建ResponseEntity对象 1419 | ResponseEntity responseEntity = new ResponseEntity<>(bytes, headers, statusCode); 1420 | //关闭输入流 1421 | is.close(); 1422 | return responseEntity; 1423 | } 1424 | 123456789101112131415161718192021222324 1425 | ``` 1426 | 1427 | ### 2、文件上传 1428 | 1429 | 文件上传要求form表单的请求方式必须为post,并且添加属性enctype=“multipart/form-data” 1430 | 1431 | SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息 1432 | 1433 | 上传步骤: 1434 | 1435 | a>添加依赖: 1436 | 1437 | ```xml 1438 | 1439 | 1440 | commons-fileupload 1441 | commons-fileupload 1442 | 1.3.1 1443 | 1444 | 123456 1445 | ``` 1446 | 1447 | b>在SpringMVC的配置文件中添加配置: 1448 | 1449 | ```xml 1450 | 1451 | 1452 | 12 1453 | ``` 1454 | 1455 | c>控制器方法: 1456 | 1457 | ```java 1458 | @RequestMapping("/testUp") 1459 | public String testUp(MultipartFile photo, HttpSession session) throws IOException { 1460 | //获取上传的文件的文件名 1461 | String fileName = photo.getOriginalFilename(); 1462 | //处理文件重名问题 1463 | String hzName = fileName.substring(fileName.lastIndexOf(".")); 1464 | fileName = UUID.randomUUID().toString() + hzName; 1465 | //获取服务器中photo目录的路径 1466 | ServletContext servletContext = session.getServletContext(); 1467 | String photoPath = servletContext.getRealPath("photo"); 1468 | File file = new File(photoPath); 1469 | if(!file.exists()){ 1470 | file.mkdir(); 1471 | } 1472 | String finalPath = photoPath + File.separator + fileName; 1473 | //实现上传功能 1474 | photo.transferTo(new File(finalPath)); 1475 | return "success"; 1476 | } 1477 | 12345678910111213141516171819 1478 | ``` 1479 | 1480 | # 十、拦截器 1481 | 1482 | ### 1、拦截器的配置 1483 | 1484 | SpringMVC中的拦截器用于拦截控制器方法的执行 1485 | 1486 | SpringMVC中的拦截器需要实现HandlerInterceptor 1487 | 1488 | SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置: 1489 | 1490 | ```xml 1491 | 1492 | 1493 | 1494 | 1495 | 1496 | 1497 | 1498 | 1499 | 1502 | 1234567891011 1503 | ``` 1504 | 1505 | ### 2、拦截器的三个抽象方法 1506 | 1507 | SpringMVC中的拦截器有三个抽象方法: 1508 | 1509 | preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法 1510 | 1511 | postHandle:控制器方法执行之后执行postHandle() 1512 | 1513 | afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation() 1514 | 1515 | ### 3、多个拦截器的执行顺序 1516 | 1517 | a>若每个拦截器的preHandle()都返回true 1518 | 1519 | 此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关: 1520 | 1521 | preHandle()会按照配置的顺序执行,而postHandle()和afterComplation()会按照配置的反序执行 1522 | 1523 | b>若某个拦截器的preHandle()返回了false 1524 | 1525 | preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行 1526 | 1527 | # 十一、异常处理器 1528 | 1529 | ### 1、基于配置的异常处理 1530 | 1531 | SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver 1532 | 1533 | HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和SimpleMappingExceptionResolver 1534 | 1535 | SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式: 1536 | 1537 | ```xml 1538 | 1539 | 1540 | 1541 | 1545 | error 1546 | 1547 | 1548 | 1551 | 1552 | 1553 | 123456789101112131415 1554 | ``` 1555 | 1556 | ### 2、基于注解的异常处理 1557 | 1558 | ```java 1559 | //@ControllerAdvice将当前类标识为异常处理的组件 1560 | @ControllerAdvice 1561 | public class ExceptionController { 1562 | 1563 | //@ExceptionHandler用于设置所标识方法处理的异常 1564 | @ExceptionHandler(ArithmeticException.class) 1565 | //ex表示当前请求处理中出现的异常对象 1566 | public String handleArithmeticException(Exception ex, Model model){ 1567 | model.addAttribute("ex", ex); 1568 | return "error"; 1569 | } 1570 | 1571 | } 1572 | 12345678910111213 1573 | ``` 1574 | 1575 | # 十二、注解配置SpringMVC 1576 | 1577 | 使用配置类和注解代替web.xml和SpringMVC配置文件的功能 1578 | 1579 | ### 1、创建初始化类,代替web.xml 1580 | 1581 | 在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类,如果找到的话就用它来配置Servlet容器。 1582 | Spring提供了这个接口的实现,名为SpringServletContainerInitializer,这个类反过来又会查找实现WebApplicationInitializer的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。 1583 | 1584 | ```java 1585 | public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { 1586 | 1587 | /** 1588 | * 指定spring的配置类 1589 | * @return 1590 | */ 1591 | @Override 1592 | protected Class[] getRootConfigClasses() { 1593 | return new Class[]{SpringConfig.class}; 1594 | } 1595 | 1596 | /** 1597 | * 指定SpringMVC的配置类 1598 | * @return 1599 | */ 1600 | @Override 1601 | protected Class[] getServletConfigClasses() { 1602 | return new Class[]{WebConfig.class}; 1603 | } 1604 | 1605 | /** 1606 | * 指定DispatcherServlet的映射规则,即url-pattern 1607 | * @return 1608 | */ 1609 | @Override 1610 | protected String[] getServletMappings() { 1611 | return new String[]{"/"}; 1612 | } 1613 | 1614 | /** 1615 | * 添加过滤器 1616 | * @return 1617 | */ 1618 | @Override 1619 | protected Filter[] getServletFilters() { 1620 | CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); 1621 | encodingFilter.setEncoding("UTF-8"); 1622 | encodingFilter.setForceRequestEncoding(true); 1623 | HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); 1624 | return new Filter[]{encodingFilter, hiddenHttpMethodFilter}; 1625 | } 1626 | } 1627 | 123456789101112131415161718192021222324252627282930313233343536373839404142 1628 | ``` 1629 | 1630 | ### 2、创建SpringConfig配置类,代替spring的配置文件 1631 | 1632 | ```java 1633 | @Configuration 1634 | public class SpringConfig { 1635 | //ssm整合之后,spring的配置信息写在此类中 1636 | } 1637 | 1234 1638 | ``` 1639 | 1640 | ### 3、创建WebConfig配置类,代替SpringMVC的配置文件 1641 | 1642 | ```java 1643 | @Configuration 1644 | //扫描组件 1645 | @ComponentScan("com.atguigu.mvc.controller") 1646 | //开启MVC注解驱动 1647 | @EnableWebMvc 1648 | public class WebConfig implements WebMvcConfigurer { 1649 | 1650 | //使用默认的servlet处理静态资源 1651 | @Override 1652 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 1653 | configurer.enable(); 1654 | } 1655 | 1656 | //配置文件上传解析器 1657 | @Bean 1658 | public CommonsMultipartResolver multipartResolver(){ 1659 | return new CommonsMultipartResolver(); 1660 | } 1661 | 1662 | //配置拦截器 1663 | @Override 1664 | public void addInterceptors(InterceptorRegistry registry) { 1665 | FirstInterceptor firstInterceptor = new FirstInterceptor(); 1666 | registry.addInterceptor(firstInterceptor).addPathPatterns("/**"); 1667 | } 1668 | 1669 | //配置视图控制 1670 | 1671 | /*@Override 1672 | public void addViewControllers(ViewControllerRegistry registry) { 1673 | registry.addViewController("/").setViewName("index"); 1674 | }*/ 1675 | 1676 | //配置异常映射 1677 | /*@Override 1678 | public void configureHandlerExceptionResolvers(List resolvers) { 1679 | SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver(); 1680 | Properties prop = new Properties(); 1681 | prop.setProperty("java.lang.ArithmeticException", "error"); 1682 | //设置异常映射 1683 | exceptionResolver.setExceptionMappings(prop); 1684 | //设置共享异常信息的键 1685 | exceptionResolver.setExceptionAttribute("ex"); 1686 | resolvers.add(exceptionResolver); 1687 | }*/ 1688 | 1689 | //配置生成模板解析器 1690 | @Bean 1691 | public ITemplateResolver templateResolver() { 1692 | WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); 1693 | // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得 1694 | ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( 1695 | webApplicationContext.getServletContext()); 1696 | templateResolver.setPrefix("/WEB-INF/templates/"); 1697 | templateResolver.setSuffix(".html"); 1698 | templateResolver.setCharacterEncoding("UTF-8"); 1699 | templateResolver.setTemplateMode(TemplateMode.HTML); 1700 | return templateResolver; 1701 | } 1702 | 1703 | //生成模板引擎并为模板引擎注入模板解析器 1704 | @Bean 1705 | public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { 1706 | SpringTemplateEngine templateEngine = new SpringTemplateEngine(); 1707 | templateEngine.setTemplateResolver(templateResolver); 1708 | return templateEngine; 1709 | } 1710 | 1711 | //生成视图解析器并未解析器注入模板引擎 1712 | @Bean 1713 | public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { 1714 | ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); 1715 | viewResolver.setCharacterEncoding("UTF-8"); 1716 | viewResolver.setTemplateEngine(templateEngine); 1717 | return viewResolver; 1718 | } 1719 | 1720 | 1721 | } 1722 | 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879 1723 | ``` 1724 | 1725 | ### 4、测试功能 1726 | 1727 | ```java 1728 | @RequestMapping("/") 1729 | public String index(){ 1730 | return "index"; 1731 | } 1732 | 1234 1733 | ``` 1734 | 1735 | # 十三、SpringMVC执行流程 1736 | 1737 | ### 1、SpringMVC常用组件 1738 | 1739 | - DispatcherServlet:**前端控制器**,不需要工程师开发,由框架提供 1740 | 1741 | 作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求 1742 | 1743 | - HandlerMapping:**处理器映射器**,不需要工程师开发,由框架提供 1744 | 1745 | 作用:根据请求的url、method等信息查找Handler,即控制器方法 1746 | 1747 | - Handler:**处理器**,需要工程师开发 1748 | 1749 | 作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理 1750 | 1751 | - HandlerAdapter:**处理器适配器**,不需要工程师开发,由框架提供 1752 | 1753 | 作用:通过HandlerAdapter对处理器(控制器方法)进行执行 1754 | 1755 | - ViewResolver:**视图解析器**,不需要工程师开发,由框架提供 1756 | 1757 | 作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView 1758 | 1759 | - View:**视图** 1760 | 1761 | 作用:将模型数据通过页面展示给用户 1762 | 1763 | ### 2、DispatcherServlet初始化过程 1764 | 1765 | DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。所以宏观上是 Servlet 生命周期来进行调度。 1766 | 1767 | [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kphToPhs-1627992919015)(img/img005.png)] 1768 | 1769 | ##### a>初始化WebApplicationContext 1770 | 1771 | 所在类:org.springframework.web.servlet.FrameworkServlet 1772 | 1773 | ```java 1774 | protected WebApplicationContext initWebApplicationContext() { 1775 | WebApplicationContext rootContext = 1776 | WebApplicationContextUtils.getWebApplicationContext(getServletContext()); 1777 | WebApplicationContext wac = null; 1778 | 1779 | if (this.webApplicationContext != null) { 1780 | // A context instance was injected at construction time -> use it 1781 | wac = this.webApplicationContext; 1782 | if (wac instanceof ConfigurableWebApplicationContext) { 1783 | ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; 1784 | if (!cwac.isActive()) { 1785 | // The context has not yet been refreshed -> provide services such as 1786 | // setting the parent context, setting the application context id, etc 1787 | if (cwac.getParent() == null) { 1788 | // The context instance was injected without an explicit parent -> set 1789 | // the root application context (if any; may be null) as the parent 1790 | cwac.setParent(rootContext); 1791 | } 1792 | configureAndRefreshWebApplicationContext(cwac); 1793 | } 1794 | } 1795 | } 1796 | if (wac == null) { 1797 | // No context instance was injected at construction time -> see if one 1798 | // has been registered in the servlet context. If one exists, it is assumed 1799 | // that the parent context (if any) has already been set and that the 1800 | // user has performed any initialization such as setting the context id 1801 | wac = findWebApplicationContext(); 1802 | } 1803 | if (wac == null) { 1804 | // No context instance is defined for this servlet -> create a local one 1805 | // 创建WebApplicationContext 1806 | wac = createWebApplicationContext(rootContext); 1807 | } 1808 | 1809 | if (!this.refreshEventReceived) { 1810 | // Either the context is not a ConfigurableApplicationContext with refresh 1811 | // support or the context injected at construction time had already been 1812 | // refreshed -> trigger initial onRefresh manually here. 1813 | synchronized (this.onRefreshMonitor) { 1814 | // 刷新WebApplicationContext 1815 | onRefresh(wac); 1816 | } 1817 | } 1818 | 1819 | if (this.publishContext) { 1820 | // Publish the context as a servlet context attribute. 1821 | // 将IOC容器在应用域共享 1822 | String attrName = getServletContextAttributeName(); 1823 | getServletContext().setAttribute(attrName, wac); 1824 | } 1825 | 1826 | return wac; 1827 | } 1828 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 1829 | ``` 1830 | 1831 | ##### b>创建WebApplicationContext 1832 | 1833 | 所在类:org.springframework.web.servlet.FrameworkServlet 1834 | 1835 | ```java 1836 | protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) { 1837 | Class contextClass = getContextClass(); 1838 | if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 1839 | throw new ApplicationContextException( 1840 | "Fatal initialization error in servlet with name '" + getServletName() + 1841 | "': custom WebApplicationContext class [" + contextClass.getName() + 1842 | "] is not of type ConfigurableWebApplicationContext"); 1843 | } 1844 | // 通过反射创建 IOC 容器对象 1845 | ConfigurableWebApplicationContext wac = 1846 | (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 1847 | 1848 | wac.setEnvironment(getEnvironment()); 1849 | // 设置父容器 1850 | wac.setParent(parent); 1851 | String configLocation = getContextConfigLocation(); 1852 | if (configLocation != null) { 1853 | wac.setConfigLocation(configLocation); 1854 | } 1855 | configureAndRefreshWebApplicationContext(wac); 1856 | 1857 | return wac; 1858 | } 1859 | 1234567891011121314151617181920212223 1860 | ``` 1861 | 1862 | ##### c>DispatcherServlet初始化策略 1863 | 1864 | FrameworkServlet创建WebApplicationContext后,刷新容器,调用onRefresh(wac),此方法在DispatcherServlet中进行了重写,调用了initStrategies(context)方法,初始化策略,即初始化DispatcherServlet的各个组件 1865 | 1866 | 所在类:org.springframework.web.servlet.DispatcherServlet 1867 | 1868 | ```java 1869 | protected void initStrategies(ApplicationContext context) { 1870 | initMultipartResolver(context); 1871 | initLocaleResolver(context); 1872 | initThemeResolver(context); 1873 | initHandlerMappings(context); 1874 | initHandlerAdapters(context); 1875 | initHandlerExceptionResolvers(context); 1876 | initRequestToViewNameTranslator(context); 1877 | initViewResolvers(context); 1878 | initFlashMapManager(context); 1879 | } 1880 | 1234567891011 1881 | ``` 1882 | 1883 | ### 3、DispatcherServlet调用组件处理请求 1884 | 1885 | ##### a>processRequest() 1886 | 1887 | FrameworkServlet重写HttpServlet中的service()和doXxx(),这些方法中调用了processRequest(request, response) 1888 | 1889 | 所在类:org.springframework.web.servlet.FrameworkServlet 1890 | 1891 | ```java 1892 | protected final void processRequest(HttpServletRequest request, HttpServletResponse response) 1893 | throws ServletException, IOException { 1894 | 1895 | long startTime = System.currentTimeMillis(); 1896 | Throwable failureCause = null; 1897 | 1898 | LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); 1899 | LocaleContext localeContext = buildLocaleContext(request); 1900 | 1901 | RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); 1902 | ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); 1903 | 1904 | WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 1905 | asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); 1906 | 1907 | initContextHolders(request, localeContext, requestAttributes); 1908 | 1909 | try { 1910 | // 执行服务,doService()是一个抽象方法,在DispatcherServlet中进行了重写 1911 | doService(request, response); 1912 | } 1913 | catch (ServletException | IOException ex) { 1914 | failureCause = ex; 1915 | throw ex; 1916 | } 1917 | catch (Throwable ex) { 1918 | failureCause = ex; 1919 | throw new NestedServletException("Request processing failed", ex); 1920 | } 1921 | 1922 | finally { 1923 | resetContextHolders(request, previousLocaleContext, previousAttributes); 1924 | if (requestAttributes != null) { 1925 | requestAttributes.requestCompleted(); 1926 | } 1927 | logResult(request, response, failureCause, asyncManager); 1928 | publishRequestHandledEvent(request, response, startTime, failureCause); 1929 | } 1930 | } 1931 | 123456789101112131415161718192021222324252627282930313233343536373839 1932 | ``` 1933 | 1934 | ##### b>doService() 1935 | 1936 | 所在类:org.springframework.web.servlet.DispatcherServlet 1937 | 1938 | ```java 1939 | @Override 1940 | protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 1941 | logRequest(request); 1942 | 1943 | // Keep a snapshot of the request attributes in case of an include, 1944 | // to be able to restore the original attributes after the include. 1945 | Map attributesSnapshot = null; 1946 | if (WebUtils.isIncludeRequest(request)) { 1947 | attributesSnapshot = new HashMap<>(); 1948 | Enumeration attrNames = request.getAttributeNames(); 1949 | while (attrNames.hasMoreElements()) { 1950 | String attrName = (String) attrNames.nextElement(); 1951 | if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { 1952 | attributesSnapshot.put(attrName, request.getAttribute(attrName)); 1953 | } 1954 | } 1955 | } 1956 | 1957 | // Make framework objects available to handlers and view objects. 1958 | request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); 1959 | request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver); 1960 | request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver); 1961 | request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); 1962 | 1963 | if (this.flashMapManager != null) { 1964 | FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response); 1965 | if (inputFlashMap != null) { 1966 | request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); 1967 | } 1968 | request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); 1969 | request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager); 1970 | } 1971 | 1972 | RequestPath requestPath = null; 1973 | if (this.parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) { 1974 | requestPath = ServletRequestPathUtils.parseAndCache(request); 1975 | } 1976 | 1977 | try { 1978 | // 处理请求和响应 1979 | doDispatch(request, response); 1980 | } 1981 | finally { 1982 | if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 1983 | // Restore the original attribute snapshot, in case of an include. 1984 | if (attributesSnapshot != null) { 1985 | restoreAttributesAfterInclude(request, attributesSnapshot); 1986 | } 1987 | } 1988 | if (requestPath != null) { 1989 | ServletRequestPathUtils.clearParsedRequestPath(request); 1990 | } 1991 | } 1992 | } 1993 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354 1994 | ``` 1995 | 1996 | ##### c>doDispatch() 1997 | 1998 | 所在类:org.springframework.web.servlet.DispatcherServlet 1999 | 2000 | ```java 2001 | protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { 2002 | HttpServletRequest processedRequest = request; 2003 | HandlerExecutionChain mappedHandler = null; 2004 | boolean multipartRequestParsed = false; 2005 | 2006 | WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); 2007 | 2008 | try { 2009 | ModelAndView mv = null; 2010 | Exception dispatchException = null; 2011 | 2012 | try { 2013 | processedRequest = checkMultipart(request); 2014 | multipartRequestParsed = (processedRequest != request); 2015 | 2016 | // Determine handler for the current request. 2017 | /* 2018 | mappedHandler:调用链 2019 | 包含handler、interceptorList、interceptorIndex 2020 | handler:浏览器发送的请求所匹配的控制器方法 2021 | interceptorList:处理控制器方法的所有拦截器集合 2022 | interceptorIndex:拦截器索引,控制拦截器afterCompletion()的执行 2023 | */ 2024 | mappedHandler = getHandler(processedRequest); 2025 | if (mappedHandler == null) { 2026 | noHandlerFound(processedRequest, response); 2027 | return; 2028 | } 2029 | 2030 | // Determine handler adapter for the current request. 2031 | // 通过控制器方法创建相应的处理器适配器,调用所对应的控制器方法 2032 | HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); 2033 | 2034 | // Process last-modified header, if supported by the handler. 2035 | String method = request.getMethod(); 2036 | boolean isGet = "GET".equals(method); 2037 | if (isGet || "HEAD".equals(method)) { 2038 | long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); 2039 | if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { 2040 | return; 2041 | } 2042 | } 2043 | 2044 | // 调用拦截器的preHandle() 2045 | if (!mappedHandler.applyPreHandle(processedRequest, response)) { 2046 | return; 2047 | } 2048 | 2049 | // Actually invoke the handler. 2050 | // 由处理器适配器调用具体的控制器方法,最终获得ModelAndView对象 2051 | mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 2052 | 2053 | if (asyncManager.isConcurrentHandlingStarted()) { 2054 | return; 2055 | } 2056 | 2057 | applyDefaultViewName(processedRequest, mv); 2058 | // 调用拦截器的postHandle() 2059 | mappedHandler.applyPostHandle(processedRequest, response, mv); 2060 | } 2061 | catch (Exception ex) { 2062 | dispatchException = ex; 2063 | } 2064 | catch (Throwable err) { 2065 | // As of 4.3, we're processing Errors thrown from handler methods as well, 2066 | // making them available for @ExceptionHandler methods and other scenarios. 2067 | dispatchException = new NestedServletException("Handler dispatch failed", err); 2068 | } 2069 | // 后续处理:处理模型数据和渲染视图 2070 | processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); 2071 | } 2072 | catch (Exception ex) { 2073 | triggerAfterCompletion(processedRequest, response, mappedHandler, ex); 2074 | } 2075 | catch (Throwable err) { 2076 | triggerAfterCompletion(processedRequest, response, mappedHandler, 2077 | new NestedServletException("Handler processing failed", err)); 2078 | } 2079 | finally { 2080 | if (asyncManager.isConcurrentHandlingStarted()) { 2081 | // Instead of postHandle and afterCompletion 2082 | if (mappedHandler != null) { 2083 | mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); 2084 | } 2085 | } 2086 | else { 2087 | // Clean up any resources used by a multipart request. 2088 | if (multipartRequestParsed) { 2089 | cleanupMultipart(processedRequest); 2090 | } 2091 | } 2092 | } 2093 | } 2094 | 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 2095 | ``` 2096 | 2097 | ##### d>processDispatchResult() 2098 | 2099 | ```java 2100 | private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, 2101 | @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, 2102 | @Nullable Exception exception) throws Exception { 2103 | 2104 | boolean errorView = false; 2105 | 2106 | if (exception != null) { 2107 | if (exception instanceof ModelAndViewDefiningException) { 2108 | logger.debug("ModelAndViewDefiningException encountered", exception); 2109 | mv = ((ModelAndViewDefiningException) exception).getModelAndView(); 2110 | } 2111 | else { 2112 | Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null); 2113 | mv = processHandlerException(request, response, handler, exception); 2114 | errorView = (mv != null); 2115 | } 2116 | } 2117 | 2118 | // Did the handler return a view to render? 2119 | if (mv != null && !mv.wasCleared()) { 2120 | // 处理模型数据和渲染视图 2121 | render(mv, request, response); 2122 | if (errorView) { 2123 | WebUtils.clearErrorRequestAttributes(request); 2124 | } 2125 | } 2126 | else { 2127 | if (logger.isTraceEnabled()) { 2128 | logger.trace("No view rendering, null ModelAndView returned."); 2129 | } 2130 | } 2131 | 2132 | if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { 2133 | // Concurrent handling started during a forward 2134 | return; 2135 | } 2136 | 2137 | if (mappedHandler != null) { 2138 | // Exception (if any) is already handled.. 2139 | // 调用拦截器的afterCompletion() 2140 | mappedHandler.triggerAfterCompletion(request, response, null); 2141 | } 2142 | } 2143 | 12345678910111213141516171819202122232425262728293031323334353637383940414243 2144 | ``` 2145 | 2146 | ### 4、SpringMVC的执行流程 2147 | 2148 | 1. 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。 2149 | 2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射: 2150 | 2151 | a) 不存在 2152 | 2153 | i. 再判断是否配置了mvc:default-servlet-handler 2154 | 2155 | ii. 如果没配置,则控制台报映射查找不到,客户端展示404错误 2156 | 2157 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/8df6a30e77184eb0bc1547dae5838f65.png) 2158 | 2159 | iii. 如果有配置,则访问目标资源(一般为静态资源,如:JS,CSS,HTML),找不到客户端也会展示404错误 2160 | 2161 | ![在这里插入图片描述](https://res.mowangblog.top/img/2021/10/67d25736b3b74dfe9145acedb0e8656d.png) 2162 | 2163 | b) 存在则执行下面的流程 2164 | 2165 | 1. 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。 2166 | 2. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。 2167 | 3. 如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】 2168 | 4. 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: 2169 | 2170 | a) HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息 2171 | 2172 | b) 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等 2173 | 2174 | c) 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等 2175 | 2176 | d) 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 2177 | 2178 | 1. Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。 2179 | 2. 此时将开始执行拦截器的postHandle(…)方法【逆向】。 2180 | 3. 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。 2181 | 4. 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。 2182 | 5. 将渲染结果返回给客户端。 2183 | -------------------------------------------------------------------------------- /docs/_coverpage.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # **SpringMVC-Demo** 4 | 5 | - 本文档是 ```SpringMVC```框架的课堂笔记
为大家提供一个较完整的在线笔记
笔记来源于B站SpringMVC教程,下面是视频链接 6 | 7 | [![stars](https://badgen.net/github/stars/mowangblog/SpringMVC-Demo?color=4ab8a1)](https://github.com/mowangblog/SpringMVC-Demo) 8 | [![forks](https://badgen.net/github/forks/mowangblog/SpringMVC-Demo?color=4ab8a1)](https://github.com/mowangblog/SpringMVC-Demo) 9 | [![](https://res.mowangblog.top/img/2021/10/bilibili-video-orange)](https://www.bilibili.com/video/BV1Ry4y1574R) 10 | 11 | [GitHub](https://github.com/mowangblog/SpringMVC-Demo) 12 | [开始阅读](?id=spring-mvc-demo) -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | top.mowang 8 | Spring-MVC-Demo 9 | 1.0-SNAPSHOT 10 | 11 | SpringMVC_Request 12 | 13 | pom 14 | 15 | 16 | 17 | 8 18 | 8 19 | 20 | 21 | 22 | --------------------------------------------------------------------------------