详解使用Spring MVC统一异常处理实战

1 描述

在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。

那么,能不能将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护?答案是肯定的。下面将介绍使用Spring MVC统一处理异常的解决和实现过程。

2 分析

Spring MVC处理异常有3种方式:

(1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver;

(2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器;

(3)使用@ExceptionHandler注解实现异常处理;

3 实战

3.1 引言

为了验证Spring MVC的3种异常处理方式的实际效果,我们需要开发一个测试项目,从Dao层、Service层、Controller层分别抛出不同的异常,然后分别集成3种方式进行异常处理,从而比较3种方式的优缺点。

3.2 实战项目

3.2.1 项目结构

3.2.2 Dao层代码

@Repository("testDao")
public class TestDao {
  public void exception(Integer id) throws Exception {
    switch(id) {
    case 1:
      throw new BusinessException("12", "dao12");
    case 2:
      throw new BusinessException("22", "dao22");
    case 3:
      throw new BusinessException("32", "dao32");
    case 4:
      throw new BusinessException("42", "dao42");
    case 5:
      throw new BusinessException("52", "dao52");
    default:
      throw new ParameterException("Dao Parameter Error");
    }
  }
}

3.2.3 Service层代码

public interface TestService {
  public void exception(Integer id) throws Exception; 

  public void dao(Integer id) throws Exception;
} 

@Service("testService")
public class TestServiceImpl implements TestService {
  @Resource
  private TestDao testDao; 

  public void exception(Integer id) throws Exception {
    switch(id) {
    case 1:
      throw new BusinessException("11", "service11");
    case 2:
      throw new BusinessException("21", "service21");
    case 3:
      throw new BusinessException("31", "service31");
    case 4:
      throw new BusinessException("41", "service41");
    case 5:
      throw new BusinessException("51", "service51");
    default:
      throw new ParameterException("Service Parameter Error");
    }
  } 

  @Override
  public void dao(Integer id) throws Exception {
    testDao.exception(id);
  }
}

3.2.4 Controller层代码

@Controller
public class TestController {
  @Resource
  private TestService testService; 

  @RequestMapping(value = "/controller.do", method = RequestMethod.GET)
  public void controller(HttpServletResponse response, Integer id) throws Exception {
    switch(id) {
    case 1:
      throw new BusinessException("10", "controller10");
    case 2:
      throw new BusinessException("20", "controller20");
    case 3:
      throw new BusinessException("30", "controller30");
    case 4:
      throw new BusinessException("40", "controller40");
    case 5:
      throw new BusinessException("50", "controller50");
    default:
      throw new ParameterException("Controller Parameter Error");
    }
  } 

  @RequestMapping(value = "/service.do", method = RequestMethod.GET)
  public void service(HttpServletResponse response, Integer id) throws Exception {
    testService.exception(id);
  } 

  @RequestMapping(value = "/dao.do", method = RequestMethod.GET)
  public void dao(HttpServletResponse response, Integer id) throws Exception {
    testService.dao(id);
  }
}

3.2.5 JSP页面代码

<%@ page contentType="text/html; charset=UTF-8"%>
<html>
<head>
<title>Maven Demo</title>
</head>
<body>
<h1>所有的演示例子</h1>
<h3>[url=./dao.do?id=1]Dao正常错误[/url]</h3>
<h3>[url=./dao.do?id=10]Dao参数错误[/url]</h3>
<h3>[url=./dao.do?id=]Dao未知错误[/url]</h3> 

<h3>[url=./service.do?id=1]Service正常错误[/url]</h3>
<h3>[url=./service.do?id=10]Service参数错误[/url]</h3>
<h3>[url=./service.do?id=]Service未知错误[/url]</h3> 

<h3>[url=./controller.do?id=1]Controller正常错误[/url]</h3>
<h3>[url=./controller.do?id=10]Controller参数错误[/url]</h3>
<h3>[url=./controller.do?id=]Controller未知错误[/url]</h3> 

<h3>[url=./404.do?id=1]404错误[/url]</h3>
</body>
</html>

3.3 集成异常处理

3.3.1 使用SimpleMappingExceptionResolver实现异常处理

1、在Spring的配置文件applicationContext.xml中增加以下内容:

<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="cn.basttg.core.exception.BusinessException">error-business</prop>
      <prop key="cn.basttg.core.exception.ParameterException">error-parameter</prop> 

      <!-- 这里还可以继续扩展对不同异常类型的处理 -->
    </props>
  </property>
</bean>

2、启动测试项目,经验证,Dao层、Service层、Controller层抛出的异常(业务异常BusinessException、参数异常ParameterException和其它的异常Exception)都能准确显示定义的异常处理页面,达到了统一异常处理的目标。

3、从上面的集成过程可知,使用SimpleMappingExceptionResolver进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,但该方法仅能获取到异常信息,若在出现异常时,对需要获取除异常以外的数据的情况不适用。

3.3.2 实现HandlerExceptionResolver 接口自定义异常处理器

1、增加HandlerExceptionResolver 接口的实现类MyExceptionHandler,代码如下:

public class MyExceptionHandler implements HandlerExceptionResolver { 

  public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
      Exception ex) {
    Map<String, Object> model = new HashMap<String, Object>();
    model.put("ex", ex); 

    // 根据不同错误转向不同页面
    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);
    }
  }
}

