• 1、前言
  • 2、自定义异常处理
  • 3、完整代码

1、前言

我们在项目开发中,难免碰到业务代码异常,无论是server 500, 还是其他异常。 我们这里简单说一下,如果抛弃try-catch,统一进行异常响应处理;

❝ 错误类型:
1、自定义业务服务代码异常(根据各自项目需求)
2、ServletException HTTP请求异常
3、内部代码异常:比如 mysql 查询表名错误
4、请求接口404:这个在统一异常中无法获取,需要额外处理

正常情况下,我们需要这么写:

❝ JobController

// 查询所有@GetMapping("/list")public Result getList() {Result result;try {List<Job> jobList = jobService.findAll();result = ResultGenerator.getSuccessResult(jobList);} catch (Exception e) {e.printStackTrace();result = ResultGenerator.getFailResult(e.getMessage());}return result;}

结果如图:

这样比较麻烦,要写很多try-catch.

那么有没有一种办法,帮我们自动拦截错误呢?

2、自定义异常处理

HandlerExceptionResolver, 就是处理异常的类;

❝ 源码:

public interface HandlerExceptionResolver {@NullableModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}

以上有四个参数其实相当于:request(请求)、response(响应)、hendler(处理器)、exception(异常);

接下来我们重写 WebMvcConfigurer.configureHandlerExceptionResolvers来处理异常:

❝ com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);// 统一异常处理@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {exceptionResolvers.add((request, response, handler, e) -> {Result result = new Result();// 错误处理if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());} else if (e instanceof ServletException) {// 2、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());} else {// 3、内部错误result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");String message;if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());} else {message = e.getMessage();}result.setMessage(message);logger.error(message, e);}responseResult(response, result);return new ModelAndView();});}// 处理返回数据格式private void responseResult(HttpServletResponse response, Result result) {response.setCharacterEncoding("UTF-8");response.setHeader("Content-type", "application/json;charset=UTF-8");response.setStatus(200);try {response.getWriter().write(JSON.toJSONString(result));} catch (IOException ex) {logger.error(ex.getMessage());}}}

❝ 1、业务失败的异常: ServiceException 服务异常类

package com.scaffold.test.base;/*** 服务(业务)异常如“ 账号或密码错误 ”,该异常只做INFO级别的日志记录 @see WebMvcConfigurer*/
public class ServiceException extends RuntimeException {public ServiceException() {}public ServiceException(String message) {super(message);}public ServiceException(String message, Throwable cause) {super(message, cause);}
}// 部分代码
if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());
}

报错400代码;

❝ 2、调用失败: Servlet异常

if (e instanceof ServletException) {// 3、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());
}

​ 结果如图:

❝ 3、内部代码异常:比如 mysql 查询表名错误

String message;
if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());
} else {message = e.getMessage();
}
result.setMessage(message);
logger.error(message, e);

结果如图:

❝ 4、请求接口404:这个在统一异常中无法获取,需要额外处理

404进入不了统一异常处理,如下图:

所以,全局异常处理捕获不到404错误,需要专门写一个类用来处理404异常

❝ com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class NotFoundException implements ErrorController {private static final String ERROR_PATH = "/error";@RequestMapping(ERROR_PATH)public Result error() {Result result = new Result();// 2、接口不存在result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");return result;}@Overridepublic String getErrorPath() {return ERROR_PATH;}
}

3、完整代码

❝ com.scaffold.test.base.Result: 统一返回实体类

package com.scaffold.test.base;import lombok.Data;/*** 统一API响应结果封装*/@Data
public class Result {private int code;private String message = "success";private Object data;public Result setCode(ResultCode resultCode){this.code = resultCode.code;return this;}public Result setMessage(String message){this.message = message;return this;}public Result setData(Object data){this.data = data;return this;}}

❝ com.scaffold.test.base.ResultCode: 统一返回响应码枚举,参考HTTP状态码

package com.scaffold.test.base;/*** 响应码枚举,参考HTTP状态码的语义*/public enum ResultCode {SUCCESS(200),//成功FAIL(400),//失败UNAUTHORIZED(401),//未认证(签名错误)NOT_FOUND(404),//接口不存在INTERNAL_SERVER_ERROR(500);//服务器内部错误public int code;ResultCode(int code) {this.code = code;}
}

❝ 统一异常拦截:com.scaffold.test.config.WebMvcConfig

package com.scaffold.test.config;import com.alibaba.fastjson.JSON;
import com.scaffold.test.base.Result;
import com.scaffold.test.base.ResultCode;
import com.scaffold.test.base.ServiceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;@Configuration
public class WebMvcConfig implements WebMvcConfigurer {private final Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class);// 统一异常处理@Overridepublic void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {exceptionResolvers.add((request, response, handler, e) -> {Result result = new Result();// 异常处理if (e instanceof ServiceException) {// 1、业务失败的异常,如“账号或密码错误”result.setCode(ResultCode.FAIL).setMessage(e.getMessage());logger.info(e.getMessage());}else if (e instanceof ServletException) {// 2、调用失败result.setCode(ResultCode.FAIL).setMessage(e.getMessage());} else {// 3、内部其他错误result.setCode(ResultCode.INTERNAL_SERVER_ERROR).setMessage("接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");String message;if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",request.getRequestURI(),handlerMethod.getBean().getClass().getName(),handlerMethod.getMethod().getName(),e.getMessage());} else {message = e.getMessage();}result.setMessage(message);logger.error(message, e);}responseResult(response, result);return new ModelAndView();});}// 处理响应数据格式private void responseResult(HttpServletResponse response, Result result) {response.setCharacterEncoding("UTF-8");response.setHeader("Content-type", "application/json;charset=UTF-8");response.setStatus(200);try {response.getWriter().write(JSON.toJSONString(result));} catch (IOException ex) {logger.error(ex.getMessage());}}
}

