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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 日韩毛片免费 | 好大水好多好爽好硬好深视频 | 成人福利在线播放 | 国产手机在线αⅴ片无码观看 | 国产欧美日韩专区 | 色综合合久久天天综合绕视看 | 亚洲大片免费观看 | 日韩无砖专区体验区 | 国产欧美日韩专区 | 亚洲精品久久久久AV无码 | 久9视频这里只有精品123 | 吉川爱美与黑人解禁 | 好大好硬快点好爽公 | 亚洲乱亚洲乱妇41p国产成人 | 国内精品久久久久久久久 | 视频一区二区三区在线观看 | 黄a一级 | 奇米影视久久777中文字幕 | 99久久香蕉国产线看观香 | 日本暖暖在线视频 | 四虎 2022 永久网站 | 国产欧美日韩图片一区二区 | 国产精品在线 | 青草碰人人澡人人澡 | 99精品国产自产在线观看 | 久久99精品国产自在自线 | 日韩精品福利视频一区二区三区 | 日韩一级片在线播放 | 男人天堂网址 | 糖心vlog麻豆精东影业传媒 | 日本成熟| 青草香蕉精品视频在线观看 | 性bbwbbwbbwbbw撒尿 | melody中文字幕 | 欧美18-19| 王的视频视ivk | 91视在线国内在线播放酒店 | 2022国产麻豆剧果冻传媒入口 | 99视频免费在线 | 美女张开腿黄网站免费精品动漫 | 日本免费一二区 |