全局异常处理实现

全局异常统一处理

​ 全局异常处理类通常用于捕获和处理应用程序中发生的所有异常,从而避免在代码的多个地方重复编写异常处理逻辑。

一、全局异常处理方案

​ 全局异常处理类有多种实现方式,每种方式都有其特定的应用场景和优势。以下是几种常见的全局异常处理类实现方式:

​ 应考虑你的应用程序的特定需求、使用的技术栈以及团队的开发习惯。例如,如果你正在使用Spring框架开发Web应用程序,那么使用 @ControllerAdvice 和 @ExceptionHandler 注解可能是一个很好的选择。

1.1 使用AOP(面向切面编程)

​ AOP允许你定义横切关注点,这些关注点跨多个类或方法。你可以创建一个切面来捕获所有方法抛出的异常。

  • AOP允许你定义横切关注点,这些关注点跨多个类或方法。在异常处理中,你可以创建一个切面来捕获所有方法抛出的异常。

  • 在Spring框架中,你可以使用 @Aspect 注解来定义切面,并使用 @Pointcut 、 @Before、@After、@AfterReturning 、@AfterThrowing 等注解来定义切面的行为和要拦截的点。

  • 当方法抛出异常时, @AfterThrowing 注解的方法会被调用,从而允许你进行全局异常处理。

AOP案例代码:

​ 我们定义了一个切面 GlobalExceptionAspect ,它会在任何com.example.myapp 包及其子包中的方法抛出异常后被执行。在 handleException 方法中,我们可以编写通用的异常处理逻辑。

@Aspect  
@Component  
public class GlobalExceptionAspect {  
  
    @AfterThrowing(pointcut = "execution(* com.example.myapp..*(..))", throwing = "ex")  
    public void handleException(JoinPoint joinPoint, Throwable ex) {  
        // 记录日志、发送通知等异常处理逻辑  
        // ...  
    }  
}

1.2 使用过滤器/拦截器实现

​ 在Web应用程序中,你可以使用过滤器(Filter)或拦截器(Interceptor)来捕获和处理异常。

  • 过滤器或拦截器来捕获和处理异常。这些组件通常在请求到达控制器之前或响应返回客户端之后执行。
  • 在Servlet规范中,你可以实现 javax.servlet.Filter 接口来创建自定义过滤器。在Spring MVC中,你可以实现org.springframework.web.servlet.HandlerInterceptor 接口来创建拦截器。
  • 在过滤器或拦截器中,你可以检查请求和响应,并在出现异常时执行相应的处理逻辑。

过滤器/拦截器案例:

​ 实现了一个 Filter 接口,并在 **doFilter **方法中捕获了所有经过该过滤器的请求的异常。你可以在这个方法中编写异常处理的逻辑。

@Component  
public class GlobalExceptionFilter implements Filter {  
  
    @Override  
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)  
            throws IOException, ServletException {  
        try {  
            chain.doFilter(request, response);  
        } catch (Throwable e) {  
            // 异常处理逻辑  
            // ...  
        }  
    }  
}

1.3 使用Spring的@ControllerAdvice和@ExceptionHandler注解

  • 在Spring框架中,@ControllerAdvice 注解允许你定义一个类,该类包含多个**@ExceptionHandler** 注解的方法,用于处理特定类型的异常。
  • 当控制器方法抛出异常时,Spring会查找匹配的 @ExceptionHandler 方法,并执行它来处理异常。这种方法非常适合于Web应用程序,因为它允许你将异常处理逻辑与控制器方法分离。

案例:

​ 例子中,我们定义了一个全局异常处理器 GlobalExceptionHandler,它有两个异常处理方法。第一个方法专门处理 CustomException 类型的异常,第二个方法则处理所有其他类型的异常。

@ControllerAdvice  
public class GlobalExceptionHandler {  
  
    @ExceptionHandler(value = CustomException.class)  
    @ResponseBody  
    public ResponseEntity<Object> handleCustomException(CustomException e) {  
        Map<String, Object> errorDetails = new HashMap<>();  
        errorDetails.put("message", e.getMessage());  
        errorDetails.put("timestamp", LocalDateTime.now());  
        return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);  
    }  
  
    @ExceptionHandler(Exception.class)  
    @ResponseBody  
    public ResponseEntity<Object> handleGeneralException(Exception e) {  
        // 处理其他所有异常  
        // ...  
    }  
}

