目录
一.数据格式化
1.基本介绍
2.基本数据类型和字符串自动转换
2.1总结:
3.特殊数据类型和字符串间的转换
二.验证以及国际化
1.概述
1.1 概述
2.JSR 303 验证框架
1.1Hibernate Validator 扩展注解
1.2实例
1.3自定义验证错误信息
1.4细节说明和注意事项
2.注解的结合使用
3.数据类型转换校验核心类-DataBinder
3.1DataBinder 工作机制-了解
4.取消某个属性的绑定
4.1 使用实例
三.中文乱码处理
1.自定义中文乱码过滤器
配置处理中文乱码的过滤器
2.Spring 提供的过滤器处理中文乱码
修改web,也不要在写过滤器
四.处理 json 和 HttpMessageConverter
1.处理 JSON-@ResponseBody
2.处理 JSON-@RequestBody
应用案例
2.2@RequestBody和@ResponseBody的区别
3.处理 JSON-注意事项和细节
4.HttpMessageConverter
4.1基本说明
4.2 工作机制简图
4.3处理 JSON-底层实现(HttpMessageConverter)
5.文件下载-ResponseEntity
5.1说明:
5.2代码
五.SpringMVC 文件上传
1. 基本介绍
六.自定义拦截器
1.什么是拦截器
1.1 说明
1.2. 自定义的拦截器必须实现 HandlerInterceptor 接口
2.自定义拦截器执行
3.自定义拦截器应用实例
3.1快速入门在 springDispatcherServlet-servlet.xml
3.2注意事项和细节
3.3执行过程解析
4.多个拦截器
4.1多个拦截器执行流程示意图编辑
4.2海绵hong的总结
4.3代码
4.4注意事项和细节
七.异常处理
1 异常处理-基本介绍
2.局部异常
2.1 应用实例
2.2代码演示
3.全局异常
异常处理时:局部异常 优先级高于 全局异常
4.自定义异常
5.统一处理异常信息--SimpleMappingExceptionResolver
5.1 基本说明
5.2 应用实例
5.3 对未知异常进行统一处理
5.4 异常处理的优先级梳理
一.数据格式化
1.基本介绍
说明
:
在我们提交数据
(
比如表单时
)SpringMVC
怎样对提交的数据进行转换和处理的
1.
基本数据类型可以和字符串之间自动完成转换,比如
:
Spring MVC
上下文中内建了很多转换器,可完成大多数
Java
类型的转换工作。
2.基本数据类型和字符串自动转换
2.1总结:
这个基本数据类型和字符串会进行自动转换,也没有啥好实例的,简单来说:就像我们使用年龄这个属性的时候,当我们在页面输入18,他就会自动转化为“18”,但是当我们在后台去获取的时候他又会是我们定义的Interage类型的18.这个就是基本数据类型和字符串的自动转
这些都是springmvc帮你去转化的
java.lang.Boolean
-> java.lang.String : org.springframework.core.convert.support.ObjectToStringConverter@f874ca
java.lang.Character -> java.lang.Number : CharacterToNumberFactory@f004c9
java.lang.Character -> java.lang.String : ObjectToStringConverter@68a961
java.lang.Enum -> java.lang.String : EnumToStringConverter@12f060a
韩顺平
Java
工程师
java.lang.Number -> java.lang.Character : NumberToCharacterConverter@1482ac5
java.lang.Number -> java.lang.Number : NumberToNumberConverterFactory@126c6f
java.lang.Number -> java.lang.String : ObjectToStringConverter@14888e8
java.lang.String -> java.lang.Boolean : StringToBooleanConverter@1ca6626
java.lang.String -> java.lang.Character : StringToCharacterConverter@1143800
java.lang.String -> java.lang.Enum : StringToEnumConverterFactory@1bba86e
java.lang.String -> java.lang.Number : StringToNumberConverterFactory@18d2c12
java.lang.String -> java.util.Locale : StringToLocaleConverter@3598e1
java.lang.String -> java.util.Properties : StringToPropertiesConverter@c90828
java.lang.String -> java.util.UUID : StringToUUIDConverter@a42f23
java.util.Locale -> java.lang.String : ObjectToStringConverter@c7e20a
java.util.Properties -> java.lang.String : PropertiesToStringConverter@367a7f
java.util.UUID -> java.lang.String : ObjectToStringConverter@112b07f ……
3.特殊数据类型和字符串间的转换
1.
特殊数据类型和字符串之间的转换使用注解
(
比如日期,规定格式的小数比如货币形式
等
)
2.
对于日期和货币可以使用
@DateTimeFormat
和
@NumberFormat
注解
.
把这两个注
解标记在字段上即可
@DateTimeFormat(pattern="yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern="###,###.##")
private float salary;
二.验证以及国际化
1.概述
1.1 概述
1.
对输入的数据
(
比如表单数据
)
,进行必要的验证,并给出相应的提示信息。
2.
对于验证表单数据,
springMVC
提供了很多实用的注解
,
这些注解由
JSR 303
验证框架提
供
.
2.JSR 303 验证框架
1. JSR 303
是
Java
为
Bean
数据合法性校验提供的标准框架,它已经包含在
JavaEE
中
2. JSR 303
通过在
Bean
属性上标注类似于
@NotNull
、
@Max
等标准的注解指定校验规则,
并通过标准的验证接口对
Bean
进行验证
3. JSR 303
提供的基本验证注解有
:
1.1Hibernate Validator 扩展注解
1. Hibernate Validator
和
Hibernate
没有关系,只是
JSR 303
实现的一个扩展
.
2. Hibernate Validator
是
JSR 303
的一个参考实现,除支持所有标准的校验注解外,它还支
持以下的扩展注解
:
3.
扩展注解有如下
1.2实例
记住需要jar包
@NotEmpty//必须非空
private String email;
//@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;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern = "###,###.##")
private Float salary;
/**
* 编写方法,处理添加妖怪
* 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 ======");
if (errors.hasErrors()) {//判断是否有错误
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println("error=" + error);
}
return "datavalid/monster_addUI";
}
return "datavalid/success";
}
1.3自定义验证错误信息
为何会需要自定义那,因为在我们前面如果输入错误后面的错误信息会是如下图这样:
所以我们需要自己设置一个自定义验证错误信息,也好让我们更加直观的看出来错误
1.5.1. 配置国际化文件 springDispatcherServlet-servlet.xml
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="99"></property>
</bean>
<!-- 配置国际化错误信息的资源处理 bean -->
<bean id="messageSource" class=
"org.springframework.context.support.ResourceBundleMessageSource">
<!-- 配置国际化文件名字
如果你这样配的话,表示 messageSource 回到 src/i18nXXX.properties 去读取错误
信息
-->
<property name="basename" value="i18n"></property>
</bean>
1.5.2创建国际化文件 D:\idea_java_projects\springmvc\src\i18n.properties
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a//姓名不能为空
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\
u0030\u4e4b\u95f4//生日格式不正确
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
1.5.3修改 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="birthday"/> <form:errors path="birthday"/> 要求
以"9999-11-11"的形式<br><br>
妖 怪 工 资 : <form:input path="salary"/> <form:errors path="salary"/> 要 求 以
"123,890.12"的形式<br><br>
电子邮件: <form:input path="email"/><br><br>
<input type="submit" value="添加妖怪"/>
</form:form>
1.4细节说明和注意事项
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=\u540D\u5B57\u4E0D\u80FD\u4E3A\u7A7A
√ typeMismatch.monster.age=\u7C7B\u578B\u4E0D\u5339\u914D
6. 注解
@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 {@code null}.
Accepts any type
.
3)
解读:如果是字符串验证空
,
建议使用
@NotEmpty
7. SpingMVC 验证时,会根据不同的验证错误
,
返回对应的信息
2.注解的结合使用
先看一个问题
●
问题提出
, age
没有
,
是空的,提交确是成功了
解决问题
1.
使用
@NotNull + @Range
组合使用解决
//email是string,使用@NotEmpty
@NotEmpty//必须非空
private String email;
//@Range(min = 1,max = 100)
//表示接收的age值,在 1-100之间
@NotNull(message = "age不能为空")
@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;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
3.数据类型转换校验核心类-DataBinder
3.1DataBinder 工作机制-了解
图例
Spring MVC
通过
反射机制对目标方法进行解析
,将请求消息绑定到处理方法的入参中。
数据绑定的核心部件是
DataBinder
,运行机制如下
4.取消某个属性的绑定
4.1 使用实例
●
说明
在默认情况下,表单提交的数据都会和
pojo
类型的
javabean
属性绑定,如果
程序员在开发中,希望取消某个属性的绑定,也就是说,
不希望接收到某个表单对应的属
性的值
,则可以通过
@InitBinder
注解取消绑定
.
1.
编写一个方法
,
使用
@InitBinder
标识的该方法,可以对
WebDataBinder
对象进行初始
化。
WebDataBinder
是
DataBinder
的子类,用于完成由表单字段到
JavaBean
属性的绑
定
2. @InitBinder
方法不能有返回值,它必须声明为
void
。
3. @InitBinder
方法的参数通常是是
WebDataBinder
●
案例
-
不希望接收怪物的名字属性
//取消绑定 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. 如果我们取消某个属性绑定,验证就没有意义了,应当把验证的注解去掉, name属性会使用默认值null
* //@NotEmpty
* private String name;
*
*/
//webDataBinder.setDisallowedFields("name");
}
* 解读
* 1. 方法上需要标注 @InitBinder springmvc底层会初始化 WebDataBinder
* 2. 调用 webDataBinder.setDisallowedFields("name") 表示取消指定属性的绑定
* 即:当表单提交字段为 name时, 就不在把接收到的name值,填充到model数据monster的name属性
* 3. 机制:springmvc 在底层通过反射调用目标方法时, 接收到http请求的参数和值,使用反射+注解技术
* 取消对指定属性的填充
* 4. setDisallowedFields支持可变参数,可以填写多个字段
* 5. 如果我们取消某个属性绑定,验证就没有意义了,应当把验证的注解去掉, name属性会使用默认值null
* //@NotEmpty
* private String name;本身就拿不到数据了,就没有必要去验证了
三.中文乱码处理
1.自定义中文乱码过滤器
●
说明
当表单提交数据为中文时,会出现乱码,我们来解决一下
/**
* @author 海绵hong
* @version 1.0
* 编写过滤器,处理中文乱码
*/
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() {
}
}
配置处理中文乱码的过滤器
<!--配置处理中文乱码的过滤器
拦截所有请求,处理编码, 提醒,把过滤器配置到web.xml前面
-->
<filter>
<filter-name>MyCharacterFilter</filter-name>
<filter-class>com.hong.web.filter.MyCharacterFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>MyCharacterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.Spring 提供的过滤器处理中文乱码
我们使用Spring 提供的过滤器处理中文乱码,那样的话我们就会发现我们之前所使用的自定义过滤器需要定义utf-8这个固定的编码格式,但是我们也不知道何时需要,那么我们还要在init()中读取内容,单手Spring 提供的过滤器处理中文乱码就可以很灵活。
修改web,也不要在写过滤器
<!--配置处理中文乱码的过滤器
拦截所有请求,处理编码, 提醒,把过滤器配置到web.xml前面
-->
<!-- <filter>-->
<!-- <filter-name>MyCharacterFilter</filter-name>-->
<!-- <filter-class>com.hong.web.filter.MyCharacterFilter</filter-class>-->
<!-- </filter>-->
<!-- <filter-mapping>-->
<!-- <filter-name>MyCharacterFilter</filter-name>-->
<!-- <url-pattern>/*</url-pattern>-->
<!-- </filter-mapping>-->
<!--配置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>
四.处理 json 和 HttpMessageConverter<T>
1.处理 JSON-@ResponseBody
●
说明
:
项目开发中,我们往往需要服务器返回的数据格式是按照
json
来返回的
,
我们看一下
SpringMVC
是如何处理的
,
功能示意图, 下面是老韩要完成的效果
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>json提交</title>
<!-- 引入jquery -->
<script type="text/javascript" src="script/jquery-3.6.0.min.js"></script>
<!-- 编写jquery代码和ajax请求 -->
<script type="text/javascript">
$(function () {
//给id="getJson"绑定点击事件
$("#getJson").click(function () {
//console.log("ok ....")
var url = this.href;
var args = {"time": new Date};//这是要发送数据,为了防止页面缓存
$.post(
url,
args,
function (data) {//data 就是返回的数据,是json格式=>如果是多个json数据,可以遍历
console.log("dataa= ", data);
console.log("dog.name=", data.name)
console.log("dog.addresss=", data.address)
},
"json"
);
return false;//这里我们返回false,就不使用href默认机制
})
//绑定按钮点击事件, 提交json数据
//springmvc 可以在在台將json轉成對象
$("button[name='butt1']").click(function () {
//目标:将userName 和 age 封装成json字符串,发送给目标方法
var url = "/springmvc/save2";
var userName = $("#userName").val();
var age = $("#age").val();
//将json对象转成json字符串
var args = JSON.stringify({"userName": userName, "age": age});
$.ajax({
url: url,
data: args,
type: "POST",
success: function (data) {
console.log("返回的data= ", data);
},
//下面这个contentType参数,是指定发送数据的编码和格式
contentType: "application/json;charset=utf-8"
})
})
})
</script>
</head>
<body>
<h1>请求一个json数据</h1>
<%--老师处理
1.当用户点击超链接时,我们发出一个ajax请求
2. 接收到请求后,我们查看这个数据
3. 使用前面讲过的jquery发出ajax请求知识
--%>
<a href="<%=request.getContextPath()%>/json/dog" id="getJson">点击获取json数据</a>
<h1>发出一个json数据</h1>
u:<input id="userName" type="text"><br/>
a:<input id="age" type="text"><br/>
<button name="butt1">添加用户</button>
<h1>下载文件的测试 </h1>
<a href="<%=request.getContextPath()%>/downFile">点击下载文件</a>
</body>
</html>
javabean--Dog
。。。。。
Handler
@Controller
public class JsonHandler {
/**
* 解读
* 1. 目标方法 @ResponseBody,表返回的数据是json格式
* 2. springmvc底层根据目标方法@ResponseBody, 返回指定格式, 根据的http请求来进行处理
* 3. 底层原理我们在前面自定义@ResponseBody讲过, 这里原生的springmvc使用转换器
* 4. HttpMessageConverter [一会debug]
*
* @return
*/
@RequestMapping(value = "/getJson")
//指定返回的数据格式 json ,靠这个@ResponseBody
@ResponseBody
public Dog getJson() {
//创建一只狗
Dog dog = new Dog();
dog.setName("大黄狗");
dog.setAddress("北京八达岭~");
return dog;
}
}
2.处理 JSON-@RequestBody
应用案例
-
前面给大家讲解的是通过表单
,
或者
url
请求携带 参数名
=
参数值 把数据提交给目
标方法
1)
给大家举例客户端发送
json
字符串数据
,
2)
使用
SpringMVC
的
@RequestBody
将客户端提交的
json
数据,封装成
JavaBean
对象
3)
再把这个
javabean
以
json
对象形式返回
2.2@RequestBody和@ResponseBody的区别
@RequestBody
用法:一般放在controller层的具体请求方法的入参中。比如:
@PostMapping("/url")
public urlBo getUrlByPhoneNumber(@RequestBody String json,HttpServetRequest request){
UrlBo ub=new Gson().fromJson(json,UrlBo.class);
....//其他处理逻辑
}
这里的@RequestBody用于读取Http请求的body部分数据——就是我们的请求数据。比如json或者xml。然后把数据绑定到 controller中方法的参数上,这里就是String json这个入参啦~。
使用时要注意能不能用@RequestBody把request请求的数据解析,并赋给我们定义的参数上是由请求头的Content-Type的值来决定的。
当我们用get/post方式提交请求后, Content-Type的值有以下几种,分别对应能不能解析数据如下:
1.application/x-www-form-urlencoded, 可选(即非必须,因为这种情况的数据@RequestParam, @ModelAttribute也可以处理,当然@RequestBody也能处理)
2.multipart/form-data, 不能处理(即使用@RequestBody不能处理这种格式的数据)
3.其他格式(其他格式包括application/json, application/xml等),这些格式的数据,必须使用@RequestBody来处理
说明:request的body部分的数据编码格式由header部分的Content-Type指定。
@ReponseBody
用法:放在controller层的方法上,将Controller的方法返回的对象,SpringMVC会根据返回值类型(Map、Llist)找到可以处理的消息转换器,并确定结果的MediaType。返回对象、map和List结果将由MappingJackson2HttpMessageConverter处理,并且MediaType为:application/json。后面写入到Response对象的body数据区。
使用时机:
当我们想让页面知道我们返回的数据不是按照html标签的页面来解析,而是其他某种格式的数据解析时(如json、xml等)使用。
例如:
@RequestMapping("/getList")
@ResponseBody
public Map<String,Object> getStudentList(HtppServletRequest request){
Map<String,Object> map=new HashMap<String,Object>();
Dto dto=getParamAsDto(request);
List li=studentAction.getList(dto.get("age"));
map.put("studentInfo",li);
}
3.处理 JSON-注意事项和细节
1.
目标方法正常返回
JSON
需要的数据
,
可以是一个对象
,
也可以是一个集合
[
举例
]
2.
前面我讲解的是返回一个
Dog
对象
->
转成
Json
数据格式返回
3.
@ResponseBody
可以直接写在
controller
上,这样对所有方法生效
4.
@ResponseBody + @Controller
可以直接写成
@RestController ,
4.HttpMessageConverter<T>
4.1基本说明
SpringMVC
处理
JSON-
底层实现是依靠
HttpMessageConverter<T>
来进行转换的
4.2 工作机制简图
4.3处理 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
将报错
5.文件下载-ResponseEntity<T>
5.1说明:
在
SpringMVC
中,通过返回
ResponseEntity<T>
的类型,可以实现文件下载的功能
5.2代码
<h1>下载文件的测试 </h1>
<a href="<%=request.getContextPath()%>/downFile">点击下载文件</a>
//响应用户下载文件的请求
@RequestMapping(value = "/downFile")
public ResponseEntity<byte[]> downFile(HttpSession session)
throws Exception {
//1. 先获取到下载文件的inputStream
InputStream resourceAsStream =
session.getServletContext().getResourceAsStream("/img/2.jpg");
//2. 开辟一个存放文件的byte数组, 这里使用byte[] 是可以支持二进制数据(图片,视频。)
byte[] bytes = new byte[resourceAsStream.available()];
//3. 将下载文件的数据,读入到byte[]
resourceAsStream.read(bytes);
//public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) {}
//4. 创建返回的HttpStatus
HttpStatus httpStatus = HttpStatus.OK;
//5. 创建 headers
HttpHeaders headers = new HttpHeaders();
//指定返回的数据,客户端应当以附件形式处理
headers.add("Content-Disposition", "attachment;filename=2.jpg");
// 文件下载响应头的设置
// content-type 指示响应内容的格式
// content-disposition 指示如何处理响应内容。
// 一般有两种方式:
// inline:直接在页面显示
// attchment:以附件形式下载
//构建一个ResponseEntity 对象1. 的http响应头headers 2. http响应状态 3. 下载的文件数据
ResponseEntity<byte[]> responseEntity =
new ResponseEntity<>(bytes, headers, httpStatus);
//如果出现找不到文件,解决方法 rebuild project -> 重启tomcat
return responseEntity;
}
五.SpringMVC 文件上传
1. 基本介绍
●
基本介绍
1. Spring MVC
为 文 件 上 传 提 供 了 直 接 的 支 持 , 这 种 支 持 是 通 过 即 插 即 用 的
MultipartResolver
实 现 的 。
Spring
用
Jakarta Commons FileUpload
技 术 实 现 了 一 个
MultipartResolver
实现类:
CommonsMultipartResovler
2. Spring MVC
上下文中默认没有装配
MultipartResovler
,因此默认情况下不能处理文件的
上传工作,如果想使用
Spring
的文件上传功能,需现在上下文中配置
MultipartResolver
/**
* @author 海绵hong
* @version 1.0
* 处理文件上传的handler
*/
@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=" + introduce);
//得到要把上传文件保存到哪个路径[全路径:包括文件名]
String fileFullPath =
request.getServletContext().getRealPath("/img/" + originalFilename);
//创建文件
File saveToFile = new File(fileFullPath);
//将上传的文件,转存到saveToFile
file.transferTo(saveToFile);
return "success";
}
六.自定义拦截器
1.什么是拦截器
1.1 说明
1. Spring MVC
也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定
的功能
.
1.2. 自定义的拦截器必须实现 HandlerInterceptor 接口
●
自定义拦截器的三个方法
1. preHandle()
:这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求
request
进行处理。
2. postHandle()
:这个方法在目标方法处理完请求后执行
3. afterCompletion()
:这个方法在完全处理完请求后被调用,可以在该方法中进行一些资源
清理的操作。
2.自定义拦截器执行
●
自定义拦截器执行流程说明
1.
如果
preHandle
方法 返回
false,
则不再执行目标方法
,
可以在此指定返回页面
2. postHandle
在目标方法被执行后执行
.
可以在方法中访问到目标方法返回的
ModelAndView
对象
3.
若
preHandle
返回
true,
则
afterCompletion
方法 在渲染视图之后被执行
.
4.
若
preHandle
返回
false,
则
afterCompletion
方法不会被调用
5.
在配置拦截器时,可以指定该拦截器对哪些请求生效,哪些请求不生效
3.自定义拦截器应用实例
3.1快速入门在 springDispatcherServlet-servlet.xml
●
应用实例需求
完成一个自定义拦截器,学习一下如何配置拦截器和拦截器的运行流程
●
应用实例
-
代码实现
配置自定义拦截器
<!--配置自定义拦截器-spring配置文件-->
<mvc:interceptors>
<!--
解读
1. 第一种配置方式
2. 使用ref 引用到对应的myInterceptor01
3. 这种方式,会拦截所有的目标方法
-->
<!--<ref bean="myInterceptor01"/>-->
<!--解读
1. 第二种配置方式
2. mvc:mapping path="/hi" 指定要拦截的路径
3. ref bean="myInterceptor01" 指定对哪个拦截器进行配置
-->
<!--<mvc:interceptor>-->
<!-- <mvc:mapping path="/hi"/>-->
<!-- <ref bean="myInterceptor01"/>-->
<!--</mvc:interceptor>-->
<!--解读
1. 第3种配置方式
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>
自定义过滤器
@Component
public class MyInterceptor01 implements HandlerInterceptor {
/**
* 解读
* 1. preHandle() 在目标方法执行前被执行
* 2. 如果preHandle() 返回false , 不再执行目标方法
* 3. 该方法可以获取到request, response, handler
* 4. 这里根据业务,可以进行拦截,并指定跳转到哪个页面
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@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
request.getRequestDispatcher("/WEB-INF/pages/warning.jsp")
.forward(request,response);
return false;
}
System.out.println("得到到keyword= "+ keyword);
return true;
}
/**
* 解读
* 1. 在目标方法执行后,会执行postHandle
* 2. 该方法可以获取到 目标方法,返回的ModelAndView对象
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("--MyInterceptor01--postHandle()--");
}
/**
* 解读
* 1. afterCompletion() 在视图渲染后被执行, 这里可以进行资源清理工作
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("--MyInterceptor01--afterCompletion()--");
}
}
Handler方法
@Controller
public class FurnHandler {
@RequestMapping(value = "/hi")
public String hi(User user) {
System.out.println("---FurnHandler--hi()---");
return "success";
}
@RequestMapping(value = "/hello")
public String hello() {
System.out.println("---FurnHandler--hello()---");
return "success";
}
@RequestMapping(value = "/ok")
public String ok() {
System.out.println("---FurnHandler--ok()---");
return "success";
}
}
3.2注意事项和细节
1
、默认配置是都所有的目标方法都进行拦截
,
也可以指定拦截目标方法
,
比如只是拦截 hi(用这个拦截器myInterceptor01)
<mvc:interceptor>
<mvc:mapping path="/hi"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
2
、
mvc:mapping
支持通配符
,
同时指定不对哪些目标方法进行拦截
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<mvc:exclude-mapping path="/hello"/>
<ref bean="myInterceptor01"/>
</mvc:interceptor>
3
、拦截器需要配置才生效,不配置是不生效的
.
4
、如果
preHandler()
方法返回了
false,
就不会执行目标方法
(
前提是你的目标方法被拦截
了
),
程序员可以在这里根据业务需要指定跳转页面
.
3.3执行过程解析
先去配置文件中寻找拦截的页面,然后进入到preHandle(),接着到目标方法,并在这个时间将对象填充到目标方法,在之后就是postHandle()方法,这个时候该方法里面的modelAndView中存储着之前的对象(换句话说就是在这个地方你可以去改变你的值),在之后就是执行资源清理工作的afterCompletion()登场了
4.多个拦截器
4.1多个拦截器执行流程示意图
4.2海绵hong的总结
根据配置顺序,先配置那个就先执行那个 。如果有有一个方法在执行的时候,有两个拦截器。
这个我就叫hong(),拦截器有a,b两个,a在前。
当有请求需要这个拦截器的时候,会先去执行a的preHandle()如果为true就继续执行b的preHandle(),当执行目标方法之后就会继续去执行b的post()方法,然后再就是a的post。然后就会继续渲染视图(因为这两个方法a,b已经把modelAndView进行了二次渲染,这个时候需要的话也可以进行二次修改)在之后就是执行b的after()最后才是a的after()。就是挑明说a配置顺序在前所以第一个就是它,但之后全部都是b先执行
4.3代码
再建立一个拦截器
@Component
public class MyInterceptor02 implements HandlerInterceptor {}
配置文件配置顺序(在第一个的后面接上的)
<!--解读
1.配置的第二个拦截器
2.多个拦截器在执行时,是顺序执行
-->
<mvc:interceptor>
<mvc:mapping path="/h*"/>
<ref bean="myInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
4.4注意事项和细节
1.
如果第
1
个拦截器的
preHandle()
返回
false ,
后面都不在执行
2.
如 果 第
2
个 拦 截 器 的
preHandle()
返 回
false ,
就 直 接 执 行 第
1
个 拦 截 器 的
afterCompletion()
方法
,
如果拦截器更多,规则类似
3.
说明
:
前面说的规则,都是目标方法被拦截的前提
七.异常处理
1 异常处理-基本介绍
基本介绍
1. Spring MVC
通过
HandlerExceptionResolver
处理程序的异常,包括
Handler
映射、数据
绑定以及目标方法执行时发生的异常。
2.
主要处理
Handler
中用
@ExceptionHandler
注解定义的方法。
3. ExceptionHandlerMethodResolver
内部若找不到
@ExceptionHandler
注解的话,会找
@ControllerAdvice
类的
@ExceptionHandler
注解方法
,
这样就相当于一个全局异常处理器
2.局部异常
2.1 应用实例
●
应用实例需求
演示局部异常处理机制
-
如果不处理异常
,
非常的不友好
2.2代码演示
基本步骤:
先执行页面请求,在页面中有handler之后,会去执行handler当我们的handler出现我们自定义的异常之后@ExceptionHandler(),这个框号中有的异常。我们就会去去执行这个注解下面的方法。
/**
* 解读
* 1. localException 方法处理局部异常
* 2. 这里处理ArithmeticException.class,NullPointerException.class异常
* 3. Exception ex: 生成的异常对象,会传递给ex, 通过ex可以得到相关的信息
* , 这里程序员可以加入自己的业务逻辑
* @return
*/
@ExceptionHandler({ArithmeticException.class,NullPointerException.class,NumberFormatException.class})
public String localException(Exception ex, HttpServletRequest request){
System.out.println("局部异常信息是-" + ex.getMessage());
//如何将异常的信息带到下一个页面.
request.setAttribute("reason", ex.getMessage());
return "exception";
}
<%-- Created by IntelliJ IDEA.
User: 海绵hong
Version: 1.0
Filename: exception
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>异常提示信息</title>
</head>
<body>
<h1>朋友,发生异常,信息如下 :</h1>
${reason }
</body>
</html>
3.全局异常
ExceptionHandlerMethodResolver
内 部 若 找 不 到
@ExceptionHandler
注解的话,会找
@ControllerAdvice
类的
@ExceptionHandler
注解方法
,
这样就相当于一个全局异常处理器
/**
* 如果类上标注了@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";
}
}
@RequestMapping(value = "/testGlobalException")
public String global(){
//解读
//1. 这里我们模拟了一个异常 NumberFormatException
//2. 该异常没有在局部异常处理,按照异常处理机制,就会交给全局异常处理类处理
int num = Integer.parseInt("hello");
return "success";
}
被@ControllerAdvice标注的类就是全局异常类。局部异常在handler中,但是全局异常在全局异常处理类
异常处理时:局部异常 优先级高于 全局异常
4.自定义异常
通过
@ResponseStatus
注解
,
可以自定义异常的说明
@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException {
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
@RequestMapping(value = "/testException02")
public String test02(){
throw new AgeException("年龄必须在1-120之间~~~");
}
当然当我们将自定义异常写入全局异常也是可以被接管的
@ExceptionHandler({NumberFormatException.class, ClassCastException.class, AgeException.class})
5.统一处理异常信息--SimpleMappingExceptionResolver
5.1 基本说明
●
基本说明
1.
如果希望对所有异常进行统一处理,可以使用
SimpleMappingExceptionResolver
2.
它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常
3.
需要在
ioc
容器中配置
4.当局部和全局异常都没有的时候我们就会使用统一处理异常
5.2 应用实例
●
应用实例
-
需求
对数组越界异常进行统一处理,使用
SimpleMappingExceptionResolver
@RequestMapping(value = "/testException03")
public String test03(){
int[] arr = new int[]{3,9,10,190};
//抛出一个数组越界的异常 ArrayIndexOutOfBoundsException
System.out.println(arr[90]);
return "success";
}
<bean>
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
</props>
</property>
</bean>
<!-- 加入两个常规配置 -->
<!-- 能支持 SpringMVC 高级功能,比如 JSR303 校验,映射动态请求 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 将 SpringMVC 不能处理的请求交给 Tomcat, 比如请求 css,js 等-->
<mvc:default-servlet-handler/>
5.3 对未知异常进行统一处理
对未知异常进行统一处理,使用
SimpleMappingExceptionResolver
//如果发生了没有归类的异常, 可以给出统一提示页面
@RequestMapping(value = "/testException04")
public String test04(){
String str = "hello";
//这里会抛出 StringIndexOutOfBoundsException
char c = str.charAt(10);
return "success";
}
<!-- 配置一个统一异常处理 -->
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">arrEx</prop>
<prop key="java.lang.Exception">otherEx</prop>
</props>
</property>
</bean>
Exception这个包括了所有异常,所有当前面三关都没有留着这个异常,那么这个就会发现
5.4 异常处理的优先级梳理
异常处理的优先级
局部异常
>
全局异常
>
SimpleMappingExceptionResolver > tomcat
默认机制