1. 继承 ResponseEntityExceptionHandler 类来实现针对 Rest 接口 的全局异常捕获,并且可以返回自定义格式:
  2.  
  3. 复制代码
  4. 1 @Slf4j
  5. 2 @ControllerAdvice
  6. 3 public class ExceptionHandlerBean extends ResponseEntityExceptionHandler {
  7. 4
  8. 5 /**
  9. 6 * 数据找不到异常
  10. 7 * @param ex
  11. 8 * @param request
  12. 9 * @return
  13. 10 * @throws IOException
  14. 11 */
  15. 12 @ExceptionHandler({DataNotFoundException.class})
  16. 13 public ResponseEntity<Object> handleDataNotFoundException(RuntimeException ex, WebRequest request) throws IOException {
  17. 14 return getResponseEntity(ex,request,ReturnStatusCode.DataNotFoundException);
  18. 15 }
  19. 16
  20. 17 /**
  21. 18 * 根据各种异常构建 ResponseEntity 实体. 服务于以上各种异常
  22. 19 * @param ex
  23. 20 * @param request
  24. 21 * @param specificException
  25. 22 * @return
  26. 23 */
  27. 24 private ResponseEntity<Object> getResponseEntity(RuntimeException ex, WebRequest request, ReturnStatusCode specificException) {
  28. 25
  29. 26 ReturnTemplate returnTemplate = new ReturnTemplate();
  30. 27 returnTemplate.setStatusCode(specificException);
  31. 28 returnTemplate.setErrorMsg(ex.getMessage());
  32. 29
  33. 30 return handleExceptionInternal(ex, returnTemplate,
  34. 31 new HttpHeaders(), HttpStatus.OK, request);
  35. 32 }
  36. 33
  37. 34 }
  38. 复制代码

  

转:

https://www.cnblogs.com/junzi2099/p/7840294.html

spring处理异常的三种方式:

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

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

  1. /**
  2. * 手动处理 Service 层异常和数据校验异常的示例
  3. * @param dog
  4. * @param errors
  5. * @return
  6. */
  7. @PostMapping(value = "")
  8. AppResponse add(@Validated(Add.class) @RequestBody Dog dog, Errors errors){
  9. AppResponse resp = new AppResponse();
  10. try {
  11. // 数据校验
  12. BSUtil.controllerValidate(errors);
  13. // 执行业务
  14. Dog newDog = dogService.save(dog);
  15. // 返回数据
  16. resp.setData(newDog);
  17. }catch (BusinessException e){
  18. LOGGER.error(e.getMessage(), e);
  19. resp.setFail(e.getMessage());
  20. }catch (Exception e){
  21. LOGGER.error(e.getMessage(), e);
  22. resp.setFail("操作失败!");
  23. }
  24. return resp;
  25. }
  • 本文讲解使用 @ControllerAdvice + @ExceptionHandler 进行全局的 Controller 层异常处理,只要设计得当,就再也不用在 Controller 层进行 try-catch 了!而且,@Validated 校验器注解的异常,也可以一起处理,无需手动判断绑定校验结果 BindingResult/Errors 了!

一、优缺点

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

二、基本使用示例

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

  1. @ControllerAdvice
  2. public class GlobalExceptionHandler {
  3. }