1.5 其它方式

1.5.1 BasicExceptionController 请求转发实现

​ 基于请求转发的异常处理方式是真正的全局异常处理。

BasicExceptionController 是Spring Boot默认处理异常的方式。当程序中发生异常时,Spring Boot会请求 /error 的URL, BasicExceptionController 类负责处理这个请求,并跳转到默认的错误页面来展示异常信息。

​ 这个默认的错误页面是可以自定义的,通常可以在项目的 src/main/resources/templates/ 目录下创建一个名为 error 的文件,这个文件可以是JSP或HTML格式。这种方式符合全局异常处理的需求。

​ 常用的异常处理有两种,一种是BasicErrorController,另一种是@ControllerAdvice,BasicErrorController用于处理非Controller抛出的异常,而@ControllerAdvice用于处理Controller抛出的异常,对于非Controller抛出的异常它是不会管的。但是,如果是Controller层调用Service层,从Service层抛出,依然会抛到Controller,所以还是会调用@ControllerAdvice进行处理。

​ 如果需要更复杂的异常处理逻辑,可以自定义一个Controller继承BasicErrorController,并覆盖其方法来实现自定义的异常处理。但是大多数请情况下,不要去继承重写,BasicErrorController,使用@ControllerAdvice已经是够用的了,因为你的所有请求都会经过Controller。

​ 下面几种方式并非是真正意义上的全局异常处理方案:

1.5.2 全局异常处理类
  • 可以创建一个单独的类,该类包含静态方法或实例方法,用于处理异常。然后在代码的多个地方调用这些方法。

  • 这种方法可能不如使用AOP或Spring的注解那样自动化,但它提供了更大的灵活性,允许你根据需要在不同地方使用不同的异常处理逻辑。

1.5.3 使用日志记录

​ 不是直接的全局异常处理方式,但使用日志记录框架(如Log4j、SLF4J等)来记录异常信息也是一种常见的做法。这样,即使异常没有被立即处理,你也可以通过查看日志文件来诊断问题。

1.5.4 自定义异常处理类
  • 你可以创建一个自定义的异常处理类,该类实现了特定的接口或继承了特定的类(如Java的 Exception 类)。这个类可以包含额外的字段和方法,用于存储与异常相关的额外信息或执行特定的处理逻辑。

二、@ExceptionHandler 使用案例

​ 在Spring框架中, @ExceptionHandler 注解用于指定一个方法,该方法用于处理控制器中抛出的特定异常。当你想要在单个控制器内部异常时,这个注解非常有用。

​ 该种方式只能作用于使用@ExceptionHandler注解的Controller的异常,对于其他Controller的异常就无能为力了,所以并不不推荐使用。

​ 此种方式是通过异常处理器实现的,使用HandlerExceptionResolverComposite异常处理器中的ExceptionHandlerExceptionResolver异常处理器处理的。

  • @ExceptionHandler 的使用案例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

/**
 * @ClassName : MyController
 * @Description : 1测试异常处理注解 @ExceptionHandler 的使用
 * @Author : AD
 */

@RestController
public class MyController {
    @Autowired
    private HttpServletRequest request;

    @GetMapping("/test1")
    public double divide(){
        System.out.println("访问地址成功进入Controller层:"+request.getRequestURI());
        throw  new IllegalArgumentException("Division by zero is not allowed");
    }

    /**
     * Description: 异常处理方法@ExceptionHandler
     *      @ExceptionHandler方法可以有多个参数,包括异常对象本身、WebRequest对象(用于访问当前请求的信息)等。这使得你可以根据具体的异常情况和请求上下文来定制异常处理逻辑。
     * @param e
     * @return java.util.Map
    */
    @ExceptionHandler(Exception.class)
    public Map exceptionHandlerMethod(Exception e){
        HashMap<String, Object> map = new HashMap<>();
        map.put("success", "这是 @ExceptionHandler 注解的异常处理方法执行的结果");
        map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        map.put("msg", e.getMessage());
        return map;
    }
}
  • 未添加 @ExceptionHandeler 异常处理方法前的效果

    在这里插入图片描述

  • 添加 @ExceptionHandeler 异常处理方法后的效果

    在这里插入图片描述

