SpringMVC精简知识点

SpringMVC

  • 数据格式化
    • 基本数据类型和字符串自动转换
    • 特殊数据类型和字符串自动转换
  • 验证及国际化
    • 应用实例
    • 注意事项和使用细节
    • 注解的结合使用
    • 数据类型转换校验核心类-DatBinder
    • 取消某个属性的绑定
    • 中文乱码解决
    • 处理json和HttpMessageConverter
    • 作业布置
    • SpringMVC文件上传
    • 自定义拦截器
    • 异常处理
    • SpringMVC执行流程 - 源码分析
    • 作业布置

数据格式化

●基本介绍
说明: 在我们提交数据(比如表单时)SpringMVC怎么对提交的数据进行转换和处理的
1.基本数据类型可以和字符串之间自动完成转换, 比如:
Spring MVC上下文中内建了很多转换器, 可完成大多数Java类型的转换工作.

基本数据类型和字符串自动转换

切换回之前写的springmvc项目
SpringMVC精简知识点_第1张图片

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 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.
         *   这里需要给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">

SpringMVC精简知识点_第2张图片

9.测试. 浏览器: http://localhost:8080/springmvc/data_valid.jsp
1)如果age输入的是 数字, 则通过, 说明SpringMVC可以将提交的字符串 数字, 比如"28", 转成 Integer/int
2)如果不是数字, 则给出400的页面

10.Postman测试
SpringMVC精简知识点_第3张图片

特殊数据类型和字符串自动转换

1.特殊数据类型和字符串之间的转换使用注解(比如日期, 规定格式的小数比如货币形式等)
2.对于日期和货币可以使用 @DataTimeFormat@NumberFormat 注解, 把这两个注解标记在字段上即可


3.修改 Monster.java, 增加 birthdaysalary 字段

@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NumberFormat(pattern = "###,###.##")
private Float salary;

//全参构造器, setter,getter,toString方法

4.修改monster_addUI.jsp, 增加 birthdaysalary 字段

妖怪生日: <form:input path="birthday"/>要求以"9999-11-11"的形式<br/><br/>
妖怪薪水: <form:input path="salary"/>要求以"123,890.12"的形式<br/><br/>

5.测试
1)如果 birthdaysalary 是按照指定格式输入, 则通过, 说明SpringMVC可以按注解指定格式转换
2)如果没有按照注解指定格式, 则给出400的页面

SpringMVC精简知识点_第4张图片
妖怪薪水只要能转成数字就行, 例如123456.12, 456.12, 0.123456, 但是12x.11就不行.

妖怪生日格式如2000-10-15就可以, 但是200010-15就不可以.

6.Postman测试
SpringMVC精简知识点_第5张图片

验证及国际化

●概述
1.对输入的数据(比如表单数据), 进行必要的验证, 并给出相应的提示信息.
2.对于验证表单数据, springMVC提供了很多使用的注解, 这些注解由JSR 303 验证框架提供.

JSR 303 验证框架
1.JSR 303JavaBean 数据合法性校验提供的标准框架, 它已经包含在 JavaEE 中.
2.JSR 303 通过在 Bean 属性上标注类似于 @NoNull, @Max 等标准的注解指定校验规则, 并通过标准的验证接口对 Bean 进行验证.
3.JSR 303 提供的基本验证注解有
在这里插入图片描述


Hibernate Validator 扩展注解
1.Hibernate ValidatorHibernate没有关系, 只是 JSR 303 实现的一个扩展.
2. Hibernate ValidatorJSR 303 的一个参考实现, 除支持所有标准的校验注解外, 它还支持以下的扩展注解:
3.扩展注解如下
SpringMVC精简知识点_第6张图片

应用实例

●应用实例 - 需求说明

●应用实例 - 代码实现

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 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.测试
SpringMVC精简知识点_第7张图片
SpringMVC精简知识点_第8张图片

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";

再次测试
SpringMVC精简知识点_第9张图片

5.配置国际化文件 springDispatcherServlet-servlet.xml


<bean id="messageSource" class=
        "org.springframework.context.support.ResourceBundleMessageSource">
    
    <property name="basename" value="i18n">property>
bean>

6.创建国际化文件 src/i18n.properties 在线Unicode转中文

NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a #用户名不能为空
#如果类型匹配错误, 自定义错误信息 => (类型匹配错误, 年龄需要在0150之间)
typeMismatch.monster.age=\u7c7b\u578b\u5339\u914d\u9519\u8bef, \u5e74\u9f84\u9700\u8981\u57280\u548c150\u4e4b\u95f4 #类型匹配错误, 年龄需要在0150之间
#如果范围错误, 自定义错误信息 => (这是新的验证错误信息, 年龄必须在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.测试
SpringMVC精简知识点_第10张图片SpringMVC精简知识点_第11张图片

如果不配置国际化文件, 默认的错误信息如下
SpringMVC精简知识点_第12张图片

注意事项和使用细节

1.在需要验证的 JavaBean/POJO 的字段上加上相应的验证注解.
2.目标方法上, 在 JavaBean/POJO 类型的参数前, 添加 @Valid 注解, 告知 SpringMVCbean 是需要验证的
在这里插入图片描述

3.在 @valid 注解之后, 添加一个 ErrorsBindingResult 类型的参数, 可以获取到验证的错误信息

4.需要使用 标签来显示错误信息, 这个标签, 需要写在 标签内生效.
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没有, 是空的, 提交确实成功了

SpringMVC精简知识点_第13张图片SpringMVC精简知识点_第14张图片

●解决方案 注解组合使用
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中
SpringMVC精简知识点_第15张图片

Error的运行类型是BeanPropertyBindingResult, BeanPropertyBindingResult实现了BindingResult接口
在这里插入图片描述

SpringMVC精简知识点_第16张图片
Debug一下DataBinder类的validate如何得到验证errors信息
SpringMVC精简知识点_第17张图片

取消某个属性的绑定

●说明
在默认情况下, 表单提交的数据都会和pojo类型的javabean属性绑定, 如果程序员在开发中, 希望取消某个属性的绑定, 也就是说, 不希望接收到某个表单对应的属性的值, 则可以通过 @InitBinder注解取消绑定

1.编写一个方法, 使用InitBinder标识的该方法, 可以对WebDataBinder对象进行初始化. WebDataBinderDataBinder的子类, 用于完成由表单字段到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属性的绑定)
SpringMVC精简知识点_第18张图片
在这里插入图片描述

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的最上面, 多个过滤器之间会形成过滤器链, 要注意顺序.


<filter>
    <filter-name>MyCharacterFilterfilter-name>
    <filter-class>com.zzw.web.filter.MyCharacterFilterfilter-class>
