Table of Contents
1. Preface
2. Method 1 of global exception handling (@ControllerAdvice and @ExceptionHandler)
3.全局异常并处理的方法二 (AOP)
Home Java javaTutorial How to configure SpringBoot global exception handler to catch exceptions

How to configure SpringBoot global exception handler to catch exceptions

May 14, 2023 pm 09:52 PM
springboot

1. Preface

In any system, we will not stupidly catch and handle exceptions in every place. Generally, we will handle exceptions in one place for the entire system, and handle global exceptions in spring boot. Very simple;

Separation of front and back ends, back-end API, generally for exception handling, there are only two things to do,

1. Record logs and corresponding notification processing, this It is internal

2. It is to return the result to the API caller, it is external

For the API caller, he only It needs a return result (including error code and prompt information), and he doesn't care about the rest.

For the backend, it only needs to record logs, notify or publish corresponding messages to other queues for processing related matters;

So: I have seen many people encapsulating many custom exception classes. In fact, it is completely unnecessary. You only need one exception handler to handle all exceptions, and then encapsulate an error identification code and prompt message. Enumeration is used to return to the API caller; then the back-end processing can be processed directly in one exception handling method. There is no need to encapsulate N multiple custom exceptions, which makes no sense;

Ideological understanding of exceptions

We should realize that all abnormalities are abnormal manifestations of the system, are defects, and are BUGs, although some exceptions are We actively throw;

What we have to do is to improve the system availability as much as possible and avoid the occurrence of any exceptions to the greatest extent, rather than relying on perfect exception handling to improve the system;

Exception Handling is an emergency measure taken when exceptions inevitably occur. The main purpose is to increase friendliness to the outside world and provide remedial clues to the inside;

Don't think that perfect exception handling is the core of the system, it is not. Don't expect exception handling to be perfect, don't expect exception handling to wipe out system defects;

If the system has too many exceptions, then what you have to do is not to improve the exception handling mechanism, but to reflect on the system architecture: Whether the design is reasonable and whether the system logic design is reasonable;

2. Method 1 of global exception handling (@ControllerAdvice and @ExceptionHandler)

============ =====================================

Under development, we will have The following scenario: There are some business exceptions in a certain interface. For example, the parameters entered by the user fail to be verified, the username and password do not exist, etc. When these business exceptions are triggered, we need to throw these custom business exceptions and handle them. Generally, we need to return the status code and exception description of these exception information to the caller in a friendly manner, and the caller uses the status code and other information to determine the specific circumstances of the exception.

In the past, we might need to handle it through try/catch at the controller layer. First catch custom exceptions, then catch other exceptions. For different exceptions, we need to encapsulate the object to be returned while catching. However, the downside of this is that the code becomes verbose. Each interface requires try/catch processing, and once it needs to be adjusted, all interfaces need to be modified, which is very detrimental to code maintenance, as shown in the following code section

@RequestMapping (value = "/test")
public ResponseEntity test() {
    ResponseEntity re = new ResponseEntity();
    // 业务处理
    // ...
    try {
        // 业务
    } catch (BusinessException e) {
        logger.info("业务发生异常,code:" + e.getCode() + "msg:" + e.getMsg());
        re.setCode(e.getCode());
        re.setMsg(e.getMsg());
        return re;
    } catch (Exception e) {
        logger.error("服务错误:", e);
        re.setCode("xxxxx");
        re.setMsg("服务错误");
        return re;
    }
    return re;
}
Copy after login

So, is there any way to do this? How to handle these exception messages easily? The answer is yes. In Spring 3.2, the @ControllerAdvice annotation is added, which can be used to define @ExceptionHandler, @InitBinder, and @ModelAttribute, and is applied to all @RequestMapping. To put it simply, you can configure a global exception handling class through the @ControllerAdvice annotation to uniformly handle exceptions in the controller layer. At the same time, there is no need to write try/catch in the controller, which makes the code clean and easy to maintain.

