SpringMVC-入门 [TOC]
0、资料 视频:
书:
博客:
1、SpringMVC 概述 1.1、什么是 MVC M:Model 模型,也就是广义上的JavaBean(Service、Dao、实体类)
V:View 视图,HTML、JSP等前端页面
C:Controller 控制器,也就是Sevlet
【流程】:
1.2、什么是 Spring MVC Spring MVC 是Spring 的一个子项目,是Spring 为表示层提供的一整套完备的解决方案。
注 :三层架构分为表示层、业务逻辑层、数据访问层。表示层主要是 前端页面 + 后端的Servlet 。
!!注意:!!!!!!
Spring 的配置文件 和 SpringMVC的配置文件可以是同一个文件,但为了方便管理,一般分开写。【格式完全一样】
1.3、Spring MVC 的特点
简洁、灵活 ,提高开发效率
Spring 的一部分
基于原生的Servlet,通过 DispacherServlet 来处理请求和响应。
全面覆盖表示层需 要解决的问题
组件化程度高,即插即用
性能好 ,适合大型、超大型互联网项目的要求。
1.4、Thymeleaf 基本用法 Thymeleaf 是一个服务端的Java 模板引擎,通过在 HTML 标签中嵌入特殊的语法糖,将后端的数据渲染到前端的模板页(替换模板页的占位符部分),生成结果文件,实现页面的展示效果。
Thymeleaf 的功能类似 JSTL 标签库,但与JSTL不同的是,浏览器可以直接预览模板文件,无需服务端支持。
官网:Thymeleaf
业界常用的模板:
FreeMarker
Thymeleaf
Velocity
Thymeleaf 的 Maven 坐标:
1 2 3 4 5 <dependency > <groupId > org.thymeleaf</groupId > <artifactId > thymeleaf</artifactId > <version > 3.0.14.RELEASE</version > </dependency >
语法:
1 2 3 4 5 6 7 8 9 10 11 // 使用语法:th:属性名=“${后端属性名}” // 在以下示例中,不连接服务器时,input标签的值是张三,连接服务器时,被替换 <input type ="text" name ="userName" value ="张三" th:value ="${user.name}" /> <p th:text ="${username}" > </p >
简单表达式:
变量表达式:${...}
选择变量表达式:*{...}
消息表达式:#{...}
链接网址表达式:@{...}
片段表达式:~{...}
url 传参:
1 2 3 <a th:href ="@{ /test/hello(username=‘zs’,pwd='root') }" > 跳转到 /test/hello 页面,并传参username 和 pwd </a >
案例:
创建普通的Maven项目,pom.xml 中导入坐标
创建 HelloThymeleaf
测试类
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 public class HelloThymeleaf { @Test public void test01 () { TemplateEngine engine = new TemplateEngine(); String inputStr = "<input type='text' th:value='${txt}'/>" ; Context context = new Context(); context.setVariable("txt" ,"hello_boys" ); String out = engine.process(inputStr,context); } @Test public void test02 () { TemplateEngine engine = new TemplateEngine(); ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); engine.setTemplateResolver(resolver); Context context = new Context(); context.setVariable("txt" ,"hello_boys" ); String outHtml = engine.process("main.html" ,context); } @Test public void test03 () { TemplateEngine engine = new TemplateEngine(); ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); resolver.setPrefix("templates/" ); resolver.setSuffix(".html" ); engine.setTemplateResolver(resolver); Context context = new Context(); context.setVariable("txt" ,"hello_boys" ); String outHtml = engine.process("index" ,context); } }
1.5、Spring MVC 版-Hello World 案例提示:
步骤:
创建Mavnen
项目
编写pom.xml
配置文件来导入jar包
在web.xml
中配置前端控制器DispacherServlet
创建请求控制器 HelloWorldController
类(一个被@Controller
注解标识的POJO)
在Spring的配置文件中开启组件扫描
在创建WEB-INF/templates/index.html
,并在Spring的配置文件中注册 视图解析器(案例使用了thymeleaf)
在请求控制器 HelloWorldController
中新建String index()
方法,返回视图名
在请求控制器 HelloWorldController
中新建String index()
方法上添加@RequestMapping("/")
注解,表示浏览器 url 访问 /
时,执行请求控制器的index()
方法
可以成功访问首页
在首页添加一个访问其他页面的超链接(<a th:href=“@{ /target }”>目标页</a>
)
IDEA中,创建Maven项目
(选择maven下的webapp模板)
项目的目录结构:
填写maven的配置文件:pom.xml
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 90 91 92 93 94 95 96 97 <?xml version="1.0" encoding="UTF-8"?> <project xmlns ="http://maven.apache.org/POM/4.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.cyw</groupId > <artifactId > SSM-05</artifactId > <version > 1.0-SNAPSHOT</version > <packaging > war</packaging > <name > SSM-05 Maven Webapp</name > <url > http://www.example.com</url > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.source > 1.7</maven.compiler.source > <maven.compiler.target > 1.7</maven.compiler.target > </properties > <dependencies > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.11</version > <scope > test</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > 5.3.9</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.10</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.thymeleaf</groupId > <artifactId > thymeleaf-spring5</artifactId > <version > 3.0.14.RELEASE</version > </dependency > </dependencies > <build > <finalName > SSM-05</finalName > <pluginManagement > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-surefire-plugin</artifactId > <configuration > <testFailureIgnore > true</testFailureIgnore > </configuration > </plugin > <plugin > <artifactId > maven-clean-plugin</artifactId > <version > 3.1.0</version > </plugin > <plugin > <artifactId > maven-resources-plugin</artifactId > <version > 3.0.2</version > </plugin > <plugin > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > </plugin > <plugin > <artifactId > maven-surefire-plugin</artifactId > <version > 2.22.1</version > </plugin > <plugin > <artifactId > maven-war-plugin</artifactId > <version > 3.2.2</version > </plugin > <plugin > <artifactId > maven-install-plugin</artifactId > <version > 2.5.2</version > </plugin > <plugin > <artifactId > maven-deploy-plugin</artifactId > <version > 2.8.2</version > </plugin > </plugins > </pluginManagement > </build > </project >
配置web.xml
,注册SpringMVC的前端控制器 DispacherServlet
默认配置的方式【不推荐】:
在此配置方式下,SpringMVC 的配置文件将默认放在WEB-INF
目录下,
且该 SpringMVC 配置文件的默认名称为servlet名-servlet.xml
。
例如:以下 SpringMVC 配置文件的名称为springMVC-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app > <display-name > Archetype Created Web Application</display-name > <servlet > <servlet-name > springMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > </servlet > <servlet-mapping > <servlet-name > springMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > </web-app >
扩展配置的方式【推荐】:
视频:https://www.bilibili.com/video/BV1Ry4y1574R?p=9
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 <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app > <display-name > Archetype Created Web Application</display-name > <servlet > <servlet-name > springMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath: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 > </web-app >
创建 请求控制器 HelloWorldController
(一个被@Controller
注解标识的POJO)
前端控制器DispatcherServlet
负责统一的处理,然后将不同的请求交给请求控制器 来处理(请求处理器)。
请求控制器 中的处理方法叫 控制器方法 。
1 2 3 4 5 6 7 8 package com.cyw.controller;import org.springframework.stereotype.Controller;@Controller public class HelloWorldController { }
开启组件扫描(在Spring的配置文件中)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd" > <context:component-scan base-package ="com.cyw.controller" /> </beans >
在WEB-INF/template
目录下创建 index.html
,在Spring 配置文件中,注册 Thymeleaf 视图解析器
创建index.html:(需要引入命名空间)
1 2 3 4 5 6 7 8 9 10 11 12 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 首页</title > </head > <body > <h1 > 首页</h1 > </body > </html >
在Spring的配置文件中,配置视图解析器(以 thymeleaf 的视图解析器为例):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <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 >
spring自带的视图解析器:
1 2 3 4 5 6 7 8 9 <context:component-scan base-package ="com.cyw.controller" /> <bean class ="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" /> <bean class ="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" />
在 请求控制器 上添加一个被RequestMapping("url路径")
注解修饰的控制方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.cyw.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class HelloWorldController { @RequestMapping("/") public String index () { return "index" ; } }
成功访问首页/
页面跳转
index.html
:
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > 首页</title > </head > <body > <h1 > 首页</h1 > <a th:href ="@{ /target }" > 目标页</a > </body > </html >
target.html
:
1 2 3 4 5 6 7 8 9 10 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 目标页</h1 > </body > </html >
请求控制器
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package com.cyw.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;@Controller public class HelloWorldController { @RequestMapping("/") public String index () { return "index" ; } @RequestMapping("/target") public String toTarget () { return "target" ; } }
1.6、Hello World 案例总结 环境搭建【视频】:https://www.bilibili.com/video/BV1Ry4y1574R?p=35
总结:
以上案例在进行页面跳转时,使用的是请求转发的方式。
2、SpringMVC 的注解 SpringMVC 的注解 可以放在类上,也可以放在方法上。
2.1、@RequestMapping 注解 2.1.1、@RequestMapping 的功能: @RequestMapping
注解的功能就是建立 请求路径 与 控制器方法 的映射关系,类似Servlet 中的@WebServlet
注解的作用。
2.1.2、@RequestMapping 的作用位置: @RequestMapping
注解 修饰一个类时:设置请求路径的初始信息
@RequestMapping
注解 修饰一个方法时:设置请求路径的具体信息
既修饰一个类,又修饰该类的方法,则先匹配类上的 url ,再匹配方法上的 url
控制器:
1 2 3 4 5 6 7 8 9 10 11 @Controller @RequestMapping("/test") public class RequestMappingController { @RequestMapping("/testRequestMapping") public String testRequestMapping () { return "success" ; } }
2.1.3、@RequestMapping 的 value 属性: @RequestMapping
的 value
属性,也就是 默认传入的属性(按 url 匹配处理方法 ),任何 htttp 的请求方式都行,如:
1 2 3 4 5 6 7 8 @RequestMapping("/hello") public String test01 () {...}------------------------------------------ @RequestMapping({"/hello",""/test""}) public String test02 () {...}
2.1.4、@RequestMapping 的 method 属性: @RequestMapping
的 method
属性,按照 HTTP 的请求方式(get、post、put、delete等)匹配控制器方法。当请求不满足要求 时,报405:Request method ‘POST’ not supported
错误。
注意:
目前浏览器只支持get和post,若在form表单提交时,为method设置了其他请求方式的字符串(put或delete),则按照默认的请求方式get处理。
http 请求方式不同的案例:
1 2 3 4 5 6 7 8 9 10 11 12 <form th:action="@{/test}" method="post" > <input type="submit" > </form> @RequestMapping( value = "/test", method = {RequestMethod.GET, RequestMethod.POST} ) public String test01 () { return "success" ; }
派生注解:
处理 get 请求的映射 –> @GetMapping
处理 post 请求的映射 –> @PostMapping
2.1.4、@RequestMapping 的 params 属性: @RequestMapping注解的 params 属性通过请求的请求参数 匹配请求映射。
@RequestMapping注解的 params 属性是一个字符串类型的数组 ,可以通过四种表达式 设置请求参数和请求映射的匹配关系。
params 属性的值的写法:
“ 参数 ”:要求请求映射所匹配的请求必须携带 param 请求参数
“ ! 参数”:要求请求映射所匹配的请求必须不能携带 param 请求参数
“ 参数 = value ”:要求请求映射所匹配的请求必须携带 param请求参数且 param = value
“ 参数 != value ”:要求请求映射所匹配的请求必须携带 param请求参数但是 param != value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <a th:href="@{/test(username='admin',password=123456)" > 测试@RequestMapping 的params属性-->/test </a> @RequestMapping( value = "/test" ,method = {RequestMethod.GET, RequestMethod.POST} ,params = {"username","password != 123456"} // 此处,参数username是为必选 ) public String testRequestMapping () { return "success" ; }
@RequestMapping
注解的headers
属性通过请求的请求头信息匹配请求映射
@RequestMapping注解的headers属性是一个字符串类型的数组 ,可以通过四种表达式设置请求头信息和请求映射的匹配关系
“header信息”:要求请求映射所匹配的请求必须携带header请求头信息
“ ! header信息”:要求请求映射所匹配的请求必须不能携带header请求头信息
“header信息= value”:要求请求映射所匹配的请求必须携带header请求头信息且header=value
“header信息 != value”:要求请求映射所匹配的请求必须携带header请求头信息且header!=value
若当前请求满足@RequestMapping
注解的value 和 method
属性,但是不满足headers
属性,此时页面显示404错误,即资源未找到
2.1.5、SpringMVC支持ant风格的路径 ant风格的路径:路径的通配符匹配。
?
:表示任意的单个 字符 => @RequestMapping(“/a?b/test”) => {“/a1b/test”,”/a2b/test”}
*
:表示任意的0~n个 字符 => @RequestMapping(“/a*b/test”) => {“/ab/test”,”/a1b/test”,”/a123b/test”}
**
:表示任意的一层或多层目录 => @RequestMapping(“/a**b/test”) => {“/a/b/test”,”/a/c/b/test”,”/a/c/d/b/test”}
注意:在使用 **
时,只能使用 /**/xxx
的方式
1 2 3 4 5 @RequestMapping("/a?b/test") public String testRequestMapping () { return "success" ; }
2.1.6、SpringMVC支持 RestFul 风格的路径占位符 原始方式:/deleteUser?id=1
RestFul方式:/deleteUser/1
SpringMVC路径中的占位符常用于RESTful 风格中,当请求路径中将某些数据通过路径的方式
传输到服务器中,就可以在相应的@RequestMapping注解的value属性中通过占位符{xxx}
表示传输的数据,在通过@PathVariable
注解,将占位符所表示的数据赋值
给控制器方法的形参。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <a th:href="@{/testRest/1/admin}" >测试路径中的占位符-->/testRest </a> <br> @RequestMapping("/testRest/{id}/{username}") public String testRest (@PathVariable("id") String id, @PathVariable("username") String username) { System.out.println("id:" +id+",username:" +username); return "success" ; }
2.2、获取请求参数 2.2.1、传统 Servlet 获取请求参数: 传统 Servlet 获取请求参数 是通过控制器参数中的 HttpServletRequest
对象的String getParamter("参数的键")
方法。
注意:前、后端的参数名要一致。
1 2 3 4 5 6 7 8 9 10 11 @ReuquestMapping("/hello/success") public String test01 (HttpServletRequest req) { String username = req.getParamter("username" ); String pwd = req.getParamter("pwd" ); System.out.println(username+"--- " + pwd); return "success" ; }
2.2.2、控制器方法的形参 获取请求参数: 注意:前、后端的参数名要一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ReuquestMapping("/hello/success") public String test01 (String username,String pwd) { System.out.println(username+"--- " + pwd); return "success" ; } @ReuquestMapping("/hello/test02") public String test02 (String username,String[] hobby) { System.out.println(username+"--- " + Arrays.toString(hobby)); return "success" ; }
2.2.3、@RequestParam 获取请求参数: 注意:前、后端的参数名可以不一致。
@RequestParam()
注解的作用:建立 前端的请求参数 与 后端控制器方法形参 的映射。
@RequestParam()
注解的 required
属性默认为 true
,不能装配则报错400,该属性表示是否为必须。
@RequestParam()
注解的 defaultValue
属性设置默认值,只要前端传过来的参数不存在或为null 时,就使用注解的 defaultValue
属性设置的默认值。
1 2 3 4 5 6 7 8 9 10 @ReuquestMapping("/hello/success") public String test01 (@RequestParam("user_name") String username) { System.out.println(username); return "success" ; }
@RequestHeader
注解的作用:建立 请求报文头的信息 与 控制器方法的形参 的映射关系。
该注解的三个参数,value、required、defaultValue。这三个注解的参数的用法同 2.2.3
小节@RequestParam
。
2.2.6、@CookieValue 获取请求参数: @CookieValue
注解的作用:建立 Cookie的信息 与 控制器方法的形参 的映射关系。
该注解的三个参数,value、required、defaultValue。这三个注解的参数的用法同 2.2.3
小节@RequestParam
。
2.2.7、POJO 获取请求参数: 将控制器方法的形参设为一个POJO,POJO的属性名与前端请求的参数名一致,那么SpringMVC会将请求参数自动封装为POJO。
前端代码:
1 2 3 4 5 6 7 <form th:action ="@{/test/testParamPOJO}" method ="post" > <input type ="text" name ="username" /> <input type ="text" name ="pwd" /> <input type ="submit" /> </form >
后端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Data public class User { private String username; private String pwd; } public String testParamPOJO (User user) { System.out.println(user); return "success" ; }
2.2.8、解决请求参数的乱码问题(编码过滤器): url 传参乱码:Tomcat 的server.xml
中的编码配置问题。
post 传参乱码:编码不一致。
解决方案: 配置 编码过滤器,并在web.xml
中注册。
注意:【编码过滤器要配在最前面,一旦之前获取过请求参数,则该过滤器失效】。
web.xml
添加如下配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <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 > forceResponseEncoding</param-name > <param-value > true</param-value > </init-param > </filter > <filter-mapping > <filter-name > CharacterEncodingFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
2.3、域对象共享数据 域对象:
在传统的Servlet
中,域对象主要有 PageContext(JSP页面)、 Request
、Session
、Cookie
、Application
。
在SpringMVC
中,域对象主要有 Request
、Session
、Application
。
域对象共享数据的几种方式: ( 5+1+1)
ServletAPI
向 Request 域对象 共享数据
ModelAndView
向 Request 域对象 共享数据【推荐使用 】
Model
向 Request 域对象 共享数据
Map
向 Request 域对象 共享数据
ModelMap
向 Request 域对象 共享数据
Session
域对象 共享数据
Application
域对象(ServletContext 对象) 共享数据
2.3.1、Request 域对象(ServletAPI) ServletAPI 的方式:控制器方法传入HttpServletRequest、HttpServletResponse、HttpServletSession。
控制器:
1 2 3 4 5 6 7 8 9 10 11 @Controller public class HelloController { @ReuquestMapping("/success") public String test01 (HttpServletRequest req) { req.setAttribute("testRequestScope" ,"hello,servletAPI" ); return "success" ; } }
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 首页</h1 > // thmeleaf 获取 request 域中键为testReqScope的数据 <p th:text ="${testRequestScope}" > </p > </body > </html >
2.3.2、Request 域对象(ModelAndView)【推荐使用】 【推荐使用】
ModelAndView
对象:其中的 Model
指的是往域对象中共享数据的功能,View
指的是指定视图名称并解析最终跳转到页面的过程。
注意:
返回值必须是ModelAndView
类型
ModelAndView
对象需要调用addObject(key,value);
方法来共享数据,类似 Servlet编程时的 request
对象的 setAttribute(key,value);
方法。
ModelAndView
对象需要调用setViewName(视图名);
方法,来设置视图名(转发给哪个页面)
控制器的方法:
1 2 3 4 5 6 7 8 9 @RequestMapping("/mav") public ModelAndView testMav () { ModelAndView mav = new ModelAndView(); mav.addObject("testRequestScope" ,"hello,mav" ); mav.setViewName("rst" ); return mav; }
前端 res.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 结果页:</h1 > ModelAndView域:【地址栏访问:http://ip:端口/项目名/mav 时跳转到该页面】 <p th:text ="${testRequestScope}" > </p > </body > </html >
2.3.3、Request 域对象(Model) 注意:
控制器方法的形参 必须要有Model
对象
控制器方法的返回值 必须要是视图名
Model
对象需要调用addAttribute(key,value);
方法来共享数据,类似 Servlet编程时的 request
对象的 setAttribute(key,value);
方法。
控制器:
1 2 3 4 5 6 @RequestMapping("/modelTest") public String testMav (Model model) { model.addAttribute("testRequestScope" ,"hello,model" ); return rst; }
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 结果页:</h1 > Model域:【地址栏访问:http://ip:端口/项目名/modelTest 时跳转到该页面】 <p th:text ="${testRequestScope}" > </p > </body > </html >
2.3.4、Request 域对象(Map) 控制器方法的形参若为Map
类型的集合,则该Map集合就是Request
域中共享的数据 。
控制器的方法:
1 2 3 4 5 6 @RequestMapping("/mapTest") public String testMav (Map<String,Object> map) { map.put("testRequestScope" ,"hello,map" ); return rst; }
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 结果页:</h1 > Map域:【地址栏访问:http://ip:端口/项目名/mapTest 时跳转到该页面】 <p th:text ="${testRequestScope}" > </p > </body > </html >
2.3.5、Request 域对象(ModelMap) 控制器的方法:
1 2 3 4 5 6 @RequestMapping("/modelMapTest") public String testMav (ModelMap modelmap) { modelmap.put("testRequestScope" ,"hello,ModelMap" ); return rst; }
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 结果页:</h1 > ModelMap域:【地址栏访问:http://ip:端口/项目名/modelMapTest 时跳转到该页面】 <p th:text ="${testRequestScope}" > </p > </body > </html >
2.3.6、Model、ModelMap、Map 之间的关系 Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap
类型的
1 2 3 4 5 6 7 public interface Model {}public class ModelMap extends LinkedHashMap <String , Object > {}public class ExtendedModelMap extends ModelMap implements Model {}public class BindingAwareModelMap extends ExtendedModelMap {}
这几种方式最终都会返回 ModelAndView
对象
2.3.7、Session 域对象 控制器的方法:
1 2 3 4 5 6 @RequestMapping("/sessionTest") public String testSession (HttpSession session) { session.setAttribute("testSessionScope" , "hello,session" ); return "rst" ; }
前端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <!DOCTYPE html > <html xmlns:th ="http://www.thymeleaf.org" > <head > <meta charset ="UTF-8" > <title > Title</title > </head > <body > <h1 > 结果页:</h1 > Session域:【地址栏访问:http://ip:端口/项目名/sessionTest 时跳转到该页面】 <p th:text ="${sesssion.testSessionScope}" > </p > </body > </html >
2.3.8、Application 域对象(ServletContext 对象) 控制器的方法:
1 2 3 4 5 6 @RequestMapping("/testApplication") public String testApplication (HttpSession session) { ServletContext application = session.getServletContext(); application.setAttribute("testApplicationScope" , "hello,application" ); return "rst" ; }
3、SpringMVC 的数据绑定 部分内容见【2.2
】小节。
SpringMVC 在程序执行过程中,将前端的请求参数 通过一定的方式绑定 到控制器参数 中。
即:在数据绑定 的过程中,SpringMVC 将 ServletRequet
对象 传递给 DataBinder
组件,然后 DataBinder
组件 调用 ConversionService
组件将请求参数的内容 进行类型与格式的转化,并填充到参数对象 中,之后调用Validator
组件对参数对象 进行数据的合法性校验。最后将校验后的BindingResult
对象赋值给控制器方法相应的参数。
3.1、SpringMVC 的绑定 默认数据类型 常用的默认数据类型:
HttpServletRequest
HttpServletResponse
HttpSession
Model 或者 ModelMap
:Model 是一个接口,ModelMap 是Model 接口的一个实现类(作用:将Model 数据填充到 Request 域)
步骤:
导入Jar 包
在 web.xml
文件中,配置前端控制器
在 applicationContext.xml
(Spring 的配置文件)中,配置 视图解析器、组件扫描。
创建一个控制器类,使用@Controller
注解修饰整个类,使用@RequestMapping
注解修饰控制器方法
编写控制器方法、要返回的页面。
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class HelloController { @ReuqestMapping("/testParam01") public String test01 (HttpServletRequest req) { String username = req.getParamter("username" ); System.out.println(username); return "success" ; } }
3.2、SpringMVC 的绑定 简单数据类型 步骤:
导入Jar 包
在 web.xml
文件中,配置前端控制器
在 applicationContext.xml
(Spring 的配置文件)中,配置 视图解析器、组件扫描。
创建一个控制器类,使用@Controller
注解修饰整个类,使用@RequestMapping
注解修饰控制器方法
编写控制器方法、要返回的页面。
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller public class HelloController { @ReuqestMapping("/testParam02") public String test02 (Integer uid) { System.out.println(uid); return "success" ; } }
3.3、SpringMVC 的绑定 POJO 步骤:
导入Jar 包
在 web.xml
文件中,配置前端控制器
在 applicationContext.xml
(Spring 的配置文件)中,配置 视图解析器、组件扫描。
创建一个控制器类,使用@Controller
注解修饰整个类,使用@RequestMapping
注解修饰控制器方法
编写控制器方法、要返回的页面。
POJO:
1 2 3 4 5 6 7 8 import lombok.*;@Data public class User { private int uid; private String username; private String pwd; }
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Controller public class HelloController { @ReuqestMapping("/testParam03") public String test03 (User user) { System.out.println(user); return "success" ; } }
3.4、SpringMVC 的绑定 包装POJO 包装POJO 的前端部分必须符合以下规则:
前端请求的参数名:为直接基本属性时,前端请求的参数名 与 POJO 的属性名一致。
前端请求的参数名:为POJO属性的子属性时,前端请求的参数名要带上POJO的属性名(如:dept.deptName
)
步骤:
导入Jar 包
在 web.xml
文件中,配置前端控制器
在 applicationContext.xml
(Spring 的配置文件)中,配置 视图解析器、组件扫描。
创建一个控制器类,使用@Controller
注解修饰整个类,使用@RequestMapping
注解修饰控制器方法
编写控制器方法、要返回的页面。
包装POJO:【有属性为 class 类型】
1 2 3 4 5 6 7 8 9 import lombok.*;@Data public class User { private int uid; private String username; private String pwd; private Dept dept; }
包装POJO内部的POJO:
1 2 3 4 5 6 7 import lombok.*;@Data public class Dept { private int id; private String deptName; }
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class HelloController { @ReuqestMapping("/testParam04") public String test04 (User user) { Dept dept = user.getDept(); System.out.println(dept); return "success" ; } }
3.5、SpringMVC 的绑定 数组 绑定 数组 ,也就是前端传过来的是 checkbox 的数据。
步骤:
导入Jar 包
在 web.xml
文件中,配置前端控制器
在 applicationContext.xml
(Spring 的配置文件)中,配置 视图解析器、组件扫描。
创建一个控制器类,使用@Controller
注解修饰整个类,使用@RequestMapping
注解修饰控制器方法
编写控制器方法、要返回的页面。
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Controller public class HelloController { @ReuqestMapping("/testParam02") public String test02 (Integer[] uids) { if (uids != null ){ for (Integer id : uids){ System.out.println(uid); } } return "success" ; } }
3.6、SpringMVC 的绑定 集合 绑定 集合时,无法直接使用,必须先创建一个POJO类,然后将集合作为POJO的成员变量,最后在控制器方法的参数中,将该POJO 作为参数类型。
POJO:
1 2 3 4 5 6 import lombok.*;@Data public class CollectionPOJO { private List<User> list; }
控制器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Controller public class HelloController { @ReuqestMapping("/testParam02") public String test02 (CollectionPOJO pojo) { List<User> list = pojo.getList(); for (User user : list){ System.out.println(user); } return "success" ; } }
4、SpringMVC 的视图 SpringMVC中的视图是View接口
,视图的作用渲染数据 ,将模型Model中的数据展示给用户。
SpringMVC视图的种类很多,默认有:转发视图 、重定向视图 。
当工程引入jstl
的依赖,转发视图 会自动转换为JstlView
。
若使用的视图技术为Thymeleaf
,在SpringMVC的配置文件中配置了 Thymeleaf 的视图解析器,由此视图解析器解析之后所得到的是 ThymeleafView
。
4.1、ThymeleafView 当控制器方法中所设置的视图名称没有任何前缀 时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀 和视图后缀 所得到的最终路径 ,会通过转发 的方式实现跳转 。
4.2、转发视图 SpringMVC中默认的转发视图 是InternalResourceView
。
”转发“,有资格获取/WEB-INF
目录下的资源,“重定向”没资格。
”转发“,无资格跨域,“重定向”有资格跨域。
SpringMVC 中创建转发视图 的情况:
当控制器方法中所设置的视图名称以"forward:"
为前缀时,创建InternalResourceView
视图,此时的视图名称不会 被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"
去掉,剩余部分 作为最终路径 通过转发的方式 实现跳转。
例如:"forward:/"
,“forward:/employee”
控制器的方法:
1 2 3 4 5 6 @RequestMapping("/testForward") public String testForward () { return "forward:/testHello" ; }
4.3、重定向视图 SpringMVC中默认的重定向视图是RedirectView
。
”转发“,有资格获取/WEB-INF
目录下的资源,“重定向”没资格。
”转发“,无资格跨域,“重定向”有资格跨域。
当控制器方法中所设置的视图名称以"redirect:"
为前缀时,创建RedirectView
视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"
去掉,剩余部分作为最终路径通过重定向的方式实现跳转。
例如:"redirect:/"
,“redirect:/employee”
。
注意: 重定向视图在解析时,会先将redirect:
前缀去掉,然后会判断剩余部分是否以/
开头,若是则会自动拼接上下文路径。
控制器的方法:
1 2 3 4 5 @RequestMapping("/testRedirect") public String testRedirect () { return "redirect:/testHello" ; }
4.4、视图控制器 view-controller 当控制器方法中,仅仅用来实现页面跳转,即:只需要设置视图名称时,可以将处理器方法使用view-controller
标签进行表示。
注意:
当SpringMVC中设置任何一个view-controller
时,其他控制器中的请求映射将全部失效 ,
此时需要在SpringMVC的配置文件
中设置开启mvc注解驱动的标签:<mvc:annotation-driven />
。
SpringMVC的配置文件中:
1 2 3 4 5 6 7 8 9 10 <mvc:view-controller path ="/testView" view-name ="success" /> <mvc:annotation-driven />
5、SpringMVC 与 RestFul 交互(HiddenHttpMethodFilter) 5.1、RestFul 简介 REST:Re presentational S tate T ransfer,表现层资源状态转移。
REST-解释:
1、资源的表述 :例如,HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
2、状态转移 :在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
5.2、RestFul 的实现 具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET
、POST
、PUT
、DELETE
。
它们分别对应四种基本操作:
GET :用来获取资源
POST :用来新建资源
PUT : 用来更新资源
DELETE :用来删除资源
REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠 分开,不使用问号键值对 方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分 ,以保证整体风格的一致性。
操作
传统方式
REST风格
查询操作
getUserById?id=1
user/1 –> get请求方式
保存操作
saveUser
user –> post请求方式
删除操作
deleteUser?id=1
user/1 –> delete请求方式
更新操作
updateUser
user –> put请求方式
HiddenHttpMethodFilter
过滤器可以将POST
请求转化为浏览器目前不支持的put
和 delete
请求。
HiddenHttpMethodFilter
处理put
和delete
请求的两个条件:
满足以上条件,**HiddenHttpMethodFilter
** 过滤器就会将当前请求的请求方式转换为请求参数\_method
的值,因此请求参数_method
的值才是最终的请求方式。(在web.xml
中配置)
web.xml:
1 2 3 4 5 6 7 8 9 10 11 12 // 以下只展示了该章节的核心配置 // 先注册 SpringMVC 的字符过滤器,再注册以下过滤器 <filter > <filter-name > HiddenHttpMethodFilter</filter-name > <filter-class > org.springframework.web.filter.HiddenHttpMethodFilter</filter-class > </filter > <filter-mapping > <filter-name > HiddenHttpMethodFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping >
前端:
1 2 3 4 5 6 7 <form method ="post" th:action ="@{/user}" > <input type ="hidden" name ="_method" value ="PUT" > 用户名: <input type ="text" name ="username" /> <input type ="submit" /> </form >
小结:
web.xml
中,配置HiddenHttpMethodFilter
过滤器。
控制器 的某个方法上的 @RequestMapping
注解的method参数中 ,将 HTTP 请求方式设为 put
或 delete
。
前端页面 ,form 表单的 method 属性设为post
,表单内必须要有一个隐藏域标签<input type="hidden" name="_method" value="put">
或<input type="hidden" name="_method" value="delete">
5.3、放行静态资源
SpringMVC.xml:
1 <mvc:delault-servlet-handler />
6、SpringMVC 与 JSON 交互(HttpMessageConverter) HttpMessageConverter
,报文信息转换器,将请求报文 转换为Java对象 ,或将Java对象 转换为响应报文 。
HttpMessageConverter
提供了两个注解 和两个类型 :
(在控制器的方法上使用)
两个注解 :
@RequestBody
:用来修饰一个代表请求体 的形参
@ResponseBody
:用来修饰一个控制器的方法 (将方法的最终返回结果作为响应体)
两个类型 :
RequestEntity
:整个完整的请求报文
ResponseEntity
:整个完整的响应报文
6.1、@RequestBody 注解 【在形参上】
@RequestBody
可以获取请求体,需要在控制器方法设置一个形参 ,使用@RequestBody
进行标识,当前请求的请求体就会为当前注解所标识的形参赋值。【post 请求报文才有请求体】
后端接收到的是form表单转为 url 的Query字符串形式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <form th:action="@{/testRequestBody}" method="post" > 用户名:<input type="text" name="username" ><br> 密码:<input type="password" name="password" ><br> <input type="submit" > </form> @RequestMapping("/testRequestBody") public String testRequestBody (@RequestBody String requestBody) { System.out.println("requestBody:=>" +requestBody); return "success" ; }
6.2、RequestEntity 类型 【在形参上】
RequestEntity
封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息。
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 @RequestMapping("/testRequestEntity") public String testRequestEntity (RequestEntity<String> requestEntity) { System.out.println("requestHeader:" +requestEntity.getHeaders()); System.out.println("requestBody:" +requestEntity.getBody()); return "success" ; }
6.3、@ResponseBody注解 【在控制器方法上】
@ResponseBody
用于标识一个控制器方法 ,可将该方法的返回值直接作为响应报文的响应体响应到浏览器。
1 2 3 4 5 6 7 8 @RequestMapping("/testResponseBody") @ResponseBody public String testResponseBody () { return "success" ; }
6.4、ResponseEntity 类型 【控制器方法的返回值】
ResponseEntity
用于作为控制器方法的返回值类型 ,该控制器方法的返回值就是响应到浏览器的响应报文。
使用场景: 文件下载
控制器:
1 2 3 4 5 6 7 @RequestMapping("/testResponseEntity") public ResponseEntity testResponseEntity () { return ResponseEntity; }
6.5、@ResponseBody 处理JSON: 6.5.1、步骤: @ResponseBody
处理 JSON
的步骤:
导入jackson
的依赖。
在SpringMVC的核心配置文件中,开启mvc的注解驱动 。
使用@ResponseBody
注解标识一个控制器方法。
控制器方法返回一个POJO(自动转化为JSON)
(1)导入Jackson 依赖:
1 2 3 4 5 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.12.1</version > </dependency >
(2)SpringMVC配置文件中,开启mvc注解驱动:
1 <mvc:annotation-driven />
(3)使用@ResponseBody
注解标识一个控制器方法
(4)将Java对象直接作为控制器方法的返回值 返回,就会自动转换为 JSON 格式的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @ResponseBody @RequestMapping("/testResponseUser") public User testResponseUser () { return new User(1001 ,"admin" ,"123456" ,23 ,"男" ); }
6.5.2、整体演示: (1)前端:
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 <div id ="app" > <a th:href ="@{/testAjax}" @click ="testAjax" > testAjax</a > <br > </div > // ============================================ <script type ="text/javascript" th:src ="@{/static/js/vue.js}" > </script > <script type ="text/javascript" th:src ="@{/static/js/axios.min.js}" > </script > <script type ="text/javascript" > var vue = new Vue({ el :"#app" , methods :{ testAjax :function (event ) { axios({ method :"post" , url :event.target.href, params :{ username :"admin" , password :"123456" } }).then(function (response ) { alert(response.data); }); event.preventDefault(); } } }); </script >
(2)控制器方法:
1 2 3 4 5 6 7 @RequestMapping("/testAjax") @ResponseBody public String testAjax (String username, String password) { System.out.println("username:" +username+",password:" +password); return "hello,ajax" ; }
6.6、@RestController注解【重点】 @RestController
注解是SpringMVC提供的一个复合注解 ,标识在控制器的类 上,
就相当于为类添加了@Controller
注解,并且为其中的每个方法添加了@ResponseBody
注解。
即:【@Controller
+ @ResponseBody
】
6.7、文件下载与上传-案例
6.7.1、文件下载 文件下载案例:使用了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 26 27 28 29 30 31 32 33 34 35 @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; }
6.7.2、文件上传 文件上传:要求form
表单的请求方式 必须为post
,并且添加属性enctype="multipart/form-data"
SpringMVC 中将上传的文件封装到MultipartFile
对象中,通过此对象可以获取文件相关信息。
步骤:
导入 commons-fileupload.jar
包依赖
在 SpringMVC 的配置文件中,添加文件解析器 的配置
编写控制器方法
(1)导入maven 依赖:
1 2 3 4 5 6 7 <dependency > <groupId > commons-fileupload</groupId > <artifactId > commons-fileupload</artifactId > <version > 1.3.1</version > </dependency >
(2)在SpringMVC配置文件中,配置文件解析器:
1 2 3 4 <bean id ="multipartResolver" class ="org.springframework.web.multipart.commons.CommonsMultipartResolver" />
(3)控制器方法:
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 @RequestMapping("/testUp") public String testUp (MultipartFile photo, HttpSession session) throws IOException { String fileName = photo.getOriginalFilename(); String houzuiName = fileName.substring(fileName.lastIndexOf("." )); fileName = UUID.randomUUID().toString() + houzuiName; 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" ; }
7、SpringMVC 的拦截器 拦截器(Interceptor),类似 Servet 编程时的 Filter 过滤器的作用,可以使用拦截器进行权限验证。
过程:
前端页面 =》Filter =》DispacherServlet =》拦截器 =》HandlerMapping =》HandlerMapping =》HandlerAdaptor =》 Handler (Controller)
自定义拦截器类的两种方式:
实现HandlerInterceptor
接口 或者 继承HandlerInterceptor
接口 的实现类。
实现WebRequestInterceptor
接口 或者 继承WebRequestInterceptor
接口 的实现类。
以实现HandlerInterceptor
接口为例:
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 public classMyInterceptor implements HandlerInterceptor{ @Override public boolean preHandle (HttpServletRequest req ,HttpServletResponse resp ,Object obj ) { ... } @Override public void postHandle (HttpServletRequest req ,HttpServletResponse resp ,Object obj. ,ModelAndView mav) { ... } @Override public void afterCompletion (HttpServletRequest req ,HttpServletResponse resp ,Object obj. ,ModelAndView mav) { ... } }
步骤:
定义一个拦截器类,实现拦截器接口(见上面的示例)
在SpringMVC.xml
(SpringMVC的配置文件)中配置拦截器
SpringMVC.xml
:
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 // 以下只展示核心配置 <mvc:interceptors > // 注册拦截器类 <bean class ="com.cyw.interceptor.MyInterceptor" /> // 配置拦截器【第一个拦截器】 <mvc:interceptor > // 拦截的路径 <mvc:mapping path ="/**" /> // 不拦截的路径 <mvc:exclude-mapping path ="" /> // 匹配的请求才进行拦截 <bean class ="com.cyw.interceptor.TestInterceptor01" /> </mvc:interceptor > // 配置拦截器【第二个拦截器】 <mvc:interceptor > // 拦截的路径 <mvc:mapping path ="/**" /> // 匹配的请求才进行拦截 <bean class ="com.cyw.interceptor.TestInterceptor02" /> </mvc:interceptor > </mvc:interceptors >
8、异常处理器 SpringMVC 提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver
接口的实现类有:
DefaultHandlerExceptionResolver
SimpleMappingExceptionResolver
8.1、基于 XML 的异常处理器 SpringMVC 提供了自定义的异常处理器SimpleMappingExceptionResolver
。
SpringMVC
配置文件:
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 >
8.2、基于 注解 的异常处理器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @ControllerAdvice public class ExceptionController { @ExceptionHandler(ArithmeticException.class) public String handleArithmeticException (Exception ex, Model model) { model.addAttribute("ex" , ex); return "error" ; } }
9、纯注解配置 SpringMVC 使用 配置类 + 注解 的方式,代替 web.xml + SpringMVC 配置文件
步骤:
创建 初始化类
,代替web.xml
。
创建SpringConfig
配置类,代替Spring
的配置文件。
创建 WebConfig
配置类,代替 SpringMVC
的配置文件。
使用。
1、创建 初始化类
,代替web.xml
:
在Servlet 3.0 环境
中,容器 会在类路径 (src目录 + resources目录)中查找javax.servlet.ServletContainerInitializer
接口的实现类,如果找到的话:就用它来配置Servlet容器
(Tomcat 服务器)。
Spring
提供了该接口的实现类,名为 SpringServletContainerInitializer
。
该实现类 反过来又会查找 WebApplicationInitializer
接口的实现类并将配置的任务交给它们来完成。
Spring3.2
引入了一个便利的 WebApplicationInitializer
基础实现,名为 AbstractAnnotationConfigDispatcherServletInitializer
。
当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer
并将其部署到 Servlet 3.0 容器
的时候,容器会自动发现它,并用它来配置Servlet上下文
。
小结:
Servlet 3.0
中,javax.servlet.ServletContainerInitializer
接口的实现类来配置Servlet 容器。
Spring
中, SpringServletContainerInitializer
=》 WebApplicationInitializer
接口的实现类来配置 Servlet 容器。
【重点】Spring3.2
中,AbstractAnnotationConfigDispatcherServletInitializer
来配置 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 41 42 43 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}; } }
2、创建SpringConfig
配置类,代替Spring
的配置文件:
SpringConfig 配置类 :
1 2 3 4 5 6 @Configuration public class SpringConfig { }
3、创建 WebConfig
配置类,代替 SpringMVC
的配置文件:
WebConfig 配置类:
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 @Configuration @ComponentScan("com.atguigu.mvc.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 addInterceptors (InterceptorRegistry registry) { FirstInterceptor firstInterceptor = new FirstInterceptor(); registry.addInterceptor(firstInterceptor).addPathPatterns("/**" ); } @Bean public ITemplateResolver templateResolver () { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); 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; } }
4、使用测试:
1 2 3 4 5 @RequestMapping("/") public String index () { return "index" ; }
10、SpringMVC 的执行流程 10.1、常用组件
DispatcherServlet
:前端控制器 。 作用: 统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求.。
HandlerMapping
:处理器映射器 。 作用: 根据请求的url、method
等信息查找Handler
,即控制器方法。
Handler
:处理器(控制器) ,需要工程师开发。 作用: 在DispatcherServlet
的控制下,Handler对具体的用户请求进行处理。
HandlerAdapter
:处理器适配器 。 作用: 通过HandlerAdapter
对处理器(控制器方法)进行执行。
ViewResolver
:视图解析器 。 作用: 进行视图解析,得到相应的视图,例如:ThymeleafView
、InternalResourceView
、RedirectView
。
View
:视图 。渲染数据后形成的页面。
10.2、DispacherServlet 初始化过程 DispatcherServlet 本质上是一个 Servlet,所以天然的遵循 Servlet 的生命周期。
所以宏观上是 Servlet 生命周期来进行调度。
步骤:
初始化 WebApplicationContext
创建 WebApplicationContext
DispatcherServlet
初始化策略
(1)初始化 WebApplicationContext
所在类:org.springframework.web.servlet.FrameworkServlet
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 protected WebApplicationContext initWebApplicationContext () { WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext()); WebApplicationContext wac = null ; if (this .webApplicationContext != null ) { wac = this .webApplicationContext; if (wac instanceof ConfigurableWebApplicationContext) { ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac; if (!cwac.isActive()) { if (cwac.getParent() == null ) { cwac.setParent(rootContext); } configureAndRefreshWebApplicationContext(cwac); } } } if (wac == null ) { wac = findWebApplicationContext(); } if (wac == null ) { wac = createWebApplicationContext(rootContext); } if (!this .refreshEventReceived) { synchronized (this .onRefreshMonitor) { onRefresh(wac); } } if (this .publishContext) { String attrName = getServletContextAttributeName(); getServletContext().setAttribute(attrName, wac); } return wac; }
(2)创建 WebApplicationContext
所在类:org.springframework.web.servlet.FrameworkServlet
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 protected WebApplicationContext createWebApplicationContext (@Nullable ApplicationContext parent) { Class<?> contextClass = getContextClass(); if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { throw new ApplicationContextException( "Fatal initialization error in servlet with name '" + getServletName() + "': custom WebApplicationContext class [" + contextClass.getName() + "] is not of type ConfigurableWebApplicationContext" ); } ConfigurableWebApplicationContext wac = (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); wac.setEnvironment(getEnvironment()); wac.setParent(parent); String configLocation = getContextConfigLocation(); if (configLocation != null ) { wac.setConfigLocation(configLocation); } configureAndRefreshWebApplicationContext(wac); return wac; }
(3)DispatcherServlet
初始化策略
FrameworkServlet
创建 WebApplicationContext
后,刷新容器,调用 onRefresh(wac)
方法,
此方法在DispatcherServlet
中进行了重写,调用了initStrategies(context)
方法,
初始化策略,即初始化DispatcherServlet
的各个组件。
所在类:org.springframework.web.servlet.DispatcherServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 protected void initStrategies (ApplicationContext context) { initMultipartResolver(context); initLocaleResolver(context); initThemeResolver(context); initHandlerMappings(context); initHandlerAdapters(context); initHandlerExceptionResolvers(context); initRequestToViewNameTranslator(context); initViewResolvers(context); initFlashMapManager(context); }
10.3、DispacherServlet 调用组件、处理请求
processRequest()
doService()
doDispatch()
processDispatchResult()
FrameworkServlet
重写HttpServlet
中的service()
和doXxx()
,
这些方法中调用了processRequest(request, response)
。
10.3.1、processRequest()
所在类:org.springframework.web.servlet.FrameworkServlet
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 protected final void processRequest (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { long startTime = System.currentTimeMillis(); Throwable failureCause = null ; LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext(); LocaleContext localeContext = buildLocaleContext(request); RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes); WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor()); initContextHolders(request, localeContext, requestAttributes); try { doService(request, response); } catch (ServletException | IOException ex) { failureCause = ex; throw ex; } catch (Throwable ex) { failureCause = ex; throw new NestedServletException("Request processing failed" , ex); } finally { resetContextHolders(request, previousLocaleContext, previousAttributes); if (requestAttributes != null ) { requestAttributes.requestCompleted(); } logResult(request, response, failureCause, asyncManager); publishRequestHandledEvent(request, response, startTime, failureCause); } }
10.3.2、doService()
所在类:org.springframework.web.servlet.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 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 @Override protected void doService (HttpServletRequest request, HttpServletResponse response) throws Exception { logRequest(request); Map<String, Object> attributesSnapshot = null ; if (WebUtils.isIncludeRequest(request)) { attributesSnapshot = new HashMap<>(); Enumeration<?> attrNames = request.getAttributeNames(); while (attrNames.hasMoreElements()) { String attrName = (String) attrNames.nextElement(); if (this .cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) { attributesSnapshot.put(attrName, request.getAttribute(attrName)); } } } request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext()); request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this .localeResolver); request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this .themeResolver); request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource()); if (this .flashMapManager != null ) { FlashMap inputFlashMap = this .flashMapManager.retrieveAndUpdate(request, response); if (inputFlashMap != null ) { request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap)); } request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap()); request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this .flashMapManager); } RequestPath requestPath = null ; if (this .parseRequestPath && !ServletRequestPathUtils.hasParsedRequestPath(request)) { requestPath = ServletRequestPathUtils.parseAndCache(request); } try { doDispatch(request, response); } finally { if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { if (attributesSnapshot != null ) { restoreAttributesAfterInclude(request, attributesSnapshot); } } if (requestPath != null ) { ServletRequestPathUtils.clearParsedRequestPath(request); } } }
10.3.3、doDispatch()
所在类:org.springframework.web.servlet.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 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 90 91 92 93 94 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null ; boolean multipartRequestParsed = false ; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null ; Exception dispatchException = null ; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); mappedHandler = getHandler(processedRequest); if (mappedHandler == null ) { noHandlerFound(processedRequest, response); return ; } HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); String method = request.getMethod(); boolean isGet = "GET" .equals(method); if (isGet || "HEAD" .equals(method)) { long lastModified = ha.getLastModified(request, mappedHandler.getHandler()); if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) { return ; } } if (!mappedHandler.applyPreHandle(processedRequest, response)) { return ; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return ; } applyDefaultViewName(processedRequest, mv); mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { dispatchException = ex; } catch (Throwable err) { dispatchException = new NestedServletException("Handler dispatch failed" , err); } processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); } catch (Exception ex) { triggerAfterCompletion(processedRequest, response, mappedHandler, ex); } catch (Throwable err) { triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed" , err)); } finally { if (asyncManager.isConcurrentHandlingStarted()) { if (mappedHandler != null ) { mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); } } else { if (multipartRequestParsed) { cleanupMultipart(processedRequest); } } } }
10.3.4、processDispatchResult()
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 private void processDispatchResult (HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { boolean errorView = false ; if (exception != null ) { if (exception instanceof ModelAndViewDefiningException) { logger.debug("ModelAndViewDefiningException encountered" , exception); mv = ((ModelAndViewDefiningException) exception).getModelAndView(); } else { Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null ); mv = processHandlerException(request, response, handler, exception); errorView = (mv != null ); } } if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } else { if (logger.isTraceEnabled()) { logger.trace("No view rendering, null ModelAndView returned." ); } } if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) { return ; } if (mappedHandler != null ) { mappedHandler.triggerAfterCompletion(request, response, null ); } }
10.4、SpringMVC 的执行流程
用户向服务器发送请求,请求被SpringMVC 前端控制器 DispatcherServlet捕获。
DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI),判断请求URI对应的映射:
若不存在 :
判断是否配置了mvc:default-servlet-handler
没配置,则控制台报映射查找不到,客户端展示404
错误
有配置,则访问目标资源(一般为静态资源,如:JS,CSS),找不到客户端也会展示404
错误。
若存在 :
根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain执行链对象的形式返回。
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。
如果成功获得HandlerAdapter,此时将开始执行拦截器的preHandler(…)方法【正向】
提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
此时将开始执行拦截器的postHandle(…)方法【逆向】。
根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
将渲染结果返回给客户端。
11、SSM 整合 11.1、常见报错: (1)SpringMVC使用@Autowired
自动装配Service对象时,报错的问题:
原因:
是否开启Spring配置文件
的组件扫描(IoC配置)。
是否给 Controller、Service
层添加注解。
是否给Controller、Service
创建Getter、Setter、构造函数
。
是否在web.xml
中配置了:context-param
标签(绑定Spring配置文件)、HiddenHttpMethodFilter
(使用HTTP的put、delete请求)、ContextLoaderListener
、DispacherServlet
。
是否给 SpringMVC配置文件
的组件扫描。
是否给 SpringMVC配置文件
的配置视图解析器。
是否给 SpringMVC配置文件
开启注解驱动。
是否给 SpringMVC配置文件
配置拦截器。