全局异常处理实现

全局异常统一处理

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

一、全局异常处理方案

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

​ 应考虑你的应用程序的特定需求、使用的技术栈以及团队的开发习惯。例如,如果你正在使用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 自定义对象抛出!

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/628002.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何解决代码循环依赖问题?

今天跟大家一起探讨在日常开发过程中经常会碰到的一个问题&#xff0c;这个问题跟代码的维护工作有很大关系。我们知道任何系统在开发了一段时间之后&#xff0c;随着业务功能和代码数量的不断增加&#xff0c;代码之间的调用和被调用场景也会逐渐变的越来越复杂。各个类或组件…

有趣的css - 卡片翻转效果

大家好&#xff0c;我是 Just&#xff0c;这里是「设计师工作日常」&#xff0c;今天分享的是利用 css3 属性 backface-visibility 让卡片翻转的过渡动画效果。 《有趣的css》系列最新实例通过公众号「设计师工作日常」发布。 目录 整体效果核心代码html 代码css 部分代码 完整…

ICode国际青少年编程竞赛- Python-5级训练场-函数练习2

ICode国际青少年编程竞赛- Python-5级训练场-函数练习2 1、 def get_item(a):Spaceship.step(1)Dev.step(a)Dev.turnLeft()Dev.step(1)Spaceship.step(1)Dev.turnRight()Dev.step(-a)Spaceship.step(1) get_item(3) get_item(2) get_item(3) get_item(1) get_item(5)2、 de…

PDF批量编辑:PDF转HTML批量操作技巧,提升文档格式转换效率

在数字化办公日益普及的今天&#xff0c;PDF&#xff08;Portable Document Format&#xff09;作为一种跨平台的文件格式&#xff0c;广泛应用于各种文档的存储和传输。然而&#xff0c;PDF文件的不可编辑性使得在某些情况下&#xff0c;我们需要将其转换为HTML格式以便更好地…

性价比王者HUSB237,极简PD Sink的“瘦身秘籍”

在小型化、高集成的要求下&#xff0c;慧能泰取电芯片进行技术升级后“瘦身成功”&#xff0c;推出最新一代极具性价比的最简PD Sink取电芯片——HUSB237。 图1&#xff1a;HUSB237 demo及封装图 HUSB237 是一款极具性价比的最简PD Sink取电芯片&#xff0c;支持PD3.1协议包含…

无人售货奶柜:掘金新零售蓝海,

无人售货奶柜&#xff1a;掘金新零售蓝海&#xff0c; 在日新月异的商业浪潮中&#xff0c;无人奶柜犹如一股清新的创业飓风&#xff0c;正以不可阻挡之势吸引着众多创业者的目光。这股新兴力量以其独到之处和庞大的市场蓝海&#xff0c;预示着一场关于健康、便捷消费方式的深…

网络故障快速定位的秘诀 - 基于 AnaTraf 全流量回溯分析

网络故障是每个 IT 从业者都深有体会的头疼问题。当网络出现异常时,如何快速定位故障原因,恢复网络正常运行,是考验运维能力的关键所在。借助 AnaTraf 网络流量分析仪的全流量回溯分析功能,您可以轻松应对各种复杂的网络问题,实现快速故障定位。 1. 网络故障分析的痛点 网络故…

【跟着例子学MySQL】生成统计报告 --分组聚合

文章目录 前言生成报告DISTINCT 关键字GROUP BY 子句GROUP BY 聚合函数HAVING 子句WITH ROLLUP 子句未完待续 前言 举例子&#xff0c;是最简单有效的学习方法。本系列文章以一个贯穿始终的场景&#xff0c;结合多个实例讲解MySQL的基本用法。 ❔ 为什么要写这个系列&#xff…

前端铺子后台管理系统:基于Vue3与Ant Design Vue的轻量级解决方案

一、引言 随着前端技术的飞速发展&#xff0c;构建高效、轻量且易于维护的后台管理系统成为了企业信息化建设的重要一环。前端铺子后台管理系统&#xff0c;作为一款基于Vue的前端框架&#xff0c;结合Ant Design Vue的UI组件库&#xff0c;为企业提供了一个高效、灵活的后台管…

铁路机辆作业移动智能终端的特点是什么?

