【SpringMVC从入门到实战教程】第七章 SpringMVC 异常处理

七、SpringMVC 异常处理

7.1 异常分类

系统中异常包括两类:编译期异常和运行时异常。

  • 编译期异常:继承java.lang.Exception,通过捕获异常(try-catch)从而获取异常信息。
  • 运行时异常:继承java.lang.RuntimeException,通过规范代码开发、测试通过手段减少运行时异常的发生。

7.2 SpringMVC的异常处理机制

在这里插入图片描述

	在 Spring MVC 应用的开发中,不管是操作底层数据库,还是业务层或控制层,都会不可避免地遇到各种可预知的、不可预知的异常。我们需要捕捉处理异常,才能保证程序不被终止。
	
	系统中的三层架构(dao、service、controller)都通过throws Exception向上抛出,最后由Spring MVC前端控制器交由`异常处理器`进行异常处理,如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AIz3WntE-1655368987527)(images/dt_20220425165922.jpg)]

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

  1. 使用 Spring MVC 提供的简单异常处理器 SimpleMappingExceptionResolver。
  2. 实现 Spring 的异常处理接口 HandlerExceptionResolver,自定义自己的异常处理器。
  3. 使用 @ExceptionHandler 注解实现异常处理。

7.3 异常模拟过程

7.3.1 maven依赖

<dependencies>
    <!-- spring核心包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springbean包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springcontext包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- spring表达式包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAOP包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springAspects包 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- spring对web的支持 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>
    <!-- springwebMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>4.3.18.RELEASE</version>
    </dependency>

    <!-- 配置javaweb环境 -->
    <!-- servlet-api -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- jsp-api -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- jstl -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>

    <!-- jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.5</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.9.5</version>
    </dependency>
</dependencies>

7.3.2 SpringMVC配置

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--字符编码过滤器-->
    <filter>
        <filter-name>CharacterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CharacterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 通过隐藏域传参解决form表单的PUT与DELETE方式的请求 -->
    <filter>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 解决put请求传递参数的问题-->
    <filter>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <filter-class>org.springframework.web.filter.HttpPutFormContentFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HttpPutFormContentFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <welcome-file-list>
        <welcome-file>/views/index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

springmvc.xml:

<?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:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:aop="http://www.springframework.org/schema/aop"
        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://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    http://www.springframework.org/schema/mvc
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- springmvc的注解式开发 -->
    <!-- 开启组件扫描 -->
    <context:component-scan base-package="com.newcapec"/>

    <!-- MVC注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 配置视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 配置视图地址的前缀和后缀:简化视图地址 -->
        <property name="prefix" value="/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--不拦截静态资源-->
    <mvc:default-servlet-handler/>
</beans>

7.3.3 自定义异常类

BusinessExeption.java:

public class BusinessExeption extends Exception {
    
    
    public BusinessExeption(){
    
    
        super("业务异常");
    }
}

CustomException.java:

public class CustomException extends Exception  {
    
    
    public CustomException(){
    
    
        super("自定义异常");
    }
}

7.3.4 dao

接口:

public interface DemoDao {
    
    
    
    void insert(String name) throws Exception;
}

实现类:

@Repository("demoDao")
public class DemoDaoImpl implements DemoDao {
    
    
    @Override
    public void insert(String name) throws Exception {
    
    
        System.out.println("dao执行了..." + name);
        
        //模拟异常
        if ("null".equals(name)) {
    
    
            System.out.println("空指针异常出现...");
            String s = null;
            s.trim();
        } else if ("business".equals(name)) {
    
    
            System.out.println("业务异常出现...");
            throw new BusinessExeption();
        } else if ("custom".equals(name)) {
    
    
            System.out.println("自定义异常出现...");
            throw new CustomException();
        } else {
    
    
            System.out.println("未出现异常,程序正常...");
        }
    }
}

7.3.5 service

接口:

public interface DemoService {
    
    
    
    void add(String name) throws Exception;
}

实现类:

@Service("demoService")
public class DemoServiceImpl implements DemoService {
    
    

    @Autowired
    private DemoDao demoDao;

