文章目录
一 SpringMVC 简介
1 MVC
- Model 模型层:工程中的 JavaBean,包含实体 Bean(Customer、Student…) 和 业务处理 Bean(Service、DAO)
- View 视图层:指工程中的 html 或 jsp 等页面,与用户进行交互,展示数据
- Controller 控制层:指工程中的 servlet,作用是接收请求和响应浏览器
- MVC 的工作流程 View <—> Controller <—> Model :用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的 Model 层处理请求,处理完毕将结果返回到 Controller,Controller 再根据请求处理的结果找到相应的 View 视图,渲染数据后最终响应给浏览器
2 SpringMVC
- 是 Spring 的子项目
- 主要作用是在 Spring 项目中进行表述层开发(是三层架构中的概念,三层架构:表述层(或表示层)、业务逻辑层、数据访问层,表述层表示前台页面和后台 servlet)
3 创建第一个 SpringMVC 项目
目录结构如下
- 配置 maven 依赖,这里遇到了一个奇怪的版本问题
<dependencies>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.9</version>
</dependency>
<!-- 日志 -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.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.11.RELEASE</version>
</dependency>
</dependencies>
- 配置 web.xml 的 Servlet 及 mapping
<!--
注册springMVC的前端控制器,对浏览器所发送的请求统一进行处理
在此配置下,springMVC的配置文件具有默认的位置和名称
默认的位置:WEB-INF
默认的名称:<servlet-name>-servlet.xml
若要为springMVC的配置文件设置自定义的位置和名称(推荐)
需要在servlet标签中添加init-param
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springMVC.xml</param-value>
</init-param>
-->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name> <!--设置配置文件的目录-->
<param-value>classpath:springMVC.xml</param-value> <!--对应resources目录下的文件-->
</init-param>
<load-on-startup>1</load-on-startup> <!--服务器启动时初始化DispatcherServlet-->
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
- 在 resources 目录下,配置 SpringMVC 的 xml:开启组件扫描、加入 thymeleaf 的名称空间并配置视图解析器(Thymeleaf 是服务器端的模板引擎,在服务器端获取模板和数据,生成结果输出给浏览器呈现结果)
<?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.atguigu.mvc.controller"></context:component-scan>
<!-- 配置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>
</beans>
- 定义 Controller 类,并用注解标注类和方法;在 templates 中定义 index.html, target.html
@Controller
public class HelloController {
/*
通过@RequestMapping注解,可以通过请求路径匹配要处理的具体的请求
/表示的当前工程的上下文路径 http://localhost:8080/project_context/
返回字符串即可,thymeleaf自动为返回值配置前后缀
方法名可以随意,根据注解进行解析
*/
@RequestMapping("/")
public String index(){
return "index";
}
@RequestMapping("/target")
public String toTarget(){
return "target";
}
}
总结:
- 浏览器发送请求,若请求地址符合前端控制器的url-pattern,该请求就会被前端控制器
DispatcherServlet
处理 - 前端控制器会读取 SpringMVC 的核心配置文件,通过扫描组件找到控制器,将请求地址和控制器中
@RequestMapping
注解的value
属性值进行匹配(要求每个方法的 value 值唯一) - 若匹配成功,该注解所标识的控制器方法就是处理请求的方法,处理请求的方法需要返回一个字符串类型的视图名称
- 该视图名称会被视图解析器解析,加上前缀和后缀组成视图的路径,通过 Thymeleaf 对视图进行渲染,最终转发到视图所对应页面
二 @RequestMapping
作用是关联 请求 和 处理请求的控制器方法。SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。
1 注解类与方法的区别
- @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
- @RequestMapping标识一个方法:设置映射请求请求路径的具体信息
@Controller
@RequestMapping("/test")
public class RequestMappingController {
//此时请求映射所映射的请求的请求路径为:/test/testRequestMapping
@RequestMapping("/testRequestMapping")
public String testRequestMapping(){
return "success";
}
}
2 value 属性
- 通过请求的请求地址匹配请求映射
- 是一个字符串类型的数组,和其中任意字符串匹配则选择该方法,满足其中一个即可
- @RequestMapping 必须设置的属性
<!--以下均能匹配 testRequestMapping()-->
<a th:href="@{/testRequestMapping}"> value1 </a>
<a th:href="@{/test}"> value2 </a>
@RequestMapping(
value = {"/testRequestMapping", "/test"}
)
public String testRequestMapping(){
return "success";
}
3 method 属性
- 根据请求方法 GET / POST / PUT / DELETE匹配请求映射
- 默认任意请求方式均可匹配
- 是
RequestMethod
枚举类型的数组,表示该请求映射能够匹配多种请求方式的请求,满足其中一个即可 @RequestMapping(value="/idx", method={RequestMethod.GET})
等于@GetMapping(value="/idx")
,其它方式同理- 如果 value 匹配而 method 不匹配,出现405错误
4 params 属性
- 根据请求携带的的参数匹配请求映射
- 是一个字符串类型的数组,需要同时满足所有条件,可以通过四种表达式设置请求参数和请求映射的匹配关系:
“param”:要求请求映射所匹配的请求必须携带 param 请求参数
“!param”:要求请求映射所匹配的请求必须不能携带 param 请求参数
“param=value”:要求请求映射所匹配的请求必须携带 param 请求参数且 param=value
“param!=value”:要求请求映射所匹配的请求必须携带 param 请求参数但 param!=value
<a th:href="@{/test(username='admin',password=123456)">测试</a>
@RequestMapping(
value = {"/testRequestMapping", "/test"}
,method = {RequestMethod.GET, RequestMethod.POST}
,params = {"username","password!=123456"}
)
public String testRequestMapping(){
return "success";
}
5 headers 属性
- 根据请求的请求头信息匹配请求映射
- 类似 params 属性,是一个字符串类型的数组,需要同时满足所有条件,可以通过四种表达式设置请求参数和请求映射的匹配关系:
“header”:要求请求映射所匹配的请求必须携带 header 请求头信息
“!header”:要求请求映射所匹配的请求必须不能携带 header 请求头信息
“header=value”:要求请求映射所匹配的请求必须携带 header 请求头信息且 header=value
“header!=value”:要求请求映射所匹配的请求必须携带 header 请求头信息且 header!=value
6 SpringMVC 支持路径中的占位符
- 将请求携带的参数以请求路径的形式,向服务器传递参数
- 如果
@RequestMapping
的value
属性中有路径的占位符,则请求时必须有占位符对应的参数,否则出现404
<a th:href="@{/goods/123}">访问goods,参数123</a>
@Controller
public class MyController {
@RequestMapping("/goods/{id}")
public String goodsPage(@PathVariable("id") int param_id) {
System.out.println(param_id);
return "goods";
}
}
三 获取 Request 的一系列参数
1 通过控制器方法的形参
- 将形参名和 Request 参数名对应即可(如果不同名,则需要使用 @RequestParam),如此只能获取 Request 参数,而不能获得请求头信息等
- 如果有多个同名的参数,则将形参类型设置为字符串数组
<a th:href="@{/testParam(username='admin',password=123456)}">测试</a>
@RequestMapping("/testParam")
public String testParam(String username, String password){
System.out.println("username:"+username+",password:"+password);
return "success";
}
2 控制器方法形参 映射 Request 参数 - @RequestParam
value
:Request 中的参数的 name 属性required
:标明该参数是否必须,如果是必须且未设置defaultValue
,则必须由 Request 传入defaultValue
:默认值
@Controller
public class MyController {
@RequestMapping("/goods/diff")
// request 传递参数为 name, passwd
public String diff(
@RequestParam(value = "name") String n,
@RequestParam(value = "passwd", required = false) String pwd) {
System.out.println(n + pwd);
return "goods";
}
}
3 控制器方法形参 映射 Request 请求头 - @RequestHeader
- 同样具有
value
,required
,defaultValue
,用法相同
4 控制器方法形参 映射 cookie数据 - @CookieValue
- 同样具有
value
,required
,defaultValue
,用法相同
5 通过 POJO 获取 Request 参数
将控制器方法形参设置为实体类类型,此时若浏览器传输的请求参数的参数名,和实体类中的属性名一致,那么请求参数就会为此属性赋值
<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){ // 根据request参数创建对象,前提是属性名和请求参数名相同
System.out.println(user);
return "success";
}
6 解决获取参数的乱码问题
- GET 请求的乱码问题可以通过更改 Tomcat 的配置文件 server.xml 解决
- POST 请求的乱码问题必须在请求参数获取之前设置编码,要比 Servlet 启动更早
- 服务器启动时,初始化顺序是 Listener -> Filter -> Servlet,可以使用 Filter 设置编码格式(不使用 Listener 是因为监听器只执行一次,负责初始化 / 销毁的动作,而 Filter 可以过滤所有符合路径请求),在 web.xml 中配置,并设置为首个 Filter
<!--配置springMVC的编码过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
四 域对象共享数据
域对象按范围从小到大分为:Request、Session、Application(ServletContext)
1 使用 ServletAPI 向 request 域对象共享数据(不建议)
@Controller
public class TestController {
@RequestMapping("/servlet")
public String m1(HttpServletRequest httpServletRequest) {
httpServletRequest.setAttribute("k", "v");
return "index";
}
}
index.html:
<p th:text="${k}"></p>
2 使用 ModelAndView 向 request 域对象共享数据
- 向 request 域对象共享数据的所有方式,本质是对使用 ModelAndView 进行共享的一种封装
- 方法的返回值必须是
ModelAndView
类型
@Controller
public class TestController {
@RequestMapping("/modelandview")
public ModelAndView m2() {
ModelAndView modelAndView = new ModelAndView();
// 设置键值对
modelAndView.addObject("k", "v");
// 设置视图名称
modelAndView.setViewName("index");
return modelAndView;
}
}
3 使用 Model 向 request 域对象共享数据
- 类似于 ServletAPI
@Controller
public class TestController {
@RequestMapping("/model")
public String m3(Model model) {
model.addAttribute("k", "v");
return "index";
}
}
4 使用 Map 向 request 域对象共享数据
@Controller
public class TestController {
@RequestMapping("/map")
public String m4(Map<String, Object> map) {
map.put("k", "v");
return "index";
}
}
5 使用 ModelMap 向 request 域对象共享数据
- 类似于 ServletAPI
@Controller
public class TestController {
@RequestMapping("/modelmap")
public String m5(ModelMap modelMap) {
modelMap.addAttribute("k", "v");
return "index";
}
}
6 使用 ServletAPI 向 Session 域对象共享数据
@RequestMapping("/testSession")
public String testSession(HttpSession session){
session.setAttribute("testSessionScope", "hello,session");
return "index";
}
7 使用 ServletAPI 向 Application 域对象共享数据
@RequestMapping("/testApplication")
public String testApplication(HttpSession session){
ServletContext application = session.getServletContext();
application.setAttribute("testApplicationScope", "hello,application");
return "index";
}
五 SpringMVC 视图
- 默认的有:转发视图、重定向视图
1 Thymeleaf 视图
- 实现了转发视图的功能
- 当控制器方法中所设置的视图名称没有任何前缀时,此时的视图名称会被 SpringMVC 配置文件中所配置的视图解析器解析,视图名称拼接视图前缀和视图后缀所得到的最终路径,会通过转发的方式实现跳转
@RequestMapping("/testHello")
public String testHello(){
return "hello"; // 没有任何前缀
}
Spring 配置文件中的视图解析器:
<!-- 配置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="/templates/"/>
<property name="suffix" value=".html"/>
<property name="templateMode" value="HTML5"/>
<property name="characterEncoding" value="UTF-8" />
</bean>
</property>
</bean>
</property>
</bean>
2 转发视图
- SpringMVC中默认的转发视图是
InternalResourceView
- 当控制器方法中所设置的视图名称以"forward:"为前缀时,创建
InternalResourceView
视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀"forward:"去掉,剩余部分作为最终路径通过转发的方式实现跳转
@RequestMapping("/testForward")
public String testForward(){
return "forward:/testHello"; // 先创建 InternalResourceView 再创建 Thymeleaf View
}
3 重定向视图
- SpringMVC 中默认的重定向视图是
RedirectView
- 当控制器方法中所设置的视图名称以"redirect:"为前缀时,创建
RedirectView
视图,此时的视图名称不会被 SpringMVC 配置文件中所配置的视图解析器解析,而是会将前缀"redirect:"去掉,剩余部分作为最终路径通过重定向的方式实现跳转 - 重定向视图在解析时,会先将 redirect: 前缀去掉,然后会判断剩余部分是否以 / 开头,若是,则自动拼接上下文路径
@RequestMapping("/testRedirect")
public String testRedirect(){
return "redirect:/testHello";
}
4 使用 view-controller 代替 控制器方法
- 当控制器方法中,仅仅用来实现页面跳转,即只需要设置视图名称时,可以将处理器方法用
view-controller
标签表示 - 当SpringMVC 中设置任何一个
view-controller
时,其他控制器中的请求映射将全部失效,可以使用标签<mvc:annotation-driven />
开启
<mvc:view-controller path="/testView" view-name="success"></mvc:view-controller>
六 RESTful
- Representational State Transfer,表现层资源状态转移
- REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性
- 对于 HTTP 的四种请求:
操作 | HTTP 请求 | RESTful |
---|---|---|
插入 | POST | /user(请求为 POST) |
删除 | DELETE | /user/id(请求为 DELETE) |
更新 | PUT | /user(请求为 PUT) |
查询 | GET | /user/id(请求为 GET) |
- SpringMVC 中提供了两个过滤器:
CharacterEncodingFilter
和HiddenHttpMethodFilter
- 使用
HiddenHttpMethodFilter
模拟 DELETE 和 PUT - 在 web.xml 中注册时,必须先注册
CharacterEncodingFilter
,再注册HiddenHttpMethodFilter
,因为CharacterEncodingFilter
要求前面不能有任何获取请求参数的操作,而CharacterEncodingFilter
获取了 request 参数
七 HttpMessageConverter
- 报文信息转换器,将 请求报文 转换为Java对象,或将Java对象转换为 响应报文
- HttpMessageConverter提供了两个注解和两个类型:@RequestBody,@ResponseBody,RequestEntity,ResponseEntity
1 @RequestBody
@RequestBody
可以获取请求体(仅仅是请求体而非整个 HTTP 报文,POST 具有请求体,而 GET 不具有)- 需要在控制器方法设置一个形参,使用
@RequestBody
进行标识,当前请求的请求体就会为当前注解所标识的形参赋值 - 可以将前端传来的 json 转为对象
@RequestMapping("/testRequestBody")
public String testRequestBody(@RequestBody String requestBody){
System.out.println(requestBody);
return "success";
}
// 输出 requestBody:username=admin&password=123456
@Override
@PostMapping("/add")
public CmsPageResult add(@RequestBody CmsPage cmsPage) { // @RequestBody将json转为对象
return this.pageService.add(cmsPage);
}
2 RequestEntity
RequestEntity
封装 HTTP 请求报文,需要在控制器方法的形参中的 泛型位置 设置要将报文转换的目标类型,当前请求的请求报文就会赋值给该形参- 通过
getHeaders()
获取请求头信息,通过getBody()
获取请求体信息
@RequestMapping("/request_entity")
public String requestEntityTest(RequestEntity<String> entity) {
System.out.println(entity.getHeaders());
System.out.println(entity.getBody());
return "success";
}
3 @ResponseBody
@ResponseBody
用于标识一个 控制器方法,可以将该方法的 返回值 直接作为响应报文的 响应体 响应到浏览器
@RequestMapping("/response_body")
@ResponseBody
public String responseBodyTest() {
return "TIGER YEAR";
}
- 如果需要以 json 格式,打印 Java对象的响应体,需要导入 jackson 依赖并开启 mvc 注解驱动
pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.1</version>
</dependency>
mvc核心配置文件:
<mvc:annotation-driven />
@RequestMapping("/response_body_json")
@ResponseBody
public User responseBodyJsonTest() {
return new User("my_name", "my_passwd");
}
4 ResponseEntity
- 用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
- 可以实现文件下载的功能
5 RestController
- 复合注解,标识控制器类,就相当于为类添加了
@Controller
注解,并且为其中的每个方法添加了@ResponseBody
注解
八 拦截器
- 拦截的是控制器方法执行
- 和过滤器 filter 的主要区别是,filter 作用于浏览器到 Servlet 的过程中,而拦截器作用于 Controller 执行前(Servlet 到 Controller 的过程中),以及 Controller 返回后 的过程中
1 配置方法
在 SpringMVC 配置文件中:
<!-- 前两种对DispatcherServlet所处理的*所有的请求*进行拦截 -->
<mvc:interceptors>
<!--方式一:-->
<bean class="interceptor.MyInterceptor"></bean>
<!--方式二:-->
<ref bean="myInterceptor"></ref>
<!--方式三:可以自定义拦截路径-->
<!--可以通过ref或bean标签设置拦截器,通过mvc:mapping设置需要拦截的请求,
通过mvc:exclude-mapping设置需要排除的请求,即不需要拦截的请求-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/testRequestEntity"/>
<ref bean="myInterceptor"></ref>
</mvc:interceptor>
</mvc:interceptors>
拦截器类:
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("pre");
return true; // 返回 true 放行
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("post");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("after");
}
}
2 HandlerInterceptor 接口的三个方法
preHandle
:返回值为 bool 类型,返回 true 放行,false 拦截postHandle
:控制器方法执行之后执行afterComplation
:渲染视图完毕之后执行
3 多个拦截器的执行顺序
- 如果所有拦截器的
preHandle()
都返回 true,则preHandle()
按照配置的顺序执行,postHandle()
和afterComplation()
按照配置的反序执行 - 如果某个拦截器的
preHandle()
返回 false,preHandle()
返回 false 的拦截器和它之前的拦截器的preHandle()
都会执行,postHandle()
都不执行,返回 false 的拦截器之前的拦截器的afterComplation()
会倒序执行 - 下图是所有拦截器的
preHandle()
都返回 true 的情况,如果拦截器3返回 false,则方法的执行顺序是: 1.prehandle->2.prehandle->3.prehandle->2.afterCompletion->1.afterCompletion
九 异常处理器
1 基于配置
在 SpringMVC 的核心配置文件中:
- 设置异常类型与视图名称的映射
- 保存异常信息到 request 域(可选)
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings"> <!--properties类型,键值对的结构-->
<props>
<!--1. key表示处理器方法执行过程中出现的异常,全类名
value(写在双标签之内)表示若出现指定异常时,设置一个新的视图名称,跳转到指定页面-->
<prop key="java.lang.ArithmeticException">error</prop>
</props>
</property>
<!--2. exceptionAttribute:将出现的异常信息在请求域中进行共享,value属性是信息的key-->
<property name="exceptionAttribute" value="info_key"></property>
</bean>
出现异常后,跳转到 error.html 访问异常信息:
<body>
<p th:text="${info_key}"></p>
</body>
2 基于注解
- 使用
@ControllerAdvice
注解异常处理类 - 使用
@ExceptionHandler(异常类的class对象)
注解异常处理方法
// 标识为异常处理组件
@ControllerAdvice
public class AnnotationExceptionController {
// 处理的异常类型,处理多种异常时 @ExceptionHandler(value = {...})
@ExceptionHandler(ArithmeticException.class)
public String divide0(Exception exception, Model model) {
// 向 request 域中写入键值对
model.addAttribute("info_key", exception);
// 交由 thymeleaf 解析
return "fail";
}
}
十 使用注解配置 SpringMVC
- 目的是使用配置类和注解代替 web.xml 和 SpringMVC 配置文件的功能
1 初始化类:代替 web.xml
- 继承自
AbstractAnnotationConfigDispatcherServletInitializer
/*
* 需要完成的功能:
* 1.servlet
* 2.servlet-mapping
* 3.过滤器
* 4.Spring配置类
* 5.SpringMVC配置类*/
public class WebInit extends AbstractAnnotationConfigDispatcherServletInitializer {
// 指定 Spring 配置类
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{MySpringConfig.class}; // 创建长度为1的Class数组,放入MySpringConfig.class
}
// 指定SpringMVC的配置类
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyWebConfig.class}; // 同上
}
// 指定DispatcherServlet的映射规则,即url-pattern
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 设置servlet-mapping
}
// 过滤器
@Override
protected Filter[] getServletFilters() {
// 过滤器1
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
encodingFilter.setEncoding("UTF-8");
encodingFilter.setForceRequestEncoding(true);
// 过滤器2
HiddenHttpMethodFilter hiddenHttpMethodFilter = new
HiddenHttpMethodFilter();
return new Filter[]{encodingFilter, hiddenHttpMethodFilter};
}
}
2 SpringConfig 类:代替 Spring 配置文件(略)
@Configuration
public class MySpringConfig {
// ...
}
3 WebConfig 类:代替 SpringMVC 配置文件
@Bean
的作用是,将方法返回值(Java 对象)交给 IOC 容器
/*
* 需要完成的功能:
* 1.组件扫描
* 2.thymeleaf视图解析器
* 3.视图控制器
* 4.mvc注解驱动
* 5.拦截器
* 6.异常处理器
* 7.default-servlet-handler
* 8.文件上传解析器*/
@Configuration // 标注当前类为配置类
@ComponentScan(value = {"config", "controller", "interceptor"}) // 开启组件扫描
@EnableWebMvc // 开启注解驱动
public class MyWebConfig implements WebMvcConfigurer {
/*************************** 重写WebMvcConfigurer类的方法 ***************************/
// 使用默认的servlet处理静态资源
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
// 拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
MyInterceptor myInterceptor = new MyInterceptor();
registry.addInterceptor(myInterceptor).addPathPatterns("/**");
}
// 视图控制器
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
}
// 异常处理器,一种实现方式,也可以配置为 @bean
@Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
// 写法参考之前的 SpringMVC 配置文件
Properties properties = new Properties();
properties.setProperty("java.lang.ArithmeticException", "fail"); // (异常全类名,跳转视图名称)
SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
simpleMappingExceptionResolver.setExceptionMappings(properties); // 设置异常映射
simpleMappingExceptionResolver.setExceptionAttribute("info_key"); // 设置异常信息的key
resolvers.add(simpleMappingExceptionResolver);
}
/************************* 配置 thymeleaf 视图解析器 *******************************/
// 1.配置生成模板解析器
@Bean // @Bean的返回值放入IOC容器
public ITemplateResolver templateResolver() {
WebApplicationContext webApplicationContext = ContextLoader.getCurrentWebApplicationContext();
// ServletContextTemplateResolver需要一个ServletContext作为构造参数,可通过WebApplicationContext 的方法获得
ServletContextTemplateResolver templateResolver = new ServletContextTemplateResolver(
webApplicationContext.getServletContext());
templateResolver.setPrefix("/templates/");
templateResolver.setSuffix(".html");
templateResolver.setCharacterEncoding("UTF-8");
templateResolver.setTemplateMode(TemplateMode.HTML);
return templateResolver;
}
// 2.生成模板引擎并为其注入模板解析器
@Bean
public SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { // !!!参数进行了自动装配
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver);
return templateEngine;
}
// 3.生成视图解析器并为其注入模板引擎
@Bean
public ViewResolver viewResolver(SpringTemplateEngine templateEngine) {
ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setCharacterEncoding("UTF-8");
viewResolver.setTemplateEngine(templateEngine);
return viewResolver;
}
/************************* 其它插件 *******************************/
// 配置文件上传解析器
// @Bean
// public CommonsMultipartResolver multipartResolver(){
// return new CommonsMultipartResolver();
// }
}
十一 SpringMVC 执行流程
1 常用组件
-
DispatcherServlet:前端控制器,不需要工程师开发,由框架提供
作用:统一处理请求和响应,整个流程控制的中心,由它调用其它组件处理用户的请求 -
HandlerMapping:处理器映射器,不需要工程师开发,由框架提供
作用:根据请求的url、method等信息查找Handler,即控制器方法 -
Handler:处理器
作用:在DispatcherServlet的控制下Handler对具体的用户请求进行处理 -
HandlerAdapter:处理器适配器,不需要工程师开发,由框架提供
作用:通过HandlerAdapter对处理器(控制器方法)进行执行 -
ViewResolver:视图解析器,不需要工程师开发,由框架提供
作用:进行视图解析,得到相应的视图,例如:ThymeleafView、InternalResourceView、RedirectView -
View:视图
作用:将模型数据通过页面展示给用户
2 流程总结
- 用户向服务器发送请求,被 SpringMVC 前端控制器
DispatcherServlet
捕获 DispatcherServlet
解析 URL(统一资源定位器),得到URI(请求资源标识符),并与配置的servlet-mapping
进行匹配,判断请求 URI 对应的映射:
(1) 可以匹配,则交给具体的 Servlet,前往步骤3
(2) 不能匹配,如果配置了mvc:default-servlet-handler
,访问目标资源(一般为静态资源,如:JS, CSS, HTML),找不到展示404错误
(3) 不能匹配,没有配置mvc:default-servlet-handler
,展示404错误- 根据 URI,调用
HandlerMapping
获得该Handler
配置的所有相关的对象(包括Handler
对象以及Handler
对象对应的拦截器),最后以HandlerExecutionChain
执行链对象的形式返回 DispatcherServlet
根据获得的Handler
,选择一个合适的HandlerAdapter
(Handler
执行需要依赖HandlerAdapter
)- 如果成功获得
HandlerAdapter
,此时将开始执行拦截器的preHandler()
方法 - 向控制器方法传递客户端请求 Request 携带的参数,执行控制器方法,此处可以配置一些额外操作:
(1)HttpMessageConveter
: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
(2) 数据格式化、数据验证… - 控制器方法执行完成后,向
DispatcherServlet
返回一个ModelAndView
对象 - 执行拦截器的
postHandle()
方法 DispatcherServlet
根据返回的ModelAndView
(首先判断是否存在异常:如果存在异常,则执行HandlerExceptionResolver
进行异常处理)选择一个适合的ViewResolver
进行视图解析,根据 Model 和 View,渲染视图- 执行拦截器的
afterCompletion()
方法 - 将渲染结果返回给客户端