SpringMVC精简知识点

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, 增加 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的页面

在这里插入图片描述
妖怪薪水只要能转成数字就行, 例如123456.12, 456.12, 0.123456, 但是12x.11就不行.

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

6.Postman测试
在这里插入图片描述

验证及国际化

●概述
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.扩展注解如下
在这里插入图片描述

应用实例

●应用实例 - 需求说明

●应用实例 - 代码实现

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 #用户名不能为空
#如果类型匹配错误, 自定义错误信息 => (类型匹配错误, 年龄需要在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.测试
在这里插入图片描述在这里插入图片描述

如果不配置国际化文件, 默认的错误信息如下
在这里插入图片描述

注意事项和使用细节

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

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

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.

种类修饰类型作用
@NotEmptyString, collection, mapnull || 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对象进行初始化. 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属性的绑定)
在这里插入图片描述
在这里插入图片描述

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)再把这个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.测试. 数据为空
在这里插入图片描述
后台. 数据为空

在这里插入图片描述

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 / @ResponseBodyHttpEntity<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 实现的. SpringJakata 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.jspaction`

<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.ExceptionHandlerMethodResolverHandler 内部若找不到 @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.classArrayIndexOutOfBoundsException.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, 主要是梳理流程)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/371060.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Sklearn、TensorFlow 与 Keras 机器学习实用指南第三版(四)

原文&#xff1a;Hands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第二部分&#xff1a;神经网络和深度学习 第十章&#xff1a;使用 Keras 入门人工神经网络 鸟类启发我们飞行&#xff0c;牛蒡植…

Qt PCL学习(一):环境搭建

参考 (QT配置pcl)PCL1.12.1QT5.15.2vs2019cmake3.22.4vtk9.1.0visual studio2019Qt5.15.2PCL1.12.1vtk9.1.0cmake3.22.2 本博客用到的所有资源 版本一览&#xff1a;Visual Studio 2019 Qt 5.15.2 PCL 1.12.1 VTK 9.1.0https://pan.baidu.com/s/1xW7xCdR5QzgS1_d1NeIZpQ?pw…

【CSS + ElementUI】更改 el-carousel 指示器样式且隐藏左右箭头

需求 前三条数据以走马灯形式展现&#xff0c;指示器 hover 时可以切换到对应内容 实现 <template><div v-loading"latestLoading"><div class"upload-first" v-show"latestThreeList.length > 0"><el-carousel ind…

2024-2-4-复习作业

源代码&#xff1a; #include <stdio.h> #include <stdlib.h> typedef int datatype; typedef struct Node {datatype data;struct Node *next;struct Node *prev; }*DoubleLinkList;DoubleLinkList create() {DoubleLinkList s(DoubleLinkList)malloc(sizeof(st…

Docker进阶篇-轻量级可视化工具Portainer

一、简介 Portainer是一款轻量级的应用&#xff0c;它提供了图形化界面&#xff0c;用于方便地管理Docker环境&#xff0c;包括单机环 境和集群环境。 Portainer分为开源社区版&#xff08;CE版&#xff09;和商用版&#xff08;BE版/EE版&#xff09;。 官网&#xff1a;P…

LeetCode:2.两数相加

目录 题目&#xff1a;​编辑2. 两数相加 - 力扣&#xff08;LeetCode&#xff09; 分析问题&#xff1a; 官方的优秀代码博主的注释&#xff1a; 博主的辣眼代码&#xff0c;无注释&#xff0c;拉出来拷打自己&#xff1a; 每日表情包&#xff1a; 2. 两数相加 - 力扣&am…

深度学习-随机梯度下降

在训练过程中使用随机梯度下降&#xff0c;但没有解释它为什么起作用。为了澄清这一点&#xff0c;将继续更详细地说明随机梯度下降&#xff08;stochastic gradient descent&#xff09;。 %matplotlib inline import math from mxnet import np, npx from d2l import mxnet …

C# CAD界面-自定义工具栏(二)

运行环境 vs2022 c# cad2016 调试成功 一、引用 acdbmgd.dllacmgd.dllaccoremgd.dllAutodesk.AutoCAD.Interop.Common.dllAutodesk.AutoCAD.Interop.dll using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.T…

RK3568平台 设备模型基本框架-kobject 和kset

一.什么是设备模型 字符设备驱动通常适用于相对简单的设备&#xff0c;对于一些更复杂的功能&#xff0c;比如说电源管理和热插拔事件管理&#xff0c;使用字符设备框架可能不够灵活和高效。为了应对更复杂的设备和功能&#xff0c;Linux内核提供了设备模型。设备模型允许开发…

西瓜书学习笔记——流形学习(公式推导+举例应用)

文章目录 等度量映射&#xff08;仅保留点与其邻近点的距离&#xff09;算法介绍实验分析 局部线性嵌入&#xff08;不仅保留点与其邻近点的距离还要保留邻近关系&#xff09;算法介绍实验分析 等度量映射&#xff08;仅保留点与其邻近点的距离&#xff09; 算法介绍 等度量映…

Unity动画循环偏移的使用

最近项目中有一个需求是做煤矿中猴车的动画&#xff0c;动画本身不复杂&#xff0c;但是猴车很多&#xff0c;怎么能简化工作量呢&#xff1f; 首先单个猴车的动画循环是必须要做的&#xff0c;重点是怎么让不同的猴车动画按顺序错开&#xff0c;研究了以下&#xff0c;可以通过…

后端登录校验

文章目录 登录校验CookieSessionJWT生成JWT校验JWT基于JWT进行身份验证CSRF Cookie、Session、Token的区别&#xff1f;过滤器(Filter)配置过滤器过滤器链 登录校验 由于HTTP协议是无状态的&#xff0c;我们在进行登录后等一系列接口请求是无法直接区分是哪一个用户的发给服务…

电路设计(10)——超温报警电路的proteus仿真

1.题目背景 在现实生活中&#xff0c;常有一种工程技术&#xff0c;即带有自动温度补偿的设备&#xff0c;能在规定温度内正常工作。但是为了设备安全&#xff0c;需设定工作的上限温度&#xff0c;万一温控补偿失效&#xff0c;设备温度一旦超出上限温度时&#xff0c;便立即切…

ES高可用架构涉及常用功能整理

ES高可用架构涉及常用功能整理 1. es的高可用系统架构和相关组件2. es的核心参数2.1 常规配置2.2 特殊优化配置2.2.1 数据分片按ip打散2.2.2 数据分片机架感知2.2.3 强制要求数据分片机架感知2.2.4 写入线程池优化2.2.5 分片balance优化2.2.6 限流控制器优化 3. es常用命令3.1 …

项目中将sass更换成less(TypeError: this.getOptions is not a function已解决)

在更换之前&#xff0c;首先了解sass与less在用法上的区别有哪些&#xff08;这里简单提几个&#xff09;&#xff1a; 变量区别&#xff1a;Less中用&#xff0c;Sass用$sass支持条件语句&#xff0c;可以使用if{}else{}、for循环等&#xff0c;而less不支持在定义变量时候&a…

PyTorch 2.2 中文官方教程(十八)

开始使用完全分片数据并行&#xff08;FSDP&#xff09; 原文&#xff1a;pytorch.org/tutorials/intermediate/FSDP_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 作者&#xff1a;Hamid Shojanazeri&#xff0c;Yanli Zhao&#xff0c;Shen Li 注意…

如何获取和查找您的Android设备的序列号

Hello大家好&#xff01;我是咕噜铁蛋&#xff01;&#xff0c;我们经常会在文章中提及到各种各样的Android设备&#xff0c;而其中一个重要的标识就是设备的序列号。设备的序列号在很多情况下被用于唯一标识一个设备&#xff0c;例如在软件开发中进行设备的调试和测试&#xf…

Python统计分析——参数估计

参考资料&#xff1a;用python动手学统计学 所谓参数就是总体分布的参数。 1、导入库 # 导入用于数值计算的库 import numpy as np import pandas as pd import scipy as sp from scipy import stats # 导入用于绘图的库 from matplotlib import pyplot as plt import seabor…

FL Studio Producer Edition 21.2.2全插件版+Crack下载链接(亲测可用,非钓鱼)

不知道为什么现在钓鱼的这么多&#xff08;有答案的请在评论区上告诉我&#xff09;&#xff0c;就一个学习版的编曲软件有必要这样子搞吗&#xff1f;我也是在各类博客上找了一大堆教程&#xff0c;根本没几个能用的&#xff0c;索性直接到海盗湾上找了一个&#xff0c;发现可…

学习ArtTs -- 初见ArkTs

作者&#xff1a;Uncle_Tom 原文链接&#xff1a;学习ArtTs -- 初见ArkTs-云社区-华为云 1. 前言 需要静态分析去检查一个语言&#xff0c;必须对这个语言有深刻的认识&#xff0c;才能有效的对这个语言进行有效的检查。 我常说:“作为一个程序分析员需要比一般的程序员考虑…