    @Override
    public void add(String name) throws Exception {
    
    
        System.out.println("service执行了...");
        demoDao.insert(name);
    }
}

7.3.6 controller

@Controller
@RequestMapping("/demo")
public class DemoController {
    
    

    @Autowired
    private DemoService demoService;

    @RequestMapping("/add")
    public String add(String name) throws Exception {
    
    
        System.out.println("controller执行了...");
        //在controller中调用业务逻辑层
        demoService.add(name);
        return "success";
    }
}

7.3.7 页面

index.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1>异常处理</h1>
        <form action="demo/add" method="post">
            <p>数据:<input type="text" name="name"></p>
            <p><button>提交</button></p>
        </form>
    </div>
</body>
</html>

success.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center;">
        <h1>跳转成功!!!</h1>
    </div>
</body>
</html>

7.4 SpringMVC的异常处理

7.4.1 自带异常处理器

	全局异常处理可使用 SimpleMappingExceptionResolver 来实现。它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。
	
	使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver,在springmvc.xml配置如下:
<!-- 配置全局异常处理器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 配置默认的异常处理页面,value属性配置为一个逻辑视图名 -->
    <property name="defaultErrorView" value="error"/>
    <!-- 配置异常处理页面用来获取异常信息的变量名,默认名为exception -->
    <property name="exceptionAttribute" value="ex"/>
    <!-- 定义需要特殊处理的异常,用类名或完全路径名作为key,异常页名作为值 -->
    <property name="exceptionMappings">
        <props>
            <!-- key为单独处理异常的全限定名,value为单独处理异常之后的跳转页面 -->
            <prop key="com.newcapec.exception.BusinessExeption">error_bus</prop>
            <prop key="com.newcapec.exception.CustomException">error_cust</prop>
        </props>
    </property>
</bean>

异常跳转页面如下:

error.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center">
        <h1 style="color: red;">系统故障,请联系管理员!</h1>
        <p>异常信息:${ex}</p>
        <p>自定义信息:${msg}</p>
    </div>
</body>
</html>

error_bus.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center;">
        <h1>业务异常出现后的跳转页面</h1>
        <p>异常信息:${ex}</p>
        <p>自定义信息:${msg}</p>
    </div>
</body>
</html>

error_cust.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <base href="${pageContext.request.contextPath}/">
    <title>Title</title>
</head>
<body>
    <div style="text-align: center;">
        <h1>自定义异常出现后的跳转页面</h1>
        <p>异常信息:${ex}</p>
        <p>自定义信息:${msg}</p>
    </div>
</body>
</html>

7.4.2 自定义异常处理器

	Spring MVC 通过 HandlerExceptionResolver 处理程序异常,包括处理器异常、数据绑定异常以及控制器执行时发生的异常。HandlerExceptionResolver 仅有一个接口方法,源码如下。
	
	发生异常时,Spring MVC 会调用 resolveException() 方法,并转到 ModelAndView 对应的视图中,返回一个异常报告页面反馈给用户。
