• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

关于java:自定义SPI使用JDK动态代理遇到UndeclaredThrowableException异常排查

java 搞代码 4年前 (2022-01-27) 35次浏览 已收录 0个评论
文章目录[隐藏]

前言

上一篇文章咱们聊了聊聊自定义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";
    }

如果应用第三方组件不想计划一,你也能够在第三方组件的根底上再加一层代理,或者对第三方组件进行切面拦挡解决

demo链接

https://github.com/lyb-geek/springboot-learning/tree/master/springboot-spi-enhance/springboot-spi-framework-circuitbreaker


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:关于java:自定义SPI使用JDK动态代理遇到UndeclaredThrowableException异常排查

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址