Spring中Aspect与全局异常处理器无法捕获BotAuthorizeException求助
Spring项目中异常无法捕获问题求助
我在Spring项目里遇到自定义异常BotAuthorizeException既无法被Aspect切面捕获,也无法被全局异常处理器捕获的问题,以下是相关代码和异常日志:
1. 自定义注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface BotExceptionCheck { }
2. Aspect切面类
@Aspect @Component public class BotExceptionCheckAspect { SendMessageProducer sendMessageProducer; UserService userService; public BotExceptionCheckAspect(SendMessageProducer sendMessageProducer, UserService userService) { this.sendMessageProducer = sendMessageProducer; this.userService = userService; } @Around("@annotation(BotExceptionCheck)") public Object handleException(ProceedingJoinPoint joinPoint) throws Throwable { Object result = null; try { result = joinPoint.proceed(); } catch (BotAuthorizeException ex) { var user = userService.findById(ex.getChatId()); sendMessageProducer.sendMessage(SendMessage.builder() .chatId(user.getChatId()) .text(ex.getMessage()) .build()); } return result; } }
3. 抛出异常的方法(已添加注解)
@BotExceptionCheck public void checkSubscriptionForChannel(ChatMember chatMember) { if (chatMember.getStatus().equals("kicked") || chatMember.getStatus().equals("left")) { throw new BotAuthorizeException(String.valueOf(chatMember.getUser().getId()), """ You don't subscribe to the channel """); } }
4. 全局异常处理器
@ControllerAdvice @Slf4j public class GlobalExceptionHandler { SendMessageProducer sendMessageProducer; UserService userService; public GlobalExceptionHandler(SendMessageProducer sendMessageProducer, UserService userService) { this.sendMessageProducer = sendMessageProducer; this.userService = userService; } @ExceptionHandler(BotAuthorizeException.class) public void handleBotAuthorizeException(BotAuthorizeException ex){ var user = userService.findById(ex.getChatId()); sendMessageProducer.sendMessage(SendMessage.builder() .chatId(user.getChatId()) .text(ex.getMessage()) .build()); } }
异常日志
2023-08-12 10:15:54.070 ERROR 11228 --- [legram Executor] o.t.t.u.DefaultBotSession : You don't subscribe to the channel. com.moviescreenbot.exceptions.authorize.BotAuthorizeException: You don't subscribe to the channel. at com.moviescreenbot.services.user.UserService.checkSubscriptionForChannel(UserService.java:53) ~[main/:na] at com.moviescreenbot.services.user.UserService.authorizeUser(UserService.java:24) ~[main/:na] at com.moviescreenbot.services.user.UserService$$FastClassBySpringCGLIB$$64cf6081.invoke(<generated>) ~[main/:na] at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.20.jar:5.3.20] at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.20.jar:5.3.20] at com.moviescreenbot.services.user.UserService$$EnhancerBySpringCGLIB$$6ca7934e.authorizeUser(<generated>) ~[main/:na]
问题排查与解决思路
1. Aspect切面未生效原因
从日志调用链能看到,checkSubscriptionForChannel是被authorizeUser方法内部调用的。Spring AOP默认基于动态代理实现,内部方法调用不会触发切面逻辑——因为此时调用的是目标对象的原生方法,而非代理对象的增强方法,所以切面的环绕通知不会执行。
解决方法:
- 方案一:把
checkSubscriptionForChannel方法迁移到另一个Spring管理的Bean中,通过依赖注入调用,确保走代理对象。 - 方案二:在当前Bean中注入自身的代理对象,通过代理对象调用目标方法:
@Service public class UserService { @Autowired private UserService selfProxy; public void authorizeUser(...) { // 使用代理对象调用方法,触发切面 selfProxy.checkSubscriptionForChannel(chatMember); } @BotExceptionCheck public void checkSubscriptionForChannel(ChatMember chatMember) { // 原有逻辑 } } - 方案三:改用AspectJ编译时/加载时织入,替代Spring默认的动态代理,这种方式支持内部方法调用触发切面。
2. 全局异常处理器未生效原因
@ControllerAdvice默认仅处理Spring MVC请求生命周期内Controller层抛出的异常,而你的异常是在Telegram Bot的独立线程[legram Executor]中抛出的,不在Spring MVC的请求处理流程里,所以全局异常处理器无法捕获。
解决方法:
- 方案一:在Bot的业务处理逻辑中手动添加try-catch,直接捕获并处理异常。
- 方案二:配置线程级别的异常处理器,捕获所有未处理的线程异常:
@Configuration public class ThreadExceptionConfig { @Bean public Thread.UncaughtExceptionHandler uncaughtExceptionHandler(SendMessageProducer sendMessageProducer, UserService userService) { return (thread, throwable) -> { if (throwable instanceof BotAuthorizeException ex) { var user = userService.findById(ex.getChatId()); sendMessageProducer.sendMessage(SendMessage.builder() .chatId(user.getChatId()) .text(ex.getMessage()) .build()); } // 其他异常的处理逻辑 }; } @PostConstruct public void setDefaultHandler(Thread.UncaughtExceptionHandler handler) { Thread.setDefaultUncaughtExceptionHandler(handler); } }
内容的提问来源于stack exchange,提问作者wanderer
相关产品推荐
相关产品推荐