2、在Spring的配置文件applicationContext.xml中增加以下内容:

代码如下:

<bean id="exceptionHandler" class="cn.basttg.core.exception.MyExceptionHandler"/>

3、启动测试项目,经验证,Dao层、Service层、Controller层抛出的异常(业务异常BusinessException、参数异常ParameterException和其它的异常Exception)都能准确显示定义的异常处理页面,达到了统一异常处理的目标。

4、从上面的集成过程可知,使用实现HandlerExceptionResolver接口的异常处理器进行异常处理,具有集成简单、有良好的扩展性、对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提供更详细的异常处理信息。

3.3.3 使用@ExceptionHandler注解实现异常处理

1、增加BaseController类,并在类中使用@ExceptionHandler注解声明异常处理,代码如下:

public class BaseController {
  /** 基于@ExceptionHandler异常处理 */
  @ExceptionHandler
  public String exp(HttpServletRequest request, Exception ex) { 

    request.setAttribute("ex", ex); 

    // 根据不同错误转向不同页面
    if(ex instanceof BusinessException) {
      return "error-business";
    }else if(ex instanceof ParameterException) {
      return "error-parameter";
    } else {
      return "error";
    }
  }
}

2、修改代码,使所有需要异常处理的Controller都继承该类,如下所示,修改后的TestController类继承于BaseController:

public class TestController extends BaseController 

3、启动测试项目,经验证,Dao层、Service层、Controller层抛出的异常(业务异常BusinessException、参数异常ParameterException和其它的异常Exception)都能准确显示定义的异常处理页面,达到了统一异常处理的目标。

4、从上面的集成过程可知,使用@ExceptionHandler注解实现异常处理,具有集成简单、有扩展性好(只需要将要异常处理的Controller类继承于BaseController即可)、不需要附加Spring配置等优点,但该方法对已有代码存在入侵性(需要修改已有代码,使相关类继承于BaseController),在异常处理时不能获取除异常以外的数据。

3.4 未捕获异常的处理

对于Unchecked Exception而言,由于代码不强制捕获,往往被忽略,如果运行期产生了Unchecked Exception,而代码中又没有进行相应的捕获和处理,则我们可能不得不面对尴尬的404、500……等服务器内部错误提示页面。

我们需要一个全面而有效的异常处理机制。目前大多数服务器也都支持在Web.xml中通过<error-page>(Websphere/Weblogic)或者<error-code>(Tomcat)节点配置特定异常情况的显示页面。修改web.xml文件,增加以下内容:

<!-- 出错页面定义 -->
<error-page>
  <exception-type>java.lang.Throwable</exception-type>
  <location>/500.jsp</location>
</error-page>
<error-page>
  <error-code>500</error-code>
  <location>/500.jsp</location>
</error-page>
<error-page>
  <error-code>404</error-code>
  <location>/404.jsp</location>
</error-page> 

<!-- 这里可继续增加服务器错误号的处理及对应显示的页面 --> 

4 解决结果

1、运行测试项目显示的首页,如下图所示:

2、业务错误显示的页面,如下图所示:

3、参数错误显示的页面,如下图所示:

4、未知错误显示的页面,如下图所示:

5、服务器内部错误页面,如下图所示:

5 总结