注: @ExceptionHandler 方法可以有多个参数,包括异常对象本身、 WebRequest 对象(用于访问当前请求的信息)等。这使得你可以根据具体的异常情况和请求上下文来定制异常处理逻辑。

三、@ControllerAdvice 处理全局异常

​ 在使用 @ExceptionHandler 注解时就存在局限性,只能作用于使用@ExceptionHandler注解的Controller的异常,对于其他Controller的异常就无能为力了。

​ 使用 @ControllerAdvice+@ExceptionHandler注解能够进行近似全局异常处理,这种方式推荐使用

​ 一般说 @ControllerAdvice + @ExceptionHandler 只能处理控制器中抛出的异常,这种说法并不准确,其实它能处理 DispatcherServlet.doDispatch 方法中DispatcherServlet.processDispatchResult 方法之前捕捉到的所有异常,包括:拦截器、参数绑定(参数解析、参数转换、参数校验)、控制器、返回值处理等模块抛出的异常。

3.1 定义@ControllerAdvice 类

​ GlobalExceptionHandler 类被标记为 @ControllerAdvice ,这意味着它包含的 @ExceptionHandler 方法将应用于所有的控制器。当控制层中的方法抛出一个IllegalArgumentException 时,Spring将查找与 IllegalArgumentException 匹配的@ExceptionHandler 方法,并调用 handleIllegalArgumentException 来处理这个异常。

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName : GlobalExceptionHandler
 * @Description : 通过 @ControllerAdvice注解该类,使得@ExceptionHandler方法将应用于所有的控制器。
 * @Author : AD
 */
//@ControllerAdvice
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 注:
     * 1.@RestControllerAdvice注解 =@ControllerAdvice注解  +@ResponseBody 注解,返回值自动为JSON的形式
     * 2.可以根据异常的类型格式,进行单独处理不同类型的异常情况
     * 3.可以自定义异常信息,通过系统中抛出后,在@ExceptionHandler异常处理方法中进行处理
     * */

    @ExceptionHandler(value = IllegalArgumentException.class)
    public Map IllegalArgumentExceptionHandler(Exception e){
        log.error("1异常日志:", e);
        HashMap<String, Object> map = new HashMap<>();
        map.put("success111", "这是 @ControllerAdvice 注解的异常处理方法执行的结果");
        map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        map.put("msg", e.getMessage());
        return map;
    }

    @ExceptionHandler(value = IllegalArgumentException.class)
    public Map NullPointExceptionHandler(Exception e){
        log.error("2异常日志:", e);
        HashMap<String, Object> map = new HashMap<>();
        map.put("success222", "这是 @ControllerAdvice 注解的异常处理方法执行的结果");
        map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        map.put("msg", e.getMessage());
        return map;
    }

    @ExceptionHandler(value = Exception.class)
    public Map AllExceptionHandler(Exception e){
        log.error("3异常日志:", e);
        HashMap<String, Object> map = new HashMap<>();
        map.put("success222", "这是 @ControllerAdvice 注解的异常处理方法执行的结果");
        map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
        map.put("msg", e.getMessage());
        return map;
    }
}

3.2 测试全局异常类

  • 创建抛出异常的控制层
package com.example.spring_globalexceptionhandle.AdviceExceptionTest22;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @ClassName : GlobalExceptionController
 * @Description : 全局异常制造控制器
 * @Author : AD
 */
@RestController
public class GlobalExceptionController {

    @GetMapping("/testNullPoint")
    public boolean testNullPoint(){
        String srt = null;
        String s = srt.toString();
        return true;
    }
    @GetMapping("/IllegalArgument")
    public void testIllegalArgument(){
        throw  new IllegalArgumentException("Division by zero is not allowed");
    }
    @GetMapping("/exception")
    public void testException() throws IOException {
        throw  new IOException();
    }
}
  • 访问对应Controller层,全局处理类中会根据对应的 @ExceptionHandler异常处理方法中对应类型的处理方法进行处理

    在这里插入图片描述

​ ps注:如果既在一个Controller层中使用了@ExceptionHandler注解方法,也定义了全局异常处理器类(@ControllerAdvice+@ExceptionHandler),优先使用Controller定义的@ExceptionHandler处理。如果处理不了,才会使用全局异常处理器处理。

