【零基础入门SpringMVC】第五期——报文、文件转移、拦截器、异常处理器

news2024/11/27 4:13:44

一、HttpMessageConverter

  • 代表报文信息转化器,可以将请求报文转换为Java对象,也可以将Java对象转换为响应报文

  • 请求报文分为三部分:请求头、请求空行和请求体

    • post 请求才会生成请求体
    • get 请求会把请求信息拼接到地址后
  • 概述:这部分我们将介绍一些关于报文的注解和类型的使用

  • HttpMessageConverter 为我们提供了两个注解和两个类型:

    • @RequestBody 用在控制器方法形参处,可以获取请求体
    • @ResponseBody 用于控制器方法上,可以让方法的返回值直接响应到浏览器【在浏览器页面显示】
    • RequestEntity 用于封装请求报文的一种类型,我们可以将其作为控制器方法的参数,封装传递过来的请求报文
    • ResponseEntity 用于控制器方法的类型处,作用和@ResponseBody相同【ResponseEntity优先级更高】
  • 接下来我们对各部分展开论述,并演示对应的用法

$ @RequestBody

  • 用在控制器方法的形参前,我们一般用于字符串类型的变量接收请求体,是以键值对的形式存在的

  • 编写我们的发起请求的表单,表单收集到的属性值和属性名就是我们请求体中的内容

    <!--测试获得请求体-->
    <form th:action="@{/testRequestBody}" method="post">
        <input type="text" name="username" >
        <input type="text" name="password" >
        <input type="submit" value="测试@RequestBody" >
    </form>
    
  • 编写对应的控制器方法

    // 获取请求体
    @RequestMapping(value = "/testRequestBody", method = RequestMethod.POST)
    public String testRequestBody(@RequestBody String requestBody){
    	System.out.println(requestBody);
    	return "success";
    }
    

请添加图片描述

$ RequestEntity

  • 类似于我们用User类来封装网页表单传过来的一个User对象的数据

  • RequestEntity 是一种请求报文的类型,作为形参使用 【我们得到的是当前的整个请求报文】

    • 通过其对象的 getBody 方法可以获取请求体
    • 通过其对象的 getHeaders 方法可以获取请求头
  • 编写我们的请求表单:

    <!--测试请求报文-->
    <form th:action="@{/testRequestEntity}" method="post">
        <input type="text" name="username" >
        <input type="text" name="password" >
        <input type="submit" value="测试RequestEntity" >
    </form>
    
  • 编写对应的控制器方法:

    // 封装整个请求报文
    @RequestMapping("/testRequestEntity")
    public String testRequestEntity(RequestEntity<String> requestEntity){
    	// 打印一下请求头和请求体
    	System.out.println(requestEntity.getHeaders());
    	System.out.println(requestEntity.getBody());
    	return "success";
    }
    

请添加图片描述

$ @ResponseBody

  • 使用该注解标记的控制器方法,返回的不再是一个视图名(用于页面跳转),而是直接作为响应报文的响应体响应到浏览器中
  • 如果我们返回一个字符串,就会将这个字符串直接打印到浏览器页面
  • 编写我们的请求链接:
    <a th:href="@{/testResponseBody}">通过@ResponseBody响应浏览器数据</a><br>
    
  • 编写我们的控制器方法 [测试的时候出现了乱码问题不大]
    // 通过注解响应浏览器数据
    @RequestMapping("/testResponseBody")
    @ResponseBody
    public String testResponseBody(){
    	return "通过注解响应到浏览器中的数据";
    }
    

请添加图片描述

  • 是否还有其他方式响应浏览器数据呢?

    • 我们可以通过ServletAPI来完成
    <a th:href="@{/testResponse}">通过ServletAPI响应浏览器数据</a><br>
    
    @RequestMapping("/testResponse")
    public void testResponse(HttpServletResponse response) throws IOException {
    response.getWriter().println("hello, response");
    }
    
    • 我们还可以将 ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文【优先级比 @ResponseBody更高】
  • 上面我们是将一个字符串作为响应报文的数据输出到浏览器页面,如果是一个我们封装的Java对象可以吗?

