该笔记来自B站SGG-SpringMVC学习记录及其配套资料,文章末贴出链接
2022.05.28
MVC
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分
M:Model,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览器
用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller
调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果
找到相应的View视图,渲染数据后最终响应给浏览器
SpringMVC
在JavaWeb的学习中,我们了解到了JavaEE开发三层架构:表述层(表示层)(Web层)、业务逻辑层、数据访问层。
其中表述层是指"前台页面和后端Servlet"
而SpringMVC就是作为表述层开发的首选方案
SpringMVC基于原生Servlet,通过"前端控制器DispatcherServlet",对请求和响应进行统一处理。
2022.05.29
基于Maven来创建工程
1. 下载maven
https://maven.apache.org/download.cgi
2. 配置maven环境变量
2.1 MAVEN_HOME
2.2 path:%MAVEN_HOME%\bin
2.3 cmd 校验是否配置成功 mvn -v
2.4 mvn help:system 检验
3. 配置本地仓库
3.1 任意位置创建文件夹 maven-repository
3.2 maven安装目录下conf中setting.xml找到<localRepository>标签
localRepository用于配置本地仓库,本地仓库其实起到了一个缓存的作用,它的默认地址是 C:\Users\用户名.m2。当我们从maven中获取jar包的时候,maven首先会在本地仓库中查找,如果本地仓库有则返回;如果没有则从远程仓库中获取包,并在本地库中保存。
<localRepository>D:\codeSoft\Java\maven-repository</localRepository>
4. 配置国内镜像
4.1 找到<mirrors>标签 添加mirror标签为阿里云镜像
<!-- 阿里云 -->
<mirror>
<id>alimaven</id>
<mirrorOf>central</mirrorOf>
<name>aliyun maven</name>
<url>http://maven.aliyun.com/nexus/content/repositories/central/</url>
</mirror>
5. idea中配置maven
5.1 settings中找到"Build,Execution,Deployment"下"Build Tools"下"Maven"
5.2 "Maven home path"设置为 本地安装包位置
5.3 "User settings file"设置为 本地安装包中的conf中的settings.xml(需要后面Override打勾)
5.4 "Local repository"设置为 本地库
6. 创建一个新模块 build system基于Maven
6.1 在main文件夹下新建webapp文件夹
6.2 项目结构(Project Structure)中选择Modules,选择上面新建的模块中的"Web" 在"Deployment Descriptors"中添加web.xml到指定的位置 也就是src\main\webapp
7. pom.xml配置
7.1 配置打包方式为war包(JavaWeb项目)
<packaging>war</packaging>
7.2 引入模块依赖
<!-- 模块依赖-->
<dependencies>
<!-- SpringMVC -->
<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>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 已被Tomcat服务器提供 不需要打进war包 因此设置scope-->
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
8. 配置Tomcat服务器
基于SpringMVC前端控制器DispatcherServlet配置web.xml
在之前的JavaWeb的学习中,我们了解到我们的每一个Servlet组件都需要到web.xml中配置,在使用SpringMVC框架后 带来最重要的改变就是一切的请求配置全部配置到"前端控制器DispatcherServlet"来统一接收,再到实际的方法内,这样就不需要我们每个servlet都去配置
1. 默认配置方式
<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>
此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为<servlet-name>-
servlet.xml,例如,以下配置所对应SpringMVC的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml 不利于我们使用maven来管理工程 因此有下面的扩展配置
2. 扩展配置方式 (同时需要到src\main\java\resources创建springMVC.xml)
<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>
基于控制器Controller概念和thymeleaf模板
在上面的配置后,我们不需要再往web.xml里面手动的注入每个Servlet组件了,因此SpringMVC中的C,提出的控制器Controller概念,它并不是个什么特殊的类,依然是个POJO(普通java类),借助我们在Spring5学习中的注解@Controller将其标识为控制层组件
1. 标识Controller
@Controller
2. 开启Spring的组件扫描为IOC容器
<context:component-scan base-package="com.home.mvc.*" />
3. 配置thymeleaf模板引擎
<!-- 配置Thymeleaf视图解析器 -->
<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>
4. webapp\WEB-INF\下新建templates文件夹及index.html
4.1 在JavaWeb的学习中,我们知道浏览器不能直接访问或者重定向"WEB-INF"下的资源 ,只能通过转发操作,这里建templates文件夹的原因是因为我们的模板引擎配置的视图前缀就是"/WEB-INF/templates/" 而后缀是".html"
2022.05.30
@RequestMapping功能
@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
@RequestMapping位置
1. 标识类
设置映射请求的请求路径的初始信息
2. 标识方法
设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/pipeline")
public class TestController {
@RequestMapping("/test")
public String test() {
return "index";
}
}
@RequestMapping属性
1. value
表示通过请求的请求地址匹配请求映射;
是一个字符串数组,表示可以匹配多个请求到映射 @RequestMapping(value = {"/test","/test2"}) String[]
@RequestMapping(value = {"/testMappingValue1", "/testMappingValue2"})
public String test2() {
return "index";
}
同时SpringMVC支持ant风格的路径匹配
?:表示任意的单个字符
*:表示任意的0个或多个字符
**:表示任意的一层或多层目录
注意:在使用**时,只能使用xxx的方式
2. method
表示通过请求的请求方式匹配请求映射;
是一个RequestType枚举类型的数组,表示可以匹配多种请求方式 @RequestMapping(method = {RequestType.GET, RequestType.POST})
@RequestMapping(value = "/testMappingMethod", method = RequestMethod.GET)
public String test3() {
return "index";
}
对于处理指定请求方式的控制器方法,SpringMVC中提供了@RequestMapping的派生注解
处理get请求的映射 --> @GetMapping
处理post请求的映射 --> @PostMapping
处理put请求的映射 --> @PutMapping
处理delete请求的映射 --> @DeleteMapping
@GetMapping("/testGetMapping")
public String test4(){
return "index";
}
3. params
表示通过请求的请求参数匹配请求映射;
是一个字符串数组,可以通过四种表达式设置请求参数和请求映射的匹配关系
"param":要求请求映射所匹配的请求必须携带param请求参数
"!param":要求请求映射所匹配的请求必须不能携带param请求参数
"param=value":要求请求映射所匹配的请求必须携带param请求参数且param=value
"param!=value":要求请求映射所匹配的请求必须携带param请求参数但是param!=value
@RequestMapping(value = "/testParams", params = {"test"})
public String test5(){
return "index";
}
注意:当满足value和method,但不满足params时,会出现http 400响应状态码
4. headers
表示通过请求的请求头信息匹配映射;
是一个字符串数组,同样可以通过四种表达式设置请求头和请求映射的匹配关系
"header":要求请求映射所匹配的请求必须携带header请求头信息
"!header":要求请求映射所匹配的请求必须不能携带header请求头信息
"header=value":要求请求映射所匹配的请求必须携带header请求头信息且header=value
"header!=value":要求请求映射所匹配的请求必须携带header请求头信息且header!=value
注意:当满足value和method,但不满足headers时,会出现http 404响应状态码
路径中的占位符
1. 原始方式
/queryUserById?id=1
2. rest方式
/queryUserById/1
常用于restful风格请求url,通过@RequestMapping中的value属性中的占位符{xxx}来表示传输的数据,再通过在方法中的@pathVariable注解,将值赋值到控制器方法的形参上。
@RequestMapping(value = "/testRest/{id}/{name}")
public String test6(@PathVariable Long id, @PathVariable String name){
System.out.println(id + name);
return "index";
}
http://localhost:8080/springMVC/pipeline/testRest/1/lidaye
2022.05.31
通过ServletAPI获取
将HttpServletRequest作为控制器方法的形参
@RequestMapping(value = "/testPramasType")
public String test7(HttpServletRequest request) {
String username = request.getParameter("username");
String id = request.getParameter("id");
String[] hobbies = request.getParameterValues("hobby");
System.out.println("通过HttpServletRequest的getParameter方式获取:" + id + "::" + username + "::" + Arrays.asList(hobbies));
return "params";
}
这种方式 在JavaWeb中的Servlet经常使用,单值参数使用getParameter, 当存在多个相同key的值时我们需要使用getParameterValues,否则只会获取到第一个值
通过控制器方法形参对应到请求参数获取
在控制器方法的形参位置,设置和请求参数同名的形参,当浏览器发送请求,匹配到请求映射时,在
DispatcherServlet中就会将请求参数赋值给相应的形参
@RequestMapping(value = "/testPramasType2")
public String test8(Integer id, String username, String hobby, String[] hobby2) {
System.out.println("通过直接控制器方法形参方式获取:" + id + "::" + username + "::" + hobby + "::" + Arrays.toString(hobby2));
return "params";
}
其中针对多同名参数,若在控制器方法中使用"非数组"接收,则此参数的值为每个数据中使用逗号拼接的结果,若使用"数组"接收,则为数组,如上面hobby和hobby2两种方式
通过参数注解@RequestParam获取
将请求参数和控制器方法的形参创建映射关系
@RequestMapping("/testPramasType3")
public String test9(
@RequestParam Integer id,
@RequestParam(
required = false,
defaultValue = "李大爷"
) String username,
@RequestParam String[] hobbies) {
System.out.println("通过@RequestParam方式获取:" + id + "::" + username + "::" + Arrays.toString(hobbies));
return "params";
}
1. value 指定为形参赋值的请求参数的参数名 (对应到前端提交上来的字段名)
2. required 设置是否必须传输此请求参数,默认值为true
若设置为true时,则当前请求必须传输value所指定的请求参数,若没有传输该请求参数,且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present;若设置为false,则当前请求不是必须传输value所指定的请求参数,若没有传输,则注解所标识的形参的值为null
3. defaultValue 不管required属性值为true或false,当value所指定的请求参数没有传输或传输的值为""时,则使用默认值为形参赋值
@RequestHeader注解
将请求头信息和控制器方法的形参创建映射关系
有三个属性:value、required、defaultValue,用法同@RequestParam
@CookieValue注解
将cookie数据和控制器方法的形参创建映射关系
有三个属性:value、required、defaultValue,用法同@RequestParam
通过POJO获取
在形参位置绑定一个实体类型,传输的请求参数和实体类中属性名一致,则会自动绑定
public class User {
private Integer id;
private String password;
private String username;
private String sex;
private Integer age;
private String email;
public User() {
}
public User(Integer id, String password, String username, String sex, Integer age, String email) {
this.id = id;
this.password = password;
this.username = username;
this.sex = sex;
this.age = age;
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", password='" + password + '\'' +
", username='" + username + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
<form th:action="@{/pipeline/testPramasType5}" 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>
@RequestMapping("/testPramasType5")
public String test11(User user) {
System.out.println(user);
return "pojo";
}
User{id=null, password='123', username='地区公司-市场开发', sex='女', age=21, email='caristop0210@gmail.com'}
解决GET方式参数中文乱码
设置Tomcat server.xml
<Connector port="8080" protocol="HTTP/1.1"
URIEncoding="UTF-8" // 重点设置此项
connectionTimeout="20000"
redirectPort="8443" />
解决Post方式参数中文乱码
在JavaWeb中,
1. 了解到三大组件,"监听器"、"过滤器"、"Servlet" 其中其执行顺序"监听器" 》 "过滤器" 》 "Servlet"
2. 我们在servlet中通过request.setCharacterEncoding方法来设置乱码问题,且需要放在获取参数前面执行(才有效)
3. 在SpringMVC中,我们也了解到它给我们作了DispatcherServlet收口,再匹配到控制器方法中,因此我们需要在此Servlet执行前进行设置Filter过滤器来设置(可以点进去看源码)
<!--配置springMVC的编码过滤器-->
<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>
2022.06.02
通过ServletAPI设置
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
request.setAttribute("testServletAPI", "i am value : testServletAPI");
return "object";
}
<h3>ServletAPI:</h3>
<span th:text="${testServletAPI}"></span>
通过ModelAndView设置
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("testModelAndView", "i am value : testModelAndView");
modelAndView.setViewName("object");
return modelAndView;
}
<h3>ModelAndView:</h3>
<span th:text="${testModelAndView}"></span>
通过Model设置
@RequestMapping("/testModel")
public String testModel(Model model) {
model.addAttribute("testModel", "i am value : testModel");
return "object";
}
<h3>testModelMap:</h3>
<span th:text="${testModelMap}"></span>
通过ModelMap设置
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap) {
modelMap.addAttribute("testModelMap", "i am value : testModelMap");
return "object";
}
<h3>testModelMap:</h3>
<span th:text="${testModelMap}"></span>
通过HttpSession设置
@RequestMapping("/testSession")
public String testSession(HttpSession httpSession) {
httpSession.setAttribute("testSession", "i am value : testSession");
return "object";
}
<h3>testSession:</h3>
<span th:text="${session.testSession}"></span>
通过servletContext设置
@RequestMapping("/testApplicationBySession")
public String testApplicationBySession(HttpSession httpSession) {
ServletContext servletContext = httpSession.getServletContext();
servletContext.setAttribute("testApplicationBySession", "i am value : testApplicationBySession");
return "object";
}
<h3>testApplicationBySession:</h3>
<span th:text="${application.testApplicationBySession}"></span>
Model、ModelMap、Map的关系
Model、ModelMap、Map类型的参数其实本质上都是 BindingAwareModelMap 类型的
public interface Model{}
public class ModelMap extends LinkedHashMap<String, Object> {}
public class ExtendedModelMap extends ModelMap implements Model {}
public class BindingAwareModelMap extends ExtendedModelMap {}
我们在JavaWeb中学习过servlet的九大内置对象和4大域数据,分别是
request
response
pageContext
session
application
config
out
page
exception
pageContext (PageContextImpl类) 当前jsp页面范围内有效
request (HttpServletRequest类) 一次请求内有效
session (HttpSession类) 一个会话范围(打开浏览器直到关闭)
application (ServletContext类) 整个web工程范围内(直到web工程停止)
因此在上面我们在模板中使用session.xxx或者application.xxx来获取域对象值
2022.06.03
SpringMVC视图
SpringMVC中的视图是view接口,视图的作用是渲染数据,将模型Model中的数据展示给用户,其中SpringMVC视图的种类很多,默认有转发视图和重定向视图
转发视图
转发视图是InternalResourceView,当控制器方法中所设置的视图名称以"forward:"为前缀时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
重定向视图
重定向视图是RedirectView,当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建RedirectView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转
视图控制器
控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法使用viewcontroller标签进行表示
<!--
path:设置处理的请求地址
view-name:设置请求地址所对应的视图名称
-->
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
当SpringMVC中设置任何一个view-controller时,其他控制器中的请求映射将全部失效,此时需
要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签:
<mvc:annotation-driven />
RESTful
1. REST:Representational State Transfer,表现层资源状态转移。
2. HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE用来删除资源。REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。
3. 由于浏览器在form表单提交时,只支持get和post请求,因此在RESTful规范下,如何发送put和delete请求
因此SpringMVC提供了"HiddenHttpMethodFilter"过滤器来帮助我们将post请求转换为delete或put请求,但次过滤器需要前端请求时必要的条件:
a. 当前请求方式必须为post方式
b. 当前请求必须传输请求参数"_method"(该filter组件源码中显式的使用了该字段)
<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>
2022.06.04
HttpMessageConverter
HttpMessageConverter,是一个报文信息转换器,它会将"请求报文"转换为"Java对象",或者将"Java对象"转换为"响应报文"
它提供了两个注解和两个类型
@RequestBody
@ResponseBody
RequestEntity
ResponseEntity
@RequestBody
1. 使用该注解时,需要在控制器方法处设置一个形参,并使用@RequestBody来进行标识,则当前请求的"请求体"就会为当前注解所标识的形参赋值
2. 一般配合http的POST\PUT等请求使用(因为它是获取请求体)
2.1 form表单的post提交
由于form表单的post提交,是在请求体中已&凭借,如key1=value1&key2=value2
此时我们使用(@RequestBody String form) 来接收
2.2 ajax的json格式post提交
大多数我们实际开发中都是以json格式来提交数据,若刚好对应到java中的实体
此时我们使用(@RequestBody User user)来接收
2.3 ajax的默认格式post提交
默认情况下,ajax默认的content-type是x-www-form-urlencoded, 它同样是以key1=value1&key2=value2来提交
@ResponseBody
@ResponseBody用于标识一个控制器方法,可以将该方法的返回值直接作为响应报文的响应体响应到
浏览器
若需要返回json格式给前端,则需要添加json依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
此时在我们的控制器方法上加上@ResponseBody,则客户端受到json格式
@RequestEntity
RequestEntity封装请求报文的一种类型,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参,可以通过getHeaders()获取请求头信息,通过getBody()获取请求体信息
(RequestEntity<String> requestEntity)
requestEntity.getHeaders()
requestEntity.getBody()
@ResponseEntity
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
@RestController
@RestController注解是springMVC提供的一个复合注解,标识在控制器的类上,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解
比如我们直接往客户端返回二进制流
public ResponseEntity<byte[]> xxx(){}
2022.06.06
拦截器
1. 作用
拦截器用于"拦截控制器方法"的执行
2. 实现
2.1 需要实现"HandlerInterceptor"
2.2 拦截必须在SpringMVC的配置文件中进行配置
拦截器三个抽象方法
preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截或放行,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法
postHandle:控制器方法执行之后执行postHandle()
afterComplation:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()
多个拦截器执行顺序
1. 若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的"顺序执行"(源码使用i++ 迭代遍历的),而postHandle()和afterComplation()会按照配置的"反序执行"(源码使用i-- 迭代遍历的)
2. 若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterComplation()会执行
拦截器实现
public class TestInterceptorF implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("TestInterceptorF::preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
System.out.println("TestInterceptorF::postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
System.out.println("TestInterceptorF::afterCompletion");
}
}
@Component
public class TestInterceptorS implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("TestInterceptorS::preHandle");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
System.out.println("TestInterceptorS::postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
System.out.println("TestInterceptorS::afterCompletion");
}
}
<!-- 配置拦截器-->
<mvc:interceptors>
<!-- 方式1:bean标签拦截所有-->
<!-- <bean class="com.company.mvc.interceptor.TestInterceptorF"/>-->
<!-- 方式2:ref标签引入bean(特别需要注意我们在Spring中学习过无自定义名称时,IOC会将类名首字母小写当作标识符)拦截所有-->
<!-- <ref bean="testInterceptorS"/>-->
<!-- 方式3:子interceptor标签可配置指定路径下匹配执行拦截器-->
<mvc:interceptor>
<!-- 需要匹配的路径-->
<mvc:mapping path="/**"/>
<!-- 需要排除的路径-->
<mvc:exclude-mapping path="/"/>
<!-- 对应的拦截器-->
<!-- <ref bean="testInterceptorS"/>-->
<bean class="com.company.mvc.interceptor.TestInterceptorF"/>
</mvc:interceptor>
</mvc:interceptors>
异常处理器
SpringMVC提供了一个处理控制器方法过程中所出现的异常的接口 "HandlerExceptionResolver"
其中它有两个实现类:
"DefaultHandlerExceptionResolver" (比如我们经常看的到404 405等异常页面)
"SimpleMappingExceptionResolver" (自定义异常处理器)
1. 基于xml配置文件开启异常处理器
<!-- 基于xml开启控制器方法异常处理器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--
properties的键表示处理器方法执行过程中出现的异常
properties的值表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面
-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!-- exceptionAttribute属性设置一个属性名,将出现的异常信息在请求域中进行共享-->
<property name="exceptionAttribute" value="iAmExceptionAreaObject"/>
</bean>
2. 基于注解开启异常处理器
@ControllerAdvice
public class TestInterceptor {
@ExceptionHandler(value = {ArithmeticException.class, NullPointerException.class})
public String handleException(Model model, Exception ex){
model.addAttribute("iAmExceptionAreaObject", ex);
return "error";
}
}
3. 自定义错误视图
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>error</title>
</head>
<body>
<h1>这里是自定义控制器方法异常处理 跳转到指定error页面 且可以通过配置的exceptionAttribute来获取异常信息</h1>
<h3 th:text="${iAmExceptionAreaObject}"></h3>
</body>
</html>
基于xml配置搭建Spring+SpringMVC开发环境
1. 新建一个空白的maven工程
1.1 配置打包方式为war包(web项目)
1.2 引入SpringMVC、日志、servlet、thymeleaf依赖包
<!-- 配置打包方式为war包(JavaWeb项目)-->
<packaging>war</packaging>
<!-- 模块依赖-->
<dependencies>
<!-- SpringMVC -->
<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>
<!-- ServletAPI -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!-- 已被Tomcat服务器提供 不需要打进war包 因此设置scope-->
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
2. 配置成web项目
2.1 project structure -> Modules -> 若maven成功拉取所有依赖包后,在我们新建的maven模块项目下有个Web
2.1 在右侧Deployment Descriptors(依赖描述)实际就是配置web.xml文件
2.3 点击"+"号 确认web.xml路径,一般是放在src\main\webapp下(默认的\WEB-INF\web.xml)因此只需添加前缀路径src\main\webapp
3. 配置web.xml
3.1 "Servlet":引入DispatcherServlet组件(SpringMVC依赖此servlet)
3.2 "Filter":加入针对POST提交,中文乱码处理过滤器(CharacterEncodingFilter)
3.3 "Filter":加入隐藏http提交方式"_method"字段支持RESTful风格的PUT、DELETE(HiddenHttpMethodFilter)
注意:
3.2的过滤器必须配置到3.3过滤器前,不然无效果
当配置了DispatcherServlet组件时,需要指定SpringMVC.xml文件配置路径因此需要创建该文件并配置关于SpringMVC的东西
<!--1. 配置编码过滤器-->
<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. 配置HiddenHttpMethodFilter-->
<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>
<!--3. 配置springMVC的前端控制器DispatcherServlet-->
<servlet>
<servlet-name>DispatcherServlet</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>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
4. 创建SpringMVC.xml并配置
在src\main\resource下创建springMVC.xml文件
在src\main\java下创建包,com.company.mvc.controller.TestController
com.company.mvc.interceptor.TestInterceptor
4.1 "context":开启Spring包扫描注册到IOC容器
4.2 "bean":配置View视图解析器 thymeleaf组件 (里面指定了视图的前缀路径,根据实际情况来配置)
4.3 "mvc":配置view-controller默认视图(不要控制器方法视图)
4.4 "mvc":由于开启了view-controller因此必须开启SpringMVC注解驱动
4.5 "mvc":配置静态资源访问(由于DispatcherServlet拦截了所有的请求,因此在匹配静态资源时会出现404,因此在部署到服务器后,需要开启DispatcherServlet转发查找不成功,则让服务器去查找,若再找不到才404)
4.6 "bean":配置文件上传解析MultipartFile(CommonsMultipartResolver)注意这个地方需要配置个id名称且为"multipartResolver"
注意:需要引入"commons-fileupload"依赖包
处理请求JSON格式也需要引入"com.fasterxml.jackson.core"依赖包
4.7 "mvc":配置拦截器
4.8 "bean":配置自定义控制器方法异常拦截(SimpleMappingExceptionResolver)
<!-- JSON处理-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
<!-- 文件上传-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
5. 配置本地Tomcat服务器
基于注解配置类搭建Spring+SpringMVC开发环境
在Servlet3.0环境中,容器会在类路径中查找实现"javax.servlet.ServletContainerInitializer"接口的类,如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为
"SpringServletContainerInitializer",这个类反过来又会查找实现"WebApplicationInitializer"的类并将配置的任务交给它们来完成。Spring3.2引入了一个便利的WebApplicationInitializer基础实现,名为
"AbstractAnnotationConfigDispatcherServletInitializer",当我们的类"扩展"(extends)了
AbstractAnnotationConfigDispatcherServletInitializer并将其部署到Servlet3.0容器的时候,容器会自动发现它,并用它来配置Servlet上下文。
public class WebConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebMVCConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
}
}
需要配置类去实现"WebMvcConfigurer"接口
在src\main下面创建webapp\WEB-INF\templates指定模板文件
@Configuration
@ComponentScan("com.company.mvc")
@EnableWebMvc
public class WebMVCConfig implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
@Bean
public MultipartResolver multipartResolver() {
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
return commonsMultipartResolver;
}
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException", "error");
exceptionResolver.setExceptionMappings(prop);
exceptionResolver.setExceptionAttribute("exception");
resolvers.add(exceptionResolver);
}
@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;
}
}
2022.06.07
SpringMVC执行流程
1. 客户端向服务器发送http请求,此时请求被SpringMVC的前端控制器"DispatcherServlet"捕获。
2. "DispatcherServlet"对请求的URL进行解析,得到请求资源标识符URI,判断请求URI所对应的映射
2.1 不存在
2.1.1 判断是否配置了"mvc:default-servlet-handler"
2.1.1.1 若未配置,则控制台报映射查找不到,客户端显示404错误
2.1.1.2 若有配置,则访问目标资源(一般为静态资源,如:JS\html\css),若还是找不到则客户端显示404错误
2.2 存在
2.2.1 根据该URI,调用"HandlerMapping"获得该"Handler"配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以"HandlerExecutionChain"执行链对象的形式返回
2.2.2 "DispatcherServlet" 根据获得的Handler,选择一个合适的"HandlerAdapter"
2.2.3 如果成功获得"HandlerAdapter",此时将开始执行拦截器的"preHandler(…)"方法【正向】
2.2.4 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)方法,处理请求。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
2.2.4.1 HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
2.2.4.2 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
2.2.4.3 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
2.2.4.4 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
2.2.5 Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象。
2.2.6 此时将开始执行拦截器的postHandle(...)方法【逆向】。
2.2.7 根据返回的ModelAndView(此时会判断是否存在异常:如果存在异常,则执行
HandlerExceptionResolver进行异常处理)选择一个适合的ViewResolver进行视图解析,根据Model和View,来渲染视图。
2.2.8 渲染视图完毕执行拦截器的afterCompletion(…)方法【逆向】。
2.2.9 将渲染结果返回给客户端。
SGG-SpringMVC
|