美文网首页
UndeclaredThrowableException的产生于

UndeclaredThrowableException的产生于

作者: 东阿 | 来源:发表于2019-06-17 15:47 被阅读0次

    接上章
    InvocationTargetException的产生与处理
    有了上一章的 积累。
    这次 的 UndeclaredThrowableException 的原因也基本 类似,也是 方法的执行过程中,方法没有被直接执行,
    而是被通过 动态代理的方式 进行了执行。
    上代码

    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    class TestAException extends Exception {
    
        TestAException(String s) {
            super(s);
        }
    }
    interface IService {
    
        void foo() throws TestAException;
    }
    
    class ServiceImpl implements IService {
    
        @Override
        public void foo() throws TestAException {
            throw new TestAException("I test throw an checked Exception");
        }
    }
    
    class IServiceProxy implements InvocationHandler {
        private Object target;
    
        IServiceProxy(Object target){
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return method.invoke(target, args);
        }
    }
    
    
    public class AppTest {
        
        public static void main(String[] args) {
            IService service = new ServiceImpl();
            IService serviceProxy = (IService) Proxy.newProxyInstance(service.getClass().getClassLoader(),
                service.getClass().getInterfaces(), new IServiceProxy(service));
            try {
                serviceProxy.foo();
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    
    

    执行结果

    java.lang.reflect.UndeclaredThrowableException
        at im.qingtui.pluto.$Proxy0.foo(Unknown Source)
        at im.qingtui.pluto.AppTest.main(AppTest.java:63)
    Caused by: java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at im.qingtui.pluto.IServiceProxy.invoke(AppTest.java:46)
        ... 2 more
    Caused by: im.qingtui.pluto.TestAException: I test throw an checked Exception
        at im.qingtui.pluto.ServiceImpl.foo(AppTest.java:33)
        ... 7 more
    

    可以看到,异常 不仅 被 包裹了,还被 包裹了两层,具体的原因是
    我们 使用 IServiceProxy 来代理 ServiceImpl。而实际执行的 代理 方法,有没有显示的 声明出,此方法会报TestAException错,动态代理执行的 时候,遇到"未知" 错误 就会将其 包装 为UndeclaredThrowableException。
    而有因为 方法本身 是 通过反射 执行 的,所有 又包装了 一层 InvocationTargetException(原因见上一篇文章)

    处理方案

    修改代理方法 invoke

    class IServiceProxy implements InvocationHandler {
    
        private Object target;
    
        IServiceProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            try {
                result = method.invoke(target, args);
            } catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
            return result;
        }
    }
    
    

    !!!请勿在生产环境直接使用
    这样,代理方法执行的 时候 所抛出的 TestAException 是 原本 IService 的 foo() 本来就声明的 错误,不是"未知" 错误,那么 错误将会被 直接抛出,就能正常处理与识别了。
    具体 到我们的 业务 逻辑 中 则 不能粗暴的 直接

    catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
    

    这个方法 应该能解决一部分需求。但是

    业务逻辑 千变万化
    网上代码 不宜照抄

    建议在异常 统一处理侧,再进行异常的解包,层层解。最后处理,好处是,异常的堆栈信息很好的保留下来,而不是贸然去除"不需要"的部分,不利于进行问题还原与问题追踪。

    具体建议的处理方式 同上一章 相同,捕获 UndeclaredThrowableException获取其所包装的异常,在进一步处理

    相关文章

      网友评论

          本文标题:UndeclaredThrowableException的产生于

          本文链接:https://www.haomeiwen.com/subject/pbnafctx.html