接上章
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获取其所包装的异常,在进一步处理
网友评论