java统一异常处理@RestControllerAdvice

@RestControllerAdvice 是Java中用于全局异常处理的注解,它可以覆盖所有的@RequestMapping注解的方法。通过它,开发者可以自定义异常处理逻辑,统一返回格式。在本文中,将展示如何使用@RestControllerAdvice进行异常处理,并定义自定义的RestResponse类来规范控制器的返回内容。

@RestControllerAdvice注解会应用到所有@RequestMapping中。
直接上代码吧

package com.example.jwt2021921.core;


import com.auth0.jwt.exceptions.TokenExpiredException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;

@RestControllerAdvice
@Slf4j
public class GlobalException {

    // 捕捉其他所有异常
    @ExceptionHandler(Exception.class)
	@ResponseStatus(HttpStatus.OK)
    public RestResponse globalException(Exception e) {
		log.error("服务器异常:", e);
        return  RestResponse.failed("服务器异常",e.getMessage());
    }
    @ExceptionHandler(TokenExpiredException.class)
    public RestResponse tokenExc(Exception e){
        log.error("token异常:", e);
        return  RestResponse.failed("token异常",e.getMessage());
    }
    //参数验证
    @ExceptionHandler(IllegalStateException.class)
    public RestResponse IllegalStateException(IllegalStateException e) {
		log.error("参数错误:", e);
        return RestResponse.failed("参数错误",e.getMessage());
    }
    //参数绑定
    @ExceptionHandler(BindException.class)
    public RestResponse<String> BeanPropertyBindingResult(HttpServletRequest request, Throwable ex, BindException e) {
        log.error("参数绑定失败:", e);
        return RestResponse.failed(e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
    }
	//运行时异常
	@ExceptionHandler(RuntimeException.class)
	public RestResponse<String> RuntimeException(RuntimeException e) {
		log.error("运行时异常:", e);
		return RestResponse.failed(e.getMessage());
	}
	//404处理
	@ExceptionHandler(NoHandlerFoundException.class)
	public RestResponse<String> NoHandlerFoundException(NoHandlerFoundException e) {
		return RestResponse.failed("请求地址错误");
	}

}

还有什么异常自己可以定义在类中。
RestResponse这个类是自己定义的,作用于controller中返回的类容
代码如下

package com.example.jwt2021921.core;


import lombok.*;
import lombok.experimental.Accessors;

import java.io.Serializable;

/**
 * 响应信息主体
 *
 * @param <T>
 * @author ptyhzw
 */

//@ToString
//@Accessors(chain = true)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor

public class RestResponse<T> implements Serializable {

    private static final long serialVersionUID = 1L;

    @Getter
    @Setter
    //返回标记:成功标记=0,失败标记=1
    private int code;

    @Getter
    @Setter
    //返回信息
    private String msg;

    @Getter
    @Setter
    //数据
    private T data;

    public static <T> RestResponse<T> ok() {
        return restResult(null, SystemConstant.SUCCESS, null);
    }

    public static <T> RestResponse<T> ok(T data) {
        return restResult(data, SystemConstant.SUCCESS, null);
    }

    public static <T> RestResponse<T> ok(int code, String msg) {
        return restResult(code, msg);
    }

    public static <T> RestResponse<T> ok(T data, String msg) {
        return restResult(data, SystemConstant.SUCCESS, msg);
    }

    public static <T> RestResponse<T> failed() {
        return restResult(null, SystemConstant.FAIL, null);
    }

    public static <T> RestResponse<T> failed(String msg) {
        return restResult(null, SystemConstant.FAIL, msg);
    }

    public static <T> RestResponse<T> failed(T data) {
        return restResult(data, SystemConstant.FAIL, null);
    }

    public static <T> RestResponse<T> failed(String msg,T data) {
        return restResult(data, SystemConstant.FAIL, msg);
    }

    private static <T> RestResponse<T> restResult(T data, int code, String msg) {
        RestResponse<T> apiResult = new RestResponse<>();
        apiResult.setCode(code);
        apiResult.setData(data);
        apiResult.setMsg(msg);
        return apiResult;
    }

    private static <T> RestResponse<T> restResult(int code, String msg) {
        RestResponse<T> apiResult = new RestResponse<>();
        apiResult.setCode(code);
        apiResult.setMsg(msg);
        return apiResult;
    }
}

package com.example.jwt2021921.core;



public final class SystemConstant {
    /**
     * 成功标记
     */
    public static final  Integer SUCCESS = 0;