在铁路机辆作业的现代化进程中&#xff0c;移动智能终端以其独特的优势成为了不可或缺的装备。这些终端以其高度的便携性&#xff0c;使得工作人员能够随时随地处理各种作业任务&#xff0c;极大地提升了工作效率。它们具备出色的抗干扰性和高防护性&#xff0c;能够在复杂多变…

Docker部署MaxKB详细步骤(window系统)

上面章节已经实现了ollama李现部署llama3&#xff0c;并实现了一些简单的问答&#xff0c;但是问答的界面是在命令提示符中&#xff0c;交互很不友好&#xff0c;也不方便局域网其他用户访问&#xff0c;所以这节用docker部署MaxKB实现网页访问llama3&#xff0c;首先电脑上需要…

26版SPSS操作教程(高级教程第二十二章)

目录 前言 粉丝及官方意见说明 第二十二章一些学习笔记 第二十二章一些操作方法 联合分析 假设数据 具体操作 结果解释 联合分析的数据建模 CONJOINT过程语法 汽车偏好研究 具体操作 结果解释 高精统计图 市场占有率模拟 结果解释 结束语 前言 #一路向光而…

STL—string类(1)

一、string类 1、为什么要学习string&#xff1f; C语言中&#xff0c;字符串是以\0结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP&#xff08;面向对象…

【Fastadmin】自定义404页面

1.修改config.php // 文件路径&#xff1a;application/config.php// 自定义错误码模板http_exception_template > [// 定义404错误的模板渲染404 > APP_PATH . common/view/404/404.html,], 2.需要把debug关闭才能生效 在.env文件中把debug true&#xff0c;改为…

阿里云OSS如果指定某个文件夹给子账户

** 第一步创建子账号 ** 创建完用户不要给任何权限&#xff01; 当前页面切换到认证管理获取AccessKey即可 第二步目录授权 找到对应桶文件目录 上面授权按钮操作 选择添加的子账号账号保存即可&#xff01;

Ardupilot Rpanion 4GLTE 网络性能测试 - 国内中转

Ardupilot Rpanion 4GLTE 网络性能测试 - 国内中转 1. 源由2. 视频效果2.1 整体刷新率不高2.2 网络延迟可接受2.3 带宽增加丢包明显2.4 实测效果流畅 3. 总结 1. 源由 上一次&#xff0c;由于ZeroTier使用了国外服务器&#xff0c;延迟~ 569 ms&#xff0c;花屏、卡顿。 本着…

[自动化]pyautogui的使用

目录 环境 包的版本 前置知识 鼠标控制函数 屏幕与鼠标位置 size() position() OnScreen() 鼠标移动 moveTo() move() 鼠标拖动 dragTo() drag() mouseDown()按下鼠标 mouseUp()松开鼠标 鼠标滚动 scroll() 键盘控制函数 write() press() keyDown()和keyU…

数据分析(二)——导入外部数据,导入Excel数据,CSV文件,txt文件,HTML网页,数据抽取,DataFrame对象的loc属性与iloc属性

一.导入外部数据 1.导入.xIs或.xIsx文件 pd.read_ excel(io,sheet_ name,header) 1.1常用参数说明 ●io:表示.xIs或.xIsx文件路径或类文件对象 ●sheet name:表示工作表&#xff0c;取值如下表所示 ●header:默认值为0&#xff0c;取第一行的值为列名&#xff0c;数据为除列…

深度剖析MyBatis的一级缓存

概述 MyBatis 的一级缓存是什么时候开启的&#xff1f; 在 MyBatis 中&#xff0c; 一级缓存是默认开启的 。 参考&#xff1a;MyBatis缓存的概念 通过场景来理解: 场景一 1、在一个 SqlSession 中&#xff0c;对 User 表进行两次根据 ID 的查询&#xff0c;查看发出 sql …

一步一步带你做网络工程

网络工程怎么做 一、网络设备交换机的应用&#xff1a; 要求&#xff1a;在此接入交换机S3700&#xff0c;上划分两个vlan&#xff0c;vlan10和vlan20分别有两个PC&#xff0c;按拓扑图完成要求&#xff1a; 划分vlan添加端口 sys [Huawei]sys S1 [S1]undo in e [S1]undo t…