SpringMVC的异常处理

处理异常

不管发生什么事情,不管是好的还是坏的,Servlet请求的输出都是一
个Servlet响应。如果在请求处理的时候,出现了异常,那它的输出依
然会是Servlet响应。异常必须要以某种方式转换为响应。

Spring提供了多种方式将异常转换为响应:
- 特定的Spring异常将会自动映射为指定的HTTP状态码;
- 异常上可以添加@ResponseStatus注解,从而将其映射为某一
个HTTP状态码;
- 在方法上可以添加@ExceptionHandler注解,使其用来处理
异常
- 使用Spring-MVC提供的SimpleMappingExceptionResolver
- 实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器;

将异常映射为HTTP状态码

在默认情况下,Spring会将自身的一些异常自动转换为合适的状态
码。

Spring异常HTTP状态码
BindException400 - Bad Request
ConversionNotSupportedException500 - Internal Server Error
HttpMediaTypeNotAcceptableException406 - Not Acceptable
HttpMediaTypeNotSupportedException415 - Unsupported Media Type
HttpMessageNotReadableException400 - Bad Request
HttpMessageNotWritableException500 - Internal Server Error
HttpRequestMethodNotSupportedException405 - Method Not Allowed
MethodArgumentNotValidException400 - Bad Request
MissingServletRequestParameterException400 - Bad Request
MissingServletRequestPartException400 - Bad Request
NoSuchRequestHandlingMethodException404 - Not Found
TypeMismatchException400 - Bad Request

异常一般会由Spring自身抛出,作为DispatcherServlet
处理过程中或执行校验时出现问题的结果。例如,如果
DispatcherServlet无法找到适合处理请求的控制器方法,那么
将会抛出NoSuchRequestHandlingMethodException异常,最
终的结果就是产生404状态码的响应(Not Found)

尽管这些内置的映射是很有用的,但是对于应用所抛出的异常它们就
无能为力了。幸好,Spring提供了一种机制,能够通过
@ResponseStatus注解将异常映射为HTTP状态码。
#

  @RequestMapping(value = "/user/2", method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, reason = "sql异常")
    public void getUser2() throws Exception {
        throw new Exception("");
    }

在引入@ResponseStatus注解之后,如果控制器方法抛出
异常的话,响应将会具有500状态码

SimpleMappingExceptionResolver

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="java.lang.Exception">business_error</prop>  
            <!-- 还可以继续扩展对不同异常类型的处理 -->  
        </props>  
    </property>  
</bean>

java代码的形式:

package com.lf.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;

import java.util.Properties;

/**
 * Created by LF on 2017/5/7.
 */
@Configuration
public class MySimpleMappingExceptionResolver {

    @Bean
    public SimpleMappingExceptionResolver create() {
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        //<!-- 定义默认的异常处理页面,当该异常类型的注册时使用 -->
        simpleMappingExceptionResolver.setDefaultErrorView("error");
        // <!-- 定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        simpleMappingExceptionResolver.setExceptionAttribute("ex");
        // <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常以页名作为值 -->
        Properties mappings = new Properties();
        mappings.setProperty("java.lang.Exception", "business_error");

        simpleMappingExceptionResolver.setExceptionMappings(
                mappings
        );
        return simpleMappingExceptionResolver;
    }
}

如果遇到的异常不是Spring内置的异常或者没有使用 @ResponseStatus注解标注的时候
就会调用MySimpleMappingExceptionResolver处理
我们对之前的controller进行改造


    @RequestMapping(value = "/user/2", method = RequestMethod.GET)
//    @ResponseStatus(value = HttpStatus.REQUEST_HEADER_FIELDS_TOO_LARGE, reason = "sql异常")
    public void getUser2() throws Exception {
        throw new Exception("");
    }

当访问这个controller的时候,会发生异常,同时会被SimpleMappingExceptionResolver捕获
会返回到business_error视图

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

package com.lf.web.config;

import org.springframework.context.annotation.Configuration;
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;

/**
 * Created by LF on 2017/5/7.
 */
@Configuration
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 Exception) {
            return new ModelAndView("error-business", model);
        }
        return new ModelAndView("error", model);
    }
}

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

@ControllerAdvice

如果控制器类的特定切面能够运用到整个应用程序的所有控制器中,
那么这将会便利很多。举例来说,如果要在多个控制器中处理异常,
那@ExceptionHandler注解所标注的方法是很有用的。

不过,如果多个控制器类中都会抛出某个特定的异常,那么你可能会发现要在
所有的控制器方法中重复相同的@ExceptionHandler方法。

或者,为了避免重复,我们会创建一个基础的控制器类,所有控制器类
要扩展这个类,从而继承通用的@ExceptionHandler方法。

Spring 3.2为这类问题引入了一个新的解决方案:控制器通知。控制器
通知(controller advice)是任意带有@ControllerAdvice注解的
类,这个类会包含一个或多个如下类型的方法:

  • @ExceptionHandler注解标注的方法;
  • @InitBinder注解标注的方法;
  • @ModelAttribute注解标注的方法。

在带有@ControllerAdvice注解的类中,以上所述的这些方法会
运用到整个应用程序所有控制器中带有@RequestMapping注解的方
法上。

@ControllerAdvice注解本身已经使用了@Component,因
此@ControllerAdvice注解所标注的类将会自动被组件扫描获取
到,就像带有@Component注解的类一样。

@ControllerAdvice最为实用的一个场景就是将所有的
@ExceptionHandler方法收集到一个类中,这样所有控制器的异
常就能在一个地方进行一致的处理.

代码示例

package com.lf.web.config;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.servlet.http.HttpServletRequest;

/**
 * Created by LF on 2017/5/8.
 */
@ControllerAdvice
public class MyExceptionHandlerAdvice {

    @ExceptionHandler
    public String exp(HttpServletRequest request, Exception ex) {
        request.setAttribute("ex", ex);
        // 根据不同错误转向不同页面
        if (ex instanceof Exception) {
            return "business_error";
        }
        return "index";
    }
}

不使用@controllerAdvice

public class BaseController {  
    /** 基于@ExceptionHandler异常处理 */  
    @ExceptionHandler  
    public String exp(HttpServletRequest request, Exception ex) {  
        request.setAttribute("ex", ex);  
        // 根据不同错误转向不同页面  
        if(ex instanceof BusinessException) {  
            return "business_error";  
        }else if(ex instanceof ParameterException) {  
            return "parameter_error";  
        } else {  
            return "error";  
        }  
    }  
}

然后需要修改现有代码,使所有需要异常处理的Controller都继承该类,如下所示:
public class TestController extends BaseController

未捕获异常的处理

对于Unchecked Exception而言,由于代码不强制捕获,往往被忽略,如果运行期产生了UncheckedException,而代码中又没有进行相应的捕获和处理,则我们可能不得不面对尴尬的404、500……等服务器内部错误提示页面。
我们需要一个全面而有效的异常处理机制。目前大多数服务器也都支持在Web.xml中通过(Websphere/Weblogic)或者(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>  
<!-- 可继续增加服务器错误号的处理及对应显示页面 -->

示例代码

http://git.oschina.net/null_064_8008/SpringLearning/tree/master/MyException?dir=1&filepath=MyException&oid=886237f2cea8f684ba2ada17702cf6fa148ba058&sha=44b3e66621b7d6a009efabcedfc1373adff0d6fe

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值