代理类的异常处理
Java从1.4开始提供了一个代理类,Proxy,此类在反射/服务/接口方面非常有用, 很多时候我们只是将我们的接口公开,并且只是公开接口中的某些方法,或者在某些方法上做一些拦截处理, 即简单的AOP控制,这时候Proxy类就大有用处了。
package demo;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyTest {
static interface ITest {
void doit() throws IOException;
}
static class DefaultTest implements ITest {
public void doit() throws IOException {
throw new IOException("unsupported");
}
}
static ITest getTest() {
final ITest obj = new DefaultTest();
return (ITest) Proxy.newProxyInstance(ITest.class.getClassLoader(), new Class[] { ITest.class }, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(obj, args);
}
});
}
public static void main(String[] args) {
ITest test = getTest();
try {
test.doit();
} catch (IOException ioe) {
System.err.println("1>" + ioe.getMessage());
ioe.printStackTrace();
} catch (Exception e) {
System.err.println("2>" + e.getMessage());
e.printStackTrace();
}
}
}
一直以来以为Proxy类是这样使用的,但是也仅限于学习,事实上没有在实际的应用中使用过, 恰好昨天用在网站上了,早上一看有一些我从来没有见过的异常,如下:
2>null
java.lang.reflect.UndeclaredThrowableException
at demo.$Proxy0.doit(Unknown Source)
at demo.ProxyTest.main(ProxyTest.java:31)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at demo.ProxyTest$1.invoke(ProxyTest.java:23)
... 2 more
Caused by: java.io.IOException: unsupported
at demo.ProxyTest$DefaultTest.doit(ProxyTest.java:15)
... 7 more
显然,我们没有得到正确的异常,而很多业务中异常是一个非常重要的环节。看了下Proxy类和InvocationHandler类以及UndeclaredThrowableException,没看明白怎么处理,google下也没有找到问题的根源。然后我就想既然这个类这么常用(Proxy很常见),那么肯定有例子。本来想看看Spring的源码,发现手头没有懒得找, 直接在Eclipse中使用F4,居然还找到了一个,com.sun.corba.se.spi.orbutil.proxy.DelegateInvocationHandlerImpl类中有一段实例:
return new InvocationHandler() {
public Object invoke( Object proxy, Method method, Object[] args )
throws Throwable {
// This throws an IllegalArgument exception if the delegate
// is not assignable from method.getDeclaring class.
try {
return method.invoke( delegate, args ) ;
} catch (InvocationTargetException ite) {
// Propagate the underlying exception as the
// result of the invocation
throw ite.getCause() ;
}
}
} ;
显然这里捕捉了异常,并且抛出了原异常,这就是关键。我改造了下我的代码就顺利的得到了我的异常。
try {
return method.invoke(obj, args);
} catch (InvocationTargetException e) {
throw e.getCause();//关键在于要抛出真实的异常,如果多层次代理调用,则需要循环处理
}
再次运行输出如下:
1>unsupported
java.io.IOException: unsupported
at demo.ProxyTest$DefaultTest.doit(ProxyTest.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at demo.ProxyTest$1.invoke(ProxyTest.java:25)
at demo.$Proxy0.doit(Unknown Source)
at demo.ProxyTest.main(ProxyTest.java:36)
此时已经得到了正确的异常。这里有一个有趣的事情,一般throw 后面都跟着一个Throwable实例,如果throw null会发生什么呢?