前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springmvc统一异常拦截方式

springmvc统一异常拦截方式

作者头像
叔牙
发布2020-11-19 14:34:35
6830
发布2020-11-19 14:34:35
举报
文章被收录于专栏:一个执拗的后端搬砖工
背景描述
代码语言:javascript
复制
web项目开发过程中和前期线上运行环境,总是会或多或少出现各种异常,
比较常见的是有些运行异常或者检测异常直接抛给了前端,导致前端页面
出现了一大坨的异常堆栈信息。
代码语言:javascript
复制
1.但是站在产品和用户的角度,不管你服务器端出现什么异常,
或者说崩溃的东西,和我没关系;
2.站在B/S交互的角度,你Server端的运行错误与否和我
Browser端没太大关联,我任何一个操作,
只有成功和失败两种结果,不存在一直加载中,同时我也不想
看到一大堆密密麻麻看不懂的东西。
3.客户端希望看到的,是服务器端处理成功或者失败对应
响应码和数据,或者处理异常被捕获后返回的提前约定的
错误码和错误信息
问题分析
代码语言:javascript
复制
 对于上述问题,我们有很多处理和解决方案,比如:1.在业务编码层面(主要针对service和dao层),做足够的参数
 检验和逻辑判断,降低由于编码不严谨或者逻辑分析不到位,
 导致的逻辑异常
2.在服务调用层面或者说和前端直接打交道层(主要针对
 controller层),对基础的请求参数做合法性校验和鉴权,
对下层服务调用用try...catch包装,可以解决下层业务层面
 的检测异常和运行异常,同时也可以捕获框架层面的调用异常,
例如使用rpc框架dubbo,我们可能会出现服务调用超时异常,
 参数过大异常(传输参数超过dubbo默认大小或者自定义
 配置大小),请求和响应序列化异常等等,如果我们不做处理,
 这些异常直接就把堆栈信息抛给前端了
解决方案
代码语言:javascript
复制
 对于预知和不可预知的异常,从用户使用,到前端渲染,
 到服务器业务处理以及最底层数据库查询和持久化分析,
 我们可以得出很多互补的解决方案: 1.服务层(逻辑处理层service和dao)对逻辑做详尽的分析,
 对参数做可靠地校验,对处理结果做好封装,对捕获的异常
 仔细斟酌要不要向调用方抛出. 2.调用层(参数解析和鉴权层controller)对前端请求
 参数做严谨的合法性校验(非空,格式)以及参数鉴权,
 对调用服务用try...catch做异常捕获,对捕获的异常做        
 处理,返回给前端错误码和错误信息 3.交互层(和用户直接打交道的前端页面)对响应信息做
 容错处理,对服务器返回的错误码和错误信息包装转换后
 变成用户可以理解的信息展示出来.前端对服务请        
 求也做超时容错处理,并考虑做错误上报处理.   前边的处理方式,貌似是比较严谨的,但是还有一点
 需要注意,就是从上述的2到3的过程中也可能出现异常,
也就是如果controller层处理不当,那么久把产生的异常
 直接抛给了前端,这肯定是一种不合理的做法.
那么我们就要考虑的springmvc框架层面对控制器返回给
 前端的结果做统一异常处理,这样的活我们的
异常处理 链,通过1和2以及下边的4步骤,一环扣一环,
 做到所有的异常都在服务器端处理掉,
统一异常处理描述如下: 4.框架层统一异常处理机制,对控制器层所有的漏网之鱼
 做拦截处理,然后跳转到        
 对应的错误页面,或者如果是前后端分离的话,可以处理后
 返回给前端相应的错误码.
代码实现
代码语言:javascript
复制
 目前springmvc框架统一异常处理方案大概有三种,
 下面将一一介绍其使用方式:
 1.使用@ExceptionHandler进行处理
代码语言:javascript
复制
代码语言:javascript
复制
直接在controller层具体的防范上加上@ExceptionHandler
注解,就可以对该方法可能抛出的异常做处理,
参考@ExceptionHandler源码:

123456789101112

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

代码语言:javascript
复制
  发现此注解可以传一个参数,也就是具体的要拦截的异常类型