请确保此 GlobalExceptionHandler 类能被扫描到并装载进 Spring 容器中。

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

  1. @ControllerAdvice
  2. public class GlobalExceptionHandler {
  3. @ExceptionHandler(Exception.class)
  4. @ResponseBody
  5. String handleException(){
  6. return "Exception Deal!";
  7. }
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

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

  1. @Target(ElementType.METHOD)
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface ExceptionHandler {
  5. /**
  6. * Exceptions handled by the annotated method. If empty, will default to any
  7. * exceptions listed in the method argument list.
  8. */
  9. Class<? extends Throwable>[] value() default {};
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

  1. @ControllerAdvice
  2. public class GlobalExceptionHandler {
  3. @ExceptionHandler()
  4. @ResponseBody
  5. String handleException(Exception e){
  6. return "Exception Deal! " + e.getMessage();
  7. }
  8. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

三、处理 Service 层上抛的业务异常

有时我们会在复杂的带有数据库事务的业务中,当出现不和预期的数据时,直接抛出封装后的业务级运行时异常,进行数据库事务回滚,并希望该异常信息能被返回显示给用户。

3.1 代码示例

封装的业务异常类:

  1. public class BusinessException extends RuntimeException {
  2. public BusinessException(String message){
  3. super(message);
  4. }
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Service 实现类:

  1. @Service
  2. public class DogService {
  3. @Transactional
  4. public Dog update(Dog dog){
  5. // some database options
  6. // 模拟狗狗新名字与其他狗狗的名字冲突
  7. BSUtil.isTrue(false, "狗狗名字已经被使用了...");
  8. // update database dog info
  9. return dog;
  10. }
  11. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

其中辅助工具类 BSUtil

  1. public static void isTrue(boolean expression, String error){
  2. if(!expression) {
  3. throw new BusinessException(error);
  4. }
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5

那么,我们应该在 GlobalExceptionHandler 类中声明该业务异常类,并进行相应的处理,然后返回给用户。更贴近真实项目的代码,应该长这样子:

  1. /**
  2. * Created by kinginblue on 2017/4/10.
  3. * @ControllerAdvice + @ExceptionHandler 实现全局的 Controller 层的异常处理
  4. */
  5. @ControllerAdvice
  6. public class GlobalExceptionHandler {
  7. private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  8. /**
  9. * 处理所有不可知的异常
  10. * @param e
  11. * @return
  12. */
  13. @ExceptionHandler(Exception.class)
  14. @ResponseBody
  15. AppResponse handleException(Exception e){
  16. LOGGER.error(e.getMessage(), e);
  17. AppResponse response = new AppResponse();
  18. response.setFail("操作失败!");
  19. return response;
  20. }
  21. /**
  22. * 处理所有业务异常
  23. * @param e
  24. * @return
  25. */
  26. @ExceptionHandler(BusinessException.class)
  27. @ResponseBody
  28. AppResponse handleBusinessException(BusinessException e){
  29. LOGGER.error(e.getMessage(), e);
  30. AppResponse response = new AppResponse();
  31. response.setFail(e.getMessage());
  32. return response;
  33. }
  34. }
  • ontroller 层的代码,就不需要进行异常处理了:
  1. @RestController
  2. @RequestMapping(value = "/dogs", consumes = {MediaType.APPLICATION_JSON_UTF8_VALUE})
  3. public class DogController {
  4. @Autowired
  5. private DogService dogService;
  6. @PatchMapping(value = "")
  7. Dog update(@Validated(Update.class) @RequestBody Dog dog){
  8. return dogService.update(dog);
  9. }
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

3.2 代码说明

Logger 进行所有的异常日志记录。

@ExceptionHandler(BusinessException.class) 声明了对 BusinessException 业务异常的处理,并获取该业务异常中的错误提示,构造后返回给客户端。

@ExceptionHandler(Exception.class) 声明了对 Exception 异常的处理,起到兜底作用,不管 Controller 层执行的代码出现了什么未能考虑到的异常,都返回统一的错误提示给客户端。

备注:以上 GlobalExceptionHandler 只是返回 Json 给客户端,更大的发挥空间需要按需求情况来做。

四、处理 Controller 数据绑定、数据校验的异常

在 Dog 类中的字段上的注解数据校验规则:

  1. @Data
  2. public class Dog {
  3. @NotNull(message = "{Dog.id.non}", groups = {Update.class})
  4. @Min(value = 1, message = "{Dog.age.lt1}", groups = {Update.class})
  5. private Long id;
  6. @NotBlank(message = "{Dog.name.non}", groups = {Add.class, Update.class})
  7. private String name;
  8. @Min(value = 1, message = "{Dog.age.lt1}", groups = {Add.class, Update.class})
  9. private Integer age;
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  1. 说明:@NotNull@Min@NotBlank 这些注解的使用方法,不在本文范围内。如果不熟悉,请查找资料学习即可。
  2. 其他说明:
  3. @Data 注解是 **Lombok** 项目的注解,可以使我们不用再在代码里手动加 getter & setter
  4. Eclipse IntelliJ IDEA 中使用时,还需要安装相关插件,这个步骤自行Google/Baidu 吧!
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Lombok 使用方法见:Java奇淫巧技之Lombok

SpringMVC 中对于 RESTFUL 的 Json 接口来说,数据绑定和校验,是这样的:

  1. /**
  2. * 使用 GlobalExceptionHandler 全局处理 Controller 层异常的示例
  3. * @param dog
  4. * @return
  5. */
  6. @PatchMapping(value = "")
  7. AppResponse update(@Validated(Update.class) @RequestBody Dog dog){
  8. AppResponse resp = new AppResponse();
  9. // 执行业务
  10. Dog newDog = dogService.update(dog);
  11. // 返回数据
  12. resp.setData(newDog);
  13. return resp;
  14. }

使用 @Validated + @RequestBody 注解实现。

当使用了 @Validated + @RequestBody 注解但是没有在绑定的数据对象后面跟上 Errors 类型的参数声明的话,Spring MVC 框架会抛出 MethodArgumentNotValidException 异常。

所以,在 GlobalExceptionHandler 中加上对 MethodArgumentNotValidException 异常的声明和处理,就可以全局处理数据校验的异常了!加完后的代码如下:

  1. /**
  2. * Created by kinginblue on 2017/4/10.
  3. * @ControllerAdvice + @ExceptionHandler 实现全局的 Controller 层的异常处理
  4. */
  5. @ControllerAdvice
  6. public class GlobalExceptionHandler {
  7. private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);
  8. /**
  9. * 处理所有不可知的异常
  10. * @param e
  11. * @return
  12. */
  13. @ExceptionHandler(Exception.class)
  14. @ResponseBody
  15. AppResponse handleException(Exception e){
  16. LOGGER.error(e.getMessage(), e);
  17. AppResponse response = new AppResponse();
  18. response.setFail("操作失败!");
  19. return response;
  20. }
  21. /**
  22. * 处理所有业务异常
  23. * @param e
  24. * @return
  25. */
  26. @ExceptionHandler(BusinessException.class)
  27. @ResponseBody
  28. AppResponse handleBusinessException(BusinessException e){
  29. LOGGER.error(e.getMessage(), e);
  30. AppResponse response = new AppResponse();
  31. response.setFail(e.getMessage());
  32. return response;
  33. }
  34. /**
  35. * 处理所有接口数据验证异常
  36. * @param e
  37. * @return
  38. */
  39. @ExceptionHandler(MethodArgumentNotValidException.class)
  40. @ResponseBody
  41. AppResponse handleMethodArgumentNotValidException(MethodArgumentNotValidException e){
  42. LOGGER.error(e.getMessage(), e);
  43. AppResponse response = new AppResponse();
  44. response.setFail(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
  45. return response;
  46. }
  47. }

注意到了吗,所有的 Controller 层的异常的日志记录,都是在这个 GlobalExceptionHandler 中进行记录。也就是说,Controller 层也不需要在手动记录错误日志了。

五、总结

本文主要讲 @ControllerAdvice + @ExceptionHandler 组合进行的 Controller 层上抛的异常全局统一处理。

其实,被 @ExceptionHandler 注解的方法还可以声明很多参数,详见文档。

@ControllerAdvice 也还可以结合 @InitBinder、@ModelAttribute 等注解一起使用,应用在所有被 @RequestMapping 注解的方法上,详见搜索引擎。

转:@ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常的更多相关文章

  1. @ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常==》记录

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

  2. 【统一异常处理】@ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常

    1.利用springmvc注解对Controller层异常全局处理 对于与数据库相关的 Spring MVC 项目,我们通常会把 事务 配置在 Service层,当数据库操作失败时让 Service ...

  3. @ControllerAdvice + @ExceptionHandler全局处理Controller层异常

    import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind ...

  4. @ControllerAdvice + @ExceptionHandler 全局处理 Controller 层异常

    @ControllerAdvice 和 @ExceptionHandler 的区别 ExceptionHandler, 方法注解, 作用于 Controller 级别. ExceptionHandle ...

  5. @ControllerAdvice + @ExceptionHandler 处理 全部Controller层异常

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

  6. SpringBoot入门教程(十九)@ControllerAdvice+@ExceptionHandler全局捕获Controller异常

    在spring 3.2中,新增了@ControllerAdvice 注解,可以用于定义@ExceptionHandler.@InitBinder.@ModelAttribute,并应用到所有@Requ ...

  7. Spring @ControllerAdvice @ExceptionHandler 全局处理异常

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

  8. (转)springboot全局处理异常(@ControllerAdvice + @ExceptionHandler)

    1.@ControllerAdvice 1.场景一 在构建RestFul的今天,我们一般会限定好返回数据的格式比如: { "code": 0, "data": ...

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

    1.@ControllerAdvice 1.场景一 在构建RestFul的今天,我们一般会限定好返回数据的格式比如: { "code": 0, "data": ...

随机推荐

  1. 雅思听听app

    最近本人呢,正在紧张的备战雅思考试,因为英语基础很弱,尤其是听力,所以老师推荐了雅思听听这个app,说是特别好使,用了一个多月的,总体来说感觉还是很nice的,但是还有一些小毛病,不过这小毛病瑕不掩瑜 ...

  2. [BUAA软工]第一次博客作业---阅读《构建之法》

    [BUAA软工]第一次博客作业 项目 内容 这个作业属于哪个课程 北航软工 这个作业的要求在哪里 第1次个人作业 我在这个课程的目标是 学习如何以团队的形式开发软件,提升个人软件开发能力 这个作业在哪 ...

  3. 实验二Java面向对象程序设计_20135129李畅宇

    ava第二次实验报告   课程:Java实验   班级:201352     姓名:池彬宁  学号:20135212 成绩:             指导教师:娄佳鹏   实验日期:15.05.05 ...

  4. git学习笔记2——ProGit2

    先附上教程--<ProGit 2> 配置信息 Git 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量. 这些变量存储在三个不同的位置: /etc/git ...

  5. docker 下运行 postgresql 的命令

    postgresql docker下启动的命令 docker run --name pgdata -p : -e POSTGRES_PASSWORD=Test6530 -v /pgdata:/var/ ...

  6. OneZero——Review报告会

    1. 时间: 2016年4月20日. 2. 成员: X 夏一鸣 * 组长 (博客:http://www.cnblogs.com/xiaym896/), G 郭又铭 (博客:http://www.cnb ...

  7. JavaScript高级程序设计 第六章 面向对象程序设计

    面向对象程序设计 ECMA-262将对象定义为:“无序属性的集合,其属性可以包含基本值.对象或者函数.”严格来讲,这就相当于说对象是一组没有特定顺序的值.对象的每个属性和方法都有一个名字,而每个名字都 ...

  8. linux bin & sbin different

    linux bin & sbin different flutter & $PATH http://blog.taylormcgann.com/2014/04/11/differenc ...

  9. 修改maven的默认jdk版本

    问题: 创建maven项目的时候,jdk版本默认使用的是1.5版本. 解决: 1.修改maven的settings.xml文件,添加如下: <profile> <id>jdk- ...

  10. 百度/头条合作命中注定!中国新BAT要来了

    据外媒报道,今日头条母公司字节跳动(ByteDace)将为中国互联网传统BAT的格局,带来一些新的活力.这家增速飞快的新闻.视频App“制造者”已经估值高达750亿美元,与三巨头之一的百度平起平坐,后 ...