SpringMVC unified exception handling

1 Description

In the development of a J2EE project, whether it is the operation of the underlying database, the processing of the business layer, or the processing of the control layer, it is inevitable to encounter various predictable and unpredictable exceptions that need to be handled. Each process handles exceptions individually. The system has a high degree of code coupling, and the workload is large and difficult to unify, and the maintenance workload is also large.

Then, can all types of exception handling be decoupled from each processing process, which not only ensures that the functions of the related processing processes are relatively single, but also realizes the unified processing and maintenance of exception information? The answer is yes. The following will introduce the solution and implementation process of using Spring MVC to handle exceptions uniformly.

 

Replenish:

As shown in the figure, this is a default abnormal display page (add user, user name already exists)

SpringMVC unified exception handling

 

This is a custom-handled exception display interface (ugly, ignore it)

SpringMVC unified exception handling

Obviously, the following one is more in line with the design.

 

2 Analysis

Spring MVC handles exceptions in 3 ways:

(1) Use the SimpleMappingExceptionResolver provided by Spring MVC;

(2) Implement Spring's exception handling interface HandlerExceptionResolver to customize your own exception handler;

(3) Use the @ExceptionHandler annotation to implement exception handling;

 

3 actual combat

3.1 Introduction

In order to verify the actual effect of the three exception handling methods of Spring MVC, we need to develop a test project, throw different exceptions from the Dao layer, the Service layer, and the Controller layer, and then integrate the three methods for exception handling, so as to compare the three advantages and disadvantages of the methods.

 

3.2.2 Exception class definition

ParameterException.java

package com.change.exception;

/**
 * Request parameter exception
 * @author Yan Zhao
 * @date 2017/12/25 8:58 pm
 */

public class ParameterException extends RuntimeException {

    /** serialVersionUID */
    private static final long serialVersionUID = 6417641452178955756L;

    public ParameterException() {
        super();
    }

    public ParameterException(String message) {
        super(message);
    }

    public ParameterException(Throwable cause) {
        super(cause);
    }

    public ParameterException(String message, Throwable cause) {
        super(message, cause);
    }
}

BusinessException.java

package com.change.exception;

/**
 * 系统业务异常
 * @author 言曌
 * @date 2017/12/25 下午8:57
 */

public class BusinessException extends RuntimeException {

    /** serialVersionUID */
    private static final long serialVersionUID = 2332608236621015980L;

    private String code;

    public BusinessException() {
        super();
    }

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }

    public BusinessException(String code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

}

3.2.2 Controller ,Service 或 Dao 层代码

适当的场合抛出异常即可,异常信息可以使用枚举管理

  1. throw new ParameterException(ResultEnum.USERNAME_IS_EXIST.getMsg());

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="com.change.exception.BusinessException">/error/error-business</prop>
               <prop key="com.change.exception.ParameterException">/error/error-parameter</prop>
               <!-- 这里还可以继续扩展对不同异常类型的处理 -->
           </props>
       </property>
   </bean>

/error/error-business 指的是 webapp/WEB-INF/views/error/error-business.jsp

/error/error-parameter 指的是 webapp/WEB-INF/views/error/error-parameter.jsp


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

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


方法二、

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

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

package com.change.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 言曌
 * @date 2017/12/26 下午12:57
 */

public class MyExceptionHandler implements HandlerExceptionResolver {

    @Override
    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/error-business", model);
        }else if(ex instanceof ParameterException) {
            return new ModelAndView("/error/error-parameter", model);
        } else {
            return new ModelAndView("/error/error", model);
        }
    }
}


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

<bean id="exceptionHandler" class="com.change.exception.MyExceptionHandler"/>

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

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

 

方法三、

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

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

package com.change.controller;

import com.change.exception.BusinessException;
import com.change.exception.ParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

/**
 * @author 言曌
 * @date 2017/12/19 下午3:35
 */

public class BaseController {


    /** 基于@ExceptionHandler异常处理 */
    @ExceptionHandler
    public ModelAndView exp(HttpServletRequest request, Exception ex) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("message",ex.getMessage());
        // 根据不同错误转向不同页面
        if(ex instanceof BusinessException) {
            modelAndView.setViewName("/error/error-business");
        }else if(ex instanceof ParameterException) {
            modelAndView.setViewName("/error/error-parameter");
        } else {
            modelAndView.setViewName("/error/error");
        }
        return modelAndView;
    }
}


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

@RequestMapping("/user")
@Controller
public class UserController 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>

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

Here, Xiaobian recommends a good place to learn Java technology for free. The course covers Java, Redis, MongoDB, MySQL, Zookeeper, Spring Cloud, Dubbo/Kafka, Hadoop, Hbase, Flink and other high-concurrency distributed and big data , machine learning and other technologies.

Transfer gate: https://ke.qq.com/course/293193?flowToken=1002121


Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324971924&siteId=291194637