❝ 404拦截处理:com.scaffold.test.base.NotFoundException

package com.scaffold.test.base;import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class NotFoundException implements ErrorController {private static final String ERROR_PATH = "/error";@RequestMapping(ERROR_PATH)public Result error() {Result result = new Result();// 4、接口不存在result.setCode(ResultCode.NOT_FOUND).setMessage("接口不存在");return result;}@Overridepublic String getErrorPath() {return ERROR_PATH;}
}

本文使用 mdnice 排版

try catch对异常进行输出到日志、_spring Boot手把手教学(7): 抛弃try-catch, 如何优雅统一处理异常(含404)...相关推荐

  1. java 如何去掉http debug日志_Spring Boot手把手教学(3):从零配置logback日志

    前言 使用logback我们可以自定义控制台日志输出,可以实现对不同日志不同级别打印到不同的文件中,可以对日志进行归档保存,并定时删除: 对于一个应用来讲,一个好的日志系统,对于对问题的追溯有很大的帮 ...

  2. try catch对异常进行输出到日志、_java安全编码指南之:异常处理

    点击上方的蓝字关注我吧 程序那些事 简介 异常是java程序员无法避免的一个话题,我们会有JVM自己的异常也有应用程序的异常,对于不同的异常,我们的处理原则是不是一样的呢? 一起来看看吧. 异常简介 ...

  3. springboot 控制台输出错误信息_Spring boot使用logback实现日志配置

    前言 日志是我们系统必备的功能之一,可以帮助我们开发人员定位系统的异常.错误以及运行流程的重要的工具.今天老顾就来介绍一下Spring boot的默认的logback日志框架. 常用日志组件 java ...

  4. springboot日志配输出路径配置_Spring Boot 日志配置方法(超详细)

    默认日志 Logback : 默认情况下,Spring Boot会用Logback来记录日志,并用INFO级别输出到控制台.在运行应用程序和其他例子时,你应该已经看到很多INFO级别的日志了. 从上图 ...

  5. log4j 压缩日志_Spring Boot 日志各种使用姿势,是时候捋清楚了!

    来自公众号:江南一点雨 1. Java 日志概览 1.1 总体概览 1.2 日志级别 1.3 综合对比 1.4 最佳实践 2. Spring Boot 日志实现 2.1 Spring Boot 日志配 ...

  6. main方法 如何去掉http debug日志_Spring Boot 常见错误及解决方法

    找不到配置?配置不对?配置被覆盖? Spring Boot 配置加载过程解析: 1.Spring Boot 配置的加载有着约定俗成的步骤: 从 resources 目录下加载 application. ...

  7. springboot打印sql日志_Spring boot 工程,http打印日志太多

    问题描述:在Idea里创建了一个spring boot工程,写了一个类,主方法访问http接口,http client打印的日志太多,设置日志级别也不管用. 代码截图: 控制台输出: 求助?????

  8. log4j记录不同的日志_Spring boot中使用log4j记录日志

    之前在Spring Boot日志管理 一文中主要介绍了Spring Boot中默认日志工具(logback)的基本配置内容.对于很多习惯使用log4j的开发者,Spring Boot依然可以很好的支持 ...

  9. springboot开启debug日志_Spring Boot SLF4J日志实例(五十)

    默认情况下,SLF4j日志记录包含在Spring Boot Web应用程序中,只需要启用它就可以了. 注意:查看此Spring Boot Logback XML模板以了解默认的日志记录模式和配置. S ...

最新文章

  1. 微型计算机硬件中访问速度最快的,习题一计算机基础知识.doc.doc
  2. 分享HTML5 canvas 的总结
  3. 如何查询SAP Cloud for Customer系统升级和维护时间
  4. php实现tcp连接的原理,PHP实现TCP实例
  5. 最大公约数python语言算法_使用Python求解最大公约数的实现方法
  6. 生物医学基础--右腿驱动
  7. 获取Button脚本挂载的事件名
  8. 通用 USB 重定向和客户端驱动器注意事项
  9. JavaScript刷新和跳转
  10. 如何压缩PDF文件的大小
  11. 哈希碰撞,改变世界的原力
  12. 使用VLAN隔离虚拟机流量
  13. spring和jump区别_JUMP与 SKIP 的区别
  14. 多家高校网站被挂马 用户应小心QQ盗号木马
  15. latex 上标 下标怎么打
  16. iphone图片编辑画笔_iPhone手机怎么编辑图片?还不知道的话真的要了解一波了~...
  17. 仿照Flexstroe3写的一个员工管理应用 (三)
  18. 写给IT自学者的入门指南
  19. dsolve 的 用法
  20. 舞蹈迁移:EverybodyDanceNow reproduced in pytorch

热门文章

  1. 优秀的软件测试人员必需具备的素质
  2. 源码编译安装php-3.5.8
  3. FLUSH TABLES WITH READ LOCK有多快
  4. Linux 命令(20)—— cat 命令
  5. 爱的十个秘密--3.尊重的力量
  6. 签名别人的公钥以及验证签名的公钥
  7. [原创][Windows] Win7安装visual c++ 2015 redistributable x64失败
  8. oracle创建表空间语句分解
  9. iScroll框架的修改
  10. 在线HTML标签清除工具