public interface HandlerExceptionResolver {
    
    
    @Nullable
    ModelAndView resolveException(
        HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

实现SpringMVC的异常处理接口HandlerExceptionResolver定义自己的异常处理器,代码如下:

/**
 * 自定义全局异常处理器
 * 1.实现HandlerExceptionResolver接口并实现其中抽象方法
 * 2.将异常处理器对象放入ioc容器中
 *
 * 优点:比自带的SimpleMappingExceptionResolver异常处理器更加灵活,可以自定义业务处理。
 * 缺点:业务实现比较麻烦,对异步请求时出现的异常,处理复杂
 */
@Component
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
    
    
    /**
     * 处理异常的方法
     * @param request 请求对象
     * @param response 响应对象
     * @param handler 处理器对象
     * @param ex 抛出的异常对象
     * @return ModelAndView对象
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response,
                                         Object handler, Exception ex) {
    
    
        ModelAndView mav = new ModelAndView();
        
        // 根据不同异常向不同的页面跳转
        if(ex instanceof BusinessExeption){
    
    
            request.setAttribute("msg", "message for business exception");
            mav.setViewName("error_bus");
        }else if(ex instanceof CustomException){
    
    
            request.setAttribute("msg", "message for custom exception");
            mav.setViewName("error_cust");
        }else {
    
    
            request.setAttribute("msg", "message for other exception");
            mav.setViewName("error");
        }
        
        request.setAttribute("ex", ex);
        
        //还可以做日志记录...
        
        return mav;
    }
}

7.4.3 注解式异常处理器

7.4.3.1 局部异常处理

在Controller中使用@ExceptionHandler注解实现异常处理。

@Controller
@RequestMapping("/other")
public class OtherController {
    
    

    @RequestMapping("/add")
    public String add() throws Exception {
    
    
        System.out.println("其他处理器中的异常...");
        //模拟异常
        Class.forName("abc");
        return "success";
    }

    /**
     * 在Controller中定义一个处理异常的方法(一个Controller里面可以定义多个处理异常的方法)
     * 方法的返回值是String类型,表示异常跳转的路径视图名
     *
     * @ExceptionHandler
     * 位置:方法
     * 作用:使当前方法可处理异常
     * 属性:value = 当前方法将要处理异常的类型(异常的Class类型)
     *
     * 注意:如果@ExceptionHandler使用在某个Controller,那么它只能处理该Controller抛出的异常
     */
    @ExceptionHandler(Exception.class)
    public String handleException(Model model){
    
    
        model.addAttribute("msg", "注解式异常处理");
        return "error";
    }
}

注意:进行异常处理的方法必须与抛出异常的方法在同一个Controller里面,不能全局控制异常。

7.4.3.2 全局异常处理

使用@ControllerAdvice + @ExceptionHandler注解实现全局异常处理。

/**
 * @ControllerAdvice 位置:类
 * 作用:注解式全局异常处理器
 */
@ControllerAdvice
public class MyControllerAdvice {
    
    
    
    @ExceptionHandler(BusinessExeption.class)
    public String handleBusinesException(Model model) {
    
    
        model.addAttribute("msg", "注解式异常处理的商务异常");
        return "error_bus";
    }

    @ExceptionHandler(CustomException.class)
    public String handleCustomException(Model model) {
    
    
        model.addAttribute("msg", "注解式异常处理的客户异常");
        return "error_cust";
    }

    @ExceptionHandler(Exception.class)
    public String handleException(Model model) {
    
    
        model.addAttribute("msg", "注解式异常处理的其他异常");
        return "error";
    }
}

7.4.3.3 异步异常处理

在异常处理的方法中,使用@ResponseBody注解响应客户端即可。

Controller中的方法:

@ResponseBody
@RequestMapping("/get")
public String get(String id) throws Exception {
    
    
    System.out.println("处理异步请求的方法执行了...");
    //模拟异常
    int i = Integer.parseInt(id);
    System.out.println("编号:" + i);
    return "success";
}

处理异常的方法:

/**
 * 处理异步请求中出现的异常
 */
@ResponseBody
@ExceptionHandler(value = NumberFormatException.class)
public String handleAsyncException(){
    
    
    return "error";
}

页面中的异步请求:

<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
    function get() {
      
      
        $.ajax({
      
      
            url : "demo/get",
            type : "get",
            data : {
      
      id : "10"},
            success : function (data) {
      
      
                alert(data);
            }
        });
    }
</script>

<p><a href="javascript:get()">异步请求</a></p>

rn “success”;
}


**处理异常的方法:**

```java
/**
 * 处理异步请求中出现的异常
 */
@ResponseBody
@ExceptionHandler(value = NumberFormatException.class)
public String handleAsyncException(){
    return "error";
}

页面中的异步请求:

<script type="text/javascript" src="js/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
    function get() {
      
      
        $.ajax({
      
      
            url : "demo/get",
            type : "get",
            data : {
      
      id : "10"},
            success : function (data) {
      
      
                alert(data);
            }
        });
    }
</script>

<p><a href="javascript:get()">异步请求</a></p>

猜你喜欢

转载自blog.csdn.net/ligonglanyuan/article/details/125318910