当前位置: 首页 > news >正文

上海网站建设门户医疗室内设计网站推荐

上海网站建设门户,医疗室内设计网站推荐,网站整体风格设计,网站备案包括空间内容吗在SpringBoot的开发中,为了提高程序运行的鲁棒性,我们经常需要对各种程序异常进行处理,但是如果在每个出异常的地方进行单独处理的话,这会引入大量业务不相关的异常处理代码,增加了程序的耦合,同时未来想改…

在SpringBoot的开发中,为了提高程序运行的鲁棒性,我们经常需要对各种程序异常进行处理,但是如果在每个出异常的地方进行单独处理的话,这会引入大量业务不相关的异常处理代码,增加了程序的耦合,同时未来想改变异常的处理逻辑,也变得比较困难。这篇文章带大家了解一下如何优雅的进行全局异常处理。

为了实现全局拦截,这里使用到了Spring中提供的两个注解,@RestControllerAdvice和@ExceptionHandler,结合使用可以拦截程序中产生的异常,并且根据不同的异常类型分别处理。下面我会先介绍如何利用这两个注解,优雅的完成全局异常的处理,接着解释这背后的原理。

1. 如何实现全局拦截?

1.1 自定义异常处理类

在下面的例子中,我们继承了
ResponseEntityExceptionHandler并使用@RestControllerAdvice注解了这个类,接着结合@ExceptionHandler针对不同的异常类型,来定义不同的异常处理方法。这里可以看到我处理的异常是自定义异常,后续我会展开介绍。


ResponseEntityExceptionHandler中包装了各种SpringMVC在处理请求时可能抛出的异常的处理,处理结果都是封装成一个ResponseEntity对象。ResponseEntityExceptionHandler是一个抽象类,通常我们需要定义一个用来处理异常的使用@RestControllerAdvice注解标注的异常处理类来继承自ResponseEntityExceptionHandler。ResponseEntityExceptionHandler中为每个异常的处理都单独定义了一个方法,如果默认的处理不能满足你的需求,则可以重写对某个异常的处理。

java复制代码@Log4j2  
@RestControllerAdvice  
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {  /**  * 定义要捕获的异常 可以多个 @ExceptionHandler({})     *  * @param request  request  * @param e        exception  * @param response response  * @return 响应结果  */  @ExceptionHandler(AuroraRuntimeException.class)  public GenericResponse customExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {  AuroraRuntimeException exception = (AuroraRuntimeException) e;  if (exception.getCode() == ResponseCode.USER_INPUT_ERROR) {  response.setStatus(HttpStatus.BAD_REQUEST.value());  } else if (exception.getCode() == ResponseCode.FORBIDDEN) {  response.setStatus(HttpStatus.FORBIDDEN.value());  } else {  response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());  }  return new GenericResponse(exception.getCode(), null, exception.getMessage());  }  @ExceptionHandler(NotLoginException.class)  public GenericResponse tokenExceptionHandler(HttpServletRequest request, final Exception e, HttpServletResponse response) {  log.error("token exception", e);  response.setStatus(HttpStatus.FORBIDDEN.value());  return new GenericResponse(ResponseCode.AUTHENTICATION_NEEDED);  }  }

1.2 定义异常码

这里定义了常见的几种异常码,主要用在抛出自定义异常时,对不同的情形进行区分。

java复制代码@Getter  
public enum ResponseCode {  SUCCESS(0, "Success"),  INTERNAL_ERROR(1, "服务器内部错误"),  USER_INPUT_ERROR(2, "用户输入错误"),  AUTHENTICATION_NEEDED(3, "Token过期或无效"),  FORBIDDEN(4, "禁止访问"),  TOO_FREQUENT_VISIT(5, "访问太频繁,请休息一会儿");  private final int code;  private final String message;  private final Response.Status status;  ResponseCode(int code, String message, Response.Status status) {  this.code = code;  this.message = message;  this.status = status;  }  ResponseCode(int code, String message) {  this(code, message, Response.Status.INTERNAL_SERVER_ERROR);  }  }

