Spring全局异常处理器 - 非MVC

17

普通的Spring(Boot)有办法进行全局异常处理吗?或者至少能捕获任何未被捕获异常(例如可能随机发生的RuntimeException)吗?

我已经在Google上搜索过,但我找到的所有内容都是关于控制器的@ControllerAdvice @ExceptionHandler 的。没有关于一般全局异常处理程序的任何信息。

我基本上只想确保如果发生了一些我还没有捕获的异常,那么我可以记录它,以便我知道这个问题。


阅读这个 https://dev59.com/nYjca4cB1Zd3GeqPsSId#28678037,它让人感觉 @ControllerAdvice 是正确的方式。为什么不呢? - Jack Flamp
还有一个ErrorController https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/autoconfigure/web/ErrorController.html,它比ControllerAdvice或ExceptionHandler高一级。但不确定这是否符合您的要求。 - pvpkiran
@JackFlamp 我猜测 OP 没有使用控制器,所以这种方法不起作用。 - Lino
为什么不使用Thread.UncaughtExceptionHandler? - mrkernelpanic
@mrkernelpanic 这种方法有点可行,但是线程在几个地方被创建和使用,我担心没有将这个异常处理程序连接到其中一个地方。我的主要目标是让未记录的异常不可能发生。大多数情况下,我都有很好的异常捕获,所以我想要最后一步的东西是防弹的。 - samanime
1
@JackFlamp 正如Lino所提到的,我在这里没有使用控制器。我的特殊情况实际上是使用@Scheduled自动触发任务。 - samanime
3个回答

3
我认为Spring没有提供一个现成的解决方案。
实现这一点的可能性是使用注释的AOP(您可以在此处找到示例:Where can I catch non rest controller exceptions in spring?)。通过这种方法,您可以为所有使用预定义注释进行注释的方法定义全局切入点。这种方法的好处是将所有错误处理逻辑放在一个地方。

2

通过AOP添加自己的内容,例如下面这样:

@Aspect
@Component
public class ExceptionHandler {

    @AfterThrowing(pointcut = "within(*.*)", throwing = "t")
    public void log(Throwable t) {
        t.printStackTrace();
    }
}

查看速查表以帮助理解切入点。


就像 OP 一样,我有一个 Spring 项目,它不提供端点。我尝试将其添加到我的 Thread.UncaughtExceptionHandler 中,但它没有起作用。 - GuiRitter
尽管我实际上得到了一个“切入点未绑定的正式形式”,这意味着如果我能修复这个方面的问题,它仍然可能起作用。 - GuiRitter

-2
最好扩展Spring的ResponseEntityExceptionHandler类并以您想要的方式进行自定义,例如:
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import javax.validation.ValidationException;

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {

    @ExceptionHandler(ValidationException.class)
    protected ResponseEntity<Object> handle(ValidationException ex) {
        return new ResponseEntity<>(getError(HttpStatus.BAD_REQUEST, ex), new HttpHeaders(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler
    protected ResponseEntity<Object> handle(Exception ex, WebRequest request) {
        try {
            return super.handleException(ex, request);
        } catch (Exception e) {
            return handleExceptionInternal(ex, null, new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR, request);
        }
    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex, @Nullable Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.Series.SERVER_ERROR == status.series()) {
            log.error("Unexpected error.", ex);
        }
        return new ResponseEntity<>(getError(status, ex), headers, status);
    }

    private ApiError getError(HttpStatus status, Exception ex) {
        return ApiError.builder()
            .status(status.value())
            .message(ex.getMessage())
            .build();
    }
}

通过这种方式,您可以覆盖Spring的默认错误响应体,声明您自定义的异常(例如ValidationException),以及允许Spring处理在ResponseEntityExceptionHandler中声明的默认异常。

请注意,尽管在Spring 4.x中不需要在super.handleException(ex, request)周围使用try-catch,但由于底层ResponseEntityExceptionHandler实现的更改,在Spring 5.x中有必要这样做。


OP要求一个非MVC的解决方案。 - Oleg Gumennyj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接