激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久久人成影片一区二区三区 | 九九视频久久 | 成人三级黄色片 | 黄色免费高清网站 | 久久久无码精品亚洲日韩按摩 | 黄色片网页 | 中文字幕电影免费播放 | 羞羞视频免费观看入口 | 久久最新网址 | 久久久资源网 | 欧美国产第一页 | 午夜视频在线免费观看 | 欧美中文日韩 | 成人在线视频一区 | 一区二区三区四区国产 | 少妇一级淫片免费放4p | 久章草在线视频 | 毛片免费一区二区三区 | 国产精品亚洲综合一区二区三区 | jizzjizz中国少妇中文 | 97干色 | 13一14毛片免费看 | 精品久久久久久国产三级 | freexxx69性欧美hd | 国产交换3p国产精品 | 国产成人av免费观看 | 国产久草视频在线 | 成人精品久久久 | 久久国产精品久久久久久电车 | 欧美在线观看黄色 | 奇米影视亚洲精品一区 | 四季久久免费一区二区三区四区 | 欧美精品一区二区性色 | 久久国产乱子伦精品 | 亚洲精品久久久久久久久久 | 在线观看国产一区二区 | 国产精品1区2区在线观看 | 精品免费国产一区二区三区 | 久久久久99精品 | 免费黄色小视频网站 | 欧美一区二区三区不卡免费观看 |