SpringMVC
- 数据格式化
- 基本数据类型和字符串自动转换
- 特殊数据类型和字符串自动转换
- 验证及国际化
- 应用实例
- 注意事项和使用细节
- 注解的结合使用
- 数据类型转换校验核心类-DatBinder
- 取消某个属性的绑定
- 中文乱码解决
- 处理json和HttpMessageConverter<T>
- 作业布置
- SpringMVC文件上传
- 自定义拦截器
- 异常处理
- SpringMVC执行流程 - 源码分析
- 作业布置
数据格式化
●基本介绍
说明: 在我们提交数据(比如表单时)SpringMVC怎么对提交的数据进行转换和处理的
1.基本数据类型可以和字符串之间自动完成转换, 比如:
Spring MVC上下文中内建了很多转换器, 可完成大多数Java类型的转换工作.
基本数据类型和字符串自动转换
切换回之前写的springmvc项目
1.新建com.zzw.web.datavalid.entity包 Monster.java
public class Monster {
private Integer id;
private String email;
private Integer age;
private String name;
//有参, 无参构造器, setter, getter, toString方法
2.新建web目录/data_valid.jsp
<head>
<title>SpringMVC[数据格式/验证等]</title>
</head>
<body>
<h1>SpringMVC[数据格式/验证等]</h1>
<a href="<%=request.getContextPath()%>/addMonsterUI">添加妖怪</a>
<hr>
3.新建web/WEB-INF/pages/datavalid/monster_addUI.jsp
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>添加妖怪</title>
</head>
<body>
<%--这里的表单, 我们使用springMVC的标签来完成
说明几点:
1.SpringMVC 表单标签在显示之前必须在 request 中有一个bean, 该 bean 的属性和表单标签的字段要对应
request 中的 key 为: form 标签的 modelAttribute 属性值, 比如这里的monster
2.SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 应用的根目录
3.这里我们使用springmvc标签的主要目的是方便提示信息回显
--%>
<form:form action="/springmvc/" method="post" modelAttribute="?">
妖怪名字: <form:input path="name"/><br/><br/>
妖怪年龄: <form:input path="age"/><br/><br/>
妖怪邮件: <form:input path="email"/><br/><br/>
<input type="submit" value="添加妖怪"/>
</form:form>
</body>
</html>
4.新建com.zzw.web.datavalid包 MonsterHandler.java
/**
* @author 赵志伟
* @version 1.0
* MonsterHandler 处理器响应用户提交数据
* @Scope(value = "prototype") 表示每次请求MonsterHandler会生成一个新的对象
*/
@SuppressWarnings({"all"})
@Controller
@Scope(value = "prototype")
public class MonsterHandler {
/**
* 显示添加monster的页面
* 1. 这里Map<String, Object> map
* 2. 当我们向map添加数据时, 会默认存放到request域中
* @param map
* @return
*/
@RequestMapping(value = "/addMonsterUI")
public String addMonsterUI(Map<String, Object> map) {
/**
* 解读
* 1.这里的表单, 我们使用springMVC的标签来完成
* 2.SpringMVC 表单标签在显示之前必须在 request 中有一个bean, 该 bean 的属性和表单标签的字段要对应
* request 中的 key 为: form 标签的 modelAttribute 属性值, 比如这里的monster
* 3.SpringMVC 的 form:form 标签的 action 属性值中的 / 不代表 WEB 引用的根目录
* 4.<form:form action="?" method="post" modelAttribute="monster">
* 这里需要给request增加一个 monster, 因为jsp 页面 的modelAttribute="monster"需要
* 这时是springMVC的内部检测机制 即使是一个空的也需要, 否则报错
*/
//再次说明, 如果你跳转的页面, 使用了springmvc标签
//就需要准备一个对象放入到request域中, 这个对象的属性名 monster, 对应
//springmvc表单标签的 modelAttribute="monster"
map.put("monster", new Monster());
return "datavalid/monster_addUI" ;
}
}
5.monster_addUI.jsp
补充 modelAttribute
modelAttribute="monster"
6.新建web/WEB-INF/pages/datavalid/success.jsp
<h1>恭喜, 添加成功~</h1>
7.MonsterHandler 新增save方法
/**
* 编写方法, 处理添加妖怪
* 1. springmvc可以将提交的数据, 按照参数名和对象的属性名匹配
* 2. 直接封装到对象中->前面讲解模型数据时讲过
* String -> Integer
* @param monster
* @return
*/
@RequestMapping(value = "/save")
public String save(Monster monster) {
System.out.println("---monster---" + monster);
return "datavalid/success";
}
8.monster_addUI.jsp
补充 action
<form:form action="save" method="post" modelAttribute="monster">
9.测试. 浏览器: http://localhost:8080/springmvc/data_valid.jsp
1)如果age输入的是 数字, 则通过, 说明SpringMVC可以将提交的字符串 数字, 比如"28", 转成 Integer/int
2)如果不是数字, 则给出400的页面
10.Postman测试
特殊数据类型和字符串自动转换
1.特殊数据类型和字符串之间的转换使用注解(比如日期, 规定格式的小数比如货币形式等)
2.对于日期和货币可以使用 @DataTimeFormat 和 @NumberFormat 注解, 把这两个注解标记在字段上即可
3.修改 Monster.java, 增加 birthday 和 salary 字段
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern = "###,###.##")
private Float salary;
//全参构造器, setter,getter,toString方法
4.修改monster_addUI.jsp, 增加 birthday 和 salary 字段
妖怪生日: <form:input path="birthday"/>要求以"9999-11-11"的形式<br/><br/>
妖怪薪水: <form:input path="salary"/>要求以"123,890.12"的形式<br/><br/>
5.测试
1)如果 birthday 和 salary 是按照指定格式输入, 则通过, 说明SpringMVC可以按注解指定格式转换
2)如果没有按照注解指定格式, 则给出400的页面
妖怪薪水只要能转成数字就行, 例如123456.12, 456.12, 0.123456, 但是12x.11就不行.
妖怪生日格式如2000-10-15就可以, 但是200010-15就不可以.
6.Postman测试
验证及国际化
●概述
1.对输入的数据(比如表单数据), 进行必要的验证, 并给出相应的提示信息.
2.对于验证表单数据, springMVC提供了很多使用的注解, 这些注解由JSR 303 验证框架提供.
●JSR 303 验证框架
1.JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架, 它已经包含在 JavaEE 中.
2.JSR 303 通过在 Bean 属性上标注类似于 @NoNull, @Max 等标准的注解指定校验规则, 并通过标准的验证接口对 Bean 进行验证.
3.JSR 303 提供的基本验证注解有
●Hibernate Validator 扩展注解
1.Hibernate Validator 和Hibernate没有关系, 只是 JSR 303 实现的一个扩展.
2. Hibernate Validator 是 JSR 303 的一个参考实现, 除支持所有标准的校验注解外, 它还支持以下的扩展注解:
3.扩展注解如下
应用实例
●应用实例 - 需求说明
●应用实例 - 代码实现
1.引入验证和国际化相关的jar包 springmvc验证需要的jar包
2.修改Monster.java
//@Range(min = 1, max = 100)
//表示接受的age值, 在 1-100 之间
@Range(min = 1, max = 100)
private Integer age;
//@NotEmpty 表示name不能为空
//Asserts that the annotated string, collection, map or array is not {@code null} or empty.
@NotEmpty
private String name;
3.修改MonsterHandler.java
/**
* 编写方法, 处理添加妖怪
* 1. springmvc可以将提交的数据, 按照参数名和对象的属性名匹配
* 2. 直接封装到对象中->前面讲解模型数据时讲过
* String -> Integer
* 3. @Valid Monster monster: 表示对monster接收的数据进行校验
* 4. Errors errors 表示如果校验出现错误, 将校验的错误信息保存到errors中
* 5. Map<String, Object> map 表示如果校验出现错误, 将校验的错误信息保存到 map 通过保存monster对象
* 6. 校验发生的时机: 在springmvc底层, 反射调用目标方法时, 会接收到http请求的数据, 然后根据注解来进行验证
* , 在验证过程中, 如果出现了错误, 就把错误信息填充到errors 和 map
* @param monster
* @return
*/
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
System.out.println("---monster---" + monster);
//我们为了看到验证的情况, 我们输出map 和 errors
System.out.println("=== map ===");
for(Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key=" + entry.getKey() + " value=" + entry.getValue());
}
System.out.println("=== errors ===");
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println("error=" + error);
//返回添加页面
return "datavalid/monster_addUI";
}
return "datavalid/success";
}
4.测试
error只输出了一条信息, 改进
System.out.println("=== errors ===");
if (errors.hasErrors()) {//判断是否有错误
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println("error=" + error);
}
//返回添加页面
return "datavalid/monster_addUI";
}
return "datavalid/success";
再次测试
5.配置国际化文件 springDispatcherServlet-servlet.xml
<!--配置国际化错误信息的资源处理bean-->
<bean id="messageSource" class=
"org.springframework.context.support.ResourceBundleMessageSource">
<!--配置国际化文件名字
如果这样配的话, 表示messageSource会到 src/i18nXXX.properties去读取错误信息
-->
<property name="basename" value="i18n"></property>
</bean>
6.创建国际化文件 src/i18n.properties
在线Unicode转中文
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a #用户名不能为空
#如果类型匹配错误, 自定义错误信息 => (类型匹配错误, 年龄需要在0和150之间)
typeMismatch.monster.age=\u7c7b\u578b\u5339\u914d\u9519\u8bef, \u5e74\u9f84\u9700\u8981\u57280\u548c150\u4e4b\u95f4 #类型匹配错误, 年龄需要在0和150之间
#如果范围错误, 自定义错误信息 => (这是新的验证错误信息, 年龄必须在1-100之间)
Range.monster.age=\u8fd9\u662f\u65b0\u7684\u9a8c\u8bc1\u9519\u8bef\u4fe1\u606f, \u5e74\u9f84\u5fc5\u987b\u57281-100\u4e4b\u95f4 #这是新的验证错误信息, 年龄必须在1-100之间
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e #生日格式不正确
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e #薪水格式不正确
7.修改monster_addUI.jsp, 回显错误信息
<form:form action="save" method="POST" modelAttribute="monster">
妖怪名字: <form:input path="name"/><form:errors path="name"/><br/><br/>
妖怪年龄: <form:input path="age"/><form:errors path="age"/><br/><br/>
妖怪邮件: <form:input path="email"/><form:errors path="email"/><br/><br/>
妖怪生日: <form:input path="birthday"/><form:errors path="birthday"/>要求以"9999-11-11"的形式<br/><br/>
妖怪薪水: <form:input path="salary"/><form:errors path="salary"/>要求以"123,890.12"的形式<br/><br/>
<input type="submit" value="添加妖怪"/>
</form:form>
8.测试
如果不配置国际化文件, 默认的错误信息如下
注意事项和使用细节
1.在需要验证的 JavaBean/POJO 的字段上加上相应的验证注解.
2.目标方法上, 在 JavaBean/POJO 类型的参数前, 添加 @Valid 注解, 告知 SpringMVC 该 bean 是需要验证的
3.在 @valid 注解之后, 添加一个 Errors 或 BindingResult 类型的参数, 可以获取到验证的错误信息
4.需要使用 <form:errors path=“email”></form:errors> 标签来显示错误信息, 这个标签, 需要写在 <form:form> 标签内生效.
5.错误消息的国际化文件i18n.properties, 中文需要是Unicode编码, 使用工具转换
√ 格式: 验证规则. 表单modelAttribute值.属性名=消息信息
√ NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
√ Range.monster.age=\u5e74\u9f84\u9700\u8981\u57280\u548c100\u4e4b\u95f4
5.注解 @NotNull 和 @NotEmpty 的区别说明
1) 查看源码可以知道: @NotEmpty Asserts that the annotated string, collection, map or array is not {@code null} or empty.
2) 查看源码可以知道: @NotNull The annotated element must not be null. Accepts any type.
种类 | 修饰类型 | 作用 |
---|---|---|
@NotEmpty | String, collection, map | null || size=0 |
@NotNull | 任意类型 | null |
3) 解读: 如果是字符串验证空, 建议使用 @NotEmpty
6.SpringMVC验证时, 会根据不同的验证错误, 返回对应的信息
注解的结合使用
●问题提出, age没有, 是空的, 提交确实成功了
●解决方案 注解组合使用
1.使用 @NotNull + @Range 组合使用解决
2.具体代码
//email是string, 使用@NotEmpty
@NotEmpty
private String email;
@NotNull(message = "age不能为空")
@Range(min = 1, max = 100)//Range也有默认的message提示信息
private Integer age;
@NotEmpty//NotEmpty有默认的message提示信息
private String name;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
数据类型转换校验核心类-DatBinder
●DataBinder工作机制-了解
图例Spring MVC 通过 反射机制对目标方法进行解析, 将请求消息绑定到处理方法的入参中. 数据绑定的核心部件是 DataBinder, 运行机制如下
1.ConversionService数据类型转换/格式化 遇到的错误, 会放入到BindingResult中
2.Validator校验器遇到的错误, 会放入到BindingResult中
Error的运行类型是BeanPropertyBindingResult, BeanPropertyBindingResult实现了BindingResult接口
Debug一下DataBinder类的validate如何得到验证errors信息
取消某个属性的绑定
●说明
在默认情况下, 表单提交的数据都会和pojo类型的javabean属性绑定, 如果程序员在开发中, 希望取消某个属性的绑定, 也就是说, 不希望接收到某个表单对应的属性的值, 则可以通过 @InitBinder注解取消绑定
1.编写一个方法, 使用InitBinder标识的该方法, 可以对WebDataBinder对象进行初始化. WebDataBinder是DataBinder的子类, 用于完成由表单字段到JavaBean属性的绑定.
2.@InitBinder方法不能有返回值, 它必须声明为void
3.@InitBinder方法的参数通常是WebDataBinder
●案例-不希望接收怪物的名字属性
1.修改MonsterHandler.java, 增加方法
//取消绑定 monster的name表单提交的值给monster.name属性
@InitBinder
public void initBinder(WebDataBinder webDataBinder) {
/**
* 解读
* 1.方法上需要标注 @InitBinder springmvc底层会初始化 WebDataBinder
* 2.调用webDataBinder.setDisallowedFields("name") 表示取消指定属性的绑定
* 即: 当表单提交字段为 name时, 就不再把接收到的name值, 填充到model数据[monster的name属性]
* 3.机制: springmvc 在底层通过反射调用目标方法时, 接收到http请求的参数和值, 使用反射+注解技术
* 取消对指定属性的填充
* 4.setDisallowedFields支持可变参数, 可以填写多个字段
* 5.如果我们取消某个属性绑定, 验证就没有意义了, 应当把验证的注解去掉
* //@NotEmpty
* private String name;
*/
webDataBinder.setDisallowedFields("name");
}
注意事项和细节说明
1.setDisallowedFields()是可变形参, 可以指定多个字段
2.当将一个字段/属性, 设置为disallowed, 就不再接收表单提交的值, 那么这个字段/属性的值, 就是该对象默认的值(具体看程序员定义时指定)
3.一般来说, 如果不接受表单字段提交数据, 则该对象字段的验证也就没有意义了, 可以注销掉, 比如 注销 //@NotEmpty
中文乱码解决
自定义中文乱码过滤器
●说明
当表单提交数据为中文时, 会出现乱码,我们来解决一下( 提示: 想恢复name属性的绑定)
1.创建过滤器 JavaWeb过滤器
在com.zzw.web.filter
包下新建MyCharacterFilter
/**
* @author 赵志伟
* @version 1.0
* 编写过滤器, 处理中文乱码
*/
@SuppressWarnings({"all"})
public class MyCharacterFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//这里加入对编码的处理
servletRequest.setCharacterEncoding("utf-8");
//放行请求, 这个规则和前面见过的java web过滤器一样
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
2.在web.xml
中配置.
注意: 不要乱写, 过滤器一般写在web.xml的最上面, 多个过滤器之间会形成过滤器链, 要注意顺序.
<!--配置处理中文乱码的过滤器
拦截所有请求, 处理编码.把过滤器配置到web.xml前面-->
<filter>
<filter-name>MyCharacterFilter</filter-name>
<filter-class>com.zzw.web.filter.MyCharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyCharacterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.完成测试.
Spring提供的过滤器处理中文
1.修改web.xml, 换成Spring提供的过滤器, 处理中文乱码.
<!--配置Spring提供的过滤器, 解决中文乱码问题-->
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.完成测试
处理json和HttpMessageConverter
处理JSON-@ResponseBody
●说明
项目开发中, 我们往往需要服务器返回的数据格式是按照json来返回的, 我们看一下SpringMVC是如何处理的,
●应用案例
1.引入处理json需要的jar包, 注意spring5.x 需要使用jackson-2.9.x.jar的包springmvc处理json需要jar
2.在web路径
下创建json.jsp
<head>
<title>json提交</title>
<%--引入jquery--%>
<%--编写jquery代码和请求--%>
</head>
<body>
<h1>请求一个json数据</h1>
<a href="?" id="getJson">点击获取json数据</a>
</body>
3.在com.zzw.web.json.entity包
下新建 Dog.java
public class Dog {
private String name;
private String address;
//全参构造器, 无参构造器, setter,getter,toString方法
}
4.在json包
下新建JsonHandler.java
@Controller
public class JsonHandler {
/**
* 解读
* 1.目标方法 @ResponseBody, 表示返回的数据是json格式
* 2.springmvc底层根据目标方法@ResponseBody, 返回指定格式,
* 3.底层原理我们在前面自定义@ResponseBody讲过, 这里原生的springmvc使用转换器
* 4.HttpMessageConverter [一会我们debug]
* @return
*/
@RequestMapping(value = "/json/dog")
@ResponseBody
public Dog getJson() {
//返回对象
//springmvc会根据你的设置, 转成json格式数据返回
Dog dog = new Dog();
dog.setName("大黄");
dog.setAddress("蜡笔小新");
return dog;
}
}
5.回填json.jsp
的action
<html>
<head>
<title>json提交</title>
<%--引入jquery--%>
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<%--编写jquery代码和请求--%>
<script type="text/javascript">
$(function () {
//给id="getJson"绑定一个点击事件
$("#getJson").click(function () {
console.log("ok");//测试一下
let url = this.href;//this是dom对象
let args = {"time": new Date};//这老师要发送数据, 为了防止页面缓存
$.post(
url,
args,
function(data) {//data就是后台返回的数据, 是json格式
console.log("data=", data);
console.log("name=", data.name);
console.log("address=", data.address);
},
"json"
)
return false;//这里我们返回false, 就不使用href默认机制
});
})
</script>
</head>
<body>
<h1>请求一个json数据</h1>
<%--处理
1.当用户点击超链接时, 我们发出一个ajax请求
2.接收到请求后, 我们查看这个数据
3.使用我们前面见过的jquery发出ajax请求的知识
--%>
<a href="<%=request.getContextPath()%>/json/dog" id="getJson">点击获取json数据</a>
</body>
</html>
6.完成测试(浏览器)
7.用postman完成测试
处理JSON-@RequestBody
●应用案例
-前面我们是通过表单, 或者 url请求携带 参数=参数值 把数据提交给目标方法
1)给大家举例客户端发送 json字符串数据
2)使用SpringMVC的**@RequestBody** 将客户端提交的json数据, 封装成JavaBean对象
3)再把这个javabean以json而对象形式返回
4)完成效果示意图
1.修改json.jsp, 增加发送json数据代码
<html>
<head>
<title>json提交</title>
<%--引入jquery--%>
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<%--编写jquery代码和请求--%>
<script type="text/javascript">
$(function () {
//....
//绑定按钮点击事件, 提交json数据
//springmvc 可以在后台将json转成对象
$("button[name='butt1']").click(function () {
//todo 具体的业务代码以后再写
})
})
</script>
</head>
<body>
<%--.....--%>
<h1>发出一个json数据</h1>
u:<input id="username" type="text"/><br/>
a:<input id="age" type="text"/><br/>
<button name="butt1">添加用户</button>
</body>
</html>
2.在com.zzw.web.json.entity包
下新建 User.java
public class User {
private String userName;
private Integer age;
//全参构造器, 无参构造器, setter,getter,toString方法
}
3.修改JsonHandler.java, 增加处理json代码. 注意: 老韩用的是 @PostMapping, 等价: @RequestMapping(method = RequestMethod.POST)
@Controller
public class JsonHandler {
//...
@RequestMapping(value = "/save2")
@ResponseBody
public User save2(User user) {
//将前台传过来的数据, 以json的格式返回给浏览器
System.out.println("user=" + user);
return user;
}
}
4.回填json.jsp
//绑定按钮点击事件, 提交json数据
//springmvc 可以在后台将json转成对象
$("button[name='butt1']").click(function () {
//目标:将userName 和 age 封装成json字符串
let url = "/springmvc/save2";
let userName = $("#userName").val();
let age = $("#age").val();
//将json对象转成json字符串
let args = JSON.stringify({"userName": userName, "age": age});
$.ajax({
url: url,
type: "POST",
data: args,
success(data) {
console.log("返回的data=", data);
},
//下面这个contentType参数, 是指定发送数据时的编码和格式
//只有$.ajax才有, $.post没有
contentType: "application/json;charset=utf-8"
})
})
5.测试. 数据为空
后台. 数据为空
6.加上 @RequestBody注解
/**
* 老师解读
* 1. @RequestBody User user 在形参指定了 @RequestBody
* 2. springmvc就会将提交的json字符串数据填充给指定Javabean
* @param user
* @return
*/
@RequestMapping(value = "/save2")
@ResponseBody
public User save2(@RequestBody User user) {
//将前台传过来的数据, 以json的格式返回给浏览器
System.out.println("user=" + user);
return user;
}
7.测试
后台
8.postman测试
postman提交json格式的数据
处理JSON-注意事项和细节
1.目标方法正常返回JSON需要的数据, 可以是一个对象, 也可以是一个集合
2.前面我们讲的是返回一个Dog对象->转成Json数据格式返回
●应用实例
JsonHandler.java
添加如下方法
//编写方法, 以json格式返回多个Dog
@RequestMapping(value = "/json/dogs")
@ResponseBody
public List<Dog> getJsons() {
List<Dog> dogs = new ArrayList<>();
dogs.add(new Dog("大黄", "蜡笔小新之家"));
dogs.add(new Dog("大黄2", "蜡笔小新之家2"));
dogs.add(new Dog("大黄3", "蜡笔小新之家3"));
return dogs;
}
postman测试
返回结果
3.@ResponseBody 可以直接写在controller上, 这样对所有方法都生效
●应用实例
完成测试
后台数据
postman测试
4.@ResponseBody + @Controller 可以直接写成 @RestController, 我们看一下源码
测试
HttpMessageConverter<T>
●基本说明
SpringMVC处理**JSON-底层实现是依靠HttpMessageConverter<T>**来进行转换的
●工作机制简图
●处理JSON-底层实现(HttpMessageConverter<T>)
1.使用 HttpMessageConverter<T> 将请求信息转化并绑定到处理方法的入参中, 或将响应结果转为对应类型的相应信息, Spring 提供了两种途径:
√ 使用 @RequestBody / @ResponseBody 对目标方法进行标注
√ 使用 @HttpEntity<T> / ResponseEntity<T> 作为目标方法的入参或返回值
2.当控制器处理方法使用到 @RequestBody / @ResponseBody 或 HttpEntity<T> / ResponseEntity<T> 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错
Debug 源码-梳理一下
一. 将请求信息转化并绑定到处理方法的入参中
二. 将响应结果转为对应类型的相应信息
文件下载-ResponseEntity<T>
●说明
在SpringMVC中, 通过返回 ResponseEntity<T> 的类型, 可以实现文件下载的功能
●案例演示
准备工作: 在web路径/img
下准备一个要下载的文件, 比如图片: 1.jpg
1.修改json.jsp
<h1>下载文件的测试</h1>
<a href="?">点击下载文件</a>
2.修改JsonHandler.java, 增加方法
//响应银狐下载文件的请求
@RequestMapping(value = "/downFile")
public ResponseEntity<byte[]> downFile(HttpSession session) throws Exception {
//1.先获取到下载文件的inputStream
InputStream inputStream = session.getServletContext().getResourceAsStream("/img/1.jpg");
//2.开辟一个存放文件的字节数组, 这里我们使用byte[] 是可以支持二进制数据(图片, 视频, 音频, doc文档)
byte[] bytes = new byte[inputStream.available()];
//3.将下载文件的数据, 读入到byte[]
inputStream.read(bytes);
/*
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {
this(body, headers, (Object) status);
}
*/
//4.创建返回的HttpStatus
HttpStatus status = HttpStatus.OK;
//5.创建 headers
HttpHeaders headers = new HttpHeaders();
//指定返回的数据, 客户端应当以附件形式处理
headers.add("Content-Disposition", "attachment;filename=1.jpg");
//构建一个ResponseEntity 对象
ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, status);
//如果出现找不到文件, 解决方法 rebuild project
return responseEntity;
}
文件下载响应头的设置
content-type 指示响应内容的格式
content-disposition 指示如何处理响应内容
一般有两种方式:
inline: 直接在页面显示
attchment: 以附件形式下载
3.回填json.jsp
<a href="<%=request.getContextPath()%>/downFile">点击下载文件</a>
4.完成测试
页面方式
postman测试, 返回二进制数据, 因为postman没有对数据进行解析
作业布置
1.把我们前面学过的数据格式化, 验证以及国际化, Json处理, 文件下载, 相关代码和案例, 自己写一遍. 一定要写一遍, 否则没有印象, 理解不会渗入
2.把Debug过的HttpMessageConverter源码, 自己再走一下, 加深理解(不用每条语句, 都debug, 找流程…)
3.DataBinder工作机制-将示意图画出
4,Debug一下validate得到验证errors信息, 加深理解(不用每一条语句都debug, 找流程)
SpringMVC文件上传
●基本介绍
1.Spring MVC 为文件上传提供了直接的支持, 这种支持是通过即插即用的 MultipartResolver 实现的. Spring 用 Jakata Commons FileUpload 技术实现了一个 MultipartResolver 实现类: CommonsMultipartResolver
2.SpringMVC 上下文中默认没有装配 Multipartresolver, 因此默认情况下不能处理文件的上传工作, 如果想使用 Spring 的文件上传功能, 需现在上下文中配置 MultipartResolver
●需求分析 / 图解
●应用实例-代码实现
1.引入springmvc文件上传需要的jar包 spingmvc上传文件需要的jar
2.在web路径
下创建fileUpload.jsp
<body>
<h1>文件上传的演示</h1>
<form action="?" method="post" enctype="multipart/form-data">
文件介绍:<input type="text" name="introduce"/><br/>
选择文件:<input type="file" name="file"/><br/>
<input type="submit" value="上传文件"/>
</form>
</body>
3.配置文件过滤器, 在web.xml
中, 使用Spring提供的, 前面已经配置过了 传送
4.配置文件上传解析器, 在springDispatcherServlet-servlet.xml, 简单看一下CommonsMultipartResolver
源码
<!--配置文件上传需要的bean-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
5.在com.zzw.web.fileupload
下新建FileUploadHandler.java
* @author 赵志伟
* @version 1.0
* 处理文件上传的handler
*/
@SuppressWarnings({"all"})
@Controller
public class FileUploadHandler {
//编写方法, 处理文件上传的请求
@RequestMapping(value = "/fileUpload")
public String fileUpload(@RequestParam(value = "file") MultipartFile file,
HttpServletRequest request, String introduce) throws IOException {
//接收到提交的文件名
String originalFilename = file.getOriginalFilename();
System.out.println("你上传的文件名=" + originalFilename);
System.out.println("文件的介绍=" + introduce);
//得到要把上传的文件保存到哪个路径[全路径: 包括文件名]
String fileFullPath =
request.getServletContext().getRealPath("/img/" + originalFilename);
//创建文件
File saveToFile = new File(fileFullPath);
//将上传的文件, 转存到saveToFile
file.transferTo(saveToFile);
return "success";
}
}
6.回填``fileUpload.jsp的
action`
<form action="<%=request.getServletContext()%>/fileUpload" method="post" enctype="multipart/form-data">
7.完成测试[页面方式], 看文件是否成功上传 http://localhost:8088/springmvc/fileUpload.jsp
简单地debug一下transferTo()
8.完成测试[postman方式]
自定义拦截器
1.什么是拦截器
●说明
1.Spring MVC也可以使用拦截器对请求进行拦截处理, 用户可以自定义拦截器来实现特定的功能.
2.自定义的拦截器必须实现HandlerInterceptor接口
●自定义拦截器的三个方法
1.preHandle(): 这个方法在业务处理器处理请求之前被调用, 在该方法中对用户请求 request 进行处理.
2.postHandler(): 这个方法在目标方法处理完请求后执行
3.afterCompletion(): 这个方法在完全处理完请求后被调用, 可以在该方法中进行一些资源清理的操作.
2.自定义拦截器执行流程分析图
●自定义拦截器执行流程说明
1.如果 preHandle 方法, 返回 false, 则不再执行目标方法, 可以在此指定返回页面
2.postHandle 在目标方法被执行后执行, 可以在方法中访问到目标方法返回的 ModelAndView 对象
3.若 preHandle 返回 true, 则 afterCompletion 方法, 在渲染视图之后被执行
4.若 preHandle 返回 false, 则 afterCompletion 方法不会被调用
5.在配置拦截器时, 可以指定该拦截器对哪些请求生效, 哪些请求不生效
3.自定义拦截器应用实例
●应用实例需求
完成一个自定义拦截器, 学习一下如何配置拦截器和拦截器的运行流程
●应用实例-代码实现
1.com.zzw.web.interceptor包
下新建MyInterceptor01.java
@Component
public class MyInterceptor01 implements HandlerInterceptor {
/**
* 解读
* 1. preHandle() 在目标方法执行前被执行
* 2. 如果preHandle() 返回false, 不再执行目标方法
* 3. 该方法可以获取到request, response, handler
* 4. 这里根据业务, 可以进行拦截, 并指定跳转到哪个页面
*
* @param request current HTTP request
* @param response current HTTP response
* @param handler chosen handler to execute, for type and/or instance evaluation
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("--MyInterceptor01-- preHandle() 被执行...");
return true;
}
/**
* 解读
* 1. 在目标方法执行后, 会执行postHandle
* 2. 该方法可以获取到 目标方法, 返回的ModelAndView
*
* @param request current HTTP request
* @param response current HTTP response
* @param handler the handler (or {@link HandlerMethod}) that started asynchronous
* execution, for type and/or instance examination
* @param modelAndView the {@code ModelAndView} that the handler returned
* (can also be {@code null})
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("--MyInterceptor01-- postHandle()被执行...");
}
/**
* 解读
* 1. afterCompletion() 在视图渲染后被执行, 这里可以进行资源清理工作
* 2.
*
* @param request current HTTP request
* @param response current HTTP response
* @param handler the handler (or {@link HandlerMethod}) that started asynchronous
* execution, for type and/or instance examination
* @param ex any exception thrown on handler execution, if any; this does not
* include exceptions that have been handled through an exception resolver
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("--MyInterceptor01-- afterCompletion()被执行...");
}
}
2.在springDispatcherServlet-servlet.xml
配置拦截器
注意: 拦截器是由spring
管理的 ; 过滤器是由web.xml
管理的
<!--配置自定义拦截器-spring配置文件-->
<mvc:interceptors>
<!--
解读
1. 第一种配置方式
2. 使用ref 引用到对应的拦截器myInterceptor01
3. 这种方式, 会拦截所有的目标方法
-->
<ref bean="myInterceptor01"/>
</mvc:interceptors>
<!--加入两个常规配置-->
<!--支持SpringMVC的高级功能, 比如JSR303校验, 映射动态请求-->
<mvc:annotation-driven></mvc:annotation-driven>
<!--将springmvc不能处理的请求, 交给tomcat处理, 比如css, js-->
<mvc:default-servlet-handler/>
3.在com.zzw.ewb.interceptor包
下新建FurnHandler.java
@Controller
public class FurnHandler {
@RequestMapping(value = "/hi")
public String hi() {
System.out.println("--FurnHandler-- hi()...");
return "success";
}
@RequestMapping(value = "/hello")
public String hello() {
System.out.println("--FurnHandler-- hello()...");
return "success";
}
}
4.web路径
下创建interceptor.jsp
<head>
<title>测试自定义拦截器</title>
</head>
<body>
<h1>测试自定义拦截器</h1>
<a href="<%=request.getContextPath()%>/hi">测试自定义拦截器-hi</a><br/><br/>
<a href="<%=request.getContextPath()%>>/hello">测试自定义拦截器-hello</a>
</body>
5.测试
浏览器测试 http://localhost:8088/springmvc/interceptor.jsp
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hello()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
postman测试
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hello()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
●注意事项和细节
1.默认配置是将所有的目标方法都进行拦截, 也可以指定拦截目标方法, 比如只拦截hi
<!--配置自定义拦截器-spring配置文件-->
<mvc:interceptors>
<!--
解读
1. 第二种配置方式
2. mvc:mapping path="/hi" 指定要拦截的路径
3. ref bean="myInterceptor01" 指定对哪个拦截器进行配置
-->
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
2.mvc:mapping 支持通配符, 同时指定不对哪些目标方法进行拦截
<!--配置自定义拦截器-spring配置文件-->
<mvc:interceptors>
<!--解读
1. 第三种配置方式
2. mvc:mapping path="/h*" 通配符方式 表示拦截 /h 打头的路径
3. mvc:exclude-mapping path="/hello" /hello不拦截
4. ref bean="myInterceptor01" 指定对哪个拦截器配置
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
FurnHandler
添加方法
@RequestMapping(value = "/ok")
public String ok() {
System.out.println("--FurnHandler-- ok()...");
return "success";
}
interceptor.jsp
添加标签
<a href="<%=request.getContextPath()%>/ok">测试自定义拦截器-ok</a>
3.拦截器需要配置才生效, 不配置是不生效的.
4.如果preHandler() 方法返回了false, 就不会执行目标方法(前提是你的目标方法被拦截了), 程序员可以在这里根据业务需要指定跳转页面.
●Debug执行流程
1.prehandle()
2.目标方法
3.postHandle()
视图解析
一直点下一步
4.render()
5.afterCompletion()
解释一下model数据怎么来的? 用 postman 再走一圈
get请求
post请求
断点打到 preHandle
目标方法
postHandle
render
afterCompletion
4.多个拦截器
●多个拦截器执行流程示意图
●应用实例1
1.代码实现
1.com.zzw.web.interceptor.MyInterceptor02
@Component
public class MyInterceptor02 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("--MyInterceptor02-- preHandle() 被执行...");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("--MyInterceptor02-- postHandle()被执行...");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
System.out.println("--MyInterceptor02-- afterCompletion()被执行...");
}
}
2.配置springDispathcerServlet-servlet.xml
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
<!--解读
1.配置的第二个拦截器
2.多个拦截器在执行时, 是按照顺序执行的
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<ref bean="myInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
3.测试
–MyInterceptor01-- preHandle() 被执行…
–MyInterceptor02-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor02-- postHandle()被执行…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor02-- afterCompletion()被执行…
–MyInterceptor01-- afterCompletion()被执行…
2.注意事项和细节
1.如果第1个拦截器的preHandle()返回false, 后面都不执行
2.如果第2个拦截器的preHandle()返回false, 就直接执行第1个拦截器的afterCompletion() 方法, 如果拦截器更多, 规则类似.
3.说明: 前面说的规则, 目标方法被拦截是前提
●应用实例2
1.需求: 如果用户提交的数据有禁用词(比如 病毒). 则, 在第1个拦截器就返回, 不执行目标方法, 功能效果如图
2.web路径/WEB-INF/pages/warning.jsp
<head>
<title>警告</title>
</head>
<body>
<h1>不要乱讲话</h1>
</body>
3.修改MyInterceptor01
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("--MyInterceptor01-- preHandle() 被执行...");
//获取到用户提交的关键字
String keyword = request.getParameter("keyword");
if ("病毒".equals(keyword)) {
//请求转发到warning.jsp
//这里是原生的请求转发, 不是springmvc里的
request.getRequestDispatcher("/WEB-INF/pages/warning.jsp").forward(request, response);
return false;
}
System.out.println("得到keyword=" + keyword);
return true;
}
3.postman测试
–MyInterceptor01-- preHandle() 被执行…
得到keyword=赵志伟
–MyInterceptor02-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor02-- postHandle()被执行…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor02-- afterCompletion()被执行…
–MyInterceptor01-- afterCompletion()被执行…
再次测试
–MyInterceptor01-- preHandle() 被执行…
5.作业布置
1.把前面我们学过的SpringMVC文件上传, 自定义拦截器和相关代码和案例, 自己写一遍. 一定要自己写一遍, 否则没有印象, 理解不会深入
2.简述SpringMVC自定义拦截器工作流程, 并画出示意图
3.debug自定义拦截器源码, 加深理解(不用每一条语句都debug), 重点是梳理流程.
异常处理
●基本介绍
1.Spring MVC 通过 HandlerExceptionResolver 处理程序的异常, 包括 Handler 映射, 数据绑定以及目标方法执行时发生的异常.
2.主要处理 Handler 中用 @ExceptionHandler 注解定义的方法
3.ExceptionHandlerMethodResolver 在 Handler 内部若找不到 @ExceptionHandler 注解的话, 会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器.
局部异常
●应用实例需求
演示局部异常处理机制
-如果不处理异常, 非常的不友好
1.在com.zzw.web.exception
新建MyExceptionHandler
@Controller
public class MyExceptionHandler {
//编写方法, 模拟异常, 算术异常
@RequestMapping(value = "/testException01")
public String test01(Integer num) {
int i = 9 / num;
return "success";
}
}
2.web路径
新建exception.jsp
<head>
<title>异常信息</title>
</head>
<body>
<h1>测试异常</h1>
<a href="<%=request.getContextPath()%>/testException01?num=0">点击测试局部异常</a><br/><br/>
</body>
3.测试, 抛错
4.MyExceptionHandler
新增 localException ()
, 处理局部异常
/**
* 解读
* 1.localException 方法处理局部异常
* 2.这里我们处理ArithmeticException.class, NullPointerException.class
* 3.Exception ex: 生成的异常对象, 会传递给ex, 通过ex可以得到相关的信息
* , 这里程序员可以加入自己的业务逻辑
* @return
*/
@ExceptionHandler({ArithmeticException.class, NullPointerException.class})
public String localException(Exception ex, HttpServletRequest request) {
System.out.println("局部异常信息是=" + ex.getMessage());
//如何将异常的信息都带到下一个页面
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
5.新增web路径/excetion_mes.jsp
<head>
<title>异常信息提示</title>
</head>
<body>
<h1>朋友, 你程序出问题了!</h1>
异常信息 - ${requestScope.reason}
</body>
6.测试
●Debug处理流程
打断点
测试
全局异常
●应用实例需求
演示全局异常处理机制, ExceptionHandlerMethodResolver 内部若找不到 @ExceptionHandler 注解的话, 会找 @ControllerAdvice 类的 @ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器
●代码实现
1.新建com.zzw.web.exception.MyGlobalException
/**
* 如果类上标注了@ControllerAdvice, 就是一个全局异常处理类
*/
@ControllerAdvice
public class MyGlobalException {
/**
* 解读
* 1.全局异常就不管是哪个Handler抛出的异常, 即都可以捕获. 格式是 @ExceptionHandler({异常类型})
* 2.这里我们处理的全局异常是 NumberFormatException.class, ClassCastException.class
* 3.Exception ex, 接收抛出的异常对象
* @return
*/
@ExceptionHandler({NumberFormatException.class, ClassCastException.class})
public String globalException(Exception ex, HttpServletRequest request) {
System.out.println("全局异常处理=" + ex.getMessage());
//如何将异常信息带到下一个页面
request.setAttribute("reason", ex.getMessage());
return "exception_mes";
}
}
2.MyExceptionHandler
新增 global()
方法
@RequestMapping(value = "/testGlobalException")
public String global() {
//解读
//1.这里我们模拟了一个异常 NumberFormatException
//2.该异常没有在局部异常处理, 按照异常处理机制, 就会交给全局异常处理类处理
int num = Integer.parseInt("hello");
return "exception_mes";
}
3.exception.jsp
新增代码
<a href="<%=request.getContextPath()%>/testGlobalException">点击测试全局异常</a><br/><br/>
4.测试
●Debug处理流程
点击下一步
继续往下走
继续往下走
继续往下走
●异常处理时: 局部异常 优先级高于 全局异常
假如我们把NumberFormatException异常也放进了局部异常处理, 那么在调用 global() 的时候, 优先去找本类的局部异常处理.
自定义异常
●应用实例需求
通过 @ResponseStatus 注解, 可以自定义异常的说明
●应用实例-代码实现
1.新建com.zzw.web.exception.AgeException
@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException {
}
2.修改MyExceptionHandler, 增加方法
@RequestMapping(value = "/testException02")
public String test02() {
throw new AgeException("年龄必须在1-120之间~~~");
}
3.修改exception.jsp
, 增加超链接
<a href="<%=request.getContextPath()%>/testException02">点击测试自定义异常</a>
4.测试
5.在自定义异常中加两个构造器
@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException {
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
被全局异常捕获
●Debug全局异常
继续往下走
找到了globalException()方法
SimpleMappingExceptionresovler
●基本说明
1.如果希望对所有异常进行统一处理, 可以使用 SimpleMappingExceptionResolver
2.它将异常类名映射为视图名, 即发生异常时使用对应的视图报告异常
3.需要在ioc容器中配置
●应用实例 - 需求
对数组越界异常进行统一处理, 使用SimpleMappingExceptionResolver
●应用实例 - 代码实现
1.修改MyExceptionHandler.java
, 增加方法test03
@RequestMapping(value = "/testException03")
public String test03() {
int[] arr = new int[]{1, 2, 3, 4, 5};
//抛出一个数据越界的异常 ArrayOutOfBoundsException
System.out.println(arr[90]);
return "success";
}
2.配置springDispatcherServlet-servlet.xml
<!--配置统一处理异常-->
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
</props>
</property>
</bean>
3.web路径/WEB-INF/pages/arrEx.jsp
<head>
<title>数组越界异常</title>
</head>
<body>
异常信息 - 数据越界
</body>
4.修改exception.jsp
, 增加代码
<a href="<%=request.getContextPath()%>/testException03">点击测试统一异常</a><br/><br/>
5,测试
●对未知异常进行统一处理
⭐应用实例 - 需求
对未知异常进行统一处理, 使用SimpleMappingExceptionResolver
⭐应用实例 - 代码实现
1.修改myExceptionHandler.java, 增加方法test04()
//如果发生了没有归类的异常, 可以给出统一提示页面
@RequestMapping(value = "/testException04")
public String test04() {
String str = "hello";
//这里会抛出 StringIndexOutOfBoundsException
char c = str.charAt(10);
return "success";
}
2.继续配置
<!--配置统一处理异常-->
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
<prop key="java.lang.Exception">allEx</prop>
</props>
</property>
</bean>
3.web路径/WEB-INF/pages/allEx.jsp
<head>
<title>未知异常信息</title>
</head>
<body>
<h1>系统发生了异常, 联系网站管理员~</h1>
</body>
4.修改exception.jsp
, 增加代码
<a href="<%=request.getContextPath()%>/testException04">点击测试未知异常</a><br/><br/>
5.测试
●对未知异常进行统一处理
局部异常 > 全局异常 > SimpleMappingExceptionResolver > tomcat默认机制
1.测试
局部异常 | 全局异常 | SimpleMappingExceptionResolver |
---|---|---|
ArrayIndexOutOfBoundsException.class | ArrayIndexOutOfBoundsException.class | <prop key=“java.lang.ArrayIndexOutOfBoundsException”>arrEx</prop> |
局部异常 | 全局异常 | SimpleMappingExceptionResolver |
---|---|---|
ArrayIndexOutOfBoundsException.class | <prop key=“java.lang.ArrayIndexOutOfBoundsException”>arrEx</prop> |
局部异常 | 全局异常 | SimpleMappingExceptionResolver |
---|---|---|
<prop key=“java.lang.ArrayIndexOutOfBoundsException”>arrEx</prop> |
局部异常 | 全局异常 | SimpleMappingExceptionResolver |
---|---|---|
<prop key=“ArrayIndexOutOfBoundsException”>arrEx</prop> |
SpringMVC执行流程 - 源码分析
执行流程图
实验设计
1.com.zzw.web.debug.HelloHandler
@Controller
public class HelloHandler {
//编写方法, 响应请求, 返回ModelAndView
@RequestMapping(value = "/debug/springmvc")
public ModelAndView hello(HttpServletRequest request, HttpServletResponse response) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("ok");//对应到 /WEB-INF/pages/ok.jsp
modelAndView.addObject("name", "老韩");//在model中放入了数据
return modelAndView;
}
}
2.web路径/WEB-INF/ok.jsp
<head>
<title>ok页面</title>
</head>
<body>
<h1>进入到ok页面</h1>
</body>
3.测试
调整ok.jsp
, 再次测试
<body>
<h1>进入到ok页面</h1>
name - ${requestScope.name}
</body>
Debug第1部分
地址栏输入: http://localhost:8088/springmvc/debug/springmvc
❀❀Spring容器结构剖析❀❀
这里就体现了SpringMVC前端控制器和容器的关系
分发请求
getHandler()
拿到目标方法
根据Handler拿到适配器, 不同的适配器对应不同的handler
因为是浏览器地址栏请求, 所以是GET请求
反射调用handler
进入
进入
进入
直接放行, 在目标方法打个断点
对modelAndView估值
不停地往下走
mav就是我们目标方法的ModelAndView
继续走, 回到DisPatcherServlet
往下走
进入
进入
前端控制器调用某个视图解析器返回
进入
下一步
进入, 拿到RequestDispatcher
请求转发
作业布置
1.把前面我们学过的SpringMVC异常处理相关代码和案例, 自己写一遍. - 一定要自己写一遍, 否则没有印象, 理解不会深入
2.简述SpringMVC执行流程, 并画出示意图
3.把我们Debug过的SpringMVC执行流程代码, 自己也走一下, 加深理解(不用每一条语句都debug, 主要是梳理流程)