文章目录
- 一 SpringMVC 简介
- 1 MVC
- 2 SpringMVC
- 3 创建第一个 SpringMVC 项目
- 二 @RequestMapping
- 1 注解类与方法的区别
- 2 value 属性
- 3 method 属性
- 4 params 属性
- 5 headers 属性
- 6 SpringMVC 支持路径中的占位符
- 三 获取 Request 的一系列参数
- 1 通过控制器方法的形参
- 2 控制器方法形参 映射 Request 参数 - @RequestParam
- 3 控制器方法形参 映射 Request 请求头 - @RequestHeader
- 4 控制器方法形参 映射 cookie数据 - @CookieValue
- 5 通过 POJO 获取 Request 参数
- 6 解决获取参数的乱码问题
- 四 域对象共享数据
- 1 使用 ServletAPI 向 request 域对象共享数据(不建议)
- 2 使用 ModelAndView 向 request 域对象共享数据
- 3 使用 Model 向 request 域对象共享数据
- 4 使用 Map 向 request 域对象共享数据
- 5 使用 ModelMap 向 request 域对象共享数据
- 6 使用 ServletAPI 向 Session 域对象共享数据
- 7 使用 ServletAPI 向 Application 域对象共享数据
- 五 SpringMVC 视图
- 1 Thymeleaf 视图
- 2 转发视图
- 3 重定向视图
- 4 使用 view-controller 代替 控制器方法
- 六 RESTful
- 七 HttpMessageConverter
- 1 @RequestBody
- 2 RequestEntity
- 3 @ResponseBody
- 4 ResponseEntity
- 5 RestController
- 八 拦截器
- 1 配置方法
- 2 HandlerInterceptor 接口的三个方法
- 3 多个拦截器的执行顺序
- 九 异常处理器
- 1 基于配置
- 2 基于注解
- 十 使用注解配置 SpringMVC
- 1 初始化类:代替 web.xml
- 2 SpringConfig 类:代替 Spring 配置文件(略)
- 3 WebConfig 类:代替 SpringMVC 配置文件
- 十一 SpringMVC 执行流程
- 1 常用组件
- 2 流程总结
一 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, passwdpublic 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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("pre");return true; // 返回 true 放行}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("post");}@Overridepublic 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 配置类@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{MySpringConfig.class}; // 创建长度为1的Class数组,放入MySpringConfig.class}// 指定SpringMVC的配置类@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{MyWebConfig.class}; // 同上}// 指定DispatcherServlet的映射规则,即url-pattern@Overrideprotected String[] getServletMappings() {return new String[]{"/"}; // 设置servlet-mapping}// 过滤器@Overrideprotected Filter[] getServletFilters() {// 过滤器1CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();encodingFilter.setEncoding("UTF-8");encodingFilter.setForceRequestEncoding(true);// 过滤器2HiddenHttpMethodFilter hiddenHttpMethodFilter = newHiddenHttpMethodFilter();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处理静态资源@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}// 拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) {MyInterceptor myInterceptor = new MyInterceptor();registry.addInterceptor(myInterceptor).addPathPatterns("/**");}// 视图控制器@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("index");}// 异常处理器,一种实现方式,也可以配置为 @bean@Overridepublic 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"); // 设置异常信息的keyresolvers.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.生成模板引擎并为其注入模板解析器@Beanpublic SpringTemplateEngine templateEngine(ITemplateResolver templateResolver) { // !!!参数进行了自动装配SpringTemplateEngine templateEngine = new SpringTemplateEngine();templateEngine.setTemplateResolver(templateResolver);return templateEngine;}// 3.生成视图解析器并为其注入模板引擎@Beanpublic 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()
方法 - 将渲染结果返回给客户端