其它类的封装:

  • 异常常量池

    /**
     * @ClassName : ExceptionConstant
     * @Description : 异常常量池
     * @Author : AD
     */
    public interface ExceptionConstant {
        /**
         * 程序异常,请联系管理
         */
        String ERROR = "exception.error";
    
        /**
         * 租户被禁用或丢失,请重新选择租户
         */
        String TENANT_DISABLED = "exception.tenant_disabled";
    
        /**
         * 登录类型为空
         */
        String AUTHENTICATION_TYPE_EMPTY ="exception.login_type_empty";;
    }
    

  • 异常枚举类

    package com.example.spring_globalexceptionhandle.aaaabaseConfig;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    /**
     * @ClassName : ErrorCode
     * @Description : 异常码枚举
     * @Author : AD
     */
    
    @Getter
    @AllArgsConstructor
    public enum ErrorCode {
    
        /**
         * 程序异常,请联系管理
         */
        ERROR(500, ExceptionConstant.ERROR),
    
        /**
         * 登录类型为空
         */
        AUTHENTICATION_TYPE_EMPTY(60001, ExceptionConstant.AUTHENTICATION_TYPE_EMPTY);
    
        private final int code;
        private  final String message;
    }
    

  • 自定义异常类

    package com.example.spring_globalexceptionhandle.aaaabaseConfig;
    import lombok.Getter;
    import lombok.Setter;
    /**
     * @ClassName : MyException
     * @Description : 自定义异常类、
     * @Author : AD
     */
    @Getter
    @Setter
    public class MyException extends RuntimeException{
        private int errorCode;
    
        public MyException(String message) {
            super(message);
        }
    
        public MyException(ErrorCode errorCode) {
            super(errorCode.getMessage());
            this.errorCode = errorCode.getCode();
        }
    }
    

三、基于SimpleMappingExceptionResolver实现异常捕获

​ SimpleMappingExceptionResolver 是Spring MVC框架中用于处理异常的组件,它能够将发生的异常映射到特定的视图,从而为用户提供友好的错误提示,而不是暴露内部的异常堆栈信息。

​ 使用简单映射异常处理器处理异常,通过配置SimpleMappingExceptionResolver类也是进行近似全局异常处理,但该种方式不能得到具体的异常信息,且返回的是视图不推荐使用

3.1 创建异常处理配置类

此种方式是通过异常处理器实现的,使用SimpleMappingExceptionResolver异常处理器处理的。

package com.example.spring_globalexceptionhandle.SimpleMappingExceptionTest33;

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

import java.util.Properties;

/**
 * @ClassName : GlobalExceptionConfig
 * @Description : 使用SimpleMappingExceptionResolver异常处理器处理全局异常,对应配置类
 * @Author : AD
 */
@Configuration
public class GlobalExceptionConfig {

    @Bean
    public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver exceptionResolver = new SimpleMappingExceptionResolver();
        /**
         * 参数一:异常的类型,这里必须要异常类型的全名
         * 参数二:要跳转的视图名称
         */
        Properties mappings = new Properties();
        mappings.put("java.lang.ArithmeticException", "error");
        mappings.put("java.lang.NullPointerException", "error");
        mappings.put("java.lang.Exception", "error");
        mappings.put("java.io.IOException", "error");
        // 设置异常与视图的映射信息
        exceptionResolver.setExceptionMappings(mappings);
        return exceptionResolver;
    }
}

若当前项目为 Spring MVC 项目,在Spring MVC的配置文件中(通常是 dispatcher-servlet.xml),你需要声明一个 SimpleMappingExceptionR

<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans  
       http://www.springframework.org/schema/beans/spring-beans.xsd">  
  
    <!-- 配置SimpleMappingExceptionResolver -->  
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">  
        <!-- 设置默认的异常处理页面 -->  
        <property name="defaultErrorView" value="error"/>  
        <!-- 设置异常处理页面用来获取异常信息的变量名 -->  
        <property name="exceptionAttribute" value="exception"/>  
        <!-- 如果需要针对特定异常进行映射,可以使用exceptionMappings属性 -->  
        <property name="exceptionMappings">  
            <props>  
                <prop key="java.lang.Exception">globalError</prop>  
                <prop key="org.springframework.web.bind.MissingServletRequestParameterException">missingParamError</prop>  
            </props>  
        </property>  
    </bean>  
    <!-- 其他bean配置 -->  
