Spring MVC ControllerAdvice深入解析

  Spring 在3.2版本后面增加了一个ControllerAdvice注解。网上的资料说的都是ControllerAdvice配合ExceptionHandler注解可以统一处理异常。而Spring MVC是如何做到的资料却比较少,下面会先给出使用的例子和踩过的一个坑。然后进行相应的源码分析,之后再介始ControllerAdvice另外的两种使用方式。

ControllerAdvice的简单使用

  • ControllerAdvice配合ExceptionHandler可以统一处理系统的异常,我们先定义一个ExceptionAdvice类用于处理系统的两种类型的异常。代码如下:
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import com.pptv.frame.dto.common.ResponseDTO;
import com.pptv.frame.dto.common.ServiceCodeEnum;

@ControllerAdvice
public class ExceptionAdvice {

    @ExceptionHandler({
                        ArrayIndexOutOfBoundsException.class
    })
    @ResponseBody
    public ResponseDTO handleArrayIndexOutOfBoundsException(ArrayIndexOutOfBoundsException e) {
        // TODO 记录log日志
        e.printStackTrace();
        ResponseDTO responseDTO = new ResponseDTO();
        responseDTO.wrapResponse(ServiceCodeEnum.E999998, "数组越界异常");

        return responseDTO;
    }

    @ExceptionHandler({
                        Exception.class
    })
    @ResponseBody
    public ResponseDTO handleException(Exception e) {
        // TODO 记录log日志
        e.printStackTrace();
        ResponseDTO responseDTO = new ResponseDTO();
        responseDTO.wrapResponse(ServiceCodeEnum.E999998, "未知异常");
        return responseDTO;
    }

}
  • Spring mvc 的配置如下(这里用到了mvc:annotation-driven):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"
    xmlns:p="http://cxf.apache.org/policy" xmlns:ss="http://www.springframework.org/schema/security"
    xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 
    http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd 
    http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd 
    http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
    http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
    http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd 
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd 
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan
        base-package="frame.web.controller;frame.web.advice" />

    <!--===================== view resovler ===================== -->
    <bean id="jstlViewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="order" value="1" />
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
    </bean>

    <!-- 配置Fastjson支持 -->
    <mvc:annotation-driven conversion-service="conversionService">
        <mvc:message-converters register-defaults="true">
            <bean
                class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes" value="text/html;charset=UTF-8" />
                <property name="features">
                    <array>
                        <value>WriteMapNullValue</value>
                        <value>WriteNullStringAsEmpty</value>
                    </array>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

    <!-- 自定义参数转换 -->
    <bean id="conversionService"
        class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    </bean>

</beans>

遇到的一个坑是当spring mvc配置文件不用<mvc:annotation-drive>这个标签而是手动将RequestMappingHandlerMapping与RequestMappingHandlerAdapter这两个类让spring容器管理,上面的ControllerAdvice将不起作用

    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" />
    <bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="webBindingInitializer">
            <bean
                class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
                <property name="conversionService" ref="conversionService" />
            </bean>
        </property>
        <property name="messageConverters">
            <list>
                <ref bean="mappingJackson2HttpMessageConverter" />
                <ref bean="stringHttpMessageConverter" />
            </list>
        </property>
    </bean>

Spring MVC是如何处理异常的

  下面来看看Spring MVC是如何处理异常的,为什么我手动配置了RequestMappingHandlerMapping和RequestMappingHandlerAdapter ControllerAdvice就不会对异常进行拦截呢而通过<mvc:annotation-drive>这个标签就可以呢?我们从Spring MVC的入口看一下异常是如何处理的。下面是关键代码(关键代码都有相应的注释):

public class DispatcherServlet extends FrameworkServlet {
/**
  *这个方法是Spring MVC的入口方法,可以看到Spring MVC人具体处理流程
 **/
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        HttpServletRequest processedRequest = request;
        HandlerExecutionChain mappedHandler = null;
        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {
            ModelAndView mv = null;
            Exception dispatchException = null;
//这里有个try,下面的catch就是用于处理异常的
            try {
                              //检查是否是上传文件的请求
                processedRequest = checkMultipart(request);
                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.
//根据请求的request得到HandlerExecutionChain 对象,里面有Inceptor和相应的Controller
                mappedHandler = getHandler(processedRequest);
                if (mappedHandler == null || mappedHandler.getHandler() == null) {
                    noHandlerFound(processedRequest, response)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值