SpringMVC

    科技2024-06-07  80

    异常处理概述

    Spring MVC 通过 HandlerExceptionResolver  处理程序的异常,包括 Handler 映射、数据绑定以及目标方法执行时发生的异常。SpringMVC 提供的 HandlerExceptionResolver 的实现类

    HandlerExceptionResolver

    DispatcherServlet  默认装配的 HandlerExceptionResolver

    没有使用 <mvc:annotation-driven/> 配置:

    使用了 <mvc:annotation-driven/> 配置

    实验代码 页面链接

    <a href="testExceptionHandlerExceptionResolver?i=1">testExceptionHandlerExceptionResolver</a>

    控制器方法

    package com.atguigu.springmvc.crud.handlers;

     

    import org.springframework.stereotype.Controller;

    import org.springframework.web.bind.annotation.RequestMapping;

    import org.springframework.web.bind.annotation.RequestParam;

     

    @Controller

    public class ExceptionHandler {

    @RequestMapping("/testExceptionHandlerExceptionResolver")

    public String testExceptionHandlerExceptionResolver(@RequestParam("i") int i){

    System.out.println("10/"+i+"="+(10/i));

    return "success";

    }

    }

    测试,出现异常情况

    处理异常,跳转到error.jsp

    在控制器中增加处理异常的方法

    @ExceptionHandler(value={java.lang.ArithmeticException.class})

    public String handleException(Exception ex){

    System.out.println("出现异常啦L"+ex);

    return "error";

    }

    增加error.jsp

    <h3>Error Page</h3>

    如何将异常对象从控制器携带给页面,做异常信息的获取

    异常对象不能通过Map集合方式传递给成功页面。(Map不可以

    // @ExceptionHandler(value={java.lang.ArithmeticException.class})

    public String handleException(Exception ex,Map<String,Object> map){

    System.out.println("出现异常啦:"+ex);

    map.put("exception",ex);

    return "error";

    }

    可以通过ModelAndView将异常对象传递给成功页面上

    @ExceptionHandler(value={java.lang.ArithmeticException.class})

    public ModelAndView handleException(Exception ex){

    System.out.println("出现异常啦:"+ex);

    ModelAndView mv = new ModelAndView("error");

    mv.addObject("exception", ex);

    return mv;

    }

    匹配异常类型,执行顺序问题

    @ExceptionHandler(value={java.lang.ArithmeticException.class})

    public ModelAndView handleException(Exception ex){

    System.out.println("出现异常啦:"+ex);

    ModelAndView mv = new ModelAndView("error");

    mv.addObject("exception", ex);

    return mv;

    }

     

    @ExceptionHandler(value={java.lang.RuntimeException.class})

    public ModelAndView handleException2(Exception ex){

    System.out.println("RuntimeException-出现异常啦:"+ex);

    ModelAndView mv = new ModelAndView("error");

    mv.addObject("exception", ex);

    return mv;

    }

    公共的处理异常的类@ControllerAdvice

    定义公共的处理异常的类

    package com.atguigu.springmvc.exceptionAdvice;

     

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

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

    import org.springframework.web.servlet.ModelAndView;

     

    @ControllerAdvice

    public class ExceptionAdviceHandler {

     

    /*        

    @ExceptionHandler(value={java.lang.ArithmeticException.class})

    public ModelAndView handleException(Exception ex){

     

    System.out.println("出现异常啦:"+ex);

    ModelAndView mv = new ModelAndView("error");

    mv.addObject("exception", ex);

     

    return mv; 

    }*/

     

    @ExceptionHandler(value={java.lang.RuntimeException.class})

    public ModelAndView handleException2(Exception ex){

     

    System.out.println("RuntimeException-出现异常啦:"+ex);

    ModelAndView mv = new ModelAndView("error");

    mv.addObject("exception", ex);

     

    return mv;

    }

    }

    ExceptionHandlerExceptionResolver

    主要处理 Handler 中用 @ExceptionHandler 注解定义的方法。@ExceptionHandler 注解定义的方法优先级问题:例如发生的是NullPointerException,但是声明的异常有 RuntimeException 和 Exception,此候会根据异常的最近继承关系找到继承深度最浅的那个 @ExceptionHandler 注解方法,即标记了 RuntimeException 的方法ExceptionHandlerMethodResolver 内部若找不到@ExceptionHandler 注解的话,会找 @ControllerAdvice 中的@ExceptionHandler 注解方法

    异常处理_ResponseStatusExceptionResolver

    在异常及异常父类中找到 @ResponseStatus 注解,然后使用这个注解的属性进行处理。定义一个 @ResponseStatus 注解修饰的异常类若在处理器方法中抛出了上述异常:若ExceptionHandlerExceptionResolver 不解析上述异常。由于触发的异常 UnauthorizedException 带有@ResponseStatus 注解。因此会被ResponseStatusExceptionResolver 解析到。最后响应HttpStatus.UNAUTHORIZED 代码给客户端。HttpStatus.UNAUTHORIZED 代表响应码401,无权限。 关于其他的响应码请参考 HttpStatus 枚举类型源码。

    org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

    Implementation of the HandlerExceptionResolver interface that uses the @ResponseStatus annotation to map exceptions to HTTP status codes.

    This exception resolver is enabled by default in the

     org.springframework.web.servlet.DispatcherServlet.

    实验代码

    页面链接

    <a href="testResponseStatusExceptionResolver?i=10">testResponseStatusExceptionResolver</a>

    自定义异常类

    package com.atguigu.springmvc.exception;

     

    import org.springframework.http.HttpStatus;

    import org.springframework.web.bind.annotation.ResponseStatus;

     

    /**

     * 自定义异常类

    HttpStatus.FORBIDDEN 不允许的,禁用的

     */

    @ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

    public class UsernameNotMatchPasswordException extends RuntimeException{}

    控制器方法

    @RequestMapping(value="/testResponseStatusExceptionResolver")

    public String testResponseStatusExceptionResolver(@RequestParam("i") int i){

    if(i==13){

    throw new UsernameNotMatchPasswordException();

    }

    System.out.println("testResponseStatusExceptionResolver...");

    return "success";

    }

    出现的错误消息没使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

    使用注解时:@ResponseStatus(value=HttpStatus.FORBIDDEN,reason="用户名称和密码不匹配")

    测试在方法上使用注解

    @ResponseStatus(value=HttpStatus.NOT_FOUND,reason="测试方法上设置响应状态码")

    @RequestMapping(value="/testResponseStatusExceptionResolver")

    public String testResponseStatusExceptionResolver(@RequestParam("i") int i){

    if(i==13){

    throw new UsernameNotMatchPasswordException();

    }

    System.out.println("testResponseStatusExceptionResolver...");

    return "success";

    }

    ResponseStatus

    package org.springframework.web.bind.annotation;

     

    import java.lang.annotation.Documented;

    import java.lang.annotation.ElementType;

    import java.lang.annotation.Retention;

    import java.lang.annotation.RetentionPolicy;

    import java.lang.annotation.Target;

     

    import org.springframework.http.HttpStatus;

     

    /**

     * Marks a method or exception class with the status code and reason that should be returned. The status code is applied

     * to the HTTP response when the handler method is invoked, or whenever said exception is thrown.

     *

     * @author Arjen Poutsma

     * @see org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver

     * @since 3.0

     */

    @Target({ElementType.TYPE, ElementType.METHOD})

    @Retention(RetentionPolicy.RUNTIME)

    @Documented

    public @interface ResponseStatus {

     

    HttpStatus value();

     

    String reason() default "";

     

    }

    HttpStatus

    异常处理_DefaultHandlerExceptionResolver

    对一些特殊的异常进行处理,比如: NoSuchRequestHandlingMethodException、HttpRequestMethodNotSupportedException、HttpMediaTypeNotSupportedException、HttpMediaTypeNotAcceptableException等。javadoc

    org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver

    Default implementation of the HandlerExceptionResolver interface that resolves standard Spring exceptions and translates them to corresponding HTTP status codes.

    This exception resolver is enabled by default in the org.springframework.web.servlet.DispatcherServlet.

    实验代码

    增加页面链接:GET请求

    <a href="testDefaultHandlerExceptionResolver">testDefaultHandlerExceptionResolver</a>

    增加处理器方法

    //@RequestMapping(value="/testDefaultHandlerExceptionResolver")

    @RequestMapping(value="/testDefaultHandlerExceptionResolver",method=RequestMethod.POST)  //不支持GET请求

    public String testDefaultHandlerExceptionResolver(){

    System.out.println("testDefaultHandlerExceptionResolver...");

    return "success";

    }

    出现异常错误

    出现异常交给DefaultHandlerExceptionResolver处理

    @Override

    protected ModelAndView doResolveException(

    HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

     

    try {

    if (ex instanceof NoSuchRequestHandlingMethodException) {

    return handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException) ex, request, response,

    handler);

    }

    else if (ex instanceof HttpRequestMethodNotSupportedException) {

    return handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException) ex, request,

    response, handler);

    }

    else if (ex instanceof HttpMediaTypeNotSupportedException) {

    return handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException) ex, request, response,

    handler);

    }

    else if (ex instanceof HttpMediaTypeNotAcceptableException) {

    return handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException) ex, request, response,

    handler);

    }

    else if (ex instanceof MissingServletRequestParameterException) {

    return handleMissingServletRequestParameter((MissingServletRequestParameterException) ex, request,

    response, handler);

    }

    else if (ex instanceof ServletRequestBindingException) {

    return handleServletRequestBindingException((ServletRequestBindingException) ex, request, response,

    handler);

    }

    else if (ex instanceof ConversionNotSupportedException) {

    return handleConversionNotSupported((ConversionNotSupportedException) ex, request, response, handler);

    }

    else if (ex instanceof TypeMismatchException) {

    return handleTypeMismatch((TypeMismatchException) ex, request, response, handler);

    }

    else if (ex instanceof HttpMessageNotReadableException) {

    return handleHttpMessageNotReadable((HttpMessageNotReadableException) ex, request, response, handler);

    }

    else if (ex instanceof HttpMessageNotWritableException) {

    return handleHttpMessageNotWritable((HttpMessageNotWritableException) ex, request, response, handler);

    }

    else if (ex instanceof MethodArgumentNotValidException) {

    return handleMethodArgumentNotValidException((MethodArgumentNotValidException) ex, request, response, handler);

    }

    else if (ex instanceof MissingServletRequestPartException) {

    return handleMissingServletRequestPartException((MissingServletRequestPartException) ex, request, response, handler);

    }

    else if (ex instanceof BindException) {

    return handleBindException((BindException) ex, request, response, handler);

    }

    else if (ex instanceof NoHandlerFoundException) {

    return handleNoHandlerFoundException((NoHandlerFoundException) ex, request, response, handler);

    }

    }

    catch (Exception handlerException) {

    logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", handlerException);

    }

    return null;

    }

    异常处理_SimpleMappingExceptionResolver

    如果希望对所有异常进行统一处理,可以使用 SimpleMappingExceptionResolver,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常

    实验代码 增加页面链接

    <a href="testSimpleMappingExceptionResolver?i=1">testSimpleMappingExceptionResolver</a>

    增加控制器方法

    @RequestMapping("/testSimpleMappingExceptionResolver")

    public String testSimpleMappingExceptionResolver(@RequestParam("i") int i){

    System.out.println("testSimpleMappingExceptionResolver..."); 

    String[] s = new String[10]; 

    System.out.println(s[i]); 

    return "success";

    }

    出现异常情况:参数i的值大于10

    配置异常解析器:自动将异常对象信息,存放到request范围内

    <!-- 配置SimpleMappingExceptionResolver异常解析器 -->

    <bean id="simpleMappingExceptionResolver"

     class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">

    <!-- exceptionAttribute默认值(通过ModelAndView传递给页面):

    exception   ->  ${requestScope.exception}

    public static final String DEFAULT_EXCEPTION_ATTRIBUTE = "exception";

    -->

    <property name="exceptionAttribute" value="exception"></property>

    <property name="exceptionMappings">

    <props>

    <prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>

    </props>

    </property>

    </bean>

    error.jsp

    <%@ page language="java" contentType="text/html; charset=UTF-8"

        pageEncoding="UTF-8"%>

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"

     "http://www.w3.org/TR/html4/loose.dtd">

    <html>

    <head>

    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

    <title>Insert title here</title>

    </head>

    <body> 

    <h3>Error Page</h3> 

    ${exception }

    ${requestScope.exception } 

    </body>

    </html>

    源码分析

    SimpleMappingExceptionResolver    L187 L339 

    @Override

    protected ModelAndView doResolveException(HttpServletRequest request,

     HttpServletResponse response,Object handler, Exception ex) {

     

    // Expose ModelAndView for chosen error view.

    String viewName = determineViewName(ex, request);

    if (viewName != null) {

    // Apply HTTP status code for error views, if specified.

    // Only apply it if we're processing a top-level request.

    Integer statusCode = determineStatusCode(request, viewName);

    if (statusCode != null) {

    applyStatusCodeIfPossible(request, response, statusCode);

    }

    return getModelAndView(viewName, ex, request);

    }else {

    return null;

    }

    }

    /**

     * Return a ModelAndView for the given view name and exception.

     * <p>The default implementation adds the specified exception attribute.

     * Can be overridden in subclasses.

     * @param viewName the name of the error view

     * @param ex the exception that got thrown during handler execution

     * @return the ModelAndView instance

     * @see #setExceptionAttribute

     */

    protected ModelAndView getModelAndView(String viewName, Exception ex) {

    ModelAndView mv = new ModelAndView(viewName);

    if (this.exceptionAttribute != null) {

    if (logger.isDebugEnabled()) {

    logger.debug("Exposing Exception as model attribute '" + this.exceptionAttribute + "'");

    }

    mv.addObject(this.exceptionAttribute, ex);

    }

    return mv;

    }

    Processed: 0.014, SQL: 8