$ springMVC 处理 json

  • 首先我们需要准备一个 pojo 类,此处我们以 User 类为例

    package com.atguigu.mvc.bean;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    public class User {
        private Integer id;
        private String username;
        private String password;
        private Integer age;
        private String sex;
    
        public User() {
        }
    
        public User(Integer id, String username, String password, Integer age, String sex) {
            this.id = id;
            this.username = username;
            this.password = password;
            this.age = age;
            this.sex = sex;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUsername() {
            return username;
        }
    
        public void setUsername(String username) {
            this.username = username;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public String getSex() {
            return sex;
        }
    
        public void setSex(String sex) {
            this.sex = sex;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", age=" + age +
                    ", sex='" + sex + '\'' +
                    '}';
        }
    }
    
  • 准备一个发起请求的超链接

    <a th:href="@{/testResponseUser}">通过@ResponseBody响应浏览器User对象</a><br>
    
  • 编写控制器处理方法

    // 测试返回的是一个对象
    @RequestMapping("/testResponseUser")
    @ResponseBody
    public User testResponseUser(){
    	return new User(1, "LiHua", "10086", 28, "男");
    }
    
  • 运行 tomcat 测试一下
    请添加图片描述

❌ 我们发现不能直接输出复杂类型,那么我们可以将其转化为 json 对象,然后进一步输出

  • 那么我们应该怎么做才能将 Java 对象转换为 json 对象呢?

    • 第一步,导入json相关依赖 【基于上面的条件我们已经完成了剩下的三步,只需要配置依赖】
    <dependency>
    	<groupId>com.fasterxml.jackson.core</groupId>
    	<artifactId>jackson-databind</artifactId>
    	<version>2.12.1</version>
    </dependency>
    
    • 第二步,在SpringMVC的核心配置文件中开启mvc注解驱动
      • HandlerAdaptor会自动装配一个消息转换器,将响应到浏览器的Java对象转换为json格式的字符串
    • 第三步,需要在控制器方法上使用 @ReponseBody 注解
    • 第四步,将我们的 Java 对象作为返回值返回
  • 重新运行,再次点击超链接测试【因为我使用了CSDN的插件】
    请添加图片描述

  • json 是一种数据交互格式,分为 json 对象和 json 数组两种

    • Json 对象外层是大括号
    • Json 数组外层是中括号
  • 对于 mvc 注解驱动有很多作用,当前我们接触到的有如下这些:

    • 使用了视图控制器后控制器中的所有方法都会失效,我们需要配置注解驱动
    • 在过滤静态资源的时候,我们如果不配置注解驱动就导致只有静态资源的请求才能被匹配到
    • 用于Java对象转换成Json对象
  • SpringMVC 为我们提供了一个复合注解 @RestController 注解,用在控制器类名上

    • 代表给类添加了 @Controller 注解
    • 给类中所有的方法都添加了 @ReponseBody 注解

$ SpringMVC 处理 Ajax

  • Ajax 本身就是页面,所以发送请求的方式不同,但是服务器的处理方式不变
  • 页面不能刷新、也不与服务器进行交互,所以在服务器中不能转发和重定向,只能浏览数据【发起一个弹窗】
  • 需求:通过一个div容器上的超链接的点击发起 ajax 请求,我们还是采用 vue 进行处理

编写我们的请求代码

<!--发送一个ajax请求-->
<div id="app">
    <!--通过@click绑定点击事件-->
    <a @click="testAxios" th:href="@{/testAxios}">SpringMVC处理Ajax</a>
</div>
<!--引入我们的js文件-->
<script type="text/javascript" th:src="@{/static/js/vue.js}"/>
<script type="text/javascript" th:src="@{/static/js/axios.min.js}"/>
<script type="text/javascript">
    new Vue({
        el:"#app", // 对应我们连接的id
        methods:{
            testAxios:function (event){ // event 代表我们当前的事件
                axios({
                    // 指定请求方式
                    method : "post",
                    // 请求地址
                    url : event.target.href,
                    // 请求参数
                    params: {
                        username : "admin",
                        password : "123456"
                    }
                }).then(function (response){
                    alert(response.data);
                })

                // 取消超链接的默认行为
                event.preventDefault();
            }
        }
    });
</script>

编写我们的控制器方法

// Ajax 测试
@RequestMapping("/testAxios")
@ResponseBody
public String testAxios(String username, String password){
	System.out.println(username + ", " + password);
	return "hello, Ajax";
}

运行测试

请添加图片描述

二、文件的上传与下载

  • 文件下载:从服务器端下载到浏览器端;文件上传:从浏览器端上传到我们服务器端 [在底层都是一个文件复制的过程]

  • SpringMVC 中,我们通过 ResponseEntity 作为控制器方法的返回值,来下载文件

    • 属于用我们自定义的响应报文去响应浏览器,发起请求后就可以把对应的文件下载到浏览器
    • 上面我们也说过,这个属性可以让控制器方法的返回值,作为响应报文响应到浏览器中
  • SpringMVC 中,我们想要上传文件需要配置对应依赖,表单发起post请求,编码方式得设置成二进制

    • 上面的是前提,在控制器方法形参接收数据,但是要转换成MultipartFile,需要在核心配置文件中配置
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    

    有个小细节,springMVC 是根据 id 去查找Bean的,,我们声明的这个类是 MultipartResolver 接口的一个实现类

  • 接下来演示如何实现在 Web 项目中进行文件的上传与下载:

$ 文件的下载

  • 我们准备一个单独的页面,只用来发起文件上传和下载的请求,先通过一个超链接点击发起下载文件的请求

    <a th:href="@{/testDown}">下载我们服务器端的1.png</a>
    
  • 新建一个控制器类:FileUpAndDownController,提供两个控制器方法,先给出下载文件的方法

    // 文件下载
    @RequestMapping("/testDown")
    public ResponseEntity<byte[]> testResponseEntity(HttpSession session) throws IOException {
    	//获取ServletContext对象 [当前的整个工程]
    	ServletContext servletContext = session.getServletContext();
    	//获取服务器中文件的真实路径
    	String realPath = servletContext.getRealPath("/static/img/1.png");
    	//创建输入流
    	InputStream is = new FileInputStream(realPath);
    	//创建字节数组 [available方法是可以得到输入流中的字节数]
    	byte[] bytes = new byte[is.available()];
    	//将流读到字节数组中
    	is.read(bytes);
    	//创建HttpHeaders对象设置响应头信息
    	MultiValueMap<String, String> headers = new HttpHeaders();
    	//设置要下载方式以及下载文件的名字 [attachment代表以附件的方式下载]
    	headers.add("Content-Disposition", "attachment;filename=1.png");
    	//设置响应状态码 [OK 代表200]
    	HttpStatus statusCode = HttpStatus.OK;
    	//创建ResponseEntity对象 [响应体、响应头、响应状态码]
    	ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
    	//关闭输入流
    	is.close();
    	return responseEntity;
    }
    
  • 无论是文件上传还是下载,都要先获取到 Session,然后进一步获取到 ServletContext

  • 既然要下载文件,肯定需要找到文件的位置,通过 getRealPath 方法将我们文件的路径传进去

  • 我们要把文件中的内容都转化成字节发送到客户端,所以根据我们的路径创建输入流,然后将流读入字符数组中

  • 因为我们传递响应报文不是只有响应体,还需要有响应头和响应状态码

    • 所以直接通过 HttpHeaders创建一个响应头,属于一种Map集合,因为请求头和响应头都是键值对的形式
    • 响应状态码 HttpStatus.OK 代表状态码200,就是成功响应,要是找不到资源就 404 了
  • 最后将响应报文封装到 ResponseEntity 的对象中,通过 return 响应到浏览器中 【状态栏会有下载成功的提示】

  • 测试一下,因为我使用特殊的下载工具,所以提示不一样
    在这里插入图片描述

  • 这部分代码可以当做模板直接使用,我们只需要修改一下文件名和请求提示就可以了

$ 文件的上传

  • 对于文件上传,我们需要配置需要的依赖

    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    
  • 上传文件要求发起的必须是 post 请求,而且需要指定为二进制编码

    <!--文件上传只能是post请求方式-->
    <!--enctype属性规定了表单数据发送到服务器之前如何进行编码-->
    <form th:action="@{/testUp}" method="post" enctype="multipart/form-data">
        头像 <input type="file" name="photo"><br>
        <input type="submit" value="上传">
    </form>
    
  • SpringMVC 的核心配置文件中

    <!--必须通过文件解析器的解析才能将文件转换为MultipartFile对象-->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>
    
  • 接着我们看一下文件上传的控制器方法应该怎么写 ?

    // 文件上传
    @RequestMapping("/testUp")
    public String testUp(MultipartFile photo, HttpSession session) throws IOException {
    	// 我们上传文件的文件名
    	String fileName = photo.getOriginalFilename();
    	// 获取文件名的后缀
    	String suffixName = fileName.substring(fileName.lastIndexOf("."));
    	// 上传之后的文件,我们使用UUID作为其文件名[通用唯一标识码,解决了文件重名覆盖问题]
    	String uuid = UUID.randomUUID().toString().replace("-", "");
    	// 将uuid和后缀拼接到一起,就是服务器中的文件名
    	fileName = uuid + suffixName;
    	// 通过session获取整个应用
    	ServletContext servletContext = session.getServletContext();
    	// 获取我们保存资源的路径
    	String photoPath = servletContext.getRealPath("photo");
    	// 获取整个路径下的文件 [主要我们用来判断是否有这个文件夹]
    	File file = new File(photoPath);
    	// 判断photoPath这个路径是否存在
    	if(!file.exists()){
    		// 不存在就去创建对应的文件夹
    		file.mkdir();
    	}
    	// 创建我们最终的文件完整路径
    	String realPath = photoPath + File.separator + fileName;
    	// 将浏览器中的资源转移到我们的服务器中 [会有一个IO流的异常,我们直接抛出]
    	photo.transferTo(new File(realPath));
    	return "success";
    }
    
  • 返回值类型为String而且没使用@ResponseBody注解、返回值无前缀,所以我们返回的是视图名【成功上传跳转页面】

  • 两个参数,第一个用来完成携带数据的封装,需要特定的配置(上面在核心文件中的配置),第二个获取 Session

  • 此处我们给出的是直接解决了文件重名的问题,采用 UUID 代替我们上传的文件名,当然文件后缀不能变

  • 需要注意,如果文件夹不存在我们需要创建一个文件夹,Maven工程文件实际保存到target目录下了

  • 最后,将浏览器中的资源转移到我们指定的路径中

在这里插入图片描述

三、拦截器

  • 什么是拦截器?

    • 拦截器是一个运行在服务端的程序,先于与之相关的servletjsp页面之前运行,实现对请求资源的过滤功能
    • SpringMVC 中拦截器用于拦截控制器方法的执行
  • 过滤器也可以拦截请求,那么拦截器有什么特别的地方吗?

    • 拦截器提供了三个方法
      • preHandle 在控制器方法执行前执行,决定是否放行这个控制器方法,true通过、false就拒绝通过
      • postHandle 在控制器方法执行结束之后执行
      • AfterComplation 在渲染视图完成之后执行
    • 描述一下大致流程:
      • 在客户端发起请求,根据请求和我们的请求映射进行匹配,找到控制器方法【又称为处理器方法 Handle
      • 在控制器方法执行前,preHandle执行,然后执行控制器方法,然后执行postHandle
      • 控制器的方法结束之后,都会返回一个ModelAndView的对象【模型和数据】
      • postHandle执行结束后,将模型中的数据渲染到视图中,然后执行AfterComplation
  • 接下来我们编写一个拦截器,演示拦截器具体是如何配置的?

$ 配置拦截器

☀️ 编写一个拦截器类 FirstInterceptor

  • 我们可以让自定义拦截器类实现 HanlderInterceptor 接口,也可以让它继承 HandlerInterceptorAdapter
    • 后者其实是前者的一个实现类,不过现在已经过时了,所以我们采用实现接口的方式
    package com.atguigu.mvc.interceptors;
    
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Component
    public class FirstInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("FirstInterceptor --> preHandle");
    //        return HandlerInterceptor.super.preHandle(request, response, handler);
            // 返回true代表放行,返回false代表拦截
    //        return false;
            return true;
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("FirstInterceptor --> postHandle");
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("FirstInterceptor --> afterCompletion");
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
  • 有几个需要注意的地方:
    • 由于这个接口中的这三个方法定义为了 default 类型 【默认方法】
    • 所以在我们实现这个接口的时候,不会报错提示必须实现这三个方法 ,在IDEA中我们可以按 Ctrl + O 弹出快速实现接口的选项窗口

☀️ 拦截器要想被我们SpringMVC识别到,需要进行配置

  • 第一步,我们通过在拦截器类上使用 @Component 注解,将拦截器的Bean添加到IoC容器中管理【BeanIoC中的一个组件/对象】
  • 第二步,我们要确保这个文件能被扫描到,在核心配置文件中的context:component-scan 标签添加路径
  • 第三步,在我们的核心配置文件中,配置拦截器
    <!--配置拦截器-->
        <mvc:interceptors>
            <!--通过bean说明某一个类型的对象就是一个拦截器-->
            <bean class="com.atguigu.mvc.interceptors.FirstInterceptor"/>
            <ref bean="firstInterceptor"/>
    
            <mvc:interceptor>
                <!--代表对当前上下文路径上的请求进行拦截-->
                <!--如果想拦截所有请求,那么使用 /** -->
                <mvc:mapping path="/*"/>
                <!--除了我们的根标签请求(主页面)-->
                <mvc:exclude-mapping path="/"/>
                <!--通过bean或ref指定我们使用哪个拦截器-->
                <ref bean="firstInterceptor"/>
            </mvc:interceptor>
        </mvc:interceptors>
    
  • 上面的这个配置文件不能直接使用,需要选择一种方案,注释掉另外两个
    • 无法设置拦截规则,统一拦截所有请求
      • 通过bean来声明拦截器,不需要指定 id,通过class属性指定为我们拦截器的全限定类名即可
      • 通过ref来声明拦截器,因为这个拦截器已经托管了,只需要使用对应bean的默认id即可
    • 可以设置拦截规则,拦截哪些请求也可以指定【也就是第三种方式 】
      • 通过 mvc:interceptor 标签的子标签完成配置
        • mvc:mapping 标签的 path属性指定拦截哪些请求
          • /* 代表当前上下文路径下的所有请求
          • /** 代表所有请求
        • mvc:exclude-mapping 标签的path属性指定不拦截哪些请求
        • 接下来需要配置一下我们的拦截器,采用 beanref 的方式指定拦截器都可以

☀️ 配置好拦截器我们可以运行测试一下 【此时测试preHandler选择的是false >> 拦截方法】

  • 我们可以看到浏览器页面什么都没有,三个方法也与我们预计的执行顺序相同
    在这里插入图片描述
  • 我们可以简单看一下各部分的调用位置 >> 决定了拦截器三个方法的执行顺序

在这里插入图片描述
在这里插入图片描述

$ 多拦截器的执行顺序

  • 上面我们演示的是一个拦截器,如果存在多个拦截器,那么执行起来的效果是怎么样的呢?

  • 首先,我们需要再准备一个拦截器 SecondInterceptor

    package com.atguigu.mvc.interceptors;
    import org.springframework.stereotype.Component;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @Component
    public class SecondInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("SecondInterceptor --> preHandle");
            return HandlerInterceptor.super.preHandle(request, response, handler);
        }
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("SecondInterceptor --> postHandle");
            HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        }
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("SecondInterceptor --> afterCompletion");
            HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        }
    }
    
  • 接着在核心配置文件中配置一下拦截器,为了方便我们不设置拦截规则,采用 ref 声明拦截器

    <mvc:interceptors>
    <ref bean="firstInterceptor"/>
    <ref bean="secondInterceptor" />
    </mvc:interceptors>
    
  • 运行测试一下:
    在这里插入图片描述

  • 我们对照源码,看一下为什么是这样的一个输出顺序?

    • preHandle 是按照在配置文件中声明的顺序执行的
      在这里插入图片描述
    • postHandle 是按照在配置文件中声明的逆序执行的
      在这里插入图片描述
    • AfterComplation 也是按照在配置文件中声明的逆序执行的
      在这里插入图片描述
  • 因为我们上面这两个拦截器都是放行状态,如果将第二个拦截器设置为拦截状态,那么会输出哪些内容呢?
    在这里插入图片描述

  • 实际上SpringMVC中还有一个默认的拦截器

  • 存在多个拦截器,只要有一个拦截器的 preHandlefalse,那么

    • 这个返回 false 的拦截器它之前的所有拦截器的 preHandle 会执行
    • 所有拦截器的 postHandle 方法都不会执行
    • 这个返回 false 的拦截器之前的所有拦截器 的 AfterComplation 方法会执行

四、异常处理器

  • 异常处理器是SpringMVC中为我们提供的一种异常处理机制,我们可以自定义异常处理

  • 主要分为两种方式进行配置:通过配置文件配置、通过注解配置

  • 异常处理调用的是doResolveException方法,返回值为ModelAndView

    • 当我们控制器方法出现了异常,这个方法执行后会产生一个新的ModelAndView代替原来的ModelAndView
    • SimpleMappingExceptionResolver 类就是用来自定义异常的
  • 捕获到我们预处理的异常后,就会根据我们的配置跳转到指定的页面,同时可以将错误信息添加到请求域中,在页面显示

$ 基于配置的异常处理

  • 就是我们在SpringMVC的核心配置文件中配置异常处理解析器,来对配置我们如何处理异常

  • 需要我们准备一个发生异常后的跳转页面,我们就输出一句话和打印异常报告

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    出现异常
    <p th:text="${ex}"/>
    </body>
    </html>
    
  • 设置一个发起异常测试请求的超链接

    <a th:href="@{/testException}">测试异常处理</a>
    
  • 我们模拟一个数学运算异常,在对应的控制器方法中添加异常

    // 测试数学运算异常
    @RequestMapping("/testException")
    public String testException(){
    	System.out.println(1/0);
    	return "success";
    }
    
  • SpringMVC的核心配置文件中,配置异常解析器

    <!--配置异常解析器-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <property name="exceptionMappings">
            <props>
                <!--key为异常的全限定类名-->
                <prop key="java.lang.ArithmeticException">error</prop>
            </props>
        </property>
        <!--将异常信息添加到request请求域中-->
        <property name="exceptionAttribute" value="ex" />
    </bean>
    
    • 有几个需要注意的点:
      • exceptionMappingsproperties 类型,是一种Map集合,采用props嵌套prop标签设置键值对
      • prop 标签的 key对应我们配置的异常的全限定类名,value 就是我们要跳转的页面
        • 遵循我们视图跳转的规则,效果和return一样,需要看是否有前缀~
      • 我们通过 exceptionAttribute 属性将异常信息以 value后参数值的名字作为key,添加到请求域中
  • 运行测试一下
    在这里插入图片描述

$ 基于注解的异常处理

  • 我们在额外定义一个控制器,专门完成各种异常处理 ExceptionController

  • 在控制类上面,我们使用的不是 @Controller而是@ControllerAdvice注解,这个注解有三个作用

    • .与@ExceptionHandler注解一起用,用于捕获Controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的
    • 与@InitBinder注解一起用,用于request中自定义参数解析方式进行注册,从而达到自定义指定格式参数的目的
    • 与@ModelAttribute注解一起用,表示其注解的方法将会在目标Controller方法执行之前执行
  • 编写我们的控制器和要测试的异常处理方法

    package com.atguigu.mvc.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    
    /**
     * @author Bonbons
     * @version 1.0
     */
    @ControllerAdvice
    public class ExceptionController {
        @ExceptionHandler(value = {
                // 我们要处理的异常
                ArithmeticException.class,
                NullPointerException.class
        })
        /**
         * Exception ex 参数代表当前这个异常
         * 使用Model model 形参为了将异常信息添加到请求域中
         */
        public String testException(Exception ex, Model model){
            model.addAttribute("ex", ex);
            return "error";
        }
    }
    
  • 此时我们就可以直接进行测试了,我们在一个控制器方法模拟一下空指针异常

    String s = null;
    s.length();
    

    在这里插入图片描述

  • SpringMVC提供了一个处理控制器方法执行过程中所出现的异常的接口 HandlerExceptionResolver

  • HandlerExceptionResolver接口的实现类有:

    • DefaultHandlerExceptionResolver 默认的异常处理器
    • SimpleMappingExceptionResolver SpringMVC提供了自定义的异常处理器

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

经验分析:数据可视化工具入门讲解,如何应用数据可视化

什么是数据可视化&#xff1f;最核心的要点&#xff0c;就是解释数据、进行信息传递、压缩数据信息、突出整体观点。 在分析过程中&#xff0c;通过比较数据的大小、差异、分布&#xff0c;让其轻松呈现。 今天小编给大家介绍一款优秀的在线数据可视化软件。 项目是个人创建的…

第一性原理谈安全性和可靠性

已剪辑自: https://mp.weixin.qq.com/s/jttd-dhv9PmNu25Z-zyd5Q 最近从各个行业对系统的安全性的关注度越来越高&#xff0c;10月28日&#xff0c;工信部公开征求的《道路机动车辆生产准入许可管理条例&#xff08;征求意见稿&#xff09;》中&#xff0c;第六条提出&#xff…

C语言航路外传之隐式转换与优先级的那点事(你程序总是出bug的一个重要原因)

目录 一、表达式求值 二、隐式类型转换 1.基本概念 2.整型提升的意义 3.详解截断与整型提升的过程 4.char类型范围有关的一些事情 5.有关整形提升的一些案例 三、算术转换 四、操作符的属性 1.优先级表格 2.运算规则 3.一些问题表达式 &#xff08;1&#xff09;a…

Linux 之 arm linux 与 windows 使用 tftp 进行文件传输的简单整理

Linux 之 arm linux 与 windows 使用 tftp 进行文件传输的简单整理 目录 Linux 之 arm linux 与 windows 使用 tftp 进行文件传输的简单整理 一、简单介绍 二、tftp 下载安装 三、arm linux 通过tftp 下载 windows 上的文件 四、tftp 命令相关说明 1、busybox tftp命令常…

Baklib|知识库应用场景:制作员工培训手册

持续的专业发展对于想要加入、保留和提升员工的组织来说是必不可少的。为了确保员工总是能从学习能力中受益&#xff0c;您需要考虑创建培训手册&#xff0c;使员工能够胜任并保持他们的工作能力。 在过去&#xff0c;您可能认为培训手册是一本厚重的册子&#xff0c;充满了密…

如何理解电商云仓出租?

为了说清这个概念&#xff0c;我们先说一下云的概念。别看云现在已经漫天飞舞&#xff0c;但真正能准确的把它说明白的并不多&#xff0c;多数人只是跟着一起用而已。云起源于云计算&#xff0c;是指通过云计算技术各种终端设备手机、电脑、电视、车载屏幕等实现互通互联&#…

【教学类-19-03】20221127《ABBABB式-规律排序-A4竖版2份》(中班)

展示效果&#xff1a; 单人使用样式&#xff1a; 单页打印样式 ​ 背景需求&#xff1a; 中班幼儿需要掌握ABBABB的排序规律 前文制作了ABCABC单元格色块&#xff0c;完全套用ABC排序word表格&#xff0c;调整python的代码&#xff0c;随机生成ABB排序样式&#xff0c;引导幼…

1_SpringMVC_概述,2_SpringMVC_项目搭建

M model 模型层 DAO封装 >>> Mybatis V view 视图层 html css js jsp C controller 控制层 Servlet封装 >>> springMVC SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一 …

疫情下如何保持高效项目交付

作者&#xff1a;饶炯 疫情以来&#xff0c;我们工作和生活的正常秩序不时受到影响&#xff0c;项目开发过程中不时出现项目成员被封闭或开发现场封锁的情况&#xff0c;很多项目不得不临时选择远程开发模式&#xff0c;项目沟通和协作中出现了各种新的问题和困难&#xff0c;严…

[附源码]计算机毕业设计springboot4S店汽车售后服务管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Python库的使用

文章目录库的使用标准库认识标准库import导入模块使用示例&#xff1a;日期差计算使用示例&#xff1a;字符串操作1&#xff09;翻转单词顺序2&#xff09;旋转字符串3&#xff09;统计是给定字符串前缀的字符串数目第三方库认识第三方库包管理器pip使用示例&#xff1a;生成二…

QFile(文件)

QFile QFile提供一个用于读/写的接口&#xff0c;是一个可以用来读/写二进制文件的Qt资源的I/O设备&#xff0c;QFile可以单独使用&#xff0c;一般配合QTextStream或QDataStream 输入文件路径时最好使用"/"作为分隔符 构造函数&#xff1a; 常用的函数&#xff1a;…

前端甘特图组件开发(二)

自定义时间轴功能 由于常见的甘特图时间轴只支持按照天/周/月/年或者固定的时间单位进行划分&#xff0c;但实际使用场景下有时候会需要按照特定且不规则的时间段对进度数据进行直观划分。因此本组件在原时间轴的基础上添加新的自定义时间轴&#xff0c;可通过数据配置自定义时…

Spring Bean的作用域

1.写在前面 前面的博客我们已经介绍完Spring的依赖的查找来源&#xff0c;依赖注入的来源等等相关知识&#xff0c;今天我们继续来介绍Spring的Bean的作用域。 2.Spring Bean作用域 作用域 3.“singleton” Bean作用域 配置 4.“prototype” Bean作用域 配置 注意事项 Sp…

[Redis]-持久化方式

[Redis]-持久化方式 森格 | 2022年11月 本文是在学习Redis中&#xff0c;对Redis持久化的个人总结。 一、 持久化与Redis 1.1 什么是持久化 持久化是一种将程序数据在瞬时状态和持久状态间的转换机制&#xff0c;也就是把数据保存到可永久保存的存储设备中去。 1.2 Redis的持…

【Hack The Box】linux练习-- Writer

HTB 学习笔记 【Hack The Box】linux练习-- Writer &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月27日&#x1f334; &#x1f3…

《web课程设计》使用HTML+CSS制作大学生校园二手交易网站

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

阿里P8现身说法,解密“架构”原理与实战笔记:从分布式到微服务

分布式架构与微服务平台是当今IT界的关键技术&#xff0c;也是资深软件工程师和系统架构师必须掌握的核心技术。 微服务、云原生、Kubernetes、Service Mesh是分布式领域的热点技术&#xff0c;它们并不是凭空出现的&#xff0c;一定继承了某些“前辈”的优点。我们不仅要了解…

十万部冷知识:足球踢进了观众席,观众能把球拿走吗?

在看足球比赛的时候&#xff0c;我们经常会看到球员一脚射门踢偏了&#xff0c;球被打飞的情况&#xff0c;这时候球可就奔着观众席去了。就是因为考虑到有这种情况的发生&#xff0c;在比赛之前&#xff0c;比赛组织者&#xff0c;都会提前准备很多的备用球&#xff0c;当球被…

R11.8-11.8-11.8-11.8-BABSL

R11.8-11.8-11.8-11.8-BABSL R11.8-11.8-11.8-11.8-BABSL哈威柱塞泵宁波秉圣&#xff0c;主要特点是高自吸转速&#xff0c;工作效率高&#xff0c;结构紧凑&#xff0c;工作压力高等。广泛用于压力机器具&#xff0c;测试和实验室设施&#xff0c;润滑装置等设备&#xff0c;在…