以下是我自己整理的关于 尚硅谷 学习视频中SpringMVC的学习笔记,源码可参考SpringMVC/learn中的代码,日常更新学习笔记,让我们一起学习一起进步!!
MVC
是一种软件架构的思想,将软件按照模型(Model)、视图(View)、控制器(Controller)来划分
模型层
,指工程中的JavaBean
,作用是处理数据
实体类Bean
:专门存储业务数据,如Student、User等业务处理Bean
:指Service或Dao对象,专门用于处理业务逻辑和数据访问
视图层
,指工程中的Html、Jsp等页面
,作用是与用户进行交互,展示数据
控制层
,指工程中的Servlet
,作用是接收请求和响应游览器
MVC的工作流程:用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果 找到相应的View视图,渲染数据后最终响应给浏览器
(用户操作 -> View -> Controller -> Model -> Controller -> View ->展示(响应)数据用户)
SpringMVC 是 Spring 为表述层开发
提供的一整套完备的解决方案,目前业界普遍选择了 SpringMVC 作为 Java EE 项目 表述层开发
的首选方案
注
:三层架构分为表述层(或表示层)、业务逻辑层、数据访问层
,表述层
表示前台页面和后台servlet
<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>
<scope>provided</scope>
</dependency>
<!-- Spring5和Thymeleaf整合包 -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
注册
SpringMVC的前端控制器DispatcherServlet
此配置作用下,SpringMVC的配置文件默认位于WEB-INF下,默认名称为<servlet-name>-servlet.xml
,例如,以下配置所对应SpringMVC
的配置文件位于WEB-INF下,文件名为springMVC-servlet.xml
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径
但是/不能匹配.jsp请求路径的请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
注
:如果使用前端控制器来处理.jsp,就会使用前端控制器的Servlet来处理,而不会掉用该Jsp所对应的Servlet进行处理,我们之前都是一个请求对应一个Servlet,可是Jsp请求本身就是一个Servlet,所以请求Jsp时已经有一个特定的Servlet来处理该请求啦(就是Jsp本身)
可通过init-param
标签设置SpringMVC配置文件的位置
和名称
,通过load-on-startup
标签设置SpringMVC前端控制器DispatcherServlet的初始化时间
<!-- 配置SpringMVC的前端控制器,对浏览器发送的请求统一进行处理 -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 通过初始化参数指定SpringMVC配置文件的位置和名称 -->
<init-param>
<!-- contextConfigLocation为固定值 -->
<param-name>contextConfigLocation</param-name>
<!-- 使用classpath:表示从类路径查找配置文件,例如maven工程中的src/main/resources -->
<param-value>classpath:springMVC.xml</param-value>
</init-param>
<!--
作为框架的核心组件,在启动过程中有大量的初始化操作要做
而这些操作放在第一次请求时才执行会严重影响访问速度
因此需要通过此标签将启动控制DispatcherServlet的初始化时间提前到服务器启动时
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!--
设置springMVC的核心控制器所能处理的请求的请求路径
/所匹配的请求可以是/login或.html或.js或.css方式的请求路径
但是/不能匹配.jsp请求路径的请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
由于前端控制器对浏览器发送的请求进行了统一的处理,但是具体的请求有不同的处理过程
,因此需要创建处理具体请求的类
,即请求控制器
请求控制器中每一个处理请求的方法
称为控制器方法
因为SpringMVC的控制器由一个POJO(普通的Java类)担任,因此需要通过@Controller
注解将其标识为一个控制层组件,交给Spring的IoC容器管理
,此时SpringMVC才能够识别控制器的存在
@Controller // 表示控制层组件
public class HelloController {}
<!-- 开启组件扫描 -->
<context:component-scan base-package="com.chen.controller"/>
<!-- 配置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>
<!--
处理静态资源,例如html、js、css、jpg
若只设置该标签,则只能访问静态资源,其他请求则无法访问
此时必须设置<mvc:annotation-driven/>解决问题
-->
<mvc:default-servlet-handler/>
<!-- 开启mvc注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- 处理响应中文内容乱码 -->
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="defaultCharset" value="UTF-8" />
<property name="supportedMediaTypes">
<list>
<value>text/html</value>
<value>application/json</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
在请求控制器中创建处理请求的方式
/*
"index"使用视图前缀加视图后缀可变为 -> "/WEB-INF/templates/index.html"
@RequestMapping注解:处理请求和控制器方法之间的映射关系
@RequestMapping注解的value属性可以通过请求地址匹配请求,/表示的当前工程的上下文路径:'/'
localhost:8080/
*/
@RequestMapping("/")
public String index(){
// 返回视图名称
return "index";
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/target}">访问目标页面target.html</a>
</body>
</html>
/*
重点:return的值要和html文件名相同,而注释中的value值要和跳转的href相同
"target" -> "/WEB-INF/templates/target.html"
*/
public String toTarget(){
// 返回视图名称
return "target";
}
浏览器发送请求,若请求地址符合前端控制器的url-pattern(/:不能匹配.jsp请求的路径请求)
,该请求就会被前端控制器 DispatcherServlet处理,前端控制器会读取SpringMVC的核心配置文件
,通过扫描组件找到控制器, 将请求地址和控制器中@RequestMapping注解的value属性值进行匹配(href:@{/target}和@RequestMapping("/target"))
,若匹配成功,该注解所标识的 控制器方法就是处理请求的方法,处理请求的方法需要返回一个字符串类型的视图名称("target")
,该视图名称
会 被视图解析器解析
,加上前缀和后缀组成视图的路径("/WEB-INF/templates/target.html"),通过Thymeleaf对视图进行渲染,最终转发到视图所对应页面(localhost:8080/target)
从注解名称上我们可以看见,@RequestMapping
注解的作用就是将请求和处理请求的控制器方法进行关联,建立映射关系
SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求
标识在类上
:设置映射请求的请求路径的初始信息
标识在方法上
:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/hello") // 标识在类:设置映射请求的请求路径的初始信息
public class RequestMappingController {
/*
标识在方法:设置映射请求的请求路径的具体信息
/test/testRequestMapping
*/
@RequestMapping("/testRequestMapping")
public String success(){
return "success";
}
}
@RequestMapping
注解的value属性
通过请求的请求地址
匹配请求映射
@RequestMapping
注解的value属性是一个字符串类型的数组
,表示该请求映射能够匹配多个请求地址所对应的请求
@RequestMapping
注解的value属性必须设置,至少通过请求地址匹配请求映射
<a th:href="@{/test}">测试RequestMapping的value属性 -> /test</a><br>
<a th:href="@{/testRequestMapping}">测试RequestMapping的value属性 -> /testRequestMapping</a><br>
@RequestMapping(
value = {"/testRequestMapping","/test"}
) // 一个控制器方法可以对应多个请求地址
public String success() {
return "success";
}
@RequestMapping
注解的method属性
通过请求的请求方式(get或post)
匹配请求映射
@RequestMapping
注解的method属性是一个RequestMethod类型的数组
,表示该请求映射能够匹配多种请求方式的请求
405:Request method 'POST' not supported
<a th:href="@{/test}">测试RequestMapping注解的method属性 -> GET</a><br>
<form th:action="@{/test}" method="post">
<input type="submit" value="测试RequestMapping注解的method属性 -> POST">
</form>
@RequestMapping(
value = {"/testRequestMapping","/test"},
method = {RequestMethod.GET,RequestMethod.POST}
) // method:匹配对应的请求方式的请求
public String success() {
return "success";
}
注:
- 对于
处理指定请求方式的控制器方法
,SpringMVC中提供了@RequestMapping的派生注解
- 处理
get
请求的映射 -> @GetMapping- 处理
post
请求的映射 -> @PostMapping- 处理
put
请求的映射 -> @PutMapping- 处理
delete
请求的映射 -> @DeleteMapping- 常用的请求方式有
get,post,put,delete
但是目前浏览器
只支持get和post请求
,若在form表单提交时,设置method为其他请求方式的字符串(put或delete)
,则按照默认的get请求处理
若要发送put和delete请求,则需要通过Spring提供的
过滤器HiddenHttpMethodFilter
,在RESTful
部分会使用到
<!-- 测试GetMapping和PostMapping注解 -->
<a th:href="@{/testGetMapping}">测试GetMapping注解 -> /testGetMapping</a><br>
<form th:action="@{/testPostMapping}" method="post">
<input type="submit" value="测试PostMapping注解 -> /testPostMapping">
</form>
@GetMapping("/testGetMapping")
public String testGetMapping(){
return "success";
}
@PostMapping("/testPostMapping")
public String testPostMapping(){
return "success";
}
@RequestMapping
注解的params属性
通过请求的请求参数
匹配请求映射
@RequestMapping
注解的params属性是一个字符串类型的数组
,可以通过四种表达式
设置请求参数和请求映射的匹配关系
必须携带param请求参数
不能携带param请求参数
必须携带param请求参数并且param=value
必须携带param请求参数但是param!=value
<a th:href="@{/testParamsAndHeaders(username='Forget',password='202428')}">测试@RequestMapping的 params属性 -> /testParamsAndHeaders</a><br>
// params中的参数必须要同时满足,才能成功匹配请求方法
@RequestMapping(
value = "/testParamsAndHeaders",
params = {"username","password=202428"}
)
public String testParamsAndHeaders(){
return "success";
}
注
:若当前请求满足
@RequestMapping
注解的value和method属性,但是不满足params属性
,此时 页面回报错400:Parameter conditions "username, password!=123456" not met for actual request parameters: username={Forget}, password={123456}
用法与param属性一致
,一共有四种表达式
设置请求头参数和请求映射的匹配关系,并且headers中的参数必须也要同时满足
若当前请求满足@RequestMapping
注解的value和method属性,但是不满足headers属性
,此时页面显示404错误,即资源未找到
?
:表示任意的单个字符
*
:表示任意的字符(0个或多个)
**
:表示任意的0层或多层目录
(注意点)
注
:在使用**
时,只能使用/**/xxx
的方式
原始方式
:/deleteUser?user_id=1
rest方式
:/deleteUser/1
SpringMVC路径中的占位符
常用于RESTful风格中
,当请求路径中将某些数据通过路径的方式传输到服务器中,就可以在相应的@RequestMapping
注解的value属性中通过占位符{xxx}表示传输的数据
,在 通过@PathVariable
注解,将占位符所表示的数据赋值给控制器方法的形参
<a th:href="@{/testPath/1/Forget}">测试路径中的占位符RESTful风格 -> /testPath</a><br>
/*
将占位符中的值,自动赋值给方法中的形参
*/
@RequestMapping("/testPath/{userId}/{userName}")
public String testPath(@PathVariable("userId") Integer id, @PathVariable("userName") String name) {
System.out.println("userId = " + id + ",userName = " + name);
return "success";
// 最终输出的内容为 -> "userId = 1,userName = Forget"
}
HttpServletRequest作为控制器方法的形参
,此时HttpServletRequest类型的参数表示封装了当前请求的请求报文的对象
/*
1、通过ServletAPI获取
形参位置的request表示当前请求
意味着可以使用原生的ServletAPI获取请求参数
*/
@RequestMapping("/testServletAPI")
public String testServletAPI(HttpServletRequest request) {
String username = request.getParameter("userName");
String password = request.getParameter("password");
System.out.println("username:" + username + ",password:" + password);
return "success";
}
设置和请求参数同名的形参
,当浏览器发送请求,匹配到请求映射时,在 DispatcherServlet中就会将请求参数赋值给相应的形参
<a th:href="@{/testParam(username='Forget',password='202428')}">测试使用控制器的形参获取请求参数</a><br>
/*
2、通过控制器方法的形参获取请求参数
设置和请求参数同名的形参,DispatcherServlet中就会将请求参数赋值给相应的形参
若请求所传输的请求参数中有多个同名的请求参数,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
-- 若使用字符串数组类型的形参,此参数的数组中包含了每一个数据
-- 若使用字符串类型的形参,此参数的值为每个数据中间使用逗号拼接的结果
*/
@RequestMapping("/testParam")
public String testParam(String username, String password, String[] hobby) {
System.out.println("username:" + username + ",password:" + password + ",hobby:" + Arrays.toString(hobby));
return "success";
}
注
:若
请求所传输的请求参数中有多个同名的请求参数
,此时可以在控制器方法的形参中设置字符串数组或者字符串类型的形参接收此请求参数
- 若使用
字符串数组类型
的形参,此参数的数组中包含了每一个数据- 若使用
字符串类型
的形参,此参数的值为每个数据中间使用逗号拼接的结果
@RequestParam
是将请求参数
和控制器方法的形参
创建映射关系三个属性
:
value
:指定为形参赋值的请求参数的参数名required
:设置是否必须传输此请求参数
,默认值为true若设置为true
时,则当前请求必须传输value所指定的请求参数
,若没有传递该请求参数且没有设置defaultValue属性,则页面报错400:Required String parameter 'xxx' is not present
若设置为false
时,则当前请求不是必须传输value所指定的请求参数
,若没有传输,则注解所标识的形参值为null
defaultValue
:不管required
属性值为true或false,当value所指定的请求参数没有传输或传输的值为""(空)时
,则使用默认值为形参赋值@RequestHeader
是将请求头信息
和控制器方法的形参
创建映射关系用法同@RequestParam
@CookieValue
是将cookie数据
和控制器方法的形参
创建映射关系用法同@RequestParam
可以在控制器方法的形参位置设置一个实体类类型的形参,此时若浏览器传输的请求参数的参数名和实体类中的属性名一致,那么请求参数就会为此属性赋值
<form th:action="@{/testPojo}" method="post">
用户名:<input type="text" name="username"><br>
密码:<input type="password" name="password"><br>
性别:<input type="radio" name="sex" value="男">男
<input type="radio" name="sex" value="女">女<br>
年龄:<input type="text" name="age"><br>
邮箱:<input type="text" name="email"><br>
<input type="submit">
</form>
@RequestMapping("/testPojo")
public String testPojo(User user) {
System.out.println(user);
return "success";
// 输出结果:User{username='Forget', password='202428', sex='男', age='20', email='1292379046@qq.com'}
}
解决获取请求参数的乱码问题,可以使用SpringMVC提供的编码过滤器CharacterEncodingFilter
,但是必须在web.xml中进行注册
<!-- 设置字符编码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- encoding:解决POST请求乱码 -->
<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>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
注
:SpringMVC中处理编码的过滤器一定要配置在其他过滤器之前,否则无效
/*
1、使用ServletAPI向request域对象共享数据
*/
@RequestMapping("/testRequestServletAPI")
public String testRequestServletAPI(HttpServletRequest request){
request.setAttribute("testRequestScope","Hello World ServletAPI");
return "success";
}
/*
2、使用ModelAndView向request域对象共享数据
ModelAndView有 Model 和 View 的功能
Model:主要用于向请求域中共享数据
View:主要用于设置视图,实现页面跳转
*/
@RequestMapping("/testModelAndView")
public ModelAndView testModelAndView(){
// 1、创建ModelAndView对象
ModelAndView mav = new ModelAndView();
// 2、向请求域添加共享数据
mav.addObject("testRequestScope","Hello World ModelAndView");
// 3、设置视图,实现页面跳转 相当于return "success",前缀+视图名称+后缀进行资源的访问
mav.setViewName("success");
return mav;
}
/*
3、使用Model向request域对象共享数据
*/
@RequestMapping("/testModel")
public String testModel(Model model){
model.addAttribute("testRequestScope","Hello World Model");
return "success";
}
/*
4、使用map向request域对象共享数据
需要在形参中创建map集合,添加到map集合中的数据会成为共享数据
Map<String,Object> 键为字符串,值为Object(任意)的对象
*/
@RequestMapping("/testMap")
public String testMap(Map<String,Object> map) {
map.put("testRequestScope","Hello World Map");
return "success";
}
/*
5、使用ModelMap向request域对象共享数据
需要在形参中创建ModelMap对象,使用modelMap的addAttribute()添加共享数据
*/
@RequestMapping("/testModelMap")
public String testModelMap(ModelMap modelMap){
modelMap.addAttribute("testRequestScope","Hello World ModelMap");
return "success";
}
Model、ModelMap、Map类型的参数
其实本质上都是 BindingAwareModelMap 类型的
/*
BindingAwareModelMap继承于ExtendedModelMap
ExtendedModelMap继承于ModelMap,并且实现Model接口
ModelMap继承于LinkedHashMap
LinkedHashMap实现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 {}
/*
6、使用原生ServletAPI向session域共享数据
需要在形参中创建HttpSession对象,使用session的setAttribute()添加共享数据
*/
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope","Hello World Session");
return "success";
}
/*
7、使用ServletAPI向Application域中共享数据
需要在形参中创建HttpSession对象,获取Application对象
*/
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
// 使用session获取application对象
ServletContext application = session.getServletContext();
application.setAttribute("testApplication","Hello World Application");
return "success";
}
SpringMVC中的视图是View接口
,视图的作用渲染数据,将模型Model中的数据展示给用户
种类
很多,默认有转发视图(InternalResourceView)
和重定向视图(RedirectView)
jstl
的依赖,转发视图会自动转换为JstlView
视图技术为Thymeleaf
,在SpringMVC的配置文件中配置了Thymeleaf的视图解析器,由此视图解析器解析之后所得到的是ThymeleafView
没有任何前缀
时,此时的视图名称会被SpringMVC配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式
实现跳转/*
1、测试ThymeleafView
*/
@RequestMapping("/testThymeleafView")
public String testThymeleafView(){
return "success";
}
转发视图
是InternalResourceView
以"forward:"为前缀
时,创建InternalResourceView视图,此时的视图名称不会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
/*
2、测试转发视图InternalResourceView
此时视图名称有forward:前缀是转发视图
会创建两次视图:"forward:+请求路径"
-- 第一次是"forward:/testThymeleafView"的转发视图
-- 第二次是"/testThymeleafView"的Thymeleaf视图
地址栏不会发生改变:localhost:8080/testForward
*/
@RequestMapping("/testForward")
public String testForward() {
return "forward:/testThymeleafView";
}
重定向视图
是RedirectView
以"redirect:"为前缀
时,创建RedirectView视图,此时的视图名称不 会被SpringMVC配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最 终路径通过重定向的方式实现跳转
/*
3、测试重定向视图RedirectView
此时视图名称有redirect:前缀是重定向视图
会创建两次视图:"redirect:+请求路径"
-- 第一次是"redirect:/testThymeleafView"的转发视图
-- 第二次是"/testThymeleafView"的Thymeleaf视图
地址栏会发生改变:localhost:8080/testThymeleafView
*/
@RequestMapping("/testRedirect")
public String testRedirect() {
return "redirect:/testThymeleafView";
}
注
:重定向视图在解析时,会先将redirect:前缀去掉,然后会判断剩余部分是否以/开头
,若是则会自动拼接上下文路径
补充:转发和重定向的区别?
转发是发起一次请求
,游览器发送再由服务器内部进行转发,重定向是发起两次请求
,第一次访问Servlet,第二次访问重定向的地址转发不会改变地址栏中的地址
,重定向会改变地址栏中的地址(重新访问服务器中的资源)
转发可以访问到request域中的共享数据
,因为转发是一次请求,使用的request对象是同一个,而重定向不行
,因为重定向是两次请求,不是使用的同一个request对象
当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时
,可以将处理器方法
使用view- controller标签进行表示
<!--
设置视图控制器,完成只有页面跳转的功能
path:设置处理的请求地址(相当于RequestMapping("/"))
view-name:设置请求地址所对应的视图名称,如success.html,相当于return "success")
-->
<mvc:view-controller path="/" view-name="index"/>
注
:当SpringMVC中设置任何一个view-controller时
,其他控制器中的请求映射将全部失效
,此时需要在SpringMVC的核心配置文件中设置开启mvc注解驱动的标签
<!-- 开启SpringMVC的注解驱动 可以使得view-controller和其他控制器中的请求映射一起使用 -->
<mvc:annotation-driven/>
REST(Representational State Transfer)
:表示层资源状态转移(前端的视图页面 -> 后端的控制层)
资源是一种看待服务器的方式
,即,将服务器看作是由很多离散的资源组成。万物皆资源:一个类、一个Jsp页面、Css文件等都是资源
资源的表述是一段对于资源在某个特定时刻的状态的描述
,资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等
状态转移是在客户端和服务器端之间转移(transfer)代表资源状态的表述
,在浏览器和服务器端的交互的一种状态,游览器发送请求和请求路径,间接的操作资源
在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请求方式 |
由于浏览器只支持发送get和post方式的请求,那么该如何发送put
和delete
请求呢?
SpringMVC 提供了 HiddenHttpMethodFilter 帮助我们将 POST 请求转换为 DELETE 或 PUT 请求
HiddenHttpMethodFilter 处理put和delete请求的条件:
满足以上条件,HiddenHttpMethodFilter 过滤器就会将当前请求的请求方式
转换为请求参数 _method的值
,因此请求参数 _method的值
才是最终的请求方式
<!-- 在web.xml中注册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>
注
:目前为止,SpringMVC中提供了两个过滤器:CharacterEncodingFilter
和HiddenHttpMethodFilter
,只需要记住编码过滤器要放在首位
实现CURD
操作,对员工信息的增删改查
准备Employee
实体类
/**
* Employee的员工实体类
*
* @author Forget_chen
* @className Employee
* @desc
* @date 2022/4/24 13:03
*/
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 1 male, 0 female
public Employee() {
}
public Employee(Integer id, String lastName, String email, Integer gender) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Integer getGender() {
return gender;
}
public void setGender(Integer gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", email='" + email + '\'' +
", gender=" + gender +
'}';
}
}
/**
* Employee实体类与数据库进行交互
*
* @author Forget_chen
* @className EmployeeDao
* @desc
* @date 2022/4/24 13:07
*/
@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;
public void save(Employee employee) {
if (employee.getId() == null) {
employee.setId(initId++);
}
employees.put(employee.getId(), employee);
}
public Collection<Employee> getAll() {
return employees.values();
}
public Employee get(Integer id) {
return employees.get(id);
}
public void delete(Integer id) {
employees.remove(id);
}
}
功能 | URL 地址 | 请求方式 |
---|---|---|
访问首页√ | / | GET |
查询全部数据√ | /employee | GET |
删除√ | /employee/2 | DELETE |
跳转到添加数据页面√ | /toAdd | GET |
执行添加√ | /employee | POST |
跳转到更新数据页面√ | /employee/2 | GET |
执行更新√ | /employee | PUT |
视图控制器
完成首页跳转
<mvc:view-controller path="/" view-name="index"/>
首页页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<a th:href="@{/employee}">查看全部员工信息</a>
</body>
</html>
查询全部员工信息
的控制器方法/*
1、查询全部数据
*/
@RequestMapping(
value = "/employee",
method = RequestMethod.GET
)
public String getAllEmployee(Model model){
Collection<Employee> employee_list = dao.getAll();
model.addAttribute( "employee_list",employee_list);
return "employee_list";
}
employee_list.html
页面(用于展示员工信息)<table id="dataTable" border="1" cellpadding="0" cellspacing="0" style="text-align: center">
<tr>
<th colspan="5">Employee Info</th>
</tr>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>options(<a th:href="@{/toAdd}">添加</a>)</th>
</tr>
<tr th:each="employee : ${employee_list}">
<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>
<!-- 使用thymeleaf语法 @{/请求地址/{参数}(参数 = 值)} -->
<a @click="deleteEmployee" th:href="@{/employee/{id}(id=${employee.id})}">delete</a>
<!-- 跳转到更新页面,因为需要实现回现功能,要根据id查询员工信息进行显示 -->
<a th:href="@{/employee/{id}(id=${employee.id})}">update</a>
</td>
</tr>
</table>
处理delete请求的表单
<!-- 作用:通过超链接控制表单的提交,将post请求转换为delete请求 -->
<form id="deleteForm" method="post">
<!-- HiddenHttpMethodFilter要求:必须传输_method请求参数,并且值为最终的请求方式 -->
<input type="hidden" name="_method" value="delete"/>
</form>
删除超链接
并且绑定点击事件
(提交事件)vue.js
<script type="text/javascript" th:src="@{/static/js/vue.js}"></script>
<a @click="deleteEmployee" th:href="@{/employee/{id}(id = ${employee.id})}">delete</a>
<script type="text/javascript">
var vue = new Vue({
// el挂载点,通过选择器设置vue管理的元素
el:"#dataTable",
methods:{
deleteEmployee:function (event){
// 根据id获取表单元素
var deleteForm = document.getElementById("deleteForm");
// 将当前触发事件的href赋值给deleteForm的action属性,相当于绑定点击事件的提交地址
deleteForm.action = event.target.href;
// 提交表单
deleteForm.submit();
// 取消超链接的默认行为
event.preventDefault()
}
}
});
</script>
根据ID删除员工信息
的控制器方法/*
2、根据id删除员工信息
*/
@RequestMapping(
value = "/employee/{id}",
method = RequestMethod.DELETE
)
public String deleteEmpById(@PathVariable("id") Integer id){
dao.delete(id);
// 需要使用重定向功能,使用浏览器进行访问,相当于Get请求
return "redirect:/employee";
}
跳转到添加数据页面
<mvc:view-controller path="/toAdd" view-name="employee_add"/>
employee_add.html
页面<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>添加员工信息</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
lastName:<input type="text" name="lastName"><br>
email:<input type="text" name="email"><br>
gender:<input type="radio" name="gender" value="1">male
<input type="radio" name="gender" value="0">female<br>
<input type="submit" value="添加">
</form>
</body>
</html>
添加员工信息
的控制器方法/*
3、添加员工信息
*/
@RequestMapping(
value = "/employee",
method = RequestMethod.POST
)
public String addEmployee(Employee emp){
dao.save(emp) ;
return "redirect:/employee";
}
(需要根据ID实现数据回现)
<!-- 跳转到更新页面,因为需要实现回现功能,要根据id查询员工信息进行显示 -->
<a th:href="@{/employee/{id}(id = ${employee.id})}">update</a>
根据ID查询单个员工信息
的控制器方法(实现数据回现)/*
4、根据id查询单个员工信息
*/
@RequestMapping(value="/employee/{id}")
public String getEmpById(@PathVariable("id") Integer id,Model model){
Employee emp = dao.get(id);
// 将查询到的员工信息,通过Model传递给request
model.addAttribute("employee",emp);
// 跳转到对应的更新页面
return "employee_update";
}
employee_update.html
页面<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>更新员工信息</title>
</head>
<body>
<form th:action="@{/employee}" method="post">
<input type="hidden" name="_method" value="put">
<input type="hidden" name="id" th:value="${employee.id}">
lastName:<input type="text" name="lastName" th:value="${employee.lastName}"/><br>
email:<input type="text" name="email" th:value="${employee.email}"/><br>
<!-- radio类型的数据,使用th:field字段回现数据 -->
gender:<input type="radio" name="gender" value="1" th:field="${employee.gender}">male
<input type="radio" name="gender" value="0" th:field="${employee.gender}">female<br>
<input type="submit" value="更新">
</form>
</body>
</html>
更新员工信息
的控制器方法/*
5、更新员工信息
*/
@RequestMapping(
value = "/employee",
method = RequestMethod.PUT
)
public String updateEmployee(Employee emp){
// 将更新的员工信息,进行覆盖更新
dao.save(emp);
return "redirect:/employee";
}
HttpMessageConverter
:报文信息转换器
,将请求报文转换为Java对象(浏览器发送的数据转换成Java对象),或将Java对象转换为响应报文(Java对象转换成服务器发送的数据)
HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody
,RequestEntity,ResponseEntity
@RequestBody可以获取请求体
,需要在控制器方法设置一个形参,使用@RequestBody进行标识,当前请求的请求体就会为当前注解所标识的形参赋值
<form th:action="@{/testRequestBody}" method="post">
用户名:<input type="text" name="username" >
密码:<input type="password" name="password" >
<input type="submit" value="测试@RequestBody">
</form>
/*
1、测试@RequestBody注释
当前请求的请求体就会为当前注解所标识的形参赋值
输出结果:requestBody:username=陈家睿&password=123456
*/
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody) {
// 使用URL解码器,使用utf-8进行解码
try {
requestBody = URLDecoder.decode(requestBody, "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("requestBody:" + requestBody);
return "success";
}
RequestEntity封装请求报文的一种类型
,需要在控制器方法的形参中设置该类型的形参,当前请求的请求报文就会赋值给该形参
getHeaders()
:获取请求头信息getBody()
:获取请求体信息<form th:action="@{/testRequestEntity}" method="post">
用户名:<input type="text" name="username" >
密码:<input type="password" name="password" >
<input type="submit" value="测试RequestEntity">
</form>
/*
2、测试RequestEntity类型
当前请求的请求报文就会赋值给该形参
-- 可以通过getHeaders()获取请求头信息
-- 通过getBody()获取请求体信息
*/
@RequestMapping("/testRequestEntity")
public String testRequestEntity(RequestEntity<String> requestEntity) {
// 当前的requestEntity表示当前整个请求报文的信息
System.out.println("requestHeader:" + requestEntity.getHeaders());
try {
String requestBody = URLDecoder.decode(Objects.requireNonNull(requestEntity.getBody()), "utf-8");
System.out.println("requestBody:" + requestBody);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return "success";
}
@ResponseBody用于标识一个控制器方法
,可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
<a th:href="@{/testResponseBody}">测试@ResponseBody注释响应浏览器数据</a><br>
/*
4、测试@ResponseBody注释响应浏览器数据
@ResponseBody用于标识一个控制器方法
可以将该方法的返回值直接作为响应报文的响应体响应到浏览器
*/
@RequestMapping(
value = "/testResponseBody",
produces = "text/html;charset=utf-8"
)
@ResponseBody
public String testResponseBody() {
return "Hello,@ResponseBody注解";
}
@ResponseBody处理json的步骤
<!-- 导入jackson依赖将实体类转换成Json格式数据 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
开启mvc的注解驱动
<mvc:annotation-driven />
此时在
HandlerAdaptor
中会自动装配一个消息转换器:MappingJackson2HttpMessageConverter
,可以将响应到浏览器的Java对象转换为Json格式的字符串
处理器方法
上使用@ResponseBody
注解进行标识Java对象
直接作为控制器方法的返回值,就会自动转换
为Json格式的字符串
<a th:href="@{/testResponseUser}">测试@Response 注释响应User对象</a><br>
@RequestMapping("/testResponseUser")
@ResponseBody
public User testResponseUser(){
return new User(1001,"张三","202428",23,"male");
}
// 浏览器页面响应的运行结果:
{
id: 1001,
username: "张三",
password: "202428",
age: 23,
sex: "male"
}
<div id="app">
<a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理Ajax</a>
</div>
// 引入vue.js和axios.js的静态文件
<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">
new Vue({
el: "#app",
methods: {
testAxios: function (event) {
axios({
method: "post", // 提交方式
url: event.target.href, // 绑定当前ajax的提交地址
params:{ // 传递的请求参数
username : "陈家睿",
password : "202428",
}
}).then(function (response) {
// 触发成功,所执行的函数
alert(response.data);
});
// 取消当前超链接的默认行为
event.preventDefault();
}
}
});
</script>
/*
5、测试SpringMVC处理Ajax
*/
@RequestMapping(
value = "/testAxios",
method = RequestMethod.POST
)
@ResponseBody // 该方法的返回值,传递给Ajax的回调函数中的response(响应到浏览器中的数据)
public String testAxios(String username,String password){
if ("陈家睿".equalsIgnoreCase(username) && "202428".equalsIgnoreCase(password)){
return "登录成功";
}
return "用户名或者密码错误";
}
@RestController注解
是springMVC提供的一个复合注解
,标识在控制器的类上
,就相当于为类添加了@Controller注解,并且为其中的每个方法添加了@ResponseBody注解
ResponseEntity
用于控制器方法的返回值类型,该控制器方法的返回值
就是响应到浏览器的响应报文
ResponseEntity
实现下载文件
的功能/*
1、测试文件下载的功能(属于文件复制的功能)
*/
@RequestMapping("/testDown")
public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
// 获取ServletContext对象
ServletContext servletContext = session.getServletContext();
// 获取服务器中文件的真实路径
String realPath = servletContext.getRealPath("/static/img/AlertStopIcon.png");
System.out.println(realPath);
// 创建输入流
InputStream is = new FileInputStream(realPath);
// 创建字节数组 is.available():获取对应文件的所有字节数
byte[] bytes = new byte[is.available()];
// 将流读到字节数组中
is.read(bytes);
// 创建HttpHeaders对象设置响应头信息 响应头是一个键值对类型的数据
MultiValueMap<String, String> headers = new HttpHeaders();
// 设置要下载方式以及下载文件的名字 attachment:以附件的形式下载
headers.add("Content-Disposition", "attachment;filename=AlertStopIcon.png");
// 设置响应状态码
HttpStatus statusCode = HttpStatus.OK;
// 创建ResponseEntity对象 new ResponseEntity(响应体,响应头信息,响应状态码)
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
// 关闭输入流
is.close();
return responseEntity;
}
文件上传
要求form表单
的请求方式必须为post
,并且添加属性enctype="multipart/form-data"(分成多段以流的方式(二进制形式传输)提交给服务器)
SpringMVC中将上传的文件封装到MultipartFile对象中
,通过此对象可以获取文件相关信息
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload 用于文件上传的依赖-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- 配置文件上传解析器 将上传的文件封装风MultipartFile对象 SpringMVC必须通过id才能查找到,且id固定为"multipartResolver" -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>
/*
2、测试文件上传功能
-- 导入上传所需的依赖
-- 在SpringMVC.xml中配置文件上传解析器
-- 通过Session获取ServletContext对象
-- ServletContext对象获取服务器中photo目录的真实路径
-- 通过transferTo上传文件到服务器target目
解决上传文件重名的覆盖问题:使用UUID+文件后缀名作为最终的文件名
*/
@RequestMapping(
value = "/testUp",
method = RequestMethod.POST
)
public String testUp(MultipartFile photo, HttpSession session) throws IOException {
// System.out.println(photo.getName()); // 获取表单中传输的name名称
// 获取源文件名称
String fileName = photo.getOriginalFilename();
// 获取上传的文件后缀名
String suffixName = fileName.substring(fileName.lastIndexOf("."));
// 将UUID作为文件名
String uuid = UUID.randomUUID().toString();
// 将uuid和文件后缀名作为最终的文件名
fileName = uuid + suffixName.replaceAll("-", "");
// 获取ServletContext对象
ServletContext servletContext = session.getServletContext();
// 获取服务器中photo目录的真实路径
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";
}
用于拦截控制器方法的执行
实现HandlerInterceptor接口
在SpringMVC的配置文件中进行配置
<!-- 配置拦截器 -->
<mvc:interceptors>
<!--
拦截所有的请求:
<bean class="com.chen.interceptors.FirstInterceptor"></bean>
<ref bean="firstInterceptor"/>
拦截指定的请求:
-->
<mvc:interceptor>
<!--
mvc:mapping设置需要拦截的请求
跟Ant风格的写法一致,/**表示多层目录拦截
-->
<mvc:mapping path="/**"/>
<!--
mvc:exclude-mapping设置需要排除的请求
排除拦截'/'的请求路径
-->
<mvc:exclude-mapping path="/"/>
<ref bean="firstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
preHandle(控制器方法执行之前执行)
:其boolean类型的返回值表示是否拦截或放行,true为放行(调用控制器方法),false为拦截(不调用控制器方法)
postHandle(控制器方法执行之后执行)
afterCompletion(渲染视图之后执行)
1、若每个拦截器的preHandle()都返回true
SpringMVC的配置文件的配置顺序有关
:preHandle()
会按照配置的顺序执行,而postHandle()
和afterComplation()
会按照配置的反序执行
2、若某个拦截器的perHandle()返回false
postHandle()
都不执行,返回false 的拦截器之前的拦截器的afterComplation()会按照配置的反序
执行
SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口:HandlerExceptionResolver
HandlerExceptionResolver接口的实现类有:DefaultHandlerExceptionResolver(默认的异常处理解析器)
和SimpleMappingExceptionResolver(自定义的异常处理解析器)
自定义的异常处理器
SimpleMappingExceptionResolver使用方法
:
<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="ex"></property>
</bean>
@ControllerAdvice // 将当前类标识为异常处理的组件
public class ExceptionController {
/*
使用@ExceptionHandler注解 处理异常进行页面跳转
*/
@ExceptionHandler(value = {ArithmeticException.class,NullPointerException.class})
public String testException(Exception ex, Model model){
model.addAttribute("ex",ex);
return "error";
}
}
web.xml
和SpringMVC核心配置文件
的功能AbstractAnnotationConfigDispatcherServletInitializer类
,容器会自动发现它,并用它来配置Servlet上下文
/**
*
* Web工程的初始化类,用来代替web.xml
*
* @author Forget_chen
* @className com.chen.config.WebInit
* @desc
* @date 2022/4/28 08:32
*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
/*
指定Spring的配置类
*/
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
/*
指定SpringMVC的配置类
*/
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{WebConfig.class};
}
/*
指定DispatcherServlet(前端控制器)的映射规则,即url-pattern
*/
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
/*
注册过滤器
*/
@Override
protected Filter[] getServletFilters() {
// 配置字符编码过滤器
CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
characterEncodingFilter.setEncoding("UTF-8");
characterEncodingFilter.setForceResponseEncoding(true);
// 配置HiddenHttpMethodFilter过滤器
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[]{characterEncodingFilter,hiddenHttpMethodFilter};
}
}
@Configuration
public class SpringConfig {
//ssm整合之后,spring的配置信息写在此类中
}
/**
* SpringMVC配置类:
* 1、扫描组件
* 2、视图解析器
* 3、视图控制器(view-controller)
* 4、默认servlet(default-servlet-handler)
* 5、注解驱动(mvc-driver)
* 6、文件上传解析器
* 7、异常处理器
* 8、拦截器
*
* @author Forget_chen
* @className WebConfig
* @desc
* @date 2022/4/28 08:38
*/
/*
实现WebMvcConfigurer接口中的方法就可以配置
视图控制器(view-controller)、默认servlet(default-servlet-handler)、文件上传解析器、异常处理器、拦截器等
*/
@Configuration
@ComponentScan("com.chen.controller") // 开启组件扫描
@EnableWebMvc // 开启MVC注解驱动
public class WebConfig implements WebMvcConfigurer {
/*
配置视图解析器Thymeleaf
*/
// 配置生成模板解析器
@Bean
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext =
ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过 WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new
ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/WEB-INF/webs/");
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;
}
/*
配置默认servlet(default-servlet-handler)
*/
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable(); // 将默认servlet启动
}
/*
配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TestInterceptor()).addPathPatterns("/**");
}
/*
配置视图控制器(view-controller)
addViewController:设置请求地址
setViewName:设置视图名称
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/hello").setViewName("hello");
}
/*
配置文件上传解析器
*/
@Bean
public MultipartResolver MultipartResolver(){
return new CommonsMultipartResolver();
}
/*
配置异常处理器
*/
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// 创建自定义的异常处理器SimpleMappingExceptionResolver
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
Properties prop = new Properties();
prop.setProperty("java.lang.ArithmeticException","error");
// 设置异常映射
simpleMappingExceptionResolver.setExceptionMappings(prop);
// 设置共享异常信息的键
simpleMappingExceptionResolver.setExceptionAttribute("ex");
resolvers.add(simpleMappingExceptionResolver);
}
}
DispatcherServlet
(前端控制器):
请求
和响应
,整个流程控制的中心,由它调用其它组件处理用户的请求
HandlerMapping
(处理器映射器):
@RequestMapping注解
Handler
(处理器):
控制器方法
HandlerAdapter
(处理器适配器):
处理器(控制器方法)
进行执行ViewResolver
(视图解析器):
进行视图解析
,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView
View
(视图):
mvc:default-servlet-handler
客户端展示404错误
找不到客户端也会展示404错误
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。