欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

jdk中動(dòng)態(tài)代理異常處理分析:UndeclaredThrowableException

 更新時(shí)間:2018年04月26日 10:53:49   作者:hebaodan  
最近在工作中遇到了報(bào)UndeclaredThrowableException的錯(cuò)誤,通過查找相關(guān)的資料,終于解決了,所以這篇文章主要給大家介紹了關(guān)于jdk中動(dòng)態(tài)代理異常處理分析:UndeclaredThrowableException的相關(guān)資料,需要的朋友可以參考下

背景

在RPC接口調(diào)用場(chǎng)景或者使用動(dòng)態(tài)代理的場(chǎng)景中,偶爾會(huì)出現(xiàn)UndeclaredThrowableException,又或者在使用反射的場(chǎng)景中,出現(xiàn)InvocationTargetException,這都與我們所期望的異常不一致,且將真實(shí)的異常信息隱藏在更深一層的堆棧中。本文將重點(diǎn)分析下UndeclaredThrowableException

先給結(jié)論

使用jdk動(dòng)態(tài)代理接口時(shí),若方法執(zhí)行過程中拋出了受檢異常但方法簽名又沒有聲明該異常時(shí)則會(huì)被代理類包裝成UndeclaredThrowableException拋出。

問題還原

// 接口定義
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");
 }
}
// 動(dòng)態(tài)代理
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();
  }
 }
}

運(yùn)行上面的MainTest,得到的異常堆棧為

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

而我們期望的是

java.sql.SQLException: I test throw an checked Exception
 at com.learn.reflect.ServiceImpl.foo(ServiceImpl.java:11)
 ...

原因分析

在上述問題還原中,真實(shí)的SQLException被包裝了兩層,先被InvocationTargetException包裝,再被UndeclaredThrowableException包裝。 其中,InvocationTargetException為受檢異常,UndeclaredThrowableException為運(yùn)行時(shí)異常。 為何會(huì)被包裝呢,還要從動(dòng)態(tài)代理的生成的代理類說起。

jdk動(dòng)態(tài)代理會(huì)在運(yùn)行時(shí)生成委托接口的具體實(shí)現(xiàn)類,我們通過ProxyGenerator手動(dòng)生成下class文件,再利用idea解析class文件得到具體代理類: 截取部分:

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());
  }
 }
}

在調(diào)用“委托類”的foo方法時(shí),實(shí)際上調(diào)用的代理類IServiceProxy$1的foo方法,而代理類主要邏輯是調(diào)用InvocationHandler的invoke方法。 異常處理的邏輯是,對(duì)RuntimeException、接口已聲明的異常、Error直接拋出,其他異常被包裝成UndeclaredThrowableException拋出。 到這里,或許你已經(jīng)get了,或許你有疑問,在接口實(shí)現(xiàn)中的確是throw new SQLException,為什么還會(huì)被包裝呢? 再來看IServiceProxy的invoke方法,它就是直接通過反射執(zhí)行目標(biāo)方法,問題就在這里了。 Method.invoke(Object obj, Object... args)方法聲明中已解釋到,若目標(biāo)方法拋出了異常,會(huì)被包裝成InvocationTargetException。(具體可查看javadoc)

所以,串起來總結(jié)就是: 具體方法實(shí)現(xiàn)中拋出SQLException被反射包裝為會(huì)被包裝成InvocationTargetException,這是個(gè)受檢異常,而代理類在處理異常時(shí)發(fā)現(xiàn)該異常在接口中沒有聲明,所以包裝為UndeclaredThrowableException。

解決方法

在實(shí)現(xiàn)InvocationHandler的invoke方法體中,對(duì)method.invoke(target, args);調(diào)用進(jìn)行try catch,重新 throw InvocationTargetException的cause。即:

@Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  try {
   return method.invoke(target, args);
  } catch (InvocationTargetException e){
   throw e.getCause();
  }
 }

題外話

為什么代理類中對(duì)未聲明的受檢異常轉(zhuǎn)為UndeclaredThrowableException? 因?yàn)镴ava繼承原則:即子類覆蓋父類或?qū)崿F(xiàn)父接口的方法時(shí),拋出的異常必須在原方法支持的異常列表之內(nèi)。 代理類實(shí)現(xiàn)了父接口或覆蓋父類方法

參考

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

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • Debian配置JDK1.7 與Linux Java Helloworld

    Debian配置JDK1.7 與Linux Java Helloworld

    這篇文章主要介紹了Debian配置JDK1.7 與Linux Java Helloworld 的相關(guān)資料,需要的朋友可以參考下
    2016-06-06
  • Java使用MySQL實(shí)現(xiàn)連接池代碼實(shí)例

    Java使用MySQL實(shí)現(xiàn)連接池代碼實(shí)例

    這篇文章主要介紹了Java使用MySQL實(shí)現(xiàn)連接池代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • Spring Security自定義失敗處理器問題

    Spring Security自定義失敗處理器問題

    這篇文章主要介紹了Spring Security自定義失敗處理器問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習(xí)題(分享)

    Java基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習(xí)題(分享)

    下面小編就為大家?guī)硪黄狫ava基礎(chǔ)篇_有關(guān)接口和抽象類的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-06-06
  • SpringCloud注冊(cè)中心部署Eureka流程詳解

    SpringCloud注冊(cè)中心部署Eureka流程詳解

    Eureka是Netflix開發(fā)的服務(wù)發(fā)現(xiàn)框架,本身是一個(gè)基于REST的服務(wù),主要用于定位運(yùn)行在AWS域中的中間層服務(wù),以達(dá)到負(fù)載均衡和中間層服務(wù)故障轉(zhuǎn)移的目的
    2022-11-11
  • 詳解什么是Java線程池的拒絕策略?

    詳解什么是Java線程池的拒絕策略?

    今天給大家總結(jié)一下線程池的拒絕策略,文中有非常詳細(xì)的介紹及代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有很好地幫助,需要的朋友可以參考下
    2021-05-05
  • 關(guān)于Java中的 JSP 詳解

    關(guān)于Java中的 JSP 詳解

    JSP 代表 Java 服務(wù)器頁面。它是一種在應(yīng)用服務(wù)器端使用的編程工具。JSP 基本上用于支持平臺(tái)–獨(dú)立和動(dòng)態(tài)的方法來構(gòu)建 Web 依賴的應(yīng)用程序。JSP 頁面類似于 ASP 頁面,因?yàn)樗鼈兪窃诜?wù)器上編譯的,而不是在用戶的 Web 瀏覽器上進(jìn)行編譯。下面來看看文章的詳細(xì)介紹內(nèi)容
    2021-11-11
  • java實(shí)現(xiàn)文件導(dǎo)入導(dǎo)出

    java實(shí)現(xiàn)文件導(dǎo)入導(dǎo)出

    這篇文章主要介紹了java實(shí)現(xiàn)文件導(dǎo)入導(dǎo)出的方法和具體示例代碼,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下
    2016-04-04
  • 關(guān)于消息中間件RocketMQ的基本概念及功能

    關(guān)于消息中間件RocketMQ的基本概念及功能

    這篇文章主要介紹了關(guān)于消息中間件RocketMQ的基本概念及功能,RocketMQ作為一款純java、分布式、隊(duì)列模型的開源消息中間件,支持事務(wù)消息、順序消息、批量消息、定時(shí)消息、消息回溯等,需要的朋友可以參考下
    2023-05-05
  • Java中的接口回調(diào)實(shí)例

    Java中的接口回調(diào)實(shí)例

    今天小編就為大家分享一篇關(guān)于Java中的接口回調(diào)實(shí)例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-01-01

最新評(píng)論