1.3 自定义异常类

这里我定义了一个AuroraRuntimeException的异常,就是在上面的异常处理函数中,用到的异常。每个异常实例会有一个对应的异常码,也就是前面刚定义好的。

java复制代码@Getter  
public class AuroraRuntimeException extends RuntimeException {  private final ResponseCode code;  public AuroraRuntimeException() {  super(String.format("%s", ResponseCode.INTERNAL_ERROR.getMessage()));  this.code = ResponseCode.INTERNAL_ERROR;  }  public AuroraRuntimeException(Throwable e) {  super(e);  this.code = ResponseCode.INTERNAL_ERROR;  }  public AuroraRuntimeException(String msg) {  this(ResponseCode.INTERNAL_ERROR, msg);  }  public AuroraRuntimeException(ResponseCode code) {  super(String.format("%s", code.getMessage()));  this.code = code;  }  public AuroraRuntimeException(ResponseCode code, String msg) {  super(msg);  this.code = code;  }  }

1.4 自定义返回类型

为了保证各个接口的返回统一,这里专门定义了一个返回类型。

java复制代码@Getter  
@Setter  
public class GenericResponse<T> {  private int code;  private T data;  private String message;  public GenericResponse() {};  public GenericResponse(int code, T data) {  this.code = code;  this.data = data;  }  public GenericResponse(int code, T data, String message) {  this(code, data);  this.message = message;  }  public GenericResponse(ResponseCode responseCode) {  this.code = responseCode.getCode();  this.data = null;  this.message = responseCode.getMessage();  }  public GenericResponse(ResponseCode responseCode, T data) {  this(responseCode);  this.data = data;  }  public GenericResponse(ResponseCode responseCode, T data, String message) {  this(responseCode, data);  this.message = message;  }  
}

实际测试异常

下面的例子中,我们想获取到用户的信息,如果用户的信息不存在,可以直接抛出一个异常,这个异常会被我们上面定义的全局异常处理方法所捕获,然后根据不同的异常编码,完成不同的处理和返回。

java复制代码public User getUserInfo(Long userId) {  // some logicUser user = daoFactory.getExtendedUserMapper().selectByPrimaryKey(userId);  if (user == null) {  throw new AuroraRuntimeException(ResponseCode.USER_INPUT_ERROR, "用户id不存在");  }// some logic....
}

以上就完成了整个全局异常的处理过程,接下来重点说说为什么@RestControllerAdvice和@ExceptionHandler结合使用可以拦截程序中产生的异常?

全局拦截的背后原理?

下面会提到@ControllerAdvice注解,简单地说,@RestControllerAdvice与@ControllerAdvice的区别就和@RestController与@Controller的区别类似,@RestControllerAdvice注解包含了@ControllerAdvice注解和@ResponseBody注解。

接下来我们深入Spring源码,看看是怎么实现的,首先DispatcherServlet对象在创建时会初始化一系列的对象,这里重点关注函数
initHandlerExceptionResolvers(context);.

java复制代码public class DispatcherServlet extends FrameworkServlet {// ......protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);// 重点关注initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}// ......
}


initHandlerExceptionResolvers(context)方法中,会取得所有实现了HandlerExceptionResolver接口的bean并保存起来,其中就有一个类型为ExceptionHandlerExceptionResolver的bean,这个bean在应用启动过程中会获取所有被@ControllerAdvice注解标注的bean对象做进一步处理,关键代码在这里:

java复制代码public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolverimplements ApplicationContextAware, InitializingBean {// ......private void initExceptionHandlerAdviceCache() {// ......List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());AnnotationAwareOrderComparator.sort(adviceBeans);for (ControllerAdviceBean adviceBean : adviceBeans) {ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());if (resolver.hasExceptionMappings()) {// 找到所有ExceptionHandler标注的方法并保存成一个ExceptionHandlerMethodResolver类型的对象缓存起来this.exceptionHandlerAdviceCache.put(adviceBean, resolver);if (logger.isInfoEnabled()) {logger.info("Detected @ExceptionHandler methods in " + adviceBean);}}// ......}}// ......
}