filter>
<filter-mapping>
    <filter-name>MyCharacterFilterfilter-name>
    <url-pattern>/*url-pattern>
filter-mapping>

3.完成测试.
SpringMVC精简知识点_第19张图片
在这里插入图片描述

Spring提供的过滤器处理中文
1.修改web.xml, 换成Spring提供的过滤器, 处理中文乱码.


<filter>
    <filter-name>CharacterEncodingFilterfilter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
    <init-param>
        <param-name>encodingparam-name>
        <param-value>utf-8param-value>
    init-param>
filter>
<filter-mapping>
    <filter-name>CharacterEncodingFilterfilter-name>
    <url-pattern>/*url-pattern>
filter-mapping>

2.完成测试
SpringMVC精简知识点_第20张图片在这里插入图片描述

处理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.完成测试(浏览器)
SpringMVC精简知识点_第21张图片

7.用postman完成测试
SpringMVC精简知识点_第22张图片


处理JSON-@RequestBody
●应用案例
-前面我们是通过表单, 或者 url请求携带 参数=参数值 把数据提交给目标方法
1)给大家举例客户端发送 json字符串数据
2)使用SpringMVC的**@RequestBody** 将客户端提交的json数据, 封装成JavaBean对象
3)再把这个javabeanjson而对象形式返回
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.测试. 数据为空
SpringMVC精简知识点_第23张图片
后台. 数据为空

在这里插入图片描述

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.测试
SpringMVC精简知识点_第24张图片
后台

在这里插入图片描述

8.postman测试

postman提交json格式的数据

SpringMVC精简知识点_第25张图片
SpringMVC精简知识点_第26张图片

处理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测试
SpringMVC精简知识点_第27张图片
SpringMVC精简知识点_第28张图片返回结果
SpringMVC精简知识点_第29张图片

3.@ResponseBody 可以直接写在controller上, 这样对所有方法都生效
●应用实例
SpringMVC精简知识点_第30张图片

完成测试
SpringMVC精简知识点_第31张图片后台数据
在这里插入图片描述

postman测试
SpringMVC精简知识点_第32张图片


4.@ResponseBody + @Controller 可以直接写成 @RestController, 我们看一下源码

SpringMVC精简知识点_第33张图片

测试
SpringMVC精简知识点_第34张图片
SpringMVC精简知识点_第35张图片

HttpMessageConverter

●基本说明
SpringMVC处理**JSON-底层实现是依靠HttpMessageConverter**来进行转换的

●工作机制简图
SpringMVC精简知识点_第36张图片


●处理JSON-底层实现(HttpMessageConverter)
1.使用 HttpMessageConverter 将请求信息转化并绑定到处理方法的入参中, 或将响应结果转为对应类型的相应信息, Spring 提供了两种途径:
√ 使用 @RequestBody / @ResponseBody 对目标方法进行标注
√ 使用 @HttpEntity / ResponseEntity 作为目标方法的入参或返回值

2.当控制器处理方法使用到 @RequestBody / @ResponseBodyHttpEntity / ResponseEntity 时, Spring 首先根据请求头或响应头的 Accept 属性选择匹配的 HttpMessageConverter, 进而根据参数类型或泛型类型的过滤得到匹配的 HttpMessageConverter, 若找不到可用的 HttpMessageConverter 将报错

Debug 源码-梳理一下
SpringMVC精简知识点_第37张图片

SpringMVC精简知识点_第38张图片

一. 将请求信息转化并绑定到处理方法的入参中

SpringMVC精简知识点_第39张图片
SpringMVC精简知识点_第40张图片
SpringMVC精简知识点_第41张图片
SpringMVC精简知识点_第42张图片
SpringMVC精简知识点_第43张图片
SpringMVC精简知识点_第44张图片
SpringMVC精简知识点_第45张图片

二. 将响应结果转为对应类型的相应信息

SpringMVC精简知识点_第46张图片

SpringMVC精简知识点_第47张图片
SpringMVC精简知识点_第48张图片


文件下载-ResponseEntity

●说明
SpringMVC中, 通过返回 ResponseEntity 的类型, 可以实现文件下载的功能

●案例演示
准备工作: 在web路径/img下准备一个要下载的文件, 比如图片: 1.jpg
SpringMVC精简知识点_第49张图片

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 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没有对数据进行解析
SpringMVC精简知识点_第50张图片

作业布置

1.把我们前面学过的数据格式化, 验证以及国际化, Json处理, 文件下载, 相关代码和案例, 自己写一遍. 一定要写一遍, 否则没有印象, 理解不会渗入

2.把Debug过的HttpMessageConverter源码, 自己再走一下, 加深理解(不用每条语句, 都debug, 找流程…)

3.DataBinder工作机制-将示意图画出

4,Debug一下validate得到验证errors信息, 加深理解(不用每一条语句都debug, 找流程)

SpringMVC文件上传

●基本介绍
1.Spring MVC 为文件上传提供了直接的支持, 这种支持是通过即插即用的 MultipartResolver 实现的. SpringJakata Commons FileUpload 技术实现了一个 MultipartResolver 实现类: CommonsMultipartResolver

2.SpringMVC 上下文中默认没有装配 Multipartresolver, 因此默认情况下不能处理文件的上传工作, 如果想使用 Spring 的文件上传功能, 需现在上下文中配置 MultipartResolver

●需求分析 / 图解


●应用实例-代码实现

1.引入springmvc文件上传需要的jar包 spingmvc上传文件需要的jar
SpringMVC精简知识点_第51张图片

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 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.jspaction`

<form action="<%=request.getServletContext()%>/fileUpload" method="post" enctype="multipart/form-data">

7.完成测试[页面方式], 看文件是否成功上传 http://localhost:8088/springmvc/fileUpload.jsp

SpringMVC精简知识点_第52张图片

SpringMVC精简知识点_第53张图片

简单地debug一下transferTo()

SpringMVC精简知识点_第54张图片
SpringMVC精简知识点_第55张图片

8.完成测试[postman方式]

SpringMVC精简知识点_第56张图片
SpringMVC精简知识点_第57张图片

自定义拦截器

1.什么是拦截器

●说明
1.Spring MVC也可以使用拦截器对请求进行拦截处理, 用户可以自定义拦截器来实现特定的功能.
2.自定义的拦截器必须实现HandlerInterceptor接口

●自定义拦截器的三个方法
1.preHandle(): 这个方法在业务处理器处理请求之前被调用, 在该方法中对用户请求 request 进行处理.
2.postHandler(): 这个方法在目标方法处理完请求后执行
3.afterCompletion(): 这个方法在完全处理完请求后被调用, 可以在该方法中进行一些资源清理的操作.

2.自定义拦截器执行流程分析图
SpringMVC精简知识点_第58张图片

●自定义拦截器执行流程说明
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管理的


<mvc:interceptors>
    
    <ref bean="myInterceptor01"/>
mvc:interceptors>



<mvc:annotation-driven>mvc:annotation-driven>

<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">测试自定义拦截器-hia><br/><br/>
<a href="<%=request.getContextPath()%>>/hello">测试自定义拦截器-helloa>
body>

5.测试

浏览器测试 http://localhost:8088/springmvc/interceptor.jsp

SpringMVC精简知识点_第59张图片
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
 
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hello()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…

postman测试
SpringMVC精简知识点_第60张图片
SpringMVC精简知识点_第61张图片
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…
 
–MyInterceptor01-- preHandle() 被执行…
–FurnHandler-- hello()…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor01-- afterCompletion()被执行…


●注意事项和细节

1.默认配置是将所有的目标方法都进行拦截, 也可以指定拦截目标方法, 比如只拦截hi


<mvc:interceptors>
    
    <mvc:interceptor>
        <mvc:mapping path="/hi"/>
        <ref bean="myInterceptor01"/>
    mvc:interceptor>
mvc:interceptors>

SpringMVC精简知识点_第62张图片

2.mvc:mapping 支持通配符, 同时指定不对哪些目标方法进行拦截


<mvc:interceptors>
    
    <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">测试自定义拦截器-oka>

SpringMVC精简知识点_第63张图片

3.拦截器需要配置才生效, 不配置是不生效的.

4.如果preHandler() 方法返回了false, 就不会执行目标方法(前提是你的目标方法被拦截了), 程序员可以在这里根据业务需要指定跳转页面.

●Debug执行流程

1.prehandle()

SpringMVC精简知识点_第64张图片

SpringMVC精简知识点_第65张图片

2.目标方法
SpringMVC精简知识点_第66张图片

3.postHandle()
SpringMVC精简知识点_第67张图片
视图解析
SpringMVC精简知识点_第68张图片
一直点下一步
SpringMVC精简知识点_第69张图片

4.render()
SpringMVC精简知识点_第70张图片

5.afterCompletion()
SpringMVC精简知识点_第71张图片

解释一下model数据怎么来的? 用 postman 再走一圈

get请求
SpringMVC精简知识点_第72张图片
post请求
SpringMVC精简知识点_第73张图片

断点打到 preHandle

SpringMVC精简知识点_第74张图片

目标方法

SpringMVC精简知识点_第75张图片

postHandle

SpringMVC精简知识点_第76张图片

render

SpringMVC精简知识点_第77张图片

afterCompletion

SpringMVC精简知识点_第78张图片

4.多个拦截器
●多个拦截器执行流程示意图
SpringMVC精简知识点_第79张图片

SpringMVC精简知识点_第80张图片
●应用实例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>

    
    <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, 后面都不执行
SpringMVC精简知识点_第81张图片

2.如果第2个拦截器的preHandle()返回false, 就直接执行第1个拦截器的afterCompletion() 方法, 如果拦截器更多, 规则类似.
SpringMVC精简知识点_第82张图片

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测试
SpringMVC精简知识点_第83张图片

–MyInterceptor01-- preHandle() 被执行…
得到keyword=赵志伟
–MyInterceptor02-- preHandle() 被执行…
–FurnHandler-- hi()…
–MyInterceptor02-- postHandle()被执行…
–MyInterceptor01-- postHandle()被执行…
–MyInterceptor02-- afterCompletion()被执行…
–MyInterceptor01-- afterCompletion()被执行…

再次测试
SpringMVC精简知识点_第84张图片

–MyInterceptor01-- preHandle() 被执行…

5.作业布置
1.把前面我们学过的SpringMVC文件上传, 自定义拦截器和相关代码和案例, 自己写一遍. 一定要自己写一遍, 否则没有印象, 理解不会深入
2.简述SpringMVC自定义拦截器工作流程, 并画出示意图
3.debug自定义拦截器源码, 加深理解(不用每一条语句都debug), 重点是梳理流程.

异常处理

●基本介绍
1.Spring MVC 通过 HandlerExceptionResolver 处理程序的异常, 包括 Handler 映射, 数据绑定以及目标方法执行时发生的异常.
2.主要处理 Handler 中用 @ExceptionHandler 注解定义的方法
3.ExceptionHandlerMethodResolverHandler 内部若找不到 @ExceptionHandler 注解的话, 会找 @ControllerAdvice 类的@ExceptionHandler 注解方法, 这样就相当于一个全局异常处理器.

局部异常

●应用实例需求
演示局部异常处理机制
-如果不处理异常, 非常的不友好
SpringMVC精简知识点_第85张图片

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.测试, 抛错
SpringMVC精简知识点_第86张图片

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.测试
SpringMVC精简知识点_第87张图片
SpringMVC精简知识点_第88张图片

●Debug处理流程

打断点
SpringMVC精简知识点_第89张图片

测试
SpringMVC精简知识点_第90张图片

SpringMVC精简知识点_第91张图片

SpringMVC精简知识点_第92张图片

SpringMVC精简知识点_第93张图片
SpringMVC精简知识点_第94张图片
在这里插入图片描述
SpringMVC精简知识点_第95张图片

全局异常

●应用实例需求
演示全局异常处理机制, 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.测试
SpringMVC精简知识点_第96张图片SpringMVC精简知识点_第97张图片

●Debug处理流程
SpringMVC精简知识点_第98张图片SpringMVC精简知识点_第99张图片SpringMVC精简知识点_第100张图片

SpringMVC精简知识点_第101张图片SpringMVC精简知识点_第102张图片
SpringMVC精简知识点_第103张图片点击下一步

SpringMVC精简知识点_第104张图片SpringMVC精简知识点_第105张图片
SpringMVC精简知识点_第106张图片

继续往下走

SpringMVC精简知识点_第107张图片
继续往下走

SpringMVC精简知识点_第108张图片
SpringMVC精简知识点_第109张图片
SpringMVC精简知识点_第110张图片

SpringMVC精简知识点_第111张图片

继续往下走

SpringMVC精简知识点_第112张图片
SpringMVC精简知识点_第113张图片
SpringMVC精简知识点_第114张图片
SpringMVC精简知识点_第115张图片

●异常处理时: 局部异常 优先级高于 全局异常

假如我们把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.测试

SpringMVC精简知识点_第116张图片
SpringMVC精简知识点_第117张图片
5.在自定义异常中加两个构造器

@ResponseStatus(reason = "年龄需要在1-120之间", value = HttpStatus.BAD_REQUEST)
public class AgeException extends RuntimeException {

    public AgeException() {
    }

    public AgeException(String message) {
        super(message);
    }
}

被全局异常捕获SpringMVC精简知识点_第118张图片

●Debug全局异常
SpringMVC精简知识点_第119张图片
SpringMVC精简知识点_第120张图片SpringMVC精简知识点_第121张图片

SpringMVC精简知识点_第122张图片SpringMVC精简知识点_第123张图片
SpringMVC精简知识点_第124张图片
SpringMVC精简知识点_第125张图片
SpringMVC精简知识点_第126张图片

SpringMVC精简知识点_第127张图片

继续往下走

SpringMVC精简知识点_第128张图片
SpringMVC精简知识点_第129张图片
SpringMVC精简知识点_第130张图片

找到了globalException()方法

SpringMVC精简知识点_第131张图片
SpringMVC精简知识点_第132张图片

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">arrExprop>
        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,测试
SpringMVC精简知识点_第133张图片
SpringMVC精简知识点_第134张图片

●对未知异常进行统一处理

⭐应用实例 - 需求
对未知异常进行统一处理, 使用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">arrExprop>
            <prop key="java.lang.Exception">allExprop>
        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.测试
SpringMVC精简知识点_第135张图片
SpringMVC精简知识点_第136张图片

●对未知异常进行统一处理

局部异常 > 全局异常 > SimpleMappingExceptionResolver > tomcat默认机制

1.测试
SpringMVC精简知识点_第137张图片

局部异常 全局异常 SimpleMappingExceptionResolver
ArrayIndexOutOfBoundsException.class ArrayIndexOutOfBoundsException.class arrEx
局部异常 全局异常 SimpleMappingExceptionResolver
  ArrayIndexOutOfBoundsException.class arrEx
局部异常 全局异常 SimpleMappingExceptionResolver
    arrEx
局部异常 全局异常 SimpleMappingExceptionResolver
    arrEx

SpringMVC执行流程 - 源码分析

执行流程图
SpringMVC精简知识点_第138张图片

实验设计
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.测试
SpringMVC精简知识点_第139张图片
调整ok.jsp, 再次测试

<body>
<h1>进入到ok页面h1>
name - ${requestScope.name}
body>

SpringMVC精简知识点_第140张图片

Debug第1部分
SpringMVC精简知识点_第141张图片

地址栏输入: http://localhost:8088/springmvc/debug/springmvc
在这里插入图片描述
SpringMVC精简知识点_第142张图片
SpringMVC精简知识点_第143张图片

❀❀Spring容器结构剖析❀❀

这里就体现了SpringMVC前端控制器和容器的关系

SpringMVC精简知识点_第144张图片
SpringMVC精简知识点_第145张图片

分发请求

SpringMVC精简知识点_第146张图片

SpringMVC精简知识点_第147张图片

getHandler()

SpringMVC精简知识点_第148张图片

拿到目标方法

SpringMVC精简知识点_第149张图片

SpringMVC精简知识点_第150张图片

根据Handler拿到适配器, 不同的适配器对应不同的handler

SpringMVC精简知识点_第151张图片
SpringMVC精简知识点_第152张图片
SpringMVC精简知识点_第153张图片

因为是浏览器地址栏请求, 所以是GET请求

SpringMVC精简知识点_第154张图片

反射调用handler

SpringMVC精简知识点_第155张图片

进入

SpringMVC精简知识点_第156张图片

进入

SpringMVC精简知识点_第157张图片
SpringMVC精简知识点_第158张图片

进入

SpringMVC精简知识点_第159张图片
SpringMVC精简知识点_第160张图片

直接放行, 在目标方法打个断点

SpringMVC精简知识点_第161张图片

对modelAndView估值

SpringMVC精简知识点_第162张图片

不停地往下走

SpringMVC精简知识点_第163张图片

mav就是我们目标方法的ModelAndView

SpringMVC精简知识点_第164张图片

继续走, 回到DisPatcherServlet

SpringMVC精简知识点_第165张图片

SpringMVC精简知识点_第166张图片

往下走

SpringMVC精简知识点_第167张图片

进入

SpringMVC精简知识点_第168张图片
SpringMVC精简知识点_第169张图片

进入

SpringMVC精简知识点_第170张图片

SpringMVC精简知识点_第171张图片

前端控制器调用某个视图解析器返回

SpringMVC精简知识点_第172张图片

SpringMVC精简知识点_第173张图片
SpringMVC精简知识点_第174张图片
SpringMVC精简知识点_第175张图片
SpringMVC精简知识点_第176张图片

进入

SpringMVC精简知识点_第177张图片

下一步

SpringMVC精简知识点_第178张图片

SpringMVC精简知识点_第179张图片

进入, 拿到RequestDispatcher

SpringMVC精简知识点_第180张图片

请求转发

SpringMVC精简知识点_第181张图片

作业布置

1.把前面我们学过的SpringMVC异常处理相关代码和案例, 自己写一遍. - 一定要自己写一遍, 否则没有印象, 理解不会深入
2.简述SpringMVC执行流程, 并画出示意图
3.把我们Debug过的SpringMVC执行流程代码, 自己也走一下, 加深理解(不用每一条语句都debug, 主要是梳理流程)

你可能感兴趣的:(SpringMVC,java,spring)