    /**
     * 失败标记
     */
    public static final  Integer FAIL = 1;

}

<think> 嗯,用户提出了一个关于Spring框架中全局异常处理的问题。ta在问为什么使用@GlobalTransactional注解时抛出的异常无法被@RestControllerAdvice捕获。 用户引用了四段参考资料,这些资料都涉及到Spring Boot全局异常处理机制。特别是引用[1]和[2]提到了@RestControllerAdvice的基本用法,引用[3]讨论了Filter层面的异常处理,引用[4]展示了异常处理的代码示例。 用户的问题很具体:"@GlobalTranslocation"应该是笔误,实际应该是"@GlobalTransactional",这是Seata分布式事务框架的注解。这种异常处理失效的情况在分布式事务场景中很常见。 关于核心原因,需要从Spring的异常处理机制说起。@RestControllerAdvice本质上是通过AOP代理Controller层的方法,但Seata的@GlobalTransactional是通过自己的代理机制包裹整个事务方法。如果Seata的代理发生在Spring MVC的代理之外,那么Controller层的代理就无法捕获到Seata代理层抛出的异常。 可能的解决方案有以下几种: 1. 最直接的是在Seata的全局事务拦截器中直接捕获异常并进行处理 2. 可以考虑配置Seata使用Spring的代理机制 3. 也可以通过自定义错误控制器来处理 4. 还可以优化异常传播机制,确保异常能传递到Controller层 用户应该是个有经验的Java开发者,ta能准确指出注解名称说明ta对Seata有了解。问题描述很清晰,说明ta已经做过一些调试但没解决。这种情况下需要给出具体可操作的代码示例,而不仅仅是理论说明。 需要特别注意解释清楚代理顺序问题,这个在分布式事务框架中很关键。同时要提醒用户注意自定义异常的继承关系,因为引用[1]特别提到这个点。 </think> 在Java中,使用Seata框架的`@GlobalTransactional`注解时,其抛出的异常无法被`@RestControllerAdvice`捕获的核心原因在于**代理机制的执行顺序**和**异常传播层级**。以下是详细分析和解决方案: --- ### **原因分析** 1. **代理嵌套顺序问题** `@GlobalTransactional`通过AOP代理实现分布式事务管理(Seata的`GlobalTransactionScanner`创建代理对象)。若该代理**包裹在Controller外层**,则其抛出的异常会**绕过**Spring MVC的异常处理机制(包括`@RestControllerAdvice`),直接进入Servlet容器层面。 2. **异常传播层级断裂** Seata的事务拦截器(如`TransactionalInterceptor`)在事务失败时抛出`TransactionException`等运行时异常。若Controller方法未显式捕获该异常,且Seata代理层级高于MVC代理,异常将无法到达`@ExceptionHandler`的作用范围。 3. **Filter/Servlet容器优先处理** 如引用[3]所述,部分异常(如404/500)可能先被Servlet容器的`ErrorController`处理,而非`@RestControllerAdvice`。分布式事务异常也可能因触发HTTP 500状态码而优先进入容器级处理流程。 --- ### **解决方案** #### 方案1:在Seata事务拦截层捕获异常(推荐) 修改Seata的事务拦截器逻辑,在异常抛出前将其封装为Spring MVC可处理的格式: ```java public class CustomGlobalTransactionInterceptor extends GlobalTransactionalInterceptor { @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { try { return super.invoke(methodInvocation); } catch (Exception e) { // 将异常转换为Controller层可识别的对象 throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "事务失败", e); } } } ``` **配置Seata使用自定义拦截器**(在`seata.conf`中指定拦截器类路径)。 #### 方案2:启用Spring AOP代理优先级 强制Seata使用Spring的代理机制,确保事务代理在Controller代理内层: ```yaml # application.yml spring: aop: proxy-target-class: true # 使用CGLIB代理 seata: enable-auto-proxy: true # 确保Seata代理由Spring管理 ``` #### 方案3:扩展`ErrorController`处理Seata异常(引用[3]) 实现Spring的`ErrorController`接口,显式捕获Seata异常类型: ```java @RestController public class SeataErrorController implements ErrorController { @RequestMapping("/error") public ResponseEntity<ErrorResponse> handleSeataError(HttpServletRequest request) { Throwable ex = (Throwable) request.getAttribute("javax.servlet.error.exception"); if (ex instanceof TransactionException) { return ResponseEntity.status(500) .body(new ErrorResponse("TX_FAILURE", "分布式事务异常")); } // 其他异常处理... } } ``` #### 方案4:异常类型继承优化(引用[1][2]) 确保自定义异常继承`RuntimeException`**且未被Seata内部捕获**: ```java // 自定义异常需显式抛出未被Seata拦截的类型 public class CustomBusinessException extends RuntimeException { public CustomBusinessException(String message) { super(message); } } // 在Service层抛出 @GlobalTransactional public void businessMethod() { if (error) throw new CustomBusinessException("业务异常"); } ``` --- ### **验证步骤** 1. 在`@RestControllerAdvice`中添加对`TransactionException`的捕获: ```java @ExceptionHandler(TransactionException.class) public ResponseEntity<String> handleSeataException(TransactionException e) { return ResponseEntity.status(500).body("Seata异常: " + e.getMessage()); } ``` 2. 抛出测试异常: ```java @GlobalTransactional @GetMapping("/test") public String test() { throw new RuntimeException("模拟事务失败"); } ``` 3. 检查响应是否经过`@RestControllerAdvice`处理。 --- ### **关键结论** | 方案 | 适用场景 | 优势 | |---------------------|----------------------------------|--------------------------| | **自定义Seata拦截器** | 需统一异常格式 | 彻底解决代理层级问题 | | **调整代理优先级** | 简单应用且Seata版本兼容 | 配置简单 | | **实现ErrorController** | 需兼容Servlet容器级异常 | 覆盖范围广 | | **优化异常类型** | 业务异常需与事务异常分离 | 精准控制异常传播路径 | > 优先推荐**方案1**或**方案4**,因二者直接解决代理顺序和异常类型问题。若异常仍未被捕获,需检查Seata版本与Spring Boot的兼容性(如Seata≥1.4.0需配合`seata-spring-boot-starter`)[^1][^3]。 --- ### 相关问题 1. **Seata分布式事务在哪些场景下会绕过Spring全局异常处理机制?** 2. **如何设计分层异常处理策略以避免代理嵌套冲突?** 3. **Spring Boot中`@ControllerAdvice`与`ErrorController`的优先级是怎样的?** 4. **Seata事务回滚时如何保证业务异常信息不丢失?**
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值