背景
RPCインターフェイスの呼び出しシナリオまたは動的プロキシシナリオを使用すると、非推測されていないエクセプトが時々発生します。この記事では、非宣言されていないexceptionの分析に焦点を当てます
最初に結論を出してください
JDKダイナミックプロキシインターフェイスを使用する場合、メソッドの実行中に検出された例外がスローされているが、メソッド署名は例外を宣言しない場合、プロキシクラスに包まれていない場合があります。
問題を復元します
//インターフェイス定義パブリックインターフェイスiservice {void foo()throws sqlexception;} public class serviceimpl iservice {@override public void foo()throws sqlexception {throw new sqlexception( "tesh throw anch exception"); }} //ダイナミックプロキシパブリッククラスiserviceproxy ImplationsInvocationHandler {private Object Target; iServiceProxy(Object Target){this.target =ターゲット; } @OverrideパブリックオブジェクトInvoke(Object Proxy、Method Method、Object [] args)Throws throwable {return method.invoke(ターゲット、args); }} public class maintest {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.Rang.UndEclaredThrowableExceptionはcom.sun.proxy。$ proxy0.foo(不明な情報源)com.learn.reflt.maint.main(maintest.java:16)で原因:java.lang.lang.reflect.invocationtargetexception方法)sun.reflt.nativemethodaccessorimpl.invoke(nativemethodaccessorimpl.java:62)sun.refllect.delegatingmethodaccessorimpl.invoke(Delegatingmethodaccessorimpl.java:43) com.learn.reflt.iserviceproxy.invoke(iserviceproxy.java:19)... 2 morecaused by:java.sql.sqlexception:com.learn.reflt.serviceimpl.foo(serviceimpl.java:11)...
私たちが期待するのはです
java.sql.sqlexception:com.learn.refllect.serviceimpl.foo(serviceimpl.java:11)でcom.learn.relect.serviceimpl.fooでチェックされた例外をテストするテスト
原因分析
上記の問題の復元では、実際のsqlexceptionは2つの層に包まれ、最初にInvocationTargetExceptionによって包まれ、次に非宣言されていないThrowableExceptionによってラップされます。その中で、InvocationTargetExceptionは検出された例外であり、非宣言されていないExceptionはランタイムの例外です。なぜ包まれているのですか?また、動的プロキシによって生成されたプロキシクラスからも始まります。
JDKダイナミックプロキシは、実行時にデリゲートインターフェイスの特定の実装クラスを生成します。 Proxygeneratorを介してクラスファイルを手動で生成し、アイデアを使用してクラスファイルを解析して特定のプロキシクラスを取得します。
パブリックファイナルクラスiserviceproxy $ 1拡張プロキシ実装IService {private static method m1;プライベート静的メソッドM2;プライベート静的メソッドM3;プライベート静的メソッドM0; Public IserviceProxy $ 1(InvocationHandler var1)throws {super(var1); } public final void foo()throws sqlexception {try {super.h.invoke(this、m3、(object [])null); } catch(runtimeexception | sqlexception | error var2){throw var2; } catch(throwable var3){新しいundeClaredThrowableException(var3); }} static {try {m1 = class.forname( "java.lang.object")。getMethod( "equals"、new class [] {class.forname( "java.lang.object")}); m2 = class.forname( "java.lang.object")。getMethod( "toString"、new class [0]); m3 = class.forname( "com.learn.refllect.iservice")。getMethod( "foo"、new class [0]); m0 = class.forname( "java.lang.object")。getMethod( "hashcode"、new class [0]); } catch(nosuchmethodexception var2){新しいnosuchmethoderror(var2.getmessage()); } catch(classNotFoundException var3){新しいnoclassdeffounderror(var3.getMessage()); }}}「Delegateクラス」のFOOメソッドを呼び出すと、プロキシクラスのIserviceProxy $ 1のFOOメソッドが実際に呼び出され、プロキシクラスの主なロジックは、InvocationHandlerのInvokeメソッドを呼び出すことです。例外処理のロジックは、runtimeexceptionを直接スローすること、インターフェイスによって宣言された例外、エラーがスローされ、他の例外はexplowableexceptionとして包まれています。この時点で、あなたはすでにそれを手に入れているかもしれません、多分あなたは質問があります、インターフェイスの実装では、それは確かに新しいsqlexceptionを投げています、なぜそれはまだラップされていますか? IserviceProxyのInvoke Methodを見てみましょう。反射を通じてターゲットメソッドを直接実行します。これが問題です。 Method.Invoke(Object OBJ、Object ... Args)メソッド宣言は、ターゲットメソッドが例外をスローする場合、InvocationTargetExceptionとしてラップされると説明されています。 (詳細については、Javadocを確認してください)
したがって、概要は次のとおりです。特定のメソッド実装では、sqlexceptionがスローされ、rikecocationTargetExceptionとして反映され、ラップされます。これはチェックされた例外です。プロキシクラスが例外を処理すると、例外がインターフェイスで宣言されていないことがわかります。したがって、宣言されていないエクセプトとしてパッケージ化されます。
解決
Invocety Handlerを実装するInvokeメソッド本体で、Method.Invoke(Target、Args)をキャッチしてください。呼び出し、rkocationTargetExceptionの原因を投げます。今すぐ:
@OverrideパブリックオブジェクトInvoke(Object Proxy、Method Method、Object args)Throws throwable {try {return method.invoke(target、args); } catch(InvocationTargetException e){throw e.getCause(); }}オフトピック
プロキシクラスの非宣言されていないチェックされた例外が、宣言されていない誘惑に変わるのはなぜですか? Java継承の原則のため:サブクラスが親クラスまたは親インターフェイスを実装する方法を上書きする場合、スローされた例外は元の方法でサポートされている例外リスト内でなければなりません。プロキシクラスは親インターフェイスを実装するか、親クラスの方法を上書きします
参照してください
https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html#icomments
要約します
上記は、この記事のコンテンツ全体です。この記事の内容には、すべての人の研究や仕事に特定の参照値があることを願っています。ご質問がある場合は、メッセージを残してコミュニケーションをとることができます。 wulin.comへのご支援ありがとうございます。