Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 480|回复: 0

十四、springboot全局处理异常(@ControllerAdvice + @ExceptionHandler)

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-9-4 15:58:56 | 显示全部楼层 |阅读模式

    1.@ControllerAdvice

    1.场景一

    在构建RestFul的今天,我们一般会限定好返回数据的格式比如:

    {
    
      "code": 0,
      "data": {},
      "msg": "操作成功"
    
    }

      但有时却往往会产生一些bug。这时候就破坏了返回数据的一致性,导致调用者无法解析。所以我们常常会定义一个全局的异常拦截器。

    2.场景二

      对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service 层抛出运行时异常,Spring 事物管理器就会进行回滚。

    如此一来,我们的 Controller 层就不得不进行 try-catch Service 层的异常,否则会返回一些不友好的错误信息到客户端。但是,Controller 层每个方法体都写一些模板化的 try-catch 的代码,很难看也难维护,特别是还需要对 Service 层的不同异常进行不同处理的时候。

      @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,只要设计得当,就再也不用在 Controller 层进行 try-catch 了!而且,@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了

    3.@ControllerAdvice的使用

    1.注意

    • 优点:将 Controller 层的异常和数据校验的异常进行统一处理,减少模板代码,减少编码量,提升扩展性和可维护性。
    • 缺点:只能处理 Controller 层未捕获(往外抛)的异常,对于 Interceptor(拦截器)层的异常,Spring 框架层的异常,就无能为力了。

    2.@ControllerAdvice

      该注解是springMVC的注解

    • @ControllerAdvice是一个@Component,用于定义@ExceptionHandler@InitBinder@ModelAttribute方法,适用于所有使用@RequestMapping方法。

    • Spring4之前,@ControllerAdvice在同一调度的Servlet中协助所有控制器。Spring4已经改变:@ControllerAdvice支持配置控制器的子集,而默认的行为仍然可以利用。

    • 在Spring4中, @ControllerAdvice通过annotations()basePackageClasses()basePackages()方法定制用于选择控制器子集。

      不过据经验之谈,只有配合@ExceptionHandler最有用,其它两个不常用。

    3.使用

      @ControllerAdvice 注解定义全局异常处理类

    @ControllerAdvice
    public class GlobalExceptionHandler {
    }

      @ExceptionHandler 注解声明异常处理方法

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler(Exception.class)
        @ResponseBody
        String handleException(){
            return "Exception Deal!";
        }
    }

      方法 handleException() 就会处理所有 Controller 层抛出的 Exception 及其子类的异常,这是最基本的用法了。

    被 @ExceptionHandler 注解的方法的参数列表里,还可以声明很多种类型的参数,详见文档。其原型如下:

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ExceptionHandler {
    
        /**
         * Exceptions handled by the annotated method. If empty, will default to any
         * exceptions listed in the method argument list.
         */
        Class<? extends Throwable>[] value() default {};
    
    }

    如果 @ExceptionHandler 注解中未声明要处理的异常类型,则默认为参数列表中的异常类型。所以上面的写法,还可以写成这样:

    @ControllerAdvice
    public class GlobalExceptionHandler {
    
        @ExceptionHandler()
        @ResponseBody
        String handleException(Exception e){
            return "Exception Deal! " + e.getMessage();
        }
    }

    参数对象就是 Controller 层抛出的异常对象!

    示例

    package com.cmc.schedule.handler;  
      
    import com.gionee.base.entity.JsonResult;  
    import org.slf4j.Logger;  
    import org.slf4j.LoggerFactory;  
    import org.springframework.beans.ConversionNotSupportedException;  
    import org.springframework.beans.TypeMismatchException;  
    import org.springframework.http.converter.HttpMessageNotReadableException;  
    import org.springframework.http.converter.HttpMessageNotWritableException;  
    import org.springframework.web.HttpMediaTypeNotAcceptableException;  
    import org.springframework.web.HttpRequestMethodNotSupportedException;  
    import org.springframework.web.bind.MissingServletRequestParameterException;  
    import org.springframework.web.bind.annotation.ControllerAdvice;  
    import org.springframework.web.bind.annotation.ExceptionHandler;  
    import org.springframework.web.bind.annotation.ResponseBody;  
      
    import java.io.IOException;  
      
    /**  
     * 异常拦截处理器  
     *  
     * @author chenmc  
     */  
    @ControllerAdvice  
    @ResponseBody  
    public class GlobalExceptionHandler {  
      
        private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";  
        private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);  
      
        //运行时异常    
        @ExceptionHandler(RuntimeException.class)  
        public String runtimeExceptionHandler(RuntimeException ex) {  
            return resultFormat(1, ex);  
        }  
      
        //空指针异常    
        @ExceptionHandler(NullPointerException.class)  
        public String nullPointerExceptionHandler(NullPointerException ex) {  
            return resultFormat(2, ex);  
        }  
      
        //类型转换异常    
        @ExceptionHandler(ClassCastException.class)  
        public String classCastExceptionHandler(ClassCastException ex) {  
            return resultFormat(3, ex);  
        }  
      
        //IO异常    
        @ExceptionHandler(IOException.class)  
        public String iOExceptionHandler(IOException ex) {  
            return resultFormat(4, ex);  
        }  
      
        //未知方法异常    
        @ExceptionHandler(NoSuchMethodException.class)  
        public String noSuchMethodExceptionHandler(NoSuchMethodException ex) {  
            return resultFormat(5, ex);  
        }  
      
        //数组越界异常    
        @ExceptionHandler(IndexOutOfBoundsException.class)  
        public String indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException ex) {  
            return resultFormat(6, ex);  
        }  
      
        //400错误    
        @ExceptionHandler({HttpMessageNotReadableException.class})  
        public String requestNotReadable(HttpMessageNotReadableException ex) {  
            System.out.println("400..requestNotReadable");  
            return resultFormat(7, ex);  
        }  
      
        //400错误    
        @ExceptionHandler({TypeMismatchException.class})  
        public String requestTypeMismatch(TypeMismatchException ex) {  
            System.out.println("400..TypeMismatchException");  
            return resultFormat(8, ex);  
        }  
      
        //400错误    
        @ExceptionHandler({MissingServletRequestParameterException.class})  
        public String requestMissingServletRequest(MissingServletRequestParameterException ex) {  
            System.out.println("400..MissingServletRequest");  
            return resultFormat(9, ex);  
        }  
      
        //405错误    
        @ExceptionHandler({HttpRequestMethodNotSupportedException.class})  
        public String request405(HttpRequestMethodNotSupportedException ex) {  
            return resultFormat(10, ex);  
        }  
      
        //406错误    
        @ExceptionHandler({HttpMediaTypeNotAcceptableException.class})  
        public String request406(HttpMediaTypeNotAcceptableException ex) {  
            System.out.println("406...");  
            return resultFormat(11, ex);  
        }  
      
        //500错误    
        @ExceptionHandler({ConversionNotSupportedException.class, HttpMessageNotWritableException.class})  
        public String server500(RuntimeException ex) {  
            System.out.println("500...");  
            return resultFormat(12, ex);  
        }  
      
        //栈溢出  
        @ExceptionHandler({StackOverflowError.class})  
        public String requestStackOverflow(StackOverflowError ex) {  
            return resultFormat(13, ex);  
        }  
      
        //其他错误  
        @ExceptionHandler({Exception.class})  
        public String exception(Exception ex) {  
            return resultFormat(14, ex);  
        }  
      
        private <T extends Throwable> String resultFormat(Integer code, T ex) {  
            ex.printStackTrace();  
            log.error(String.format(logExceptionFormat, code, ex.getMessage()));  
            return JsonResult.failed(code, ex.getMessage());  
        }  
      
    } 

    注:  

      springboot中这样就可以了!!!

      如果是springMVC配置文件中增加下面的配置

    <!-- 处理异常 -->  
        <context:component-scan base-package="com.gionee.xo" use-default-filters="false">    
            <!-- base-package 如果多个,用“,”分隔 -->    
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />    
            <!--控制器增强,使一个Contoller成为全局的异常处理类,类中用@ExceptionHandler方法注解的方法可以处理所有Controller发生的异常-->    
            <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />    
        </context:component-scan>  

    参考:

      https://blog.csdn.net/kinginblue/article/details/70186586

      https://blog.csdn.net/u014044812/article/details/78219692

      https://blog.csdn.net/w372426096/article/details/78429141

        

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-7 08:28 , Processed in 0.073589 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表