SpringMVC简介
什么是MVC
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。
M:Model,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
- 一类称为实体类Bean:专门存储业务数据的,如 Student、User 等。
- 一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据。
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器。
MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果 找到相应的View视图,渲染数据后最终响应给浏览器。
什么是SpringMVC
SpringMVC是Spring的一个后续产品,是Spring的一个子项目。
SpringMVC 是 Spring 为表述层开发提供的一整套完备的解决方案。在表述层框架历经 Strust、 WebWork、Strust2 等诸多产品的历代更迭之后,目前业界普遍选择了 SpringMVC 作为 Java EE 项目 表述层开发的首选方案。
注:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 servlet
SpringMVC快速入门
创建Maven工程
- 添加Web模块

修改路径在src\main\webapp
下

pom.xml
打包方式修改成war
包
1
| <packaging>war</packaging>
|
- 添加依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.1</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.12.RELEASE</version> </dependency> </dependencies>
|
注:由于 Maven 的传递性,我们不必将所有需要的包全部配置依赖,而是配置最顶端的依赖,其他靠传递性导入。
配置web.xml
注册SpringMVC的前端控制器DispatcherServlet。
默认配置方式
此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为- servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVCservlet.xml。
1 2 3 4 5 6 7 8 9 10 11 12
| <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern> </servlet-mapping>
|
扩展配置方式
可通过init-param
标签设置SpringMVC配置文件的位置和名称,通过load-on-startup
标签设置SpringMVC前端控制器DispatcherServlet的初始化时间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <servlet> <servlet-name>springMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servletclass> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param>
<load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern> </servlet-mapping>
|
注意:<url-pattern>
中
/
所匹配的请求可以是/login或.html或.js或.css方式的请求路径,但是/
不能匹配.jsp请求路径的请求。
/:匹配浏览器向服务器发送的所有请求(不包括.jsp)
/*:匹配浏览器向服务器发送的所有请求(包括.jsp)
扩展:
jsp的请求需要tomcat中的jspServlet来处理
创建请求控制器
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程,因此需要创建处理具体请求的类,即请求控制器。
请求控制器中每一个处理请求的方法成为控制器方法。
因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller注解将其标识 为一个控制层组件,交给Spring的IoC容器管理,此时SpringMVC才能够识别控制器的存在。
1 2 3
| @Controller public class HelloController { }
|
创建SpringMVC的配置文件
1 2 3 4 5 6 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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <context:component-scan base-package="com.atguigu.mvc.controller"/>
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <property name="order" value="1"/> <property name="characterEncoding" value="UTF-8"/> <property name="templateEngine"> <bean class="org.thymeleaf.spring5.SpringTemplateEngine"> <property name="templateResolver"> <bean class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/WEB-INF/templates/"/> <property name="suffix" value=".html"/> <property name="templateMode" value="HTML5"/> <property name="characterEncoding" value="UTF-8" /> </bean> </property> </bean> </property> </bean>
<mvc:default-servlet-handler/>
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="defaultCharset" value="UTF-8" /> <property name="supportedMediaTypes"> <list> <value>text/html</value> <value>application/json</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven>
|
物理视图和逻辑视图
物理视图
在Servlet中,将请求转发到一个HTML页面文件时,使用的完整的转发路径就是物理视图。
/pages/user/login_success.html
如果我们把所有的HTML页面都放在某个统一的目录下,那么转发地址就会呈现出明显的规律:
/pages/user/login.html /pages/user/login_success.html /pages/user/regist.html
/pages/user/regist_success.html
……
路径的开头都是:/pages/user/
路径的结尾都是:.html
所以,路径开头的部分我们称之为视图前缀,路径结尾的部分我们称之为视图后缀。
逻辑视图
物理视图=视图前缀+逻辑视图+视图后缀
上面的例子中:
视图前缀 |
逻辑视图 |
视图后缀 |
物理视图 |
/pages/user/ |
login |
.html |
/pages/user/login.html |
/pages/user/ |
login_success |
.html |
/pages/user/login_success.html |
测试helloWorld
- 在请求控制器中创建处理请求的方法。
1 2 3 4 5 6 7 8
|
@RequestMapping("/") public String index() { return "index"; }
|
- 通过超链接跳转到指定页面
1 2 3 4 5 6 7 8 9 10 11
| <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>首页</h1> <a th:href="@{/hello}">HelloWorld</a><br/> </body> </html>
|
- 在请求控制器中创建处理请求的方法
1 2 3 4
| @RequestMapping("/hello") public String HelloWorld() { return "target"; }
|
总结
浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器DispatcherServlet处理。前端控制器会读取SpringMVC的核心配置文件,通过扫描组件找到控制器, 将请求地址和控制器中@RequestMapping注解的value属性值进行匹配,若匹配成功,该注解所标识的 控制器方法就是处理请求的方法。处理请求的方法需要返回一个字符串类型的视图名称,该视图名称会 被视图解析器解析,加上前缀和后缀组成视图的路径,通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面。
@RequestMapping注解
@RequestMapping注解的功能
@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
@RequestMapping注解的位置
@RequestMapping标识一个类:设置映射请求的请求路径的初始信息。
@RequestMapping标识一个方法:设置映射请求请求路径的具体信息。
1 2 3 4 5 6 7 8 9 10
| @Controller @RequestMapping("/test") public class RequestMappingController { @RequestMapping("/testRequestMapping") public String testRequestMapping(){ return "success"; } }
|
@RequestMapping注解的value属性
@RequestMapping注解的value属性通过请求的请求地址匹配请求映射。
@RequestMapping注解的value属性是一个字符串类型的数组,表示该请求映射能够匹配多个请求地址所对应的请求。
@RequestMapping注解的value属性必须设置,至少通过请求地址匹配请求映射。
1 2
| <a th:href="@{/testRequestMapping}">测试@RequestMapping的value属性-->/testRequestMapping</a><br> <a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br>
|
1 2 3 4 5 6
| @RequestMapping( value = {"/testRequestMapping", "/test"} ) public String testRequestMapping(){ return "success"; }
|
@RequestMapping注解的method属性
@RequestMapping注解的method属性通过请求的请求方式(get或post)匹配请求映射。
@RequestMapping注解的method属性是一个RequestMethod类型的数组,表示该请求映射能够匹配 多种请求方式的请求。
若当前请求的请求地址满足请求映射的value属性,但是请求方式不满足method属性,则浏览器报错:
405:Request method ‘POST’ not supported
1 2 3 4
| <a th:href="@{/test}">测试@RequestMapping的value属性-->/test</a><br> <form th:action="@{/test}" method="post"> <input type="submit"> </form>
|
1 2 3 4 5 6 7
| @RequestMapping( value = {"/testRequestMapping", "/test"}, method = {RequestMethod.GET, RequestMethod.POST} ) public String testRequestMapping(){ return "success"; }
|
注:
1、对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
- 处理get请求的映射–>@GetMapping
- 处理post请求的映射–>@PostMapping
- 处理put请求的映射–>@PutMapping
- 处理delete请求的映射–>@DeleteMapping
2、常用的请求方式有get,post,put,delete
但是目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符 串(put或delete),则按照默认的请求方式get处理。
若要发送put和delete请求,则需要通过spring提供的过滤器HiddenHttpMethodFilter,在 RESTful部分会讲到。
@RequestMapping注解的params属性(了解)
@RequestMapping注解的params属性通过请求的请求参数匹配请求映射。
@RequestMapping注解的params属性是一个字符串类型的数组,可以通过四种表达式设置请求参数 和请求映射的匹配关系。
- “param”:要求请求映射所匹配的请求必须携带param请求参数
- “!param”:要求请求映射所匹配的请求必须不能携带param请求参数
- “param=value”:要求请求映射所匹配的请求必须携带param请求参数且param=value
- “param!=value”:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
1 2 3
| <a th:href="@{/test(username='admin',password=123456)"> 测试@RequestMapping的params属性-->/test </a><br>
|
1 2 3 4 5 6 7 8
| @RequestMapping( value = {"/testRequestMapping", "/test"} ,method = {RequestMethod.GET, RequestMethod.POST} ,params = {"username","password!=123456"} ) public String testRequestMapping(){ return "success"; }
|
@RequestMapping注解的headers属性通过请求的请求头信息匹配请求映射。
@RequestMapping注解的headers属性是一个字符串类型的数组,可以通过四种表达式设置请求头信 息和请求映射的匹配关系。
- “header”:要求请求映射所匹配的请求必须携带header请求头信息
- “!header”:要求请求映射所匹配的请求必须不能携带header请求头信息
- “header=value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
- “header!=value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value
SpringMVC支持ant风格的路径
?:表示任意的单个字符
*:表示任意的0个或多个字符
**:表示任意层数的任意目录
1
| 注意:在使用**时,只能使用/**/xxx的方式
|
SpringMVC支持路径中的占位符(重点)
原始方式:/deleteUser?id=1
rest方式:/user/delete/1
SpringMVC路径中的占位符常用于RESTful风格中,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}表示传输的数据,在 通过@PathVariable注解,将占位符所表示的数据赋值给控制器方法的形参。
1
| <a th:href="@{/testRest/1/admin}">测试路径中的占位符-->/testRest</a><br>
|
1 2 3 4 5 6 7 8
| @RequestMapping("/testRest/{id}/{username}") public String testRest(@PathVariable("id") String id, @PathVariable("username"), String username){ System.out.println("id:"+id+",username:"+username); return "success"; }
|
SpringMVC获取请求参数
通过ServletAPI获取
将HttpServletRequest作为控制器方法的形参,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象。
1 2 3 4 5 6 7
| @RequestMapping("/testParam") public String testParam(HttpServletRequest request){ String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println("username:"+username+",password:"+password); return "success"; }
|
通过控制器方法的形参获取请求参数
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在 DispatcherServlet中就会将请求参数赋值给相应的形参。
1 2 3
| <a th:href="@{/testParam(username='admin',password=123456)}"> 测试获取请求参数-->/testParam </a><br>
|
1 2 3 4 5
| @RequestMapping("/testParam") public String testParam(String username, String password){ System.out.println("username:"+username+",password:"+password); return "success"; }
|
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数。
@RequestParam
@RequestParam是将请求参数和控制器方法的形参创建映射关系
@RequestParam注解一共有三个属性:
- value:指定为形参赋值的请求参数的参数名。
- required:设置是否必须传输此请求参数,默认值为true。
- 若设置为true时,则当前请求必须传输value所指定的请求参数。若没有传输该请求参数,且没有设置 defaultValue属性,则页面报错
400:Required String parameter 'xxx' is not present
- 若设置为 false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为 null
- defaultValue:不管required属性值为true或false,当value所指定的请求参数**没有传输或传输的值为””**时,则使用默认值为形参赋值。
@RequestHeader是将请求头信息和控制器方法的形参创建映射关系。
@RequestHeader注解一共有三个属性:value、required、defaultValue,用法同@RequestParam。
@CookieValue
@CookieValue是将cookie数据和控制器方法的形参创建映射关系 。
@CookieValue注解一共有三个属性:value、required、defaultValue,用法同@RequestParam。
通过POJO获取请求参数
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实 体类中的属性名一致,那么请求参数就会为此属性赋值。
1 2 3 4 5 6 7 8 9
| <form th:action="@{/testpojo}" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> 性别:<input type="radio" name="sex" value="男">男 <input type="radio" name="sex" value="女">女<br> 年龄:<input type="text" name="age"><br> 邮箱:<input type="text" name="email"><br> <input type="submit"> </form>
|
1 2 3 4 5 6
| @RequestMapping("/testpojo") public String testPOJO(User user){ System.out.println(user); return "success"; }
|
解决获取请求参数的乱码问题
解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter,但是 必须在web.xml中进行注册。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
|
注意:SpringMVC中处理编码的过滤器一定要配置到其他过滤器之前,否则无效。
域对象共享数据
使用ServletAPI向request域对象共享数据
1 2 3 4 5
| @RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request){ request.setAttribute("testScope", "hello,servletAPI"); return "success"; }
|
1
| <p th:text="${testScope}"></p>
|
使用ModelAndView向request域对象共享数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){
ModelAndView mav = new ModelAndView(); mav.addObject("testScope", "hello,ModelAndView"); mav.setViewName("success"); return mav; }
|
使用Model向request域对象共享数据
1 2 3 4 5
| @RequestMapping("/testModel") public String testModel(Model model){ model.addAttribute("testScope", "hello,Model"); return "success"; }
|
使用ModelMap向request域对象共享数据
1 2 3 4 5
| @RequestMapping("/testModelMap") public String testModelMap(ModelMap modelMap){ modelMap.addAttribute("testScope", "hello,ModelMap"); return "success"; }
|
使用map向request域对象共享数据
1 2 3 4 5
| @RequestMapping("/testMap") public String testMap(Map<String, Object> map){ map.put("testScope", "hello,Map"); return "success"; }
|
Model、ModelMap、Map的关系
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的。
1 2 3 4
| public interface Model{} public class ModelMap extends LinkedHashMap<String, Object> {} public class ExtendedModelMap extends ModelMap implements Model {} public class BindingAwareModelMap extends ExtendedModelMap {}
|
向session域共享数据
1 2 3 4 5
| @RequestMapping("/testSession") public String testSession(HttpSession session){ session.setAttribute("testSessionScope", "hello,session"); return "success"; }
|
1
| <p th:text="${session.testSessionScope}"></p>
|
向application域共享数据
1 2 3 4 5 6
| @RequestMapping("/testApplication") public String testApplication(HttpSession session){ ServletContext application = session.getServletContext(); application.setAttribute("testApplicationScope", "hello,application"); return "success"; }
|
1
| <p th:text="${session.testApplicationScope}"></p>
|
SpringMVC的视图
SpringMVC中的视图是View接口,视图的作用渲染数据,将模型Model中的数据展示给用户。
SpringMVC视图的种类很多,默认有转发视图和重定向视图。
当工程引入jstl的依赖,转发视图会自动转换为JstlView。
若使用的视图技术为Thymeleaf,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView。
ThymeleafView
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置 的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转。
1 2 3 4
| @RequestMapping("/testHello") public String testHello(){ return "hello"; }
|
转发视图
SpringMVC中默认的转发视图是InternalResourceView
当控制器方法中所设置的视图名称以”forward:
“为前缀时,创建InternalResourceView视图,此时的视 图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”forward:”去掉,剩余部分作为最终路径通过转发的方式实现跳转。
1 2 3 4
| @RequestMapping("/testForward") public String testForward(){ return "forward:/testHello"; }
|
注意:使用转发时通常使用第一种ThymeleafView方式,因为使用forward:
实现时,不会进行页面的渲染,页面使用ThymeleafView模板语法时会失效。
重定向视图
SpringMVC中默认的重定向视图是RedirectView。
当控制器方法中所设置的视图名称以”redirect:
“为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀”redirect:”去掉,剩余部分作为最 终路径通过重定向的方式实现跳转。
1 2 3 4
| @RequestMapping("/testRedirect") public String testRedirect(){ return "redirect:/testHello"; }
|
视图控制器view-controller
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用viewcontroller标签进行表示。
1 2 3 4 5 6 7
|
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
<mvc:annotation-driven />
|
注: 当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需要在
SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />
RESTful
RESTful的实现
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方 式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。
它们分别对应四种基本操作:GET 用来获取资源
,POST 用来新建资源
,PUT 用来更新资源
,DELETE 用来删除资源
。
操作 |
传统方式 |
REST风格 |
查询操作 |
getUserById?id=1 |
user/1–>get请求方式 |
保存操作 |
saveUser |
user–>post请求方式 |
更新操作 |
updateUser |
user–>put请求方式 |
删除操作 |
deleteUser?id=1 |
user/1–>delete请求方式 |
HiddenHttpMethodFilter
由于浏览器只支持发送get和post方式的请求,那么该如何发送put和delete请求呢?
SpringMVC 提供了 HiddenHttpMethodFilter
帮助我们将 POST 请求转换为 DELETE 或 PUT 请求。
但必须满足两个条件
- 当前请求的请求方式必须为post。
- 当前请求必须传输请求参数_method。
1 2 3 4 5
| <form th:action="@{/user}" method="post"> <input type="hidden" name="_method" value="put"> <input type="submit" value="修改信息"/> </form>
|
满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式转换为请求参数 _method的值,因此请求参数_method的值才是最终的请求方式。
在web.xml中注册HiddenHttpMethodFilter
1 2 3 4 5 6 7 8
| <filter> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filterclass> </filter> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
|
注:
目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter和 HiddenHttpMethodFilter。
在web.xml中注册时,必须先注册CharacterEncodingFilter,再注册HiddenHttpMethodFilter。
原因:
在 CharacterEncodingFilter 中通过 request.setCharacterEncoding(encoding) 方法设置字符集的。
request.setCharacterEncoding(encoding) 方法要求前面不能有任何获取请求参数的操作
而 HiddenHttpMethodFilter 恰恰有一个获取请求方式的操作:
1
| String paramValue = request.getParameter(this.methodParam);
|
SpringMVC处理ajax请求
@RequestBody
@RequestBody可以获取请求体信息,使用@RequestBody注解标识控制器方法的形参,当前请求的请求体就会为当前注解所标识的形参赋值。
1 2 3 4 5 6
| <form th:action="@{/test/RequestBody}" method="post"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"><br> <input type="submit"> </form>
|
1 2 3 4 5
| @RequestMapping("/test/RequestBody") public String testRequestBody(@RequestBody String requestBody){ System.out.println("requestBody:"+requestBody); return "success"; }
|
输出结果:requestBody:username=admin&password=123456
@RequestBody获取json格式的请求参数
在使用了axios发送ajax请求之后,浏览器发送到服务器的请求参数有两种格式:
name=value&name=value…
此时的请求参数可以通过request.getParameter()获取,对应 SpringMVC中,可以直接通过控制器方法的形参获取此类请求参数。
{key:value,key:value,…}
此时无法通过request.getParameter()获取,之前我们使用操作 json的相关jar包gson或jackson处理此类请求参数,可以将其转换为指定的实体类对象或map集 合。在SpringMVC中,直接使用@RequestBody
注解标识控制器方法的形参即可将此类请求参数 转换为java对象
使用@RequestBody获取json格式的请求参数的条件:
- 导入jackson的依赖
1 2 3 4 5
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency>
|
- 开启mvc的注解驱动
1 2
| <mvc:annotation-driven />
|
3、在控制器方法的形参位置,设置json格式的请求参数要转换成的java类型(实体类或map)的参数,并使用@RequestBody注解标识
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| <input type="button" value="测试@RequestBody获取json格式的请求参数" @click="testRequestBody()"><br> <script type="text/javascript" th:src="@{/js/vue.js}"></script> <script type="text/javascript" th:src="@{/js/axios.min.js}"></script> <script type="text/javascript"> var vue = new Vue({ el:"#app", methods:{ testRequestBody(){ axios.post( "/SpringMVC/test/RequestBody/json", {username:"admin",password:"123456"} ).then(response=>{ console.log(response.data); }); } } }); </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @RequestMapping("/test/RequestBody/json") public void testRequestBody(@RequestBody Map<String, Object> map, HttpServletResponse response) throws IOException { System.out.println(map); response.getWriter().print("hello,axios"); }
@RequestMapping("/test/RequestBody/json") public void testRequestBody(@RequestBody User user, HttpServletResponse response) throws IOException { System.out.println(user); gender='null'} response.getWriter().print("hello,axios"); }
|
@ResponseBody
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到 浏览器
1 2 3 4 5 6 7 8 9 10 11
| @RequestMapping("/testResponseBody") public String testResponseBody(){ return "success"; } @RequestMapping("/testResponseBody") @ResponseBody public String testResponseBody(){ return "success"; }
|
@ResponseBody响应浏览器json数据
服务器处理ajax请求之后,大多数情况都需要向浏览器响应一个java对象,此时必须将java对象转换为 json字符串才可以响应到浏览器,之前我们使用操作json数据的jar包gson或jackson将java对象转换为 json字符串。在SpringMVC中,我们可以直接使用@ResponseBody
注解实现此功能。
@ResponseBody响应浏览器json数据的条件:
- 导入jackson的依赖
1 2 3 4 5
| <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.12.1</version> </dependency>
|
- 开启SpringMVC的注解驱动
1 2
| <mvc:annotation-driven />
|
- 使用@ResponseBody注解标识控制器方法,在方法中,将需要转换为json字符串并响应到浏览器的java对象作为控制器方法的返回值,此时SpringMVC就可以将此对象直接转换为json字符串并响应到浏览器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <input type="button" value="测试@ResponseBody响应浏览器json格式的数据" @click="testResponseBody()"><br> <script type="text/javascript" th:src="@{/js/vue.js}"></script> <script type="text/javascript" th:src="@{/js/axios.min.js}"></script> <script type="text/javascript"> var vue = new Vue({ el:"#app", methods:{ testResponseBody(){ axios.post("/SpringMVC/test/ResponseBody/json").then(response=>{ console.log(response.data); }); } } }); </script>
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @RequestMapping("/test/ResponseBody/json") @ResponseBody public List<User> testResponseBody(){ User user1 = new User(1001,"admin1","123456",23,"男"); User user2 = new User(1002,"admin2","123456",23,"男"); User user3 = new User(1003,"admin3","123456",23,"男"); List<User> list = Arrays.asList(user1, user2, user3); return list; }
@RequestMapping("/test/ResponseBody/json") @ResponseBody public Map<String, Object> testResponseBody(){ User user1 = new User(1001,"admin1","123456",23,"男"); User user2 = new User(1002,"admin2","123456",23,"男"); User user3 = new User(1003,"admin3","123456",23,"男"); Map<String, Object> map = new HashMap<>(); map.put("1001", user1); map.put("1002", user2); map.put("1003", user3); return map; }
@RequestMapping("/test/ResponseBody/json") @ResponseBody public User testResponseBody(){ return user; }
|
@RestController注解
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了 @Controller注解,并且为其中的每个方法添加了@ResponseBody注解。
文件上传和下载
文件下载
ResponseEntity
用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文。
使用ResponseEntity实现下载文件的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| @RequestMapping("/testDown") public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException { ServletContext servletContext = session.getServletContext(); String realPath = servletContext.getRealPath("/static/img/1.jpg"); InputStream is = new FileInputStream(realPath); byte[] bytes = new byte[is.available()]; is.read(bytes); MultiValueMap<String, String> headers = new HttpHeaders(); headers.add("Content-Disposition", "attachment;filename=1.jpg"); HttpStatus statusCode = HttpStatus.OK; ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers,statusCode); is.close(); return responseEntity; }
|
文件上传
文件上传要求form表单的请求方式必须为post,并且添加属性enctype=”multipart/form-data”
SpringMVC中将上传的文件封装到MultipartFile对象中,通过此对象可以获取文件相关信息。
上传步骤
- 添加依赖:
1 2 3 4 5
| <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency>
|
- 在SpringMVC的配置文件中添加配置:
1 2 3 4
| <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> </bean>
|
- 控制器方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @RequestMapping("/testUp") public String testUp(MultipartFile photo, HttpSession session) throws IOException { String fileName = photo.getOriginalFilename(); String hzName = fileName.substring(fileName.lastIndexOf(".")); fileName = UUID.randomUUID().toString() + hzName; ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("photo"); File file = new File(photoPath); if(!file.exists()){ file.mkdir(); } String finalPath = photoPath + File.separator + fileName; photo.transferTo(new File(finalPath)); return "success"; }
|
拦截器
拦截器的配置
SpringMVC中的拦截器用于拦截控制器方法的执行。
SpringMVC中的拦截器需要实现HandlerInterceptor
接口。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class FirstInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { }
@Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { }
@Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
|
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| <mvc:interceptors> <bean class="com.atguigu.interceptor.FirstInterceptor"></bean> <ref bean="firstInterceptor"></ref> <mvc:interceptor> <mvc:mapping path="/**"/> <mvc:exclude-mapping path="/testRequestEntity"/> <ref bean="firstInterceptor"></ref> </mvc:interceptor>
</mvc:interceptors>
|
拦截器的三个抽象方法
SpringMVC中的拦截器有三个抽象方法:
preHandle
:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返 回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法。
postHandle
:控制器方法执行之后执行postHandle()。
afterCompletion
:处理完视图和模型数据,渲染视图完毕之后执行afterCompletion()
多个拦截器的执行顺序
①若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
②若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false 的拦截器之前的拦截器的afterCompletion()会执行
异常处理器
基于配置的异常处理
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver。
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver和 SimpleMappingExceptionResolver
SpringMVC提供了自定义的异常处理器SimpleMappingExceptionResolver,使用方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props>
<prop key="java.lang.ArithmeticException">error</prop> </props> </property>
<property name="exceptionAttribute" value="ex"></property> </bean>
|
基于注解的异常处理
1 2 3 4 5 6 7 8 9 10 11
| @ControllerAdvice public class ExceptionController { @ExceptionHandler(ArithmeticException.class) public String handleArithmeticException(Exception ex, Model model){ model.addAttribute("ex", ex); return "error"; } }
|
注解配置SpringMVC
创建初始化类,代替web.xml
在Servlet3.0环境中,容器会在类路径中查找实现javax.servlet.ServletContainerInitializer接口的类, 如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为SpringServletContainerInitializer这个类反过来又会查找实现WebApplicationInitializer的类并将配 置的任务交给它们来完成。
Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为 AbstractAnnotationConfigDispatcherServletInitializer,当我们的类扩展了 AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自 动发现它,并用它来配置Servlet上下文。
1 2 3 4 5 6 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 33 34 35 36 37 38 39 40
| public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; }
@Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; }
@Override protected String[] getServletMappings() { return new String[]{"/"}; }
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter(); encodingFilter.setEncoding("UTF-8"); encodingFilter.setForceRequestEncoding(true); HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); return new Filter[]{encodingFilter, hiddenHttpMethodFilter}; } }
|
创建SpringConfig配置类,代替spring的配置文件
1 2 3 4
| @Configuration public class SpringConfig { }
|
创建WebConfig配置类,代替SpringMVC的配置文件
1 2 3 4 5 6 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 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 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| @Configuration
@ComponentScan("com.atguigu.controller")
@EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
@Bean public CommonsMultipartResolver multipartResolver(){ return new CommonsMultipartResolver(); } @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); } @Override public void addInterceptors(InterceptorRegistry registry) { FirstInterceptor firstInterceptor = new FirstInterceptor(); registry.addInterceptor(firstInterceptor).addPathPatterns("/**"); }
@Bean public ITemplateResolver templateResolver() { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); WebApplicationContext 的方法获得 ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver( webApplicationContext.getServletContext()); templateResolver.setPrefix("/WEB-INF/templates/"); templateResolver.setSuffix(".html"); templateResolver.setCharacterEncoding("UTF-8"); templateResolver.setTemplateMode(TemplateMode.HTML); return templateResolver; } @Bean public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } @Bean public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); viewResolver.setCharacterEncoding("UTF-8"); viewResolver.setTemplateEngine(templateEngine); return viewResolver; } }
|
SpringMVC的执行流程
常用组件
DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求
HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
作用:根据请求的url、method等信息查找Handler,即控制器方法
HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
作用:通过HandlerAdapter对处理器(控制器方法)进行执行
Handler:处理器(Controller),需要工程师开发
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理
ViewResolver:视图解析器,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、 RedirectView
View:视图
作用:将模型数据通过页面展示给用户
SpringMVC的执行流程
- 用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
- DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
- 不存在,报404错误
- 再判断是否配置了mvc:default-servlet-handler(视图控制器),没有报404错误
- 如果没配置,则控制台报映射查找不到,客户端展示404错误
- 根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及 Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象(包含三个内容,控制器方法,拦截器集合,拦截器索引)的形式返回。
- DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
- 如果成功获得HandlerAdapter,此时将**开始执行拦截器的preHandler(…)**方法【正向】
- 提取Request中的模型数据,填充Handler入参,**开始执行Handler(Controller)**方法,处理请求。
- Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
- 此时将**开始执行拦截器的postHandle(…)**方法【逆向】。
- 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行 HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model 和View,来渲染视图。
- 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
- 将渲染结果返回给客户端。
