JDK內置 Proxy
類和 InvocationHandler
接口來提供動態代理的實現。在實現鏈接池的時候動態代理就能夠派上用場了。經過代理接管close方法, connectoin關閉的時候就不須要真正關閉,而只是放回鏈接池,具體實現原理能夠參考紅薯關於鏈接池的文章。java
我要寫的呢是關於在測試使用動態代理時碰到的一個問題,先看個人代碼:app
首先是接口 IFunction
ide
public interface IFunction { void doSomething () throws IllegalStateException; }
接口實現 FunctionImpl
post
public class FunctionImpl implements IFunction { @Override public void doSomething() throws IllegalStateException { // 方法什麼也不作, 只拋異常 throw new IllegalStateException(); } }
攔截 IFunctioin 的動態代理類 FunctionHandler
測試
public class FunctionHandler implements InvocationHandler{ private IFunction fun; public FunctionHandler(IFunction function) { this.fun = function; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(fun, args); } }
最後是簡單的調用this
public class Client { public static void main(String[] args) { IFunction fun = (IFunction) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{IFunction.class}, new FunctionHandler(new FunctionImpl())); try { fun.doSomething(); } catch (IllegalStateException e) { e.printStackTrace(); } } }
如此,一個簡單的動態代理完成了, 一眼瞥上去沒什麼問題, 惋惜一運行就拋異常了.net
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException at com.sun.proxy.$Proxy0.doSomething(Unknown Source) at designpattern.proxy.Client.main(Client.java:18) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) Caused by: java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:21) ... 7 more Caused by: java.lang.IllegalStateException at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9) ... 12 more
很奇怪啊,接口只聲明瞭IllegalStateException
, 結果卻拋出了 InvocationTargetException
及 UndeclaredThrowableException
.代理
接下來咱們看看這兩個不速之客是如何產生的。code
首先這兩個異常確定是接口實現類拋出 IllegalStateException
引發的, 因而能夠定位到 java.lang.reflect.Method
的 invoke(Object obj, Object... args)
, 該方法文檔已經說明: 當代理的方法拋出異常時 invoke(Object obj, Object... args)
方法會拋出 InvocationTargetException
異常, 也就是說個人 IllegalStateException
此時會被包裝成 InvocationTargetException
。接口
好,如今已經知道 InvocationTargetException
是由於Method反射機制包裝產生的。
接下來再看 UndeclaredThrowableException
如何產生。
在 InvocationHandler
聲明的方法 invoke(Object proxy, Method method, Object[] args)
的文檔中有這麼一句話
Throwable the exception to throw from the method invocation on the proxy instance. The exception's type must be assignable either to any of the exception types declared in the
throws
clause of the interface method or to the unchecked exception typesjava.lang.RuntimeException
orcode java.lang.Error
. If a checked exception is thrown by this method that is not assignable to any of the exception types declared in thethrows
clause of the interface method, then anUndeclaredThrowableException
containing the exception that was thrown by this method will be thrown by the method invocation on the proxy instance.
這裏也就是說被代理接口的方法在執行的時候拋出的受檢異常必須是接口定義中聲明的異常, 若是拋出的受檢異常未被接口聲明, 那麼此時這個異常就會被包裝成UndeclaredThrowableException
。
那麼也就清楚了,以前已經看到Method.invoke()
時拋出了異常InvocationTargetException
剛好不在 接口聲明的異常範圍內, 所以動態代理執行的時候會拋出異常 UndeclaredThrowableException
。
對於這個問題能夠改良下 FunctionHandler
的代碼就可解決
public class FunctionHandler implements InvocationHandler{ private IFunction fun; public FunctionHandler(IFunction function) { this.fun = function; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { return method.invoke(fun, args); } catch (InvocationTargetException e) { throw e.getCause(); } } }
這樣再次運行測試獲得的結果以下
java.lang.IllegalStateException at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:23) at com.sun.proxy.$Proxy0.doSomething(Unknown Source) at designpattern.proxy.Client.main(Client.java:18) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:606) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
兩個不速之客已經消失了, "老闆不再用擔憂個人異常", 哈哈。