</beans>

3.2 创建error视图页面

​ 需要创建与配置中指定的错误视图对应的JSP页面。例如,对于 error 视图,你可以创建一个名为 error.jsp 的页面,并在其中使用 ${exception}来获取异常信息。

<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Error Page</title>
</head>
<body>
    <h1>Error Page</h1>
    <p>${exception.message}</p>
</body>
</html>

​ 通过调用接口触发异常发生即可。

四、基于HandlerExceptionResolver实现

​ HandlerExceptionResolver 是 Spring MVC 提供的一个接口,它允许你实现自定义的全局异常处理。当你的 Spring MVC 控制器方法抛出异常时,Spring MVC 会查找合适的 HandlerExceptionResolver 来处理这个异常。

​ 实现HandlerExceptionResolver接口来处理异常,该种方式是近似全局异常处理此种方式是通过异常处理器实现的,使用自定义的异常处理器(实现HandlerExceptionResolver接口)处理的。

4.1 创建自定义HandlerExceptionResolver

​ 需要创建一个实现了 HandlerExceptionResolver 接口的类。这个接口有一个方法 resolveException ,你需要在这个方法中实现异常处理逻辑。

  • 实现代码
package com.example.spring_globalexceptionhandle.HandlerExceptionTest44;

import com.alibaba.fastjson.JSONObject;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;

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

/**
 * @ClassName : MyExceptionResolver
 * @Description : 创建一个实现了 HandlerExceptionResolver 接口的类。这个接口有一个方法 resolveException,你需要在这个方法中实现你的异常处理逻辑
 * @author AD
 */
public class MyExceptionResolver extends AbstractHandlerExceptionResolver{


    /**
     * Description: 异常处理方法
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @return org.springframework.web.servlet.ModelAndView
    */
    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
        System.out.println("44异常处理器执行了!!!");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        Map<Object, Object> result = new HashMap<>();
        result.put("code", 500);
        result.put("msg", ex.getMessage());
        try {
            response.getWriter().write(JSONObject.toJSONString(result));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return new ModelAndView("static/error44.jsp");
    }

    /**
     * Description: 可设置异常解析器的顺序,数值越小,表示优先级越高
     *
     * @param
     * @return int
    */
    @Override
    public int getOrder(){
        return -99;
    }
}

​ 上面示例我们通过继承 AbstractHandlerExceptionResolver 类,因为该类实现了 HandlerExceptionResolver类:

在这里插入图片描述

并且重写了 resolveException 方法:

在这里插入图片描述

4.2 注册HandlerExceptionResolver

​ 有多种方式可以注册你的 HandlerExceptionResolver。一种常见的方式是在 Spring MVC 的配置文件中进行配置,也可以在配置类 Java Config 中进行注册。

  • 通过配置类注入异常处理器
package com.example.spring_globalexceptionhandle.HandlerExceptionTest44;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

/**
 * @ClassName : WebConfig
 * @Description : 配置类文件:注入全局异常处理器 MyExceptionResolver
 * @Author : AD
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * Description:通过配置类注册全局异常处理器
     *
     * @param resolvers
     * @return void
    */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        resolvers.add(new MyExceptionResolver());
    }
}
  • 方式二:通过XML配置异常处理类
<beans ...>  
    <bean class="com.example.spring_globalexceptionhandle.HandlerExceptionTest44;"/>  
</beans>

4.3 创建错误视图

​ 在你的 resolveException 方法中,你返回了一个 ModelAndView 对象,该对象指定了一个名为 “error” 的视图。你需要确保在你的项目中有一个名为 “error” 的视图文件。这个文件可以是一个 JSP、Thymeleaf 模板或其他任何 Spring MVC 支持的视图类型。在这个视图中,你可以显示异常信息或其他任何你想要显示的内容。