优点:1.灵活,可以配置到具体的方法层面 2.简单,不需要再做
  多余的配置
缺点:在一个很庞大的网站或者系统中,每个方法上边都要
  加注解,并且容易溜掉2.使用SimpleMappingExceptionResolver实现异常处理  在springmvc主配置文件中添加一下内容:

123456789101112131415

<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><!-- 定义默认的异常处理页面,当该异常类型的注册时使用 --><property name="defaultErrorView" value="error"></property><!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception --><property name="exceptionAttribute" value="ex"></property><!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常也页名作为值 --><property name="exceptionMappings"><props><prop key="com.typhoon.spring_shiro.exception.BusinessException">error-business</prop><prop key="com.typhoon.spring_shiro.exception.ParameterException">error-parameter</prop><!-- 这里还可以继续扩展对不同异常类型的处理 --></props></property></bean>

代码语言:javascript
复制
优点:能拦截所有可能出现的异常,可以对自定义异常做特殊处理
缺点:①.需要添加配置 ②.在多人团队开发的多模块业务中,
如果有人添加了自定义异常,需要修改配置3.实现HandlerExceptionResolver接口自定义异常处理器  我们在web层自定义异常处理器,然后添加到配置中

代码实现

123456789101112131415161718192021222324252627282930313233343536

/*** web层统一异常处理类** @author Typhoon* @date 2017-05-22 18:17* @since V2.0*/public class WebExceptionResolver implements HandlerExceptionResolver {private static final Logger LOGGER = LoggerFactory.getLogger(WebExceptionResolver.class); @Overridepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { LOGGER.info("捕获web层异常",ex);Map<String, Object> results = new HashMap<String, Object>();results.put("code", "999");results.put("msg", "系统异常");if(ex instanceof UnauthenticatedException || ex instanceof UnauthorizedException) {//授权异常results.put("code", "403");results.put("msg", "无权操作");} ModelAndView mv = new ModelAndView();/* 使用FastJson提供的FastJsonJsonView视图返回,不需要捕获异常 */FastJsonJsonView view = new FastJsonJsonView();view.setAttributesMap(results);mv.setView(view);return mv;//// 根据不同错误转向不同页面//if(ex instanceof BusinessException) {//return new ModelAndView("error-business", model);//}else if(ex instanceof ParameterException) {//return new ModelAndView("error-parameter", model);//} else {//return new ModelAndView("error", model);//}}}

添加配置

12

<!-- 统一异常捕获 --><bean id="exceptionResolver" class="com.typhoon.spring_shiro.controller.resolver.WebExceptionResolver"/>

代码语言:javascript
复制
优点:①可以自定义处理逻辑,根据不同的异常类型跳转到
不同的错误页面,也可以在前后端分离的情况下,
根据不同的异常返回给前端不同的错误码和错误信息
缺点:①有增加新的异常需要抛出,可能需要修改拦截
处理逻辑(例如:新增了退费逻辑,在退费异常或者
失败情况下前端需要根据响应码做提示或跳转,那么
就需要在拦截器层增加退费异常的判断)
②需要修改spring配置
总结
代码语言:javascript
复制
    不同的场景可以选择不同的异常拦截方式(在某些场景下
    可以配合使用),但是不能一概而定,不要根据具体的
    业务区做判定。
但是在zhenaiwang我们的处理方式是选择第三种拦截方式,
    因为我们做了前后端分离,所有的前端请求都是返回的json,
这个时候自定义异常处理的优势有显现出来,
    注解和通用配置略显不足.   *ps*:欢迎各位大神指教和拍砖.如果能给您带来收获,那是我的荣幸!!!
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-07-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景描述
  • 问题分析
  • 解决方案
  • 代码实现
  • 总结
相关产品与服务
日志服务
日志服务(Cloud Log Service,CLS)是腾讯云提供的一站式日志服务平台,提供了从日志采集、日志存储到日志检索,图表分析、监控告警、日志投递等多项服务,协助用户通过日志来解决业务运维、服务监控、日志审计等场景问题。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档