2019独角兽企业重金招聘Python工程师标准>>>

背景

在RPC接口调用场景或者使用动态代理的场景中,偶尔会出现UndeclaredThrowableException,又或者在使用反射的场景中,出现InvocationTargetException,这都与我们所期望的异常不一致,且将真实的异常信息隐藏在更深一层的堆栈中。本文将重点分析下UndeclaredThrowableException

先给结论

使用jdk动态代理接口时,若方法执行过程中抛出了受检异常但方法签名又没有声明该异常时则会被代理类包装成UndeclaredThrowableException抛出。

问题还原

// 接口定义
public interface IService {void foo() throws SQLException;
}
public class ServiceImpl implements IService{@Overridepublic 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;}@Overridepublic 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.UndeclaredThrowableExceptionat com.sun.proxy.$Proxy0.foo(Unknown Source)at com.learn.reflect.MainTest.main(MainTest.java:16)
Caused by: java.lang.reflect.InvocationTargetExceptionat 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 Exceptionat com.learn.reflect.ServiceImpl.foo(ServiceImpl.java:11)... 7 more

而我们期望的是

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

原因分析

在上述问题还原中,真实的SQLException被包装了两层,先被InvocationTargetException包装,再被UndeclaredThrowableException包装。 其中,InvocationTargetException为受检异常,UndeclaredThrowableException为运行时异常。 为何会被包装呢,还要从动态代理的生成的代理类说起。

jdk动态代理会在运行时生成委托接口的具体实现类,我们通过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());}}
}

在调用“委托类”的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。即:

@Overridepublic 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

jdk动态代理异常处理分析,UndeclaredThrowableException相关推荐

  1. 08.jdk动态代理原理

    课程标题<jdk动态代理底层原理分析> 课程内容: 1.什么是代理模式 2.代理模式应用场景有哪些 3.代理模式实现方式有哪些 4.静态代理与动态代理区别 5.JDK动态代理原理分析 6. ...

  2. JDK动态代理的底层实现原理

    JavaEE的开发中,许多框架用到了动态代理机制,例如Spring的AOP编程. 这里不介绍动态代理和静态代理概念,有兴趣的朋友自行百度. Java中的动态代理一般就两种:1. JDK自带 : 2. ...

  3. Java动态代理原理分析

    JDK动态代理原理分析 interface Foo {void foo(); }class Target implements Foo {@Overridepublic void foo() {Sys ...

  4. JDK动态代理实现原理详解(源码分析)

    无论是静态代理,还是Cglib动态代理,都比较容易理解,本文就通过进入源码的方式来看看JDK动态代理的实现原理进行分析 要了解动态代理的可以参考另一篇文章,有详细介绍,这里仅仅对JDK动态代理做源码分 ...

  5. Jdk动态代理 底层源码分析

    前言 java动态代理主要有2种,Jdk动态代理.Cglib动态代理,本文主要讲解Jdk动态代理的使用.运行机制.以及源码分析.当spring没有手动开启Cglib动态代理,即:<aop:asp ...

  6. Java JDK 动态代理实现和代码分析

    JDK 动态代理 内容 一.动态代理解析 1. 代理模式 2. 为什么要使用动态代理 3. JDK 动态代理简单结构图 4. JDK 动态代理实现步骤 5. JDK 动态代理 API 5.1 java ...

  7. 代理模式及JDK动态代理(InvocationHandler)的简单实现与分析

    在慕课网上学习了讲解代理模式的一个课程--<模式的秘密--代理模式>,感叹于David老师屌炸天的PPT,同时,老师一步一步模仿JDK源码去写code,教我们去简单实现JDK中的动态代理, ...

  8. JDK动态代理实现与原理分析

    1.何为代理 代理在我们的日常生活中,就有很多体现,房屋租赁代理,校园辅导班招生代理,化妆产品销售代理等.为什么我们要找代理呢,代理是专业的,是方便的.例如我们买一件化妆品,或买一本书,肯定是不能去找 ...

  9. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  10. Spring原理学习(七)JDK动态代理与CGLIB代理底层实现

    AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能. 除此以外,aspectj 提供了两种另外的 AOP 底层实现: 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知 ...

最新文章

  1. 一文搞懂PointNet全家桶——强势的点云处理神经网络
  2. String长度有限制吗?是多少?还好我看过
  3. 代码组织和部署 文件操作 node.js
  4. wxWidgets:wxTimerEvent类用法
  5. python网络编程---TCP客户端
  6. 【C++ Primer | 09】容器适配器
  7. springContext
  8. 如何使用links方式安装Eclipse插件
  9. 使用SQL存储过程有什么好处 用视图有什么好处
  10. EOS Keosd概述
  11. 关于JSBuilder2的使用.
  12. 基于java语言的C/S模式网络聊天室软件
  13. 数据分析——KMeans聚类算法
  14. 全面理解 DNS 及 HTTPDNS
  15. 操作系统PV编程题目总结一
  16. 快手私信名片如何制作?
  17. JavaSE :自问自答
  18. CSS之border
  19. 【Android 仿微信通讯录 导航分组列表-上】使用ItemDecoration为RecyclerView打造带悬停头部的分组列表
  20. dorehtml.php,脚本命令 window.open 的所有参数详解及实例 涉及页面执行后跳转和多久跳转...

热门文章

  1. java 代码解压7z(带密码)转载请注明出处,谢谢
  2. mongodb日志分析工具mtools之mplotqueries
  3. 线上多服务管理工具实例剖析
  4. flex学习笔记 富文本编辑(一)-RichEditableText
  5. MySQL最好的写的_mysql中写sql的好习惯
  6. 最简单的YUV422转jpg的例程
  7. [RK3399][Android7.1] ADB功能怎么Debug
  8. linux内核奇遇记之md源代码解读之三
  9. Linux设备驱动程序(LDD)中snull的编译问题
  10. Android studio 设置函数功能悬浮提示(鼠标放到上班提示参数和功能)