UndeclaredThrowableException的产生于

2019-06-17  本文已影响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获取其所包装的异常,在进一步处理

上一篇下一篇

猜你喜欢

热点阅读