当Controller抛出异常时,DispatcherServlet通过
ExceptionHandlerExceptionResolver来解析异常,而ExceptionHandlerExceptionResolver又通过ExceptionHandlerMethodResolver 来解析异常, ExceptionHandlerMethodResolver 最终解析异常找到适用的@ExceptionHandler标注的方法是这里:

java复制代码public class ExceptionHandlerMethodResolver {// ......private Method getMappedMethod(Class<? extends Throwable> exceptionType) {List<Class<? extends Throwable>> matches = new ArrayList<Class<? extends Throwable>>();// 找到所有适用于Controller抛出异常的处理方法,例如Controller抛出的异常// 是AuroraRuntimeException(继承自RuntimeException),那么@ExceptionHandler(AuroraRuntimeException.class)和// @ExceptionHandler(Exception.class)标注的方法都适用此异常for (Class<? extends Throwable> mappedException : this.mappedMethods.keySet()) {if (mappedException.isAssignableFrom(exceptionType)) {matches.add(mappedException);}}if (!matches.isEmpty()) {/* 这里通过排序找到最适用的方法,排序的规则依据抛出异常相对于声明异常的深度,例如Controller抛出的异常是是AuroraRuntimeException(继承自RuntimeException),那么AuroraRuntimeException相对于@ExceptionHandler(AuroraRuntimeException.class)声明的AuroraRuntimeException.class其深度是0,相对于@ExceptionHandler(Exception.class)声明的Exception.class其深度是2,所以@ExceptionHandler(BizException.class)标注的方法会排在前面 */Collections.sort(matches, new ExceptionDepthComparator(exceptionType));return this.mappedMethods.get(matches.get(0));}else {return null;}}// ......
}

整个@RestControllerAdvice处理的流程就是这样,结合@ExceptionHandler就完成了对不同异常的灵活处理。

http://www.15wanjia.com/news/183358.html

相关文章:

  • 漯河 做网站如何查找网站的死链接
  • 深圳做企业网站的宿迁房产网官方网站
  • 一个基于php网站开发课题设计的业务流程描述河南建设信息网首页
  • 电子商务 做网站2345手机浏览器
  • 视觉网站建设企业加盟网站建设
  • wordpress 运费设置seo外链群发网站
  • 厦门市住房和建设局网站爱站网关键词
  • 菜谱分享网站开发开题报告网站备案 四川
  • 做网站怎么推广收益大高端品牌羽绒服前十名
  • 想用vs做网站 学什么wordpress 数据导出
  • 淘宝客网站要备案吗在网站做淘宝推广
  • 诸城 网站 建设本地wordpress 上传到服务器
  • 管城网站建设建设网站费用评估
  • 小网站下载渠道有哪些一个人在线观看视频播放免费
  • 郑州专业网站制作的公司哪家好北京seo课程
  • 58同城网站官网服务器配置wordpress
  • 学校网站怎么做专做服装的网站
  • 襄阳做网站哪家好网站建设费用预算
  • A华企网络网站建设触屏版网站开发
  • 成都 商业网站建设做企业展示网站
  • 做网站建设挣钱吗苏州推广排名
  • 架设个人网站采集1024图片到wordpress
  • aspnet网站模板聊城做网站多少钱
  • 国家关于网站信息建设管理文件seo推广引擎优化
  • 自己做的网站怎么让别人访问南京网站排名优化费用
  • 如何让自己做的网站在google搜索引擎上搜到网站后台链接怎么做
  • 网站制作商业模式天眼查 企业查询官网
  • 网站开发软件搭配wordpress mysql扩展
  • 广州网站建设怎么样国内十大管理咨询公司
  • 网络文化经营许可证多少钱seo页面代码优化