​ 现在,当你的 Spring MVC 控制器方法抛出异常时,Spring MVC 会调用你的 CustomHandlerExceptionResolver 的 resolveException 方法来处理这个异常。你可以在这个方法中实现任何你想要的异常处理逻辑。例如,你可以记录异常信息,发送电子邮件通知,或者重定向用户到一个错误页面。

五、Filter实现异常处理

​ 基于过滤器的异常处理是另一种在 Java Web 应用中实现全局异常处理的策略。过滤器(Filter)是 Servlet 规范中定义的一种组件,它可以在请求到达 Servlet 之前或响应发送回客户端之后执行一些操作。通过在过滤器中捕获和处理异常,我们可以实现一种全局的异常处理机制。

5.1 创建自定义过滤器

​ 创建一个实现了 javax.servlet.Filter 接口的类。在这个类中,你需要重写 doFilter方法,并在该方法中捕获并处理异常。

package com.example.spring_globalexceptionhandle.FilterExceptionHandle;

import lombok.extern.slf4j.Slf4j;

import javax.servlet.*;
import javax.servlet.FilterConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName : MyExceptionFilter
 * @Description :  * 自定义异常过滤器  * 用于处理Controller外抛出的异常(如Filter抛出的异常)
 *
 *  extends OncePerRequestFilter 可以继承该过滤器类
 * @Author : AD
 */
@Slf4j
public class GlobalExceptionFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig){
        // 初始化方法,可以在这里进行一些配置或资源加载
    }
    @Override
    public void destroy() {
        //销毁方法,用于释放资源
    }

    /**
     * Description: 异常处理方法
     *
     * @param request
     * @param response
     * @param filterChain
     * @return void
    */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;

        try {
            // 继续执行后续过滤器或目标资源
            filterChain.doFilter(request, response);
        } catch (Throwable e) {
            // 捕获异常并进行处理
            handleException(req, res, e);
        }
    }
    private void handleException(HttpServletRequest req, HttpServletResponse res, Throwable e) {
        // 在这里处理异常,比如记录日志、设置错误页面等
        // 可以将异常信息保存到 request 属性中,以便在错误页面中显示
        req.setAttribute("msg", e.getMessage());
        req.setAttribute("code",500);
        // 转发到错误页面
        try {
            req.getRequestDispatcher("pages/error44.html").forward(req, res);
        } catch (ServletException | IOException ex) {
            // 处理转发异常
            ex.printStackTrace();
        }
    }
}

5.2 注册过滤器

​ 将过滤器注册到 Servlet 容器中,以便它能够拦截请求。这可以通过在 web.xml 文件中添加 <filter> 和 <filter-mapping> 配置来实现,或者如果你使用 Java Config,可以在配置类中添加 @WebFilter 注解或使用 FilterRegistrationBean。

  • web.xml配置方式
<web-app ...>  
    <filter>  
        <filter-name>globalExceptionFilter</filter-name>  
        <filter-class>com.xxxx.MyExceptionFilter</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>globalExceptionFilter</filter-name>  
        <url-pattern>/*</url-pattern> <!-- 拦截所有请求 -->  
    </filter-mapping>  
</web-app>
  • 配置类注册方式

    ​ 基于过滤器的异常处理方式,比异常处理器处理的范围要大一些(能处理到Filter过滤器抛出的异常),更近似全局异常处理。使用自定义过滤器进行异常处理时,该过滤器应该放到过滤链的第一个位置,这样才能保证能处理到后续过滤器抛出的异常。

package com.example.spring_globalexceptionhandle.FilterExceptionHandle;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @ClassName : FilterConfig
 * @Description : 监听器配置类
 * @Author : AD
 */

@Configuration
public class FilterConfig {
    @Bean
    public FilterRegistrationBean<GlobalExceptionFilter> globalExceptionFilterRegistration() {
        FilterRegistrationBean<GlobalExceptionFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new GlobalExceptionFilter());
        registrationBean.setName("globalExceptionFilter");
        /**
         * 拦截所有请求
         * */
        registrationBean.addUrlPatterns("/*");
        /**
         * 此处尽量小,要比其他Filter靠前
         */
        registrationBean.setOrder(-1);
        return registrationBean;
    }
}
  • 创建错误页面

      在你的 Web 应用中,你需要创建一个错误页面(例如  error.jsp),该页面将显示过滤器中设置的异常信息。你可以通过  request 对象的属性来访问这个信息。
    
