文章目录
SpringMVC学习笔记
![image-20220921151139280](https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/12497/202303011244436.png)
1、SpringMVC概述
-
SpringMVC是什么?
SpringMVC是Spring的一个子项目,他是Spring为表现层开发而提供的一个框架,采用了MVC这种思想来进行设计的,能够很好地将模型和视图进行分离。
Spring官网:Spring | Home
-
MVC是什么?
MVC(Model View Controller)是一种软件设计的架构模式,它要求软件开发时,需要进行将【业务的处理】和【视图显示】 的实现代码进行分离,然后使用控制器对他们进行调度。
推荐阅读:浅析三层架构和MVC模式的区别和联系
-
SpringMVC的特点:
- 继承Spring框架的特点:轻量、低侵入、灵活、组件化、声明式……
- 角色划分清晰。采用MVC的思想,让代码角色划分十分清晰,每一个不同的角色都有一个专门的对象来实现,同时各细分领域需要解决的问题全访问覆盖,提供一套全面且实用的解决方案
- 性能优异。SpringMVC底层源码使用了大量的设计模式,具有优秀的高并发能力,十分适合现代大型、超大型的互联网项目要求
- 基于Servlet。SpringMVC的本质是对Servlet的封装,通过功能强大的前端控制器DispatcherServlet(相当于是一个Filter),对请求和响应进行统一处理,使用SpringMVC后就不需要手动创建Servlet
……
-
SpringMVC的优点:
- 提高项目开发效率。清晰的角色划分、灵活的model转换、大量重复代码的封装、.可定制的绑定和验证,同时拥有简洁而强大的JSP标签库,无一不彰显SpringMVC框架的优秀,能够极大地提高软件开发,是当前表现层最主流的框架
- 容易上手,灵活轻巧。清晰的角色划分,让使用者能很舒服地上手这框架,声明式的开发让开发变得更加方便;同时SpringMVC支持组件化开发,可以很方便地搭配其他框架和插件使用
- 能够让Java程序性能更加优异。
……
2、快速入门
任务:使用Thymeleaf对HTML页面进行渲染,使用SpringMVC对Servlet进行封装
-
Step1:创建Maven项目
目录结构:
-
Step2:导入依赖
pom.xml:
<?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>org.example</groupId> <artifactId>day09_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <!--只在编译和测试的时候用,运行servlet时不发生冲突,tomcat内置有servlet-api--> <scope>provided</scope> </dependency> <!--Spring和thymeleaf的整合包--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--spring-mvc,间接引入了Spring-framework--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--logback,由于thymeleaf必须依赖slf4j日志门面,logback是slf4j日志门面的具体实现(和EHCache的使用一样)--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
-
Step3:编写web.xml
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--为SpringMVC的前端控制器起别名--> <servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> </servlet> <!--配置DispatcherServlet的拦截路径(DispatcherServlet本质是一个Filter)--> <servlet-mapping> <!-- 为SpringMVC这个Servlet设置访问路径,要让所有静态资源的请求和响应能被SpringMVC进行统一处理 备注:default是Tomcat中默认的Servlet,用于处理静态资源的 --> <servlet-name>SpringMVC</servlet-name> <!-- 使用/会拦截除.jsp以外的所有请求(注意会覆盖Tomcat默认的Servlet访问路径) --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
Step4:创建SpringMVC配置文件
SpringMVC的配置文件不需要手动加载,当我们初始化
DispatcherServlet
时,SpringMVC的配置文件就会被自动加载,这也就说明:SpringMVC的配置文件的命名和位置必须固定-
SpringMVC配置文件默认的位置:
WEB-INF
下后面需要将SpringMVC的配置文件放到Resources目录下,设置如下:
//通过init-param标签进行设置, <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param>
-
SpringMVC配置文件默认的名称:
DispatcherServlet的别名-serlvet.xml
知识拓展:
- 放到WEB-INF下的资源浏览器无法直接通过URL进行访问,只能通过请求转发的方式进行访问(这让WEB-INF目录下的资源更加安全)
- 复制到项目中的文件,在运行时可能不会被找到,可以使用Maven的
clean
和packge
指令
SpringMVC-servlet.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--扫描控制层组件--> <context:component-scan base-package="com.hhxy.controller"></context:component-scan> <!--配置Thymeleaf视图解析器 注意:id是SpringMVC规定死的,该视图解析器由SpringMVC内部实现 --> <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> </beans>
-
-
Step5:编写html
index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>欢迎来到首页</h1> <!-- 使用Thymeleaf的链接表达式,会自动将/转义成: localhost:8080/ + day09_springmvc/(上下文路径,也称虚拟目录) + hello --> <a th:href="@{/hello}">使用thymeleaf的链接表达式</a><br/> <!-- 而原始的href属性,不会自动补全上下文路径: localhost:8080/ + hello --> <a href="/hello">使用原始的超链接</a> </body> </html>
success.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>访问成功</title> </head> <body> <h1>success.html</h1> </body> </html>
-
Step6:编写控制器
HelloController:
package com.hhxy.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @author ghp * @date 2022/9/21 */ @Controller public class HelloController { /** * 访问index.html的方法 * 描述:当浏览器访问项目时,该方法直接进行拦截,并返回一个逻辑视图, * 然后逻辑视图被Thymeleaf视图解析器进行解析生成访问路径 * @return 返回一个逻辑视图 */ //将浏览器发送的请求映射到该方法(想当于这个方法就是一个Servlet),让Tomcat执行该方法 @RequestMapping("/") public String entrance(){ //将逻辑视图返回 return "index"; } /** * 访问hello.html的方法 * @return */ @RequestMapping("/hello") public String hello(){ return "success"; } }
-
Step7:测试
入门案例的执行流程:
- 请求拦截。浏览器发送请求,请求如果能被
DispatcherServlet
(前端控制器)拦截,则进行下一步;- 请求匹配。前端控制器加载SpringMVC的配置文件,通过扫描组件(注解实现Bean管理),将请求地址和控制器(使用
@Controller
表示的类)中的@RequestMapping
中的value属性
的值进行匹配- 视图解析。
@RequestMapping
表示的方法会返回一个字符串,这个字符串是逻辑视图,它会被Thymeleaf视图解析器进行解析,然后将它变成一个物理视图(给物理视图加上前缀和后缀)- 运行响应。控制器根据这个物理视图,请求转发到对应的html页面,然后Thymeleaf对这个html页面进行渲染,呈现出最终在页面上展示的效果
DispatcherServlet
继承FrameworkServlet
,而Framework的继承关系如图所示:
可以看到Framework继承了一大堆类,由于类加载的特性:当子类被加载时,会先初始化父类。这就导致初次使用SpringMVC进行访问资源时,会加载比较慢,这是比较影响用户体验的!由于这是Java的底层机制,我们是无法对其进行更改的,但机制的死的人是活的,我们可以通过将初始化操作提前到服务器启动时,这样其他用户进行访问时就会比较流畅了😄
具体可以再web.xml中进行设置:
<!--配置DispatcherServlet--> <servlet> <!--为SpringMVC的前端控制器起别名--> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--设置SpringMVC配置文件访问路径,默认是再WEB-INF下,现在是再resources下--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc.xml</param-value> </init-param> <!--将DispatcherServlet的初始化操作提前到服务器启动时--> <load-on-startup>1</load-on-startup> </servlet>
3、@RequestMapping注解详解
3.1 注解的功能和位置
-
@RequestMapping
注解的功能用于标识控制器中的方法,然后将请求的URL和
@RequestMapping
的Value属性的值进行映射,这样当浏览器发送请求时,DispatcherServlet就会通过请求的URL,依据映射关系找到对应控制器中的方法,让该方法处理请求。PS:它的作用就相当于Servlet中的
@WebServlet()
注解 -
@RequestMapping
注解的位置-
用于标识方法:设置映射请求请求路径的具体信息
-
用于标识类:设置映射请求的请求路径的初始信息(相当于是添加了一个虚拟目录)
使用
@RequestMapping
注解标识类,一般用于一个项目中多个控制器中存在同名的处理请求方法的情况,作用就是让区分不同控制器中的方法
示例:
@Controller @RequestMapping("/test") public class TestRequestMappingController { @RequestMapping("/") public String entrance(){ return "index"; } @RequestMapping("/hello") public String hello(){ return "success"; } }
如上所示,在之前快速入门的案例中我们没有使用
@RequestMapping
注解进行标识,按照上面这种写法能够直接访问到index.html,但是现在却不行了:需要在访问的URL上添加上标识在类上的
@RequestMapping
注解的vlaue的值 -
3.2 注解的属性
这里详细讲解
value
、method
、params
、heads
四个属性其它属性可以参考这篇文章:@RequestMapping属性详解 - SpringMVC高手进阶
-
value
属性:用于请求映射-
属性介绍:它是一个String类型的数组,具体可以参考
@RequestMapping
注解的源码 -
属性的使用:
//当访问的URL是/hello或者是/sucess时,都会执行这个方法 @RequestMapping(value = {"/hello","/success"}) public String hello(){ return "success"; }
备注:当访问的资源没有匹配到映射关系时,会直接报
404
-
-
method
属性:设置处理请求的方法只能处理某种类型的请求方式-
属性介绍:是一个RequestMethod类型的数组,可以设置多种请求方式(默认是支持所有的HTTP请求)
当请求地址能够匹配映射关系,但是处理请求的方法不支持这种请求方式,会报
405
-
属性的使用:
@RequestMapping(value = "/hello",method = RequestMethod.POST) public String hello(){ return "success"; } //这时候使用了浏览器访问,直接报405: Request method 'GET' not supported //因为浏览器模式的请求方式是GET,这里设置的POST
@RequestMapping
拥有派生注解,它和method
搭配起来,可以有:@GetMapping
、@PostMapping
、@PutMapping
和@DeleteMapping
,它们相当于是自带了method属性,分别对应get、post、put、delete四种请求方式,用法和@RequestMapping是一样的,只是自带了method属性 -
-
params
属性:指定请求中一定要含有或不含有指定参数,才会处理请求-
属性介绍:是一个字符串类型的数组,可以通过四种表达式设置请求参数 和请求映射的匹配关系
!param
:要求请求映射所匹配的请求必须不能携带param请求参数param=value
:要求请求映射所匹配的请求必须携带param请求参数且param=valueparam!=value
:要求请求映射所匹配的请求必须携带param请求参数但是param!=value
-
属性的使用:
//请求参数一定要有username和password,但是password的值不能是123 @RequestMapping(value = "/hello",params = {"username","password!=123"}) public String hello(){ return "success"; }
注意:若当前请求满足@RequestMapping注解的value和method属性,但是不满足params属性,此时 页面回报错
400
-
-
headers
属性:指定请求中一定要包含指定的请求头,才会去处理请求-
属性介绍:是一个字符串类型的数组,可以通过四种表达式设置请求头信 息和请求映射的匹配关系
header
:要求请求映射所匹配的请求必须携带header请求头信息!header
:要求请求映射所匹配的请求必须不能携带header请求头信息header=value
:要求请求映射所匹配的请求必须携带header请求头信息且header=valueheader!=value
:要求请求映射所匹配的请求必须携带header请求头信息且header!=value
-
属性的使用:
//表示直接收本机发送的请求 @RequestMapping(value = "/hello",headers="Referer=http://localhost:8080") public String hello(){ return "success"; }
-
3.3 路径相关设置和占位符的使用
-
SpringMVC支持Ant风格的路径方式,具体方式如下:
-
?
:表示任意的单个字符 -
*
:表示任意的0个或多个字符 -
**
:表示任意层数的任意目录
注意事项:
-
在使用
?
时,必须要有一个字符,这个字符可以任意,但是对于一些特殊字符,如:?
、\
、/
会访问失败 -
在使用
*
时,同样对于上面哪些特殊符号也会访问失败 -
在使用
**
时,只能使用xxx/**/xxx
的方式,同时对于?
会访问失败,对于斜线
访问有效
-
-
SpringMVC支持在路径中使用占位符:
{}
- 不使用占位符,传递参数:/test?id=1
- 使用占位符,传递参数:test/1
测试1:一个参数的传递
测试2:多个参数的传递
index.html:
<a th:href="@{/test/张三/123}">测试在value属性中使用占位符</a>
Controller:
@RequestMapping("test/{username}/{password}") public String testAnt(@PathVariable("password") String password,@PathVariable("username") String username){ System.out.println("username = " + username); System.out.println("password = " + password); return "success"; }
注意:参数的获取一定要依据URL中参数的顺序,同时要考虑参数的类型
乱码的解决方案:
待定
4、SpringMVC获取请求参数
4.1 获取请求参数的方式
这里会介绍获取请求参数的6种方式,如下所示
- 通过
@PathVariable
获取:只能搭配占位符使用- 通过
Servlet API
获取:无法搭配占位符使用,可以搭配?
、&
或表单来使用,还能获取Header、Cookie相关参数的值- 通过
方法的形参
来获取:无法搭配占位符使用,可以搭配?
、&
或表单来使用- 通过
@RequestHeader
注解获取:只能获取Header相关的参数的值- 通过
@CookieValue
注解获取:只能获取Cookie相关的参数的值- 通过
POJO
获取:
-
通过
@PathVariable
获取,这个只能搭配占位符使用 -
通过Servlet API获取:
@RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request){ String username = request.getParameter("username"); String password = request.getParameter("password"); System.out.println("username = " + username); System.out.println("password = " + password); return "success"; }
-
通过
方法的形参
来获取:@RequestMapping("/testParam") public String testParam(String username,String password){ System.out.println("username = " + username); System.out.println("password = " + password); return "success"; }
注意事项:形参要求和请求参数的名字一致,如果一定想要使用不同的形参来获取,可以使用
@RequestParam
注解@RequestMapping("/testParam") public String testParam( @RequestParam(value = "username",required = false) String name, String password){ System.out.println("username = " + name); System.out.println("password = " + password); return "success"; }
备注:required属性用于设置浏览器必须传递该请求参数,没有则报错,默认是true,即:浏览器必须传递该参数
-
通过
@RequestHeader
注解获取:@RequestMapping("/testRequestHeader") public String testRequestHeader(@RequestHeader("referer") String referer){ System.out.println("referer = " + referer); //输出:referer = http://localhost:8080/day10_springmvc/form return "success"; }
备注:它是用来获取请求头参数的值
-
通过
@CookieValue
注解获取:@RequestMapping("/testCookieValue") public String testCookieValue(@CookieValue("JSESSIONID") String jsessionId){ System.out.println("JSESSIONID = " + jsessionId); return "success"; }
注意事项:它是用来获取Cookie相关的参数的值,执行上面的代码前提是得要先有一个SessonId
-
通过
POJO
获取请求参数:@RequestMapping("/testPojo") public String testPojo(User user){ System.out.println(user); return "success"; }
注意事项:实体类的属性名要和请求参数名称一致
4.2 解决请求参数中文乱码
只需要在web.xml中加入以下代码,即可:
<!--配置一个Spring编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!--框架拥有自定义的编码(ISO-8859-1),我们需要修改默认的编码-->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!--上面的编码默认只用于请求,我们还需要修改forceEncoding,让他能它用于响应-->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!--设置Spring编码过滤器的拦截路径-->
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注意事项:SpringMVC的编码过滤器一定要在其他过滤器之前,否则无效
5、向域对象共享数据
在JavaWeb种我们学过了四大域对象,其中PageContext是JSP中用到的,由于JSP已经被淘汰了,所以这个域对象已经被淘汰了。所以在向域对象中共享数据这里介绍7种方法,分别是:
向Request域对象中共享数据共4种方式:
使用
Servlet API
向Request域对象共享数据使用
ModelAndView
向Request域对象共享数据使用
Model
向Request域对象共享数据使用
ModelMap
向request域对象共享数据向Session域对象中共享数据:使用
Servlet API
向Application域对象中共享数据:使用
Servlet API
-
使用
Servlet API
向Request域对象共享数据:@RequestMapping("/testServletAPI") public String testServletAPI(HttpServletRequest request){ request.setAttribute("testScope", "hello,servletAPI"); return "success"; }
-
使用
ModelAndView
向Request域对象共享数据:这是Spring官方推荐的方法,在SpringMVC底层其他所有在域对象中共享的数据都会封装成一个ModelAndView对象
@RequestMapping("/test/mav") public ModelAndView testMAV(){ /** * ModeAndView包含Model和View的功能 * Model: 向请求域中共享数据 * View: 设置逻辑视图,实现页面跳转 */ //1、获取ModelAndView对象 ModelAndView mav = new ModelAndView(); //2、往域对象中共享数据 mav.addObject("information","hello,ModelAndView!"); //3、设置逻辑视图,实现网页跳转 mav.setViewName("success"); //4、返回ModelAndView对象 return mav; }
index.html:
<!--测试域对象--> <a th:href="@{/test/mav}">测试通过ModelAndView向请求域中共享数据</a>
success.html:
<h1>跳转成功</h1> <!--获取域对象中的数据--> <p th:text="${information}"></p> </body>
测试结果:
-
使用
Model
向Request域对象共享数据:@RequestMapping("/test/model") public String testModel(Model model){ model.addAttribute("information","hello,Model!"); return "success"; }
-
使用
ModelMap
向Request域对象共享数据:@RequestMapping("/test/madelMap") public String testModel(ModelMap modelMap){ modelMap.addAttribute("information","hello,ModelMap!"); return "success"; }
-
使用
Map
向Request域对象共享数据:@RequestMapping("/test/map") public String testMap(Map<String, Object> map){ map.put("information", "hello,Map!"); return "success"; }
知识拓展:
本质上,Model、ModelMap、Map类型的参数其实本质上都是
BindingAwareModelMap
类型的可以通过
对象.getClass().getName()
输出测试
-
向Session域共享数据:
@RequestMapping("/test/session") public String testSession(HttpSession session){ session.setAttribute("testSessionScope", "hello,session!"); return "success"; }
-
向Application域共享数据:
@RequestMapping("/test/application") public String testApplication(HttpSession session){ ServletContext application = session.getServletContext(); application.setAttribute("testApplicationScope", "hello,application!"); return "success"; }
6、SpringMVC视图
SpringMVC中的视图是View接口,视图的作用渲染数据,也就是将模型Model中的数据展示在网页上,让用户能够看到。
而在SpringMVC中视图的种类有很多,默认的有转发视图和重定向视图;当引入JSTL依赖后,视图会自动转换成JSTL视图;
当引入Thymeleaf依赖后,并且配置好Thymeleaf视图解析器后,又会得到Thymeleaf视图。
-
转发视图
SpringMVC中默认的转发视图是
InternalResourceView
, SpringMVC中创建转发视图的情况: 当控制器方法中所设置的视图名称以forward:
为前缀时,创建InternalResourceView视图,此时的视 图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部 分作为最终路径通过转发的方式实现跳转@RequestMapping("/test/view/forward") public String testForward() { //请求转发到TestScopeController中的test/model方法,让它处理请求,URL不会变 return "forward:/test/model"; }
-
重定向视图
SpringMVC中默认的重定向视图是
RedirectView
,当控制器方法中所设置的视图名称以redirect:
为前缀时,创建RedirectView视图,此时的视图名称不 会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最 终路径通过重定向的方式实现跳转@RequestMapping("/test/view/redirect") public String testRedirect(){ //重定向到TestScopeController中的test/model方法,让它处理请求,URL会变 return "redirect:/test/model"; }
注意:在SpringMVC中使用重定向,
redirect:/
会自动解析成上下文路径(虚拟目录),不需要像JavaWeb中一样去手动添加 -
Thymeleaf视图
当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被SpringMVC配置文件中所配置 的视图解析器解析,视图名称拼接视图前缀和视图 后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/test/view/thymeleaf") public String testThymeleafView(){ return "success"; }
温馨提示:一般都极少使用SpringMVC默认的视图,都是直接使用Thymeleaf视图,Thymeleaf视图既能转发也能重定向
-
视图控制器
<!--配置视图控制器,配置后就可以不用未index页面写一个访问方法了(其实那个方法就是一个视图控制器) path: 用于设置访问路径 view-name: 访问路径对应的逻辑视图 --> <mvc:view-controller path="/" view-name="index"></mvc:view-controller>
备注:上面配置的那个控制器和3.1中的那个
entrance
方法是一致的注意事项:如果在SpringMVC配置文件中设置了视图控制器,只有配置文件中的的视图能被
DispatcherServlet
处理,需要开启MVC的注解驱动才能让Controller中的视图被DispatcherServlet
处理:<!--开启SpringMVC的注解驱动--> <mvc:annotation-driven/>
不开启SpringMVC注解驱动,如下所示
DispatcherServlet
不会处理那些使用@RquestMapping
设置的方法,也就会导致浏览器无法访问方法对应的页面:
7、RESTful
7.1 RESTful概述
-
什么是RESTful?
RESTful是指满足REST的应用程序或设计。本质是一种网络应用程序的设计风格和开发方式,它是基于HTTP,可以使用XML格式定义或JSON格式定义。
个人见解:RESTful就像是一种编程风格,类似于OOP,OOP是面对对象编程,在OOP中一切皆对象,而在RESTful中,RESTful是面向资源编程,在RESTful中一切皆资源
-
什么是REST?
REST(Representational State Transfer,表现形式状态转换)是一组架构约束条件和原则。
-
REST的原则:
- 为每一个资源设置一个URI,每一个URI代表1种资源
- 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源
- 通过操作资源的表现形式(URI)来操作资源
- 资源的表现形式是XML或者HTML
- 通过XML或JSON来传递数据
- 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息
-
REST的约束:
- 客户端-服务端约束。以客户端-服务器形式建立一个基本的分布式架构,从而支持客户端逻辑与服务端逻辑独立演化。客户端处理js交互,CSS, HTML渲染等,服务器处理业务逻辑,数据存储。客户端可以升级某个版本,而不影响服务器的功能。
- 无状态约束。任意一个Web请求 端提出请求时,请求本身包含了响应端为响应这一请求所需的全部信息(请求的信息都保留在客户端)
- 缓存约束。缓存约束建立在 客户端-服务器 和 无状态 之上,对请求的响应中的数据隐式或显式标记为可缓存或不可缓存。如果响应是可缓存的,则客户端缓存有权重用该响应数据以用于以后的等效请求
- 统一接口约束。所有的客户端和服务器必须共用一套接口约束
- 分层系统约束。分层约束建立在 客户端-服务器约束 之上,可以向架构中添加中间件,每一层都可以独立的部署和进化。常见的中间件有,负载均衡设备(F5, Nginx, LVS),缓存组件(Redis, memcache),消息队列(kafka, rabbitmq)
- 按需代码约束。按需代码属于可选约束,允许通过以小程序或脚本的形式下载和执行代码来扩展客户端功能
-
RESTful中相关基础概念:
- 资源:资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、 数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴 趣的客户端应用,可以通过资源的URI与其进行交互
- 资源的表述:资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式
- 状态的转移:在客户端和服务器端之间转移代表资源状态的表述,通过转移和操作资源的表述,来间接实现操作资源的目的
7.2 RESTful的实现
满足REST的原则和约束的的设计方式或开发方式称之为RESTful(REST风格),从这里我们可以知道要实现RESTful首先要满足REST的原则和约束。REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方 式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性
7.2.1 使用RESTful风格实现CRUD(一)
任务:使用RESTful风格完成CRUD基本操作
备注:并没有进行真正的CRUD,只是对CRUD方法进行了访问
要点:
- put、delete请求方式的表示
- 编码过滤器和请求方式过滤器的顺序
- 注解驱动的开启
-
Step1:创建Maven工程
目录结构:
-
Step2:导入依赖
pom.xml:
<?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>org.example</groupId> <artifactId>day11_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--Spring整合Thymeleaf的依赖--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--SpringMVC依赖,间接引入了spring-framework上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--引入logback-classic,slf4j日志门面的实现包,thymeleaf需要依赖slf4j日志门面--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
-
Step3:编写web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <!--配置SpringMVC编码过滤器--> <filter> <!--给CharacterEncodingFilter起别名--> <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> <!--设置CharacterEncodingFilter的拦截路径--> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--设置处理请求方式的过滤器,用于获取put或delete请求--> <filter> <!--给过滤器起别名--> <filter-name>HiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <!--设置HiddenHttpMethodFilter的拦截路径--> <filter-mapping> <filter-name>HiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!--SpringMVC的前端控制器--> <servlet> <!--给DispatcherServlet起别名--> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--设置SpringMVC配置文件的位置(默认是在WEB-INF目录下的)--> <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> </web-app>
-
Step4:编写SpringMVC配置文件
springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--扫描组件,让IOC能够管理组件--> <context:component-scan base-package="com.hhxy.controller"></context:component-scan> <!--配置thymeleaf视图解析器--> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <!--设置优先级,让视图能够优先被thymeleaf视图解析器解析(默认优先级是Integer的最大值)--> <property name="order" value="1"/> <!--设置视图解析器的编码格式--> <property name="characterEncoding" value="UTF-8"/> <!--设置thymeleaf模板引擎--> <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> <!--开启SpringMVC的注解驱动,当在SpringMVC配置文件中配置了视图解析器 如果不开启注解驱动,则只有SpringMVC配置文件中的视图解析器能被DispatcherServlet进行处理 --> <mvc:annotation-driven/> <!--为index.html配置一个视图控制器--> <mvc:view-controller path="/" view-name="index"></mvc:view-controller> </beans>
-
Step5:编写HTML
index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>index.html</h1> <!--使用RESTful风格实现增、删、改、查方法--> <a th:href="@{/user}">查询所有的用户信息</a><br/> <a th:href="@{/user/1}">查询id为1的用户信息</a><br/> <form th:action="@{/user}" method="post"> <input type="submit" value="测试添加用户信息"> </form> <form th:action="@{/user}" method="post"> <!--设置put或delete请求,前提是method必须是post--> <input type="hidden" name="_method" value="put"> <input type="submit" value="测试修改用户信息"> </form> <form th:action="@{/user/1}" method="post"> <!--设置put或delete请求,前提是method必须是post--> <input type="hidden" name="_method" value="delete"> <input type="submit" value="测试根据id删除用户信息"> </form> </body> </html>
success.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>成功跳转页面</title> </head> <body> <h1>success.html</h1> </body> </html>
-
Step6:编写Java代码
package com.hhxy.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; /** * @author ghp * @date 2022/10/1 * @title 实现REST * @description 测试使用RESTful风格实现的增、删、改、查方法 */ @Controller public class TestRestController { // @RequestMapping(value = "/user",method = RequestMethod.GET) @GetMapping("/user") public String selectAll(){ System.out.println("查询所有用户信息-->/user--->get"); return "success"; } // @RequestMapping(value = "/user/{id}",method = RequestMethod.GET) @GetMapping("user/{id}") public String selectById(@PathVariable("id")int id){ System.out.println("根据id查询用户信息-->/user/"+id+"-->get"); return "success"; } // @RequestMapping(value = "/user",method = RequestMethod.POST) @PostMapping("/user") public String add(){ System.out.println("添加用户信息-->/user-->post"); return "success"; } // @RequestMapping(value = "/user",method = RequestMethod.PUT) @PutMapping("/user") public String update(){ System.out.println("修改用户信息-->/user-->put"); return "success"; } // @RequestMapping(value = "/user/{id}",method = RequestMethod.DELETE) @DeleteMapping("/user/{id}") public String deleteById(@PathVariable("id") int id){ System.out.println("修改用户信息-->/user/"+id+"-->delete"); return "success"; } }
-
Step7:测试
7.2.2 使用RESTful风格实现CRUD(二)
任务:使用RESTful风格实现CRUD,并动态显示数据
备注:并没有连接数据库,而是通过一个EmployeeDao类来模拟存储员工信息的数据库(效果都是一样的)
要点:
- DispatecherServlet和DefaultServlet的冲突
- 修改数据时,需要进行数据回显
- 不同操作对应不同的请求方式
-
Step1:创建Maven工程
目录结构见7.2.1 Step1,略……
-
Step2:导入依赖
pom.xml见7.2.1 Step2,略……
-
Step3:编写web.xml
见7.2.1 Step3,略……
-
Step4:编写SpringMVC配置文件
在7.2.1 的SpringMVC配置文件中添加如下内容:
<!--扫描dao层的组件,让IOC能够管理--> <context:component-scan base-package="com.hhxy.dao"></context:component-scan> <!--让DefaultServlet处理静态资源,解决DispatecherServlet和DefaultServelt产生的冲突 这样写的效果是:当DispatecherServlet无法处理请求时,会让DefaultServlet来处理--> <mvc:default-servlet-handler/>
知识拓展:
DefalutServlet
和DispatecherServlet
冲突产生的原因:DefaultServlet是Tomcat默认用来处理静态资源1的,可以在Tomcat的web.xml
中进行查看,它的url-patter是/
;而我们在配置DispatecherServlet的url-pattern时也是设置的/
,这就导致静态资源由DispatecherServlet来处理,而DispatecherServlet它没有处理静态资源的能力,就会导致404
,从而是静态资源无法被加载到页面上Tomcat的web.xml和我们项目中的web.xml是一个”继承关系“,当我们在Tomcat中设置了一个Servlet的访问路径为
/
时,会覆盖掉Tomcat中Servlet的访问路径,让DefaultServlet失效,从而导致静态资源由DisptecherServlet来处理 -
Step5:编写HTML页面
1)static,略……
2)index.html,略……
3)employee_list.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>employee_list</title> <!--引入CSS样式--> <link rel="stylesheet" th:href="@{/static/css/index_work.css}"> </head> <body> <table> <tr> <th colspan="5">Employee List</th> </tr> <tr> <th>id</th> <th>lastName</th> <th>email</th> <th>gender</th> <th>options</th> </tr> <tr th:each="employee : ${allEmployee}"> <td th:text="${employee.id}"></td> <td th:text="${employee.lastName}"></td> <td th:text="${employee.email}"></td> <td th:text="${employee.gender}"></td> <td> <a href="">delete</a> <a href="">update </a> </td> </tr> </table> </body> </html>
-
Step6:编写Java
1)编写EmployeeDao
package com.hhxy.dao; import com.hhxy.pojo.Employee; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * @author ghp * @date 2022/10/1 * @title 员工数据类 * @description 模拟存储员工数据的员工表 */ @Repository public class EmployeeDao { private static Map<Integer, Employee> employees = null; static{ employees = new HashMap<Integer, Employee>(); employees.put(1001, new Employee(1001, "E-AA", "aa@163.com", 1)); employees.put(1002, new Employee(1002, "E-BB", "bb@163.com", 1)); employees.put(1003, new Employee(1003, "E-CC", "cc@163.com", 0)); employees.put(1004, new Employee(1004, "E-DD", "dd@163.com", 0)); employees.put(1005, new Employee(1005, "E-EE", "ee@163.com", 1)); } private static Integer initId = 1006; /** * 添加和修改功能 * @param employee 待添加的员工数据 */ public void save(Employee employee){ if(employee.getId() == null){ //当员工的id为null时,就是添加功能 employee.setId(initId++); } //将员工数据添加到Map集合中(如果员工的id不为null,就会直接覆盖已有id的员工信息) employees.put(employee.getId(), employee); } /** * 获取所有员工的信息 * @return */ public Collection<Employee> getAll(){ return employees.values(); } /** * 根据id获取员工信息 * @param id * @return */ public Employee get(Integer id){ return employees.get(id); } /** * 根据id删除员工信息 * @param id */ public void delete(Integer id){ employees.remove(id); } }
2)Employee:
/** * @author ghp * @date 2022/10/1 * @title * @description */ public class Employee { private Integer id; private String lastName; private String email; //1 male, 0 female private Integer gender; //构造、get、set、toString省略 }
3)EmployeeController
package com.hhxy.controller; import com.hhxy.dao.EmployeeDao; import com.hhxy.pojo.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; import java.util.Collection; /** * @author ghp * @date 2022/10/1 * @title 员工控制器 * @description 用于处理前端发送的对员工数据进行操作的请求 */ @Controller public class EmployController { //自动装配一个EmployeeDao对象,方便调用方法实现增、删、改、查的功能 @Autowired private EmployeeDao employeeDao; /** * 展示所有员工的信息 * * @param model * @return */ @GetMapping("/employee") public String selectAll(Model model) { //从数据库中获取所有的员工信息 Collection<Employee> allEmployee = employeeDao.getAll(); //将数据存储到域对象中 model.addAttribute("allEmployee", allEmployee); //返回逻辑视图 return "employee_list"; } /** * 添加数据 * * @param employee * @return */ @PostMapping("/employee") public String add(Employee employee) { //将employee_add.html页面传过来的数据添加到”数据库“中 employeeDao.save(employee); //重定向到selectAll,同时刷新页面数据(这里重定向是用浏览器进行重新访问的,默认是get方式,所以不用去担心不会重定向到selectAll) return "redirect:/employee"; } /** * 回显数据(点击update跳转到employee_update页面,并展示之前的数据) */ @GetMapping("/employee/{id}") public String toUpdate(@PathVariable("id") Integer id, Model model) { //根据id查询员工信息 Employee employee = employeeDao.get(id); //将查询出来的员工数据存储到域对象中 model.addAttribute("employee", employee); //跳转到employ_update.html return "employee_update"; } /** * 将修改的输出保存到数据库中 */ @PutMapping("/employee") public String updateEmployee(Employee employee) { //修改员工信息(本质是覆盖) employeeDao.save(employee); //重定向到employee_list.html return "redirect:/employee"; } /** * 删除数据的方法 */ @DeleteMapping("/employee/{id}") public String deleteById(@PathVariable("id") Integer id) { employeeDao.delete(id); return "redirect:/employee"; } }
-
Step7:测试
这里只展示查询功能,具体完整代码可以参观我的Gitee或Github仓库
8、SpringMVC处理AJAX请求
8.1 使用@RequestBody处理AJAX请求
示例:
-
Step1:创建Maven工程
目录结构:
-
Step2:导入依赖
pom.xml:
<?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>org.example</groupId> <artifactId>day12_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--spring和thymeleaf整合包--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--springMVC依赖,间接导入了spring-framework上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--引入logback-classic,slf4j日志门面的实现包,thymeleaf需要依赖slf4j日志门面--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
-
Step3:编写web.xml
配置一个编码过滤器、一个获取请求参数的过滤器、一个前端控制器。略……
-
Step4:编写SpringMVC配置文件
springmvc.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--扫描组件--> <context:component-scan base-package="com.hhxy"></context:component-scan> <!--配置thymeleaf视图解析器--> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <!--设置优先级,让视图能够优先被thymeleaf视图解析器解析(默认优先级是Integer的最大值)--> <property name="order" value="1"/> <!--设置视图解析器的编码格式--> <property name="characterEncoding" value="UTF-8"/> <!--设置thymeleaf模板引擎--> <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> <!--让DefaultServlet处理静态资源--> <mvc:default-servlet-handler/> <!--开启注解驱动--> <mvc:annotation-driven/> <!--为index.html配置一个视图控制器--> <mvc:view-controller path="/" view-name="index"></mvc:view-controller> </beans>
-
Step5:编写HTML
1)index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <div id="app"> <h1>index.html</h1> <button @click="testAjax">测试SpringMVC处理ajax</button><br/> <button @click="testRequestBody">测试@RequestBody注解处理Json格式的请求参数</button> </div> </body> <!--引入vue--> <script th:src="@{/js/vue.js}"></script> <!--引入axios--> <script th:src="@{/js/axios.min.js}"></script> <script> new Vue({ el:"#app", methods:{ testAjax(){ /* axios({ url:"", method:"", //以?和&的形式发送的数据,存储在请求行中,请求参数可以通过request.getParameter() params:{}, //以json字符串的格式发送数据,存储在请求体中(所以请求方式必须是post、put、patch),请求参数需要序列化、反序列化获取(使用fastjson) data:{} }).then(resp => { console.log(resp.data); }); */ axios.post( "/day12_springmvc//test/ajax?id=1001", {username:"admin",password:"123"} ).then(resp => { console.log(resp.data); }); } } }) </script> </html>
2)success.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>成功页面</title> </head> <body> <h1>success.html</h1> </body> </html>
-
Step6:编写Java代码
package com.hhxy.controller; import com.hhxy.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author ghp * @date 2022/10/3 * @title * @description */ @Controller public class TestAjaxController { @RequestMapping("/test/ajax") public void testAjax(Integer id, @RequestBody String requestBody, HttpServletResponse response) throws IOException { System.out.println("id = " + id); System.out.println("requestBody = " + requestBody); response.getWriter().write("hello,Ajax!"); } }
-
Step7:测试
但是此时虽然适用
@RequestBody
注解获取到了请求体的参数,但是参数仍然是Json字符串,无法直接使用,我们需要将其转成Java对象才能直接适用,解决方案:
Step1:导入jaxkson依赖
<!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.4</version> </dependency>
Step2:在SpringMVC配置文件中开启注解驱动
<mvc:annotation-driven/>
Step3:使用
@RequestBody
注解在控制器方法的形参位置,设置一个Json字符串要转换对象的Java对象Step4:使用Response对象向前端返回响应数据
具体实现:
Controller中的代码:
@RequestMapping("/test/RequestBody/json") public void testRequestBody(@RequestBody User user,HttpServletResponse response) throws IOException { System.out.println(user); response.getWriter().write("hello,RequestBody!"); }
index.html中的代码:
testRequestBody(){ axios.post( "/day12_springmvc/test/RequestBody/json", {username:"admin",password:"123",age:22,gender:"男"} ).then(resp => { console.log(resp.data); }); }
测试结果:
除了适用实体类获取,还可以适用Map集合获取
@RequestMapping("/test/RequestBody/json") public void testRequestBody(@RequestBody Map<String,Object> map, HttpServletResponse response) throws IOException { System.out.println(map); response.getWriter().write("hello,RequestBody!"); }
8.2 使用@ResponseBody处理AJAX请求
前面我们的控制器方法返回值是一个逻辑视图,Thymeleaf视图解析器会将他解析成一个页面,我们想要响应数据给HTML需要使用Servlet API,这显得很不方便。而通过将
@ResponseBody
注解加在控制器方法上,就能够让控制器方法的返回值直接以响应体数据发送给HTML实现步骤:
- Step1:导入
jackson
的依赖- Step2:在SpringMVC配置文件开启注解驱动
<mvc:annotation-driven/>
- Step3:给控制器方法配置
@ResponseBody
注解- Step4:直接通过方法返回值向前端返回响应数据
备注:这种方式最常用
@RequestMapping("test/ResponseBody/json")
@ResponseBody
public User testResponseBodyJson(){
User user = new User(1001,"admin","123",20);
return user;
}
PS:同理也可以使用Map集合或者List集合来发送响应数据,这里就不再演示了……具体代码请参考Gitee或Github仓库
知识拓展:
@RestController
@RestController
注解是一个复合注解,将它标识在类上相当于:给类标识了@Controller
注解,给类中所有方法标识了@ResponseBody
注解
9、文件下载和上传
9.1 文件下载
文件的下载需要使用到
ResponseEntity
,它用于控制器方法的返回值类型,该控制器方法的返回值类型就是响应给浏览器的数据。ResponseEntity
对象是Spring
对请求响应的封装。它继承了HttpEntity
对象,包含了Http的响应码(httpstatus
)、响应头(header
)、响应体(body
)三个部分。
html:
<a th:href="@{/test/down}">测试文件的下载</a>
Java:
package com.hhxy.controller;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @author ghp
* @date 2022/10/4
* @title 使用SpringMVC实现文件上传和下载
* @description
*/
@Controller
public class TestResponseEntity {
@RequestMapping("/test/down")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
//1、将图片加载到内存中
//1.1 获取ServletContext对象
ServletContext servletContext = session.getServletContext();
//1.2 获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/img/1.jpg");
//1.3 将文件以流的形式加载内存中
InputStream is = new FileInputStream(realPath);
//2、将存储文件的流对象转成字节数组
//2.1 创建字节数组
byte[] bytes = new byte[is.available()];
//2.2 将流读到字节数组中
is.read(bytes);
//3、将字节数组封装到ResponseEntity对象中
//3.1 创建HttpHeaders对象设置响应头信息
MultiValueMap<String, String> headers = new HttpHeaders();
//3.2 设置要下载方式以及下载文件的名字
headers.add("Content-Disposition", "attachment;filename=ghp.jpg");
//3.3 设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
//3.4 创建ResponseEntity对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
//4、释放资源
is.close();
//5、返回ResponseEntity对象,浏览器受到这个对象就会直接将该对象中的数据下载到本地
return responseEntity;
}
}
测试:
9.2 文件上传
文件上传要求form表单的请求方式必须为post,并且添加属性
enctype="multipart/form-data"
。SpringMVC中将上传的文件封装到MultipartFile
对象中,通过此对象可以获取文件相关信息。
-
Step1:导入依赖
<?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>org.example</groupId> <artifactId>day12_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--spring和thymeleaf整合包--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--springMVC依赖,间接导入了spring-framework上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--引入logback-classic,slf4j日志门面的实现包,thymeleaf需要依赖slf4j日志门面--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!--文件上传依赖--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> </dependencies> </project>
-
Step2:编写SpringMVC配置文件
<!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean> <!--备注:还需扫描组件、配置Thymeleaf视图解析器、默认Servlet、开启注解驱动-->
-
Step3:编写HTML
<form action="@{/test/up}" method="post" enctype="multipart/form-data"> 头像: <input type="file" name="photo"><br/> <input type="submit" value="上传"> </form>
注意:input标签的name一定要和MultipartFile类型的形参名称一样,否则报空指针异常
-
Step4:编写Java
/** * 实现文件上传的控制器方法 */ @RequestMapping("/test/up") public String testUp(MultipartFile photo,HttpSession session) throws IOException { //1、设置上传文件存储在本地时的文件名(需要解决文件名重复问题,当文件名重复时后传文件的内容会覆盖前传文件的内容) //1.1 获取上传文件的文件名 String fileName = photo.getOriginalFilename(); //1.2 获取文件后缀名 String suffix = fileName.substring(fileName.lastIndexOf(".")); //1.3 获取一个永远不重复文件名(也可以使用时间戳实现) fileName = UUID.randomUUID().toString() + suffix; //2、设置上传文件存储在服务器的位置 //2.1 获取服务器中photo目录的路径 ServletContext servletContext = session.getServletContext(); String photoPath = servletContext.getRealPath("photo"); //2.2 创建File对象,用于判断路径是否存在 File file = new File(photoPath); if(!file.exists()){ //当路劲不存在时,就在target下创建photo路径(与就是文件夹) file.mkdir(); } //2.3 获取上传文件的在服务器中的位置 String finalPath = photoPath + File.separator + fileName; //3、将前端上传的文件的数据放在Web服务器路径下的fileName中 photo.transferTo(new File(finalPath)); //4、上传成功后,跳转到success.html页面 return "success"; }
-
Stpe5:测试
可以看到上传的文件在
target/photo
下:
10、拦截器
示例:
-
Step1:创建Maven工程
-
Step2:导入依赖
<?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>org.example</groupId> <artifactId>day13_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--spring和thymeleaf整合包--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--springMVC依赖,间接导入了spring-framework上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--引入logback-classic,slf4j日志门面的实现包,thymeleaf需要依赖slf4j日志门面--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
-
Step3:编写web.xml
配置编码过滤器、请求过滤器、前端控制器。略……
-
Step4:编写SpringMVC配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--扫描组件--> <context:component-scan base-package="com.hhxy"></context:component-scan> <!--配置thymeleaf视图解析器--> <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver"> <!--设置优先级,让视图能够优先被thymeleaf视图解析器解析(默认优先级是Integer的最大值)--> <property name="order" value="1"/> <!--设置视图解析器的编码格式--> <property name="characterEncoding" value="UTF-8"/> <!--设置thymeleaf模板引擎--> <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> <!--让DefaultServlet处理静态资源--> <mvc:default-servlet-handler/> <!--开启注解驱动--> <mvc:annotation-driven/> <!--为index.html配置一个视图控制器--> <mvc:view-controller path="/" view-name="index"></mvc:view-controller> <!--配置SpringMVC拦截器--> <mvc:interceptors> <bean class="com.hhxy.interceptor.FirstInterceptor"></bean> </mvc:interceptors> </beans>
知识拓展:SpringMVC拦截器相关知识
-
SpringMVC拦截器的配置方式
-
方式一:根据类型引入Bean,直接使用class属性进行配置,如上所示
-
方式二:使用ref引入Bean
<!--为拦截器配置一个Bean--> <bean id="firstInterceptor" class="com.atguigu.interceptor.FirstInterceptor"></bean> <!--配置SpringMVC拦截器--> </mvc:interceptor> <ref bean="firstInterceptor"></ref> </mvc:interceptor>
-
方式三:使用注解默认的id引入Bean
前提:需要先使用
@Component
注解标记拦截器所在类,然后再配置文件中要扫描组件这时候拦截器就会拥有一个默认的id,id为拦截器类名的小驼峰
</mvc:interceptor> <!--拦截器的类名为FirstInterceptor--> <ref bean="firstInterceptor"></ref> </mvc:interceptor>
方式一和方式二都是使用XML管理Bean,方式三是使用注解管理Bean,切记要开启注解驱动!
-
-
拦截路径配置
前面三种方式配置的拦截器会拦截所有的请求,可以通过以下方式配置详细的拦截路径
<mvc:interceptors> <!-- <bean class="com.hhxy.interceptor.FirstInterceptor"></bean>--> <mvc:interceptor> <!--设置拦截器的拦截路径 /* 只能拦截单层目录, /** 拦截多层目录的请求--> <mvc:mapping path="/*"/> <!--排除路径,虚拟目录 /abc 请求不会被拦截--> <mvc:exclude-mapping path="/abc"/> <!--设置拦截器(这里同样有三种方式)--> <bean class="com.hhxy.interceptor.FirstInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
-
拦截器的执行顺序
-
情况一:Interceptor的preHandle方法返回都是true
根拦截器的配置顺序有关,配置在前的拦截器的preHandle方法先执行,后面两个方法反序执行(和配置顺序相反)
<mvc:interceptors> <bean class="com.hhxy.interceptor.FirstInterceptor"/> <bean class="com.hhxy.interceptor.SecondInterceptor"/> </mvc:interceptors>
-
情况二:若某个拦截器的preHandle()返回了false(这里将First拦截器的preHandle返回值设为false)
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false的拦截器之前的拦截器的afterCompletion()会执行
-
-
-
Step5:编写HTML
1)index.html:
<a th:href="@{/test/filter}">测试拦截器</a>
2)success.html:
<h1>success.html</h1>
-
Step6:编写Java
1)编写TestController:
package com.hhxy.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @author ghp * @date 2022/10/4 * @title * @description */ @Controller public class TestController { @RequestMapping("/test/filter") public String testFilter(){ return "success"; } }
2)编写FirstInterceptor:
package com.hhxy.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * @author ghp * @date 2022/10/4 * @title * @description */ public class FirstInterceptor implements HandlerInterceptor{ /** * 所有的拦截器执行后,Controller方法还未开始处理请求 * @return true表示放行 */ @Override public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle方法被调用了"); return true; } /** * 必要条件:preHandle返回false * Controller方法处理完之后,DispatcherServlet进行视图的渲染之前 */ @Override public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle被调用了"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } /** * 必要条件:postHandle返回false * DispatcherServlet进行视图的渲染之后 */ @Override public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion被调用了"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
-
Step7:测试
11、异常处理器
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:
HandlerExceptionResolver
。HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver
和SimpleMappingExceptionResolver
一个是SpringMVC默认的异常处理类,一个是自定义异常处理类
11.1 使用XML配置异常处理器
1)Java:
@RequestMapping("/test/exception")
public String testException(){
System.out.println(1/0);
return "success";
}
2)SpringMVC配置文件:
<!--设置SpringMVC异常解析器-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!--当发生ArithmeticException异常时,直接跳转到error.html页面-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--将异常信息存储到域对象中-->
<property name="exceptionAttribute" value="exception"></property>
</bean>
3)HTML:
index.html:
<a th:href="@{/test/exception}">测试异常处理器</a>
error.html:
<a th:href="@{/test/exception}">测试异常处理器</a>
4)测试结果:
11.3 使用注解配置异常处理器
将上面XML配置的异常处理器换成下面的代码:
package com.hhxy.controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
/**
* @author ghp
* @date 2022/10/5
* @title
* @description
*/
//@ControllerAdvice声明这个类是一个异常处理器
@ControllerAdvice
public class TestExceptionController {
//当发生ArithmeticException异常就直接跳转到error.html页面
@ExceptionHandler(ArithmeticException.class)
public String testHandleException(Throwable exception,Model model){
//将异常信息存储到域对象中
model.addAttribute("exception",exception);
return "error";
}
}
测试:
可以发现结果和前面是一致的O(∩_∩)O
备注:先注释掉前面在SpringMVC配置文件中配置的异常处理器
12、注解配置SpringMVC
在Servlet3.0环境中,容器会在类路径中查找实现
javax.servlet.ServletContainerInitializer
接口的类, 如果找到的话就用它来配置Servlet容器。 Spring提供了这个接口的实现,名为SpringServletContainerInitializer
,这个类反过来又会查找实现WebApplicationInitializer
的类并将配 置的任务交给它们来完成。Spring3.2
引入了一个便利的WebApplicationInitializer
基础实现,名为AbstractAnnotationConfigDispatcherServletInitializer
,当我们的类扩展了AbstractAnnotationConfigDispatcherServletInitializer
并将其部署到Servlet3.0容器的时候,容器会自 动发现它,并用它来配置Servlet上下文。简言之:当我们使用一个类继承
AbstractAnnotationConfigDispatcherServletInitializer
类,并且是部署在Servlet3.0环境中,这时候Tomcat就会自动将该类替代web.xmlPS:这种方式可能现在看来很麻烦,但是大型项目会经常使用注解来配置SpringMVC,因为注解的出现就是为了简化
-
Step1:创建Maven工程
目录结构:
-
Step2:导入依赖
pom.xml:
<?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>org.example</groupId> <artifactId>day14_springmvc</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!--servlet-api--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <!--spring和thymeleaf整合包--> <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> <version>3.0.15.RELEASE</version> </dependency> <!--springMVC依赖,间接导入了spring-framework上下文--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.22</version> </dependency> <!--引入logback-classic,slf4j日志门面的实现包,thymeleaf需要依赖slf4j日志门面--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
-
Step3:编写HTML
-
index.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>首页</title> </head> <body> <h1>index.html</h1> <hr> <a th:href="@{/test/firstInterceptor}">测试使用注解配置的SpringMVC的拦截器</a><br/> <a th:href="@{/test/exception}">测试使用注解配置的SpringMVC的异常处理器</a><br/> </body> </html>
-
success.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>成功</title> </head> <body> <h1>success.html</h1> </body> </html>
-
error.html:
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>错误</title> </head> <body> <h1>error.html</h1> <p th:text="${exception}"></p> </body> </html>
-
-
Step4:编写Java
-
编写配置类
-
WebInit:
package com.hhxy.config; import org.springframework.web.filter.CharacterEncodingFilter; import org.springframework.web.filter.HiddenHttpMethodFilter; import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; import javax.servlet.Filter; /** * @author ghp * @date 2022/10/6 * @title * @description 用来替代web.xml */ //在Servlet3.0的环境,继承了该类,Tomcat自动会将该类替代web.xml public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer { /** * 设置一个配置类,用于代替Spring的配置文件 * @return */ @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } /** * 设置一个配置类,用于代替SpringMVC的配置文件 * @return */ @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{WebConfig.class}; } /** * 设置SpringMVC的前端控制器DispatcherServlet的 url-pattern * @return */ @Override protected String[] getServletMappings() { return new String[]{"/"}; } /** * 设置过滤器 * @return */ @Override protected Filter[] getServletFilters() { //创建编码过滤器 CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter(); characterEncodingFilter.setEncoding("UTF-8"); //让响应的数据也是用这个编码过滤器 characterEncodingFilter.setForceEncoding(true); //设置请求方式过滤器 HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter(); return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter}; } }
-
SpringConfig:
package com.hhxy.config; /** * @author ghp * @date 2022/10/6 * @title * @description 代替Spring的配置文件 */ public class SpringConfig { }
-
WebConfig:
package com.hhxy.config; import com.hhxy.interceptor.FirstInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.*; import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver; import org.thymeleaf.spring5.SpringTemplateEngine; import org.thymeleaf.spring5.view.ThymeleafViewResolver; import org.thymeleaf.templatemode.TemplateMode; import org.thymeleaf.templateresolver.ITemplateResolver; import org.thymeleaf.templateresolver.ServletContextTemplateResolver; import java.util.List; import java.util.Properties; /** * @author ghp * @date 2022/10/6 * @title * @description 代替SpringMVC的配置文件 * 扫描组件、开启SpringMVC的注解驱动、配置视图解析器、启用默认DefaultServlet、 * 配置视图控制器、拦截器、异常解析器、文件上传解析器 */ //将类标识为配置类 @Configuration //扫描组件的注解(是一个数组形式的,可以扫描多个组件) @ComponentScan(value = {"com.hhxy.controller"}) //开启SpringMVC的注解驱动 @EnableWebMvc public class WebConfig implements WebMvcConfigurer { /** * 配置视图解析器,并为视图解析器注入模板引擎 * @param templateEngine * @return */ //@Bean注解可以将标识的方法的返回值作为Bean进行管理,Bean的默认id为方法名 @Bean public ViewResolver viewResolver(SpringTemplateEngine templateEngine) { ThymeleafViewResolver viewResolver = new ThymeleafViewResolver(); //设置视图解析器的编码格式 viewResolver.setCharacterEncoding("UTF-8"); viewResolver.setTemplateEngine(templateEngine); //返回视图解析器对象是为了让IOC容器进行管理 return viewResolver; } /** * 配置模板引擎,并为模板引擎注入模板解析器 * @param templateResolver * @return */ @Bean public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { SpringTemplateEngine templateEngine = new SpringTemplateEngine(); templateEngine.setTemplateResolver(templateResolver); return templateEngine; } /** * 配置模板解析器 * @return */ @Bean public ITemplateResolver templateResolver() { WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext(); // ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过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; } /** * 启用默认DefaultServlet * @param configurer */ @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } /** * 配置视图控制器 * @param registry */ @Override public void addViewControllers(ViewControllerRegistry registry) { //重定向方式的视图控制器,默认跳转到index.html页面 registry.addViewController("/").setViewName("index"); } /** * 配置拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { //配置一个拦截器,这个拦截器拦截出 *.jsp 这类请求以外的所有请求 FirstInterceptor firstInterceptor = new FirstInterceptor(); registry.addInterceptor(firstInterceptor).addPathPatterns("/**").excludePathPatterns("*.jsp"); } /** * 配置异常处理器 * @param resolvers */ @Override public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) { SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); properties.setProperty("java.lang.ArithmeticException", "error"); //当发生properties的key值类型的异常,就会跳转到error.html页面 simpleMappingExceptionResolver.setExceptionMappings(properties); //将异常信息存储在域中,域对象的名称可以自定义,HTML页面可以通过该域对象获取异常信息 simpleMappingExceptionResolver.setExceptionAttribute("exception"); resolvers.add(simpleMappingExceptionResolver); //固定步骤,将配置好的异常解析器添加到resolvers对象中 resolvers.add(simpleMappingExceptionResolver); } /** * 配置文件上传解析器(使用它记得添加jackson依赖) * @return */ // @Bean // public CommonsMultipartResolver multipartResolver() { // return new CommonsMultipartResolver(); // } }
-
-
编写组件
-
Controller:
package com.hhxy.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; /** * @author ghp * @date 2022/10/6 * @title 测试类 * @description */ @Controller public class testController { /** * 测试使用了注解配置SpringMVC后,拦截器的效果 * @return */ @RequestMapping("/test/firstInterceptor") public String testFirstInterceptor(){ return "success"; } /** * 测试使用注解配置SpringMVC后,异常处理器的效果 */ @RequestMapping("/test/exception") public String testException(){ System.out.println(1/0); return "success"; } }
-
Interceptor:
package com.hhxy.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author ghp * @date 2022/10/6 * @title * @description */ public class FirstInterceptor implements HandlerInterceptor { /** * 在DispatcherServlet处理前执行该方法 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("FirstInterceptor的preHandle方法被执行了"); return true; } /** * 在视图渲染后执行该方法 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("FirstInterceptor的postHandle方法被执行了"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } /** * 在DispatcherServlet处理后执行该方法 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("FirstInterceptor的afterCompletion方法被执行了"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
-
-
-
Step5:测试
测试拦截器:
测试异常处理器:
13、SpringMVC执行流程
前置知识
-
DispatcherServlet
:前端控制器,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求DispatcherServlet继承了FrameworkServlet,下面是FramworkServlet的继承关系
-
HandlerMapping
:处理器映射器,不需要工程师开发,由框架提供
作用:根据请求的url、method等信息查找Handler,即控制器方法 -
Handler
:处理器,需要工程师开发
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理 -
HandlerAdapter
:处理器适配器,不需要工程师开发,由框架提供
作用:通过HandlerAdapteri对处理器(控制器方法)进行执行 -
ViewResolver
:视图解析器,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView -
View
:视图
作用:将模型数据通过页面展示给用户
参考文章:
静态资源:指的是在程序载入内存时对资源的一次性使用,之后就不再去访问这个资源了,比如:CSS、JS、图片、text文件、文字 ↩︎