Usage method

Define custom exceptions

I won’t go into details about the relevant knowledge points about custom exceptions here. If you don’t know, search it yourself. Paste a simple custom business exception class here.

/**
 * 自定义业务异常类
 *
 * @author Yuzhe Ma
 * @date 2018/11/28
 */
@Data
public class BusinessException extends RuntimeException {
    private String code;
    private String msg;
 
    public BusinessException(String code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}
Copy after login

Note: @Data is a Lombok plug-in. Automatically generate set/get methods. The specific usage method will not be introduced here.

@ControllerAdvice @ExceptionHand` Configure the global exception handling class

/**
 * 全局异常处理器
 *
 * @author Yuzhe Ma
 * @date 2018/11/12
 */
@ControllerAdvice
public class GlobalExceptionHandler {
    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
 
    /**
     * 处理 Exception 异常
     *
     * @param httpServletRequest httpServletRequest
     * @param e                  异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ResponseEntity exceptionHandler(HttpServletRequest httpServletRequest, Exception e) {
        logger.error("服务错误:", e);
        return new ResponseEntity("xxx", "服务出错");
    }
 
    /**
     * 处理 BusinessException 异常
     *
     * @param httpServletRequest httpServletRequest
     * @param e                  异常
     * @return
     */
    @ResponseBody
    @ExceptionHandler(value = BusinessException.class)
    public ResponseEntity businessExceptionHandler(HttpServletRequest httpServletRequest, BusinessException e) {
        logger.info("业务异常。code:" + e.getCode() + "msg:" + e.getMsg());
        return new ResponseEntity(e.getCode(), e.getMsg());
    }
}
Copy after login

@ControllerAdvice

Define this class as global exception handling kind.

@ExceptionHandler

Define this method as an exception handling method. The value of value is the class file of the exception class that needs to be handled. In the example, the method is passed in two parameters. One is the corresponding Exception exception class, and the other is the HttpServletRequest class. Of course, in addition to these two parameters, some other parameters are also supported.

In this way, different exceptions can be handled uniformly. Usually, in order to avoid using any try/catch in the controller, Exception can also be processed uniformly in GlobalExceptionHandler. In this way, other exceptions not configured with @ExceptionHandler will be handled uniformly.

Just throw an exception when encountering an exception

In business, when encountering a business exception, just use throw to throw the corresponding business exception. For example,

throw new BusinessException("3000", "账户密码错误");
Copy after login

is written in Controller

Controller 中,不需要再写 try/catch,除非特殊用途。

@RequestMapping(value = "/test")
public ResponseEntity test() {
    ResponseEntity re = new ResponseEntity();
    // 业务处理
    // ...
    return re;
}
Copy after login

结果展示

异常抛出后,返回如下结果。

{
    "code": "3000",
    "msg": "账户密码错误",
    "data": null
}
Copy after login

注意 不一定必须在 controller 层本身抛出异常才能被 GlobalExceptionHandler 处理,只要异常最后是从 contoller 层抛出去的就可以被全局异常处理器处理。异步方法中的异常不会被全局异常处理。抛出的异常如果被代码内的 try/catch 捕获了,就不会被 GlobalExceptionHandler 处理了。总结

本文介绍了在 SpringBoot 中,通过配置全局异常处理器统一处理 Controller 层引发的异常。

优点

减少代码冗余,代码便于维护

缺点

只能处理 controller 层抛出的异常,对例如 Interceptor(拦截器)层的异常、定时任务中的异常、异步方法中的异常,不会进行处理。

3.全局异常并处理的方法二 (AOP)

虽然@ControllerAdvice注解通常和@ExceptionHandler注解用于全局异常的处理。

但是这种方式有个缺点就是,只是对控制层进行了异常拦截,比如像工具类中或者其他类中的异常,并不会拦截。

由于业务执行时不能保证程序不出错,所以写代码必须添加try-catch,但是如果频繁的添加try-catch则必然导致代码结构混乱.所以需要进行优化.

原则:如果出现了问题一般将检查异常,转化为运行时异常.

核心原理: 代理动态思想------->AOP操作

采用自定义AOP的方式可以实现拦截。

有几个关键点

  1. 定义切入点为最大项目包

  2. 采用AOP的@AfterThrowing注解获取到全局异常捕获一个例子package com.example.promethuesdemo.exception; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; /** * @author chenzhen * Created by chenzhen on 2020/7/20.

*/
    @Aspect
    @Slf4j
    @Component
    public class GlobalExceptionAspect {
        @Pointcut("execution(* com.example..*.*(..))")
        public void pointcut(){
 
        }
 
        @AfterThrowing(pointcut = "pointcut()",throwing = "e")
        public void afterThrowing(JoinPoint joinPoint,Throwable e){
            log.error("全局捕获到异常了..............");
            //纪录错误信息
            log.error("系统错误:{}", e.getMessage());
            // todo 想要执行的操作
        }
 
    }
Copy after login

aop中相关概念

Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。* Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它

joint point。* Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。* Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。* Target(目标对象):织入 Advice 的目标对象.。 Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

Advice(增强)的类型

before advice, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中,不能人为地决定是否继续执行 join point 中的代码)* after return advice, 在一个 join point 正常返回后执行的 advice* after throwing advice, 当一个 join point 抛出异常后执行的 advice* after(final) advice, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice.* around advice, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice.* introduction,introduction可以为原有的对象增加新的属性和方法。

注意

spring AOP中的AfterThrowing增强处理可以对目标方法的异常进行处理,但这种处理与直接使用catch捕捉处理异常的方式不同,catch捕捉意味着能完全处理异常,即只要catch块本身不抛出新的异常,则被处理的异常不会往上级调用者进一步传播下去;但是如果使用了AfterThrowing增强处理用于对异常进行处理,处理后异常仍然会往上一级调用者传播,如果是在main中调用的目标方法,那么异常会直接传到JVM,如下截图所示:

How to configure SpringBoot global exception handler to catch exceptions

SpringBoot 之配置全局异常处理器捕获异常

Also note that if an exception occurs in the target method and is caught and processed by catch and catch does not throw a new exception, then the AfterThrowing enhancement processing for the target method will not be executed.

The above is the detailed content of How to configure SpringBoot global exception handler to catch exceptions. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How Springboot integrates Jasypt to implement configuration file encryption How Springboot integrates Jasypt to implement configuration file encryption Jun 01, 2023 am 08:55 AM

Introduction to Jasypt Jasypt is a java library that allows a developer to add basic encryption functionality to his/her project with minimal effort and does not require a deep understanding of how encryption works. High security for one-way and two-way encryption. , standards-based encryption technology. Encrypt passwords, text, numbers, binaries... Suitable for integration into Spring-based applications, open API, for use with any JCE provider... Add the following dependency: com.github.ulisesbocchiojasypt-spring-boot-starter2. 1.1Jasypt benefits protect our system security. Even if the code is leaked, the data source can be guaranteed.

How SpringBoot integrates Redisson to implement delay queue How SpringBoot integrates Redisson to implement delay queue May 30, 2023 pm 02:40 PM

Usage scenario 1. The order was placed successfully but the payment was not made within 30 minutes. The payment timed out and the order was automatically canceled. 2. The order was signed and no evaluation was conducted for 7 days after signing. If the order times out and is not evaluated, the system defaults to a positive rating. 3. The order is placed successfully. If the merchant does not receive the order for 5 minutes, the order is cancelled. 4. The delivery times out, and push SMS reminder... For scenarios with long delays and low real-time performance, we can Use task scheduling to perform regular polling processing. For example: xxl-job Today we will pick

How to use Redis to implement distributed locks in SpringBoot How to use Redis to implement distributed locks in SpringBoot Jun 03, 2023 am 08:16 AM

1. Redis implements distributed lock principle and why distributed locks are needed. Before talking about distributed locks, it is necessary to explain why distributed locks are needed. The opposite of distributed locks is stand-alone locks. When we write multi-threaded programs, we avoid data problems caused by operating a shared variable at the same time. We usually use a lock to mutually exclude the shared variables to ensure the correctness of the shared variables. Its scope of use is in the same process. If there are multiple processes that need to operate a shared resource at the same time, how can they be mutually exclusive? Today's business applications are usually microservice architecture, which also means that one application will deploy multiple processes. If multiple processes need to modify the same row of records in MySQL, in order to avoid dirty data caused by out-of-order operations, distribution needs to be introduced at this time. The style is locked. Want to achieve points

How to solve the problem that springboot cannot access the file after reading it into a jar package How to solve the problem that springboot cannot access the file after reading it into a jar package Jun 03, 2023 pm 04:38 PM

Springboot reads the file, but cannot access the latest development after packaging it into a jar package. There is a situation where springboot cannot read the file after packaging it into a jar package. The reason is that after packaging, the virtual path of the file is invalid and can only be accessed through the stream. Read. The file is under resources publicvoidtest(){Listnames=newArrayList();InputStreamReaderread=null;try{ClassPathResourceresource=newClassPathResource("name.txt");Input

Comparison and difference analysis between SpringBoot and SpringMVC Comparison and difference analysis between SpringBoot and SpringMVC Dec 29, 2023 am 11:02 AM

SpringBoot and SpringMVC are both commonly used frameworks in Java development, but there are some obvious differences between them. This article will explore the features and uses of these two frameworks and compare their differences. First, let's learn about SpringBoot. SpringBoot was developed by the Pivotal team to simplify the creation and deployment of applications based on the Spring framework. It provides a fast, lightweight way to build stand-alone, executable

How to implement Springboot+Mybatis-plus without using SQL statements to add multiple tables How to implement Springboot+Mybatis-plus without using SQL statements to add multiple tables Jun 02, 2023 am 11:07 AM

When Springboot+Mybatis-plus does not use SQL statements to perform multi-table adding operations, the problems I encountered are decomposed by simulating thinking in the test environment: Create a BrandDTO object with parameters to simulate passing parameters to the background. We all know that it is extremely difficult to perform multi-table operations in Mybatis-plus. If you do not use tools such as Mybatis-plus-join, you can only configure the corresponding Mapper.xml file and configure The smelly and long ResultMap, and then write the corresponding sql statement. Although this method seems cumbersome, it is highly flexible and allows us to

How SpringBoot customizes Redis to implement cache serialization How SpringBoot customizes Redis to implement cache serialization Jun 03, 2023 am 11:32 AM

1. Customize RedisTemplate1.1, RedisAPI default serialization mechanism. The API-based Redis cache implementation uses the RedisTemplate template for data caching operations. Here, open the RedisTemplate class and view the source code information of the class. publicclassRedisTemplateextendsRedisAccessorimplementsRedisOperations, BeanClassLoaderAware{//Declare key, Various serialization methods of value, the initial value is empty @NullableprivateRedisSe

How to get the value in application.yml in springboot How to get the value in application.yml in springboot Jun 03, 2023 pm 06:43 PM

In projects, some configuration information is often needed. This information may have different configurations in the test environment and the production environment, and may need to be modified later based on actual business conditions. We cannot hard-code these configurations in the code. It is best to write them in the configuration file. For example, you can write this information in the application.yml file. So, how to get or use this address in the code? There are 2 methods. Method 1: We can get the value corresponding to the key in the configuration file (application.yml) through the ${key} annotated with @Value. This method is suitable for situations where there are relatively few microservices. Method 2: In actual projects, When business is complicated, logic

See all articles