一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|JavaScript|易語言|

服務器之家 - 編程語言 - Java教程 - jdk中動態代理異常處理分析:UndeclaredThrowableException

jdk中動態代理異常處理分析:UndeclaredThrowableException

2021-04-24 10:28hebaodan Java教程

最近在工作中遇到了報UndeclaredThrowableException的錯誤,通過查找相關的資料,終于解決了,所以這篇文章主要給大家介紹了關于jdk中動態代理異常處理分析:UndeclaredThrowableException的相關資料,需要的朋友可以參考下

背景

在rpc接口調用場景或者使用動態代理的場景中,偶爾會出現undeclaredthrowableexception,又或者在使用反射的場景中,出現invocationtargetexception,這都與我們所期望的異常不一致,且將真實的異常信息隱藏在更深一層的堆棧中。本文將重點分析下undeclaredthrowableexception

先給結論

使用jdk動態代理接口時,若方法執行過程中拋出了受檢異常但方法簽名又沒有聲明該異常時則會被代理類包裝成undeclaredthrowableexception拋出。

問題還原

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 接口定義
public interface iservice {
 void foo() throws sqlexception;
}
public class serviceimpl implements iservice{
 @override
 public void foo() throws sqlexception {
  throw new sqlexception("i test throw an checked exception");
 }
}
// 動態代理
public 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 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();
  }
 }
}

運行上面的maintest,得到的異常堆棧為

?
1
2
3
4
5
6
7
8
9
10
11
12
13
java.lang.reflect.undeclaredthrowableexception
 at com.sun.proxy.$proxy0.foo(unknown source)
 at com.learn.reflect.maintest.main(maintest.java:16)
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 com.learn.reflect.iserviceproxy.invoke(iserviceproxy.java:19)
 ... 2 more
caused by: java.sql.sqlexception: i test throw an checked exception
 at com.learn.reflect.serviceimpl.foo(serviceimpl.java:11)
 ... 7 more

而我們期望的是

?
1
2
3
java.sql.sqlexception: i test throw an checked exception
 at com.learn.reflect.serviceimpl.foo(serviceimpl.java:11)
 ...

原因分析

在上述問題還原中,真實的sqlexception被包裝了兩層,先被invocationtargetexception包裝,再被undeclaredthrowableexception包裝。 其中,invocationtargetexception為受檢異常,undeclaredthrowableexception為運行時異常。 為何會被包裝呢,還要從動態代理的生成的代理類說起。

jdk動態代理會在運行時生成委托接口的具體實現類,我們通過proxygenerator手動生成下class文件,再利用idea解析class文件得到具體代理類: 截取部分:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public final class iserviceproxy$1 extends proxy implements iservice {
 private static method m1;
 private static method m2;
 private static method m3;
 private static method 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) {
   throw new 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.reflect.iservice").getmethod("foo", new class[0]);
   m0 = class.forname("java.lang.object").getmethod("hashcode", new class[0]);
  } catch (nosuchmethodexception var2) {
   throw new nosuchmethoderror(var2.getmessage());
  } catch (classnotfoundexception var3) {
   throw new noclassdeffounderror(var3.getmessage());
  }
 }
}

在調用“委托類”的foo方法時,實際上調用的代理類iserviceproxy$1的foo方法,而代理類主要邏輯是調用invocationhandler的invoke方法。 異常處理的邏輯是,對runtimeexception、接口已聲明的異常、error直接拋出,其他異常被包裝成undeclaredthrowableexception拋出。 到這里,或許你已經get了,或許你有疑問,在接口實現中的確是throw new sqlexception,為什么還會被包裝呢? 再來看iserviceproxy的invoke方法,它就是直接通過反射執行目標方法,問題就在這里了。 method.invoke(object obj, object... args)方法聲明中已解釋到,若目標方法拋出了異常,會被包裝成invocationtargetexception。(具體可查看javadoc)

所以,串起來總結就是: 具體方法實現中拋出sqlexception被反射包裝為會被包裝成invocationtargetexception,這是個受檢異常,而代理類在處理異常時發現該異常在接口中沒有聲明,所以包裝為undeclaredthrowableexception。

解決方法

在實現invocationhandler的invoke方法體中,對method.invoke(target, args);調用進行try catch,重新 throw invocationtargetexception的cause。即:

?
1
2
3
4
5
6
7
8
@override
 public object invoke(object proxy, method method, object[] args) throws throwable {
  try {
   return method.invoke(target, args);
  } catch (invocationtargetexception e){
   throw e.getcause();
  }
 }

題外話

為什么代理類中對未聲明的受檢異常轉為undeclaredthrowableexception? 因為java繼承原則:即子類覆蓋父類或實現父接口的方法時,拋出的異常必須在原方法支持的異常列表之內。 代理類實現了父接口或覆蓋父類方法

參考

https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html#icomments

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://my.oschina.net/hebaodan/blog/1584134

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产精品青青青高清在线观看 | 日本人妖在线 | 日韩欧美高清视频 | 四虎免费看黄 | 国产成人www | 亚洲成人三级 | 精品性久久 | 51国产午夜精品免费视频 | 精品国产免费观看一区高清 | 欧美一级艳片视频免费观看 | 免费国产成人α片 | 欧美一二区 | 亚洲精品欧洲久久婷婷99 | 国产靠逼视频 | 三级视频中文字幕 | 久久精品亚洲热综合一本 | 福利一区三区 | 风间由美一区二区播放合集 | 国产福利不卡一区二区三区 | 欧美伊人影院 | 国产大片51精品免费观看 | 日产欧产va高清 | 把内裤拔到一边高h1v1 | 小柔的性放荡羞辱日记 | 91桃花| 亚洲日本在线观看网址 | 亚洲精品电影天堂网 | 狠狠燥 | 好 舒服 好 粗 好硬免费视频 | 日韩视频免费看 | 香蕉久久一区二区不卡无毒影院 | 99re视频精品全部免费 | 亚洲日本aⅴ片在线观看香蕉 | 精品久久久久久久国产潘金莲 | 乌克兰13一14娇小 | 亚洲人成网站在线观看妞妞网 | 毛片免费在线视频 | 成人免费观看网欧美片 | 草草在线免费视频 | 天堂网在线网站成人午夜网站 | 极端 成熟 性别 视频 |