<%@ page contentType="text/html;charset=UTF-8" language="java" %>  
<html>  
<head>  
    <title>Error Page</title>  
</head>  
<body>  
    <h1>Error Occurred</h1>  
    <p>${msg}</p> <!-- 显示异常信息 -->  
</body>  
</html>

​ 现在,当你的 Web 应用中的任何控制器方法抛出异常时,这个异常将被 GlobalExceptionFilter 捕获并处理。过滤器会设置异常信息到 request 对象中,并将请求转发到一个错误页面。

六、异常基础知识

  • 什么是异常?

    异常是程序在“编译”或者“执行”的过程中可能出现的问题,注意:语法错误不算在异常体系中。

    比如:数组索引越界、空指针异常、 日期格式化异常,等…

  • 为什么要学习、处理异常?

    异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止.

    研究异常并且避免异常,然后提前处理异常,体现的是程序的安全, 健壮性。

6.1 异常体系

在这里插入图片描述

  • Error

    系统级别问题、JVM退出等,代码无法控制。

  • Exception:

    java.lang包下,称为异常类,它表示程序本身可以处理的问题

    • RuntimeException及其子类:运行时异常就是在运行时出现的异常,编译阶段不会报错。(空指针异常,数组索引越界异常)
    • 除RuntimeException之外所有的异常:编译时异常就是在编译的时候出现的异常,编译期必须处理的,否则程序不能通过编译。 (日期格式化异常)。

    在这里插入图片描述

常见的运行时异常

  • 数组索引越界异常: ArrayIndexOutOfBoundsException
  • 空指针异常 : NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
  • 数学操作异常:ArithmeticException
  • 类型转换异常:ClassCastException
  • 数字转换异常: NumberFormatException

6.2 异常的默认处理流程

​ 默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!

  1. 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
  2. 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机。
  3. 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
  4. 直接从当前执行的异常点干掉当前程序。
  5. 后续代码没有机会执行了,因为程序已经死亡。

异常的处理机制:

​ 编译时异常是编译阶段就出错的,所以必须处理,否则代码根本无法通过

编译时异常的处理形式有三种:

  • 出现异常直接抛出去给调用者,调用者也继续抛出去。

    throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。

    这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起程序死亡。

    method throws Exception{
        //方法体
    }
    
    method2 throws 异常1 ,异常2 ,异常3 ..{
    	//方法体
    }
    

  • 出现异常自己捕获处理,不麻烦别人。

    监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。

    这种方式还可以,发生异常的方法自己独立完成异常的处理,程序可以继续往下执行。

    try{
    	// 监视可能出现异常的代码!
     }catch(异常类型1 变量){
     	// 处理异常
     }catch(异常类型2 变量){
     	// 处理异常
     }...
    
    
    
    try{
    	// 可能出现异常的代码!
    }catch (Exception e){
    	e.printStackTrace(); 
    	// 直接打印异常栈信息
    }
    //Exception可以捕获处理一切异常类型!
    

  • 前两者结合,出现异常直接抛出去给调用者,调用者捕获处理。

方法直接将异通过throws抛出去给调用者

调用者收到异常后直接捕获处理。

在开发中按照规范来说第三种方式是最好的:底层的异常抛出去给最外层,最外层集中捕获处理。

实际应用中,只要代码能够编译通过,并且功能能完成,那么每一种异常处理方式似乎也都是可以的。

6.3 自定义异常

  • 自定义异常的必要:

    Java无法为这个世界上全部的问题提供异常类。

    如果企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类了。

  • 自定义异常的好处:

    可以使用异常的机制管理业务问题,如提醒程序员注意。

    同时一旦出现bug,可以用异常的形式清晰的指出出错的地方。

6.3.1 自定义异常的分类:
  1. 自定义编译时异常

    • 定义一个异常类继承Exception.
    • 重写构造器。
    • 在出现异常的地方用throw new 自定义对象抛出,

    作用:编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!

  2. 自定义运行时异常

    • 定义一个异常类继承RuntimeException.
    • 重写构造器。
    • 在出现异常的地方用throw new 自定义对象抛出!

    作用:提醒不强烈,编译阶段不报错!!运行时才可能出现!!

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值