最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

背景

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

先給結論

使用jdk動態(tài)代理接口時,若方法執(zhí)行過程中拋出了受檢異常但方法簽名又沒有聲明該異常時則會被代理類包裝成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");
 }
}
// 動態(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();
  }
 }
}

運行上面的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)
 ...

原因分析

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

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

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

解決方法

在實現(xiàn)InvocationHandler的invoke方法體中,對method.invoke(target, args);調(diào)用進行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();
  }
 }

題外話

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

參考

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

總結

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

相關文章

  • Java漢字轉拼音pinyin4j用法詳解

    Java漢字轉拼音pinyin4j用法詳解

    這篇文章主要介紹了Java漢字轉拼音pinyin4j用法詳解,需要的朋友可以參考下
    2020-02-02
  • Java中用爬蟲進行解析的實例方法

    Java中用爬蟲進行解析的實例方法

    在本篇文章里小編給大家整理的是一篇關于Java中用爬蟲進行解析的實例方法,有需要的朋友們可以學習參考下。
    2020-12-12
  • Java中的字符編碼問題處理心得總結

    Java中的字符編碼問題處理心得總結

    Java中的String類默認是Unicode編碼,大家平常所說的UTF-8就是使用最廣的一種unicode的實現(xiàn)方式,以下我們還是來看一下Java中的字符編碼問題處理心得總結:
    2016-06-06
  • 使用Spring組合自定義的注釋 mscharhag操作

    使用Spring組合自定義的注釋 mscharhag操作

    這篇文章主要介紹了使用Spring組合自定義的注釋 mscharhag,本文通過實例代碼給大家介紹的非常詳細,對大家的工作或?qū)W習有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • SpringMVC REST風格深入詳細講解

    SpringMVC REST風格深入詳細講解

    這篇文章主要介紹了SpringMVC REST風格,Rest全稱為Representational State Transfer,翻譯為表現(xiàn)形式狀態(tài)轉換,它是一種軟件架構
    2022-10-10
  • Java實現(xiàn)圖書借閱系統(tǒng)

    Java實現(xiàn)圖書借閱系統(tǒng)

    這篇文章主要為大家詳細介紹了Java實現(xiàn)圖書借閱系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • Spring自動注入失敗的解決方法

    Spring自動注入失敗的解決方法

    這篇文章主要介紹了Spring自動注入失敗的解決方法,幫助大家更好的理解和學習使用Spring框架,感興趣的朋友可以了解下
    2021-05-05
  • 深入理解Java設計模式之裝飾模式

    深入理解Java設計模式之裝飾模式

    這篇文章主要介紹了JAVA設計模式之裝飾模式的的相關資料,文中示例代碼非常詳細,供大家參考和學習,感興趣的朋友可以了解下
    2021-11-11
  • 在項目中集成jetty server步驟解析

    在項目中集成jetty server步驟解析

    這篇文章主要介紹了在項目中集成jetty server步驟解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-02-02
  • 一文帶你了解RabbitMQ消息轉換器

    一文帶你了解RabbitMQ消息轉換器

    這篇文章主要為大家詳細介紹了RabbitMQ中消息轉換器的相關知識,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解一下
    2023-04-04

最新評論

禄劝| 四子王旗| 泰和县| 望谟县| 织金县| 平山县| 南华县| 新源县| 宁化县| 和田市| 乌鲁木齐市| 龙泉市| 常德市| 滦平县| 视频| 通州区| 长阳| 乌鲁木齐县| 武夷山市| 林口县| 卢湾区| 烟台市| 恭城| 天柱县| 浠水县| 连南| 兖州市| 建德市| 双柏县| 顺昌县| 枞阳县| 灯塔市| 汽车| 陇西县| 建始县| 宁武县| 浦北县| 长海县| 武穴市| 金川县| 江津市|