前言
上一篇文章咱们聊了聊聊自定义SPI如何与sentinel整合实现熔断限流。在实现整合测试的过程,呈现一个乏味的异样java.lang.reflect.UndeclaredThrowableException,过后在代码层做了一个全局异样捕捉,示例如下
<code class="java">@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public AjaxResult handleException(Exception e) { String msg = e.getMessage(); return AjaxResult.error(msg,500); } @ExceptionHandler(BlockException.class) public AjaxResult handleBlockException(BlockException e) { String msg = e.getMessage(); return AjaxResult.error(msg,429); } }
原本预期是触发限流时,就会捕捉BlockException 异样,再封装一层渲染进来,没想到死活捕捉不到BlockException 异样。
问题排查
通过debug发现,该问题是因为jdk动静代理引起,前面查找了一些材料,前面在官网的API文档查到这么一段话
他的粗心大略是如果代理实例的调用处理程序的 invoke 办法抛出一个通过查看的异样(不可调配给 RuntimeException 或 Error 的 Throwable),且该异样不可调配给该办法的throws子局申明的任何异样类,则由代理实例上的办法调用抛出UndeclaredThrowableException异样。
这段话咱们能够剖析出如下场景
1、实在实例办法上没有申明异样,代理实例调用时抛出了受检异样
2、实在实例办法申明了非受检异样,代理实例调用时抛出了受检异样
解决方案
计划一:实在实例也申明受检异样
示例:
<code class="java">public class SqlServerDialect implements SqlDialect { @Override public String dialect() throws Exception{ return "sqlserver"; }
计划二:jdk动静代理的invoke进行捕捉,同时能够自定义异样抛出
示例:
<code class="java"> @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args); try { return new CircuitBreakerInvoker().proceed(invocation); } catch (Throwable e) { throw new CircuitBreakerException(429,"too many request"); } }
计划三:捕捉InvocationTargetException异样,并抛出真正的异样
为啥要InvocationTargetException,起因是因为咱们自定义的异样是会被InvocationTargetException包裹
示例
<code class="java"> @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { CircuitBreakerInvocation invocation = new CircuitBreakerInvocation(target,method,args); try { return new CircuitBreakerInvoker().proceed(invocation); //用InvocationTargetException包裹是java.lang.reflect.UndeclaredThrowableException问题 } catch (InvocationTargetException e) { throw e.getTargetException(); } }
总结
如果是咱们本人实现的组件,举荐间接应用计划三,即捕捉InvocationTargetException异样。
如果是用第三方实现的组件,举荐计划一即在调用的实例办法申明异样,比方在应用springcloud alibaba sentinel熔断降级是有概率会呈现UndeclaredThrowableException异样的,因为它也是基于动静代理,他抛出来的BlockException也是一个受检异样。示例如下
<code class="java">public class SqlServerDialect implements SqlDialect { @Override public String dialect() throws BlockException{ return "sqlserver"; }
如果应用第三方组件不想计划一,你也能够在第三方组件的根底上再加一层代理,或者对第三方组件进行切面拦挡解决