综合上述可知,Spring MVC集成异常处理3种方式都可以达到统一异常处理的目标。从3种方式的优缺点比较,若只需要简单的集成异常处理,推荐使用SimpleMappingExceptionResolver即可;若需要集成的异常处理能够更具个性化,提供给用户更详细的异常信息,推荐自定义实现HandlerExceptionResolver接口的方式;若不喜欢Spring配置文件或要实现“零配置”,且能接受对原有代码的适当入侵,则建议使用@ExceptionHandler注解方式。

6 源代码

源代码项目如下所示,为Maven项目,若需运行,请自行获取相关的依赖包。

点击这里获取源代码

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot全局异常处理解析

    本文为大家分享了Spring Boot全局异常处理,供大家参考,具体内容如下 1.后台处理异常 a.引入thymeleaf依赖 <!-- thymeleaf模板插件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>

  • springboot springmvc抛出全局异常的解决方法

    springboot中抛出异常,springboot自带的是springmvc框架,这个就不多说了. springmvc统一异常解决方法这里要说明的是.只是结合了springboot的使用而已.直接上代码,有效有用的才是ok. 1.定义异常捕获 package com.example.rest.error; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.Exce

  • SpringBoot初始教程之统一异常处理详解

    1.介绍 在日常开发中发生了异常,往往是需要通过一个统一的异常处理处理所有异常,来保证客户端能够收到友好的提示.SpringBoot在页面发生异常的时候会自动把请求转到/error,SpringBoot内置了一个BasicErrorController对异常进行统一的处理,当然也可以自定义这个路径 application.yaml server: port: 8080 error: path: /custom/error BasicErrorController提供两种返回错误一种是页面返回.当

  • springboot全局异常处理详解

    一.单个controller范围的异常处理 package com.xxx.secondboot.web; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import

  • SpringBoot添加Email发送功能及常见异常详解

    1.完整的邮件发送代码 1.1.依赖包 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-support</artifactId> <version>2.0.8</version> <exclusions> <exclusion> <groupId>javax.servlet</groupI

  • Spring Boot统一异常处理详解

    Spring Boot中默认带了error的映射,但是这个错误页面显示给用户并不是很友好. 统一异常处理 通过使用@ControllerAdvice定义统一异常处理的类,而不是在每个Controller中逐个定义. @ExceptionHandler用来定义函数针对的函数类型,最后将Exception对象和请求URL映射到URL中. @ControllerAdvice class ExceptionTranslator { public static final String DEFAULT_E

  • 详解Springboot自定义异常处理

    背景 Springboot 默认把异常的处理集中到一个ModelAndView中了,但项目的实际过程中,这样做,并不能满足我们的要求.具体的自定义异常的处理,参看以下 具体实现 如果仔细看完spring boot的异常处理详解,并且研究过源码后,我觉得具体的实现可以不用看了... 重写定义错误页面的url,默认只有一个/error @Bean public EmbeddedServletContainerCustomizer containerCustomizer(){ return new E

  • 详解使用Spring MVC统一异常处理实战

    1 描述 在J2EE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大. 那么,能不能将所有类型的异常处理从各处理过程解耦出来,这样既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护?答案是肯定的.下面将介绍使用Spring MVC统一处理异常的解决和实现过程. 2 分析 Spring MVC处理异常有3种方

  • 详解在Spring MVC或Spring Boot中使用Filter打印请求参数问题

    使用Spring MVC或Spring Boot中打印或记录日志一般使用AOP记录Request请求和Response响应参数,在不使用AOP的前提下,如果在Filter中打印日志,在打印或消费请求类型为Content-Type:application/json的请求时,会出现严重的问题. 在Spring体系中,过滤器的定义我们一般采用继承OncePerRequestFilter的方式,当然也可以使用原始的Filter. 错误写法一: 如果不对request和response进行处理,使用伪代码

  • 详解SpringCloud Finchley Gateway 统一异常处理

    SpringCloud Finchley Gateway 统一异常处理 全文搜索[@@]搜索重点内容标记 1 . 问题:使用SpringCloud Gateway时,会出现各种系统级异常,默认返回HTML. 2 . Finchley版本的Gateway,使用WebFlux形式作为底层框架,而不是Servlet容器,所以常规的异常处理无法使用 翻阅源码,默认是使用DefaultErrorWebExceptionHandler这个类实现结构如下: 可以实现参考DefaultErrorWebExcep

  • 详解在Spring MVC中使用注解的方式校验RequestParams

    概述 Spring MVC支持Bean Validation,通过这个验证技术,可以通过注解方式,很方便的对输入参数进行验证,之前使用的校验方式,都是基于Bean对象的,但是在@RequestParam中,没有Bean对象,这样使得校验无法进行,可以通过使用@Validated注解,使得校验可以进行. 校验bean对象 一般校验bean对象,为了可以自动的校验属性,可以通过两步解决: 一.声明对象 package com.github.yongzhizhan.draftbox.model; im

  • 详解获取Spring MVC中所有RequestMapping以及对应方法和参数

    在Spring MVC中想要对每一个URL进行权限控制,不想手工整理这样会有遗漏,所以就动手写程序了.代码如下: /** * @return * @author Elwin ZHANG * 创建时间:2017年3月8日 上午11:48:22 * 功能:返回系统中的所有控制器映射路径,以及对应的方法 */ @RequestMapping(value = "/maps", produces = "application/json; charset=utf-8") @Re

  • 详解React 16 中的异常处理

    详解React 16 中的异常处理 异常处理 在 React 15.x 及之前的版本中,组件内的异常有可能会影响到 React 的内部状态,进而导致下一轮渲染时出现未知错误.这些组件内的异常往往也是由应用代码本身抛出,在之前版本的 React 更多的是交托给了开发者处理,而没有提供较好地组件内优雅处理这些异常的方式.在 React 16.x 版本中,引入了所谓 Error Boundary 的概念,从而保证了发生在 UI 层的错误不会连锁导致整个应用程序崩溃:未被任何异常边界捕获的异常可能会导致

  • 详解Flask开发技巧之异常处理

    目录 一.Flask内置异常处理 二.HTTPException类分析 三.自定义异常处理类 四.方便的定义自己的错误类 五.注意事项 一.Flask内置异常处理 要想在Flask中处理好异常,有一套自己的异常处理机制,首先,我们必须先知道Flask自己是如何处理异常的.去flask的源码里找一找会发现,在flask源码的app.py文件下,有很多会抛出异常的方法,其中拿一个举例: def handle_exception(self, e): """Default excep

  • 详解高性能缓存Caffeine原理及实战

    目录 一.简介 二.Caffeine 原理 2.1.淘汰算法 2.1.1.常见算法 2.1.2.W-TinyLFU 算法 2.2.高性能读写 2.2.1.读缓冲 2.2.2.写缓冲 三.Caffeine 实战 3.1.配置参数 3.2.项目实战 四.总结 一.简介 下面是Caffeine 官方测试报告. 由上面三幅图可见:不管在并发读.并发写还是并发读写的场景下,Caffeine 的性能都大幅领先于其他本地开源缓存组件. 本文先介绍 Caffeine 实现原理,再讲解如何在项目中使用 Caffe

  • 详解SpringBoot如何实现统一后端返回格式

    目录 1.为什么要对SpringBoot返回统一的标准格式 1.1 返回String 1.2 返回自定义对象 1.3 接口异常 2.定义返回对象 3.定义状态码 4.统一返回格式 5.高级实现方式 5.1 ResponseBodyAdvice的源码 5.2 String类型判断 在前后端分离的项目中后端返回的格式一定要友好,不然会对前端的开发人员带来很多的工作量.那么SpringBoot如何做到统一的后端返回格式呢?今天我们一起来看看. 1.为什么要对SpringBoot返回统一的标准格式 在默

  • SpringBoot详解整合Spring Boot Admin实现监控功能

    目录 监控 监控的意义 可视化监控平台 监控原理 自定义监控指标 监控 ​ 在说监控之前,需要回顾一下软件业的发展史.最早的软件完成一些非常简单的功能,代码不多,错误也少.随着软件功能的逐步完善,软件的功能变得越来越复杂,功能不能得到有效的保障,这个阶段出现了针对软件功能的检测,也就是软件测试.伴随着计算机操作系统的逐步升级,软件的运行状态也变得开始让人捉摸不透,出现了不稳定的状况.伴随着计算机网络的发展,程序也从单机状态切换成基于计算机网络的程序,应用于网络的程序开始出现,由于网络的不稳定性,

随机推荐