`
darrenzhu
  • 浏览: 782330 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

java动态代理UndeclaredThrowableException InvocationTargetException

    博客分类:
  • Java
阅读更多

原文链接:说说动态代理中碰到的一个小问题 https://my.oschina.net/GivingOnenessDestiny/blog/153300

JDK内置 Proxy类和 InvocationHandler接口来提供动态代理的实现。在实现连接池的时候动态代理就可以派上用场了。通过代理接管close方法, connectoin关闭的时候就不需要真正关闭,而只是放回连接池,具体实现原理可以参考红薯关于连接池的文章。

我要写的呢是关于在测试使用动态代理时碰到的一个问题,先看我的代码:

首先是接口 IFunction

public interface IFunction {
    void doSomething () throws IllegalStateException;
}
接口实现 FunctionImpl

public class FunctionImpl implements IFunction {
    @Override
    public void doSomething() throws IllegalStateException {
        // 方法什么也不做, 只抛异常
        throw new IllegalStateException();
    }
}
拦截 IFunctioin 的动态代理类 FunctionHandler

public class FunctionHandler  implements InvocationHandler{

    private IFunction fun;

    public FunctionHandler(IFunction function) {
        this.fun = function;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return method.invoke(fun, args);
    }
}
最后是简单的调用

public class Client {

    public static void main(String[] args) {
        IFunction fun = (IFunction) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{IFunction.class}, new FunctionHandler(new FunctionImpl()));
        try {
            fun.doSomething();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }
}
如此,一个简单的动态代理完成了, 一眼瞥上去没什么问题, 可惜一运行就抛异常了

Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
    at com.sun.proxy.$Proxy0.doSomething(Unknown Source)
    at designpattern.proxy.Client.main(Client.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:21)
    ... 7 more
Caused by: java.lang.IllegalStateException
    at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9)
    ... 12 more
很奇怪啊,接口只声明了IllegalStateException, 结果却抛出了 InvocationTargetException 及 UndeclaredThrowableException.

接下来我们看看这两个不速之客是如何产生的。

首先这两个异常肯定是接口实现类抛出 IllegalStateException 引起的, 于是可以定位到 java.lang.reflect.Method 的 invoke(Object obj, Object... args), 该方法文档已经说明: 当代理的方法抛出异常时 invoke(Object obj, Object... args) 方法会抛出 InvocationTargetException 异常, 也就是说我的 IllegalStateException 此时会被包装成 InvocationTargetException。

好,现在已经知道 InvocationTargetException 是因为Method反射机制包装产生的。

接下来再看 UndeclaredThrowableException 如何产生。

在 InvocationHandler 声明的方法 invoke(Object proxy, Method method, Object[] args) 的文档中有这么一句话

Throwable the exception to throw from the method invocation on the proxy instance. The exception's type must be assignable either to any of the exception types declared in the throws clause of the interface method or to the unchecked exception types java.lang.RuntimeException or code java.lang.Error. If a checked exception is thrown by this method that is not assignable to any of the exception types declared in the throws clause of the interface method, then an UndeclaredThrowableException containing the exception that was thrown by this method will be thrown by the method invocation on the proxy instance.

这里也就是说被代理接口的方法在执行的时候抛出的受检异常必须是接口定义中声明的异常, 如果抛出的受检异常未被接口声明, 那么此时这个异常就会被包装成UndeclaredThrowableException。

那么也就清楚了,之前已经看到Method.invoke()时抛出了异常InvocationTargetException 恰好不在 接口声明的异常范围内, 因此动态代理执行的时候会抛出异常 UndeclaredThrowableException。

对于这个问题可以改良下 FunctionHandler 的代码就可解决

public class FunctionHandler  implements InvocationHandler{

    private IFunction fun;

    public FunctionHandler(IFunction function) {
        this.fun = function;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            return method.invoke(fun, args);
        } catch (InvocationTargetException e) {
            throw e.getCause();
        }
    }
}
这样再次运行测试得到的结果如下

java.lang.IllegalStateException
    at designpattern.proxy.FunctionImpl.doSomething(FunctionImpl.java:9)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at designpattern.proxy.FunctionHandler.invoke(FunctionHandler.java:23)
    at com.sun.proxy.$Proxy0.doSomething(Unknown Source)
    at designpattern.proxy.Client.main(Client.java:18)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
两个不速之客已经消失了, "老板再也不用担心我的异常", 哈哈。

分享到:
评论

相关推荐

    Java动态代理的异常处理问题

    在使用Java动态代理时出现了一个很棘手的问题,实现类里抛出了一个自定义异常,但外面捕获不到。  虽然使用 printStack 可以输出调试信息,但通过 getMessage 获取不到提示,因为项目需求是捕捉到同一种自定义异常...

    JAVA面试题最全集

    一、Java基础知识 1.Java有那些基本数据类型,String是不是基本数据类型,他们有何区别。 2.字符串的操作: 写一个方法,实现字符串的反转,如:输入abc,输出cba 写一个方法,实现字符串的替换,如:输入...

    java 面试题 总结

    JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用...

    超级有影响力霸气的Java面试题大全文档

    超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。...

    大学物理册答案上下都有

    大学物理册答案上下都有

    UWB超宽带信道研究及源码.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    Suno AI语音技术的资源描述

    Suno AI 是一家领先的人工智能(AI)公司,专注于开发创新的语音技术和解决方案。以下是关于 Suno AI 的资源描述: 语音识别技术:Suno AI 拥有先进的语音识别技术,能够准确地将语音转换为文本。他们的语音识别系统经过了大量的训练和优化,具有高度的准确性和鲁棒性,可应用于多种场景,如语音助手、语音搜索、智能客服等。 语音合成技术:除了语音识别,Suno AI 还致力于开发自然、流畅的语音合成技术。他们的语音合成系统能够生成高质量、逼真的语音输出,可以用于语音助手、自动化客服、有声书等领域。 智能语音应用开发:Suno AI 提供了一系列工具和平台,帮助开发者快速构建和部署智能语音应用。他们的开发工具包括 API 接口、SDK、开发文档等,支持多种编程语言和平台,让开发者可以轻松地集成语音功能到自己的应用中。 行业解决方案:Suno AI 为各行各业提供定制化的语音解决方案,满足客户特定的需求和业务场景。他们与零售、金融、医疗等领域的企业合作,提供智能语音客服、语音导航、语音支付等解决方案,帮助客户提升服务质量和效率。

    基于matlab的射线信道模型内含数据集.zip

    基于matlab的射线信道模型内含数据集.zip

    基于Lidar数据的概率占位栅格图(Occupancy Grid Map)+Python代码实现.pdf

    基于Lidar数据的概率占位栅格图(Occupancy+Grid+Map)+Python代码实现.pdf

    autosar多核操作系统的监控和优化python代码.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    基于flink的实时流计算web平台.zip

    flink基于flink的实时流计算web平台.zip 基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip基于flink的实时流计算web平台.zip

    行政数据分析看板12.xlsx

    Excel数据看板,Excel办公模板,Excel模板下载,Excel数据统计,数据展示

    基于关节空间和笛卡尔空间对PUMA560机械臂进行了轨迹规划在Simulink中实现.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    重庆大学电磁场原理10年考题(a卷)答案及评分标准.pdf

    重庆大学期末考试试卷,重大期末考试试题,试题及答案

    营业额日报.xlsx

    Excel数据看板,Excel办公模板,Excel模板下载,Excel数据统计,数据展示

    项目快速部署与日志查看工具:一键启动Jar的Shell脚本

    这个资源是一对功能强大的Shell脚本,专为简化在Linux环境下的项目部署与日志查看而设计。首先,用户只需修改脚本中的Jar包名称,并赋予执行权限(使用"chmod +x xxx.sh"命令),即可轻松启动项目。通过执行"./startJar.sh"命令,用户可以快速启动Jar包,无需手动查找进程并进行kill操作,从而显著提高了部署效率,尤其适用于频繁部署或二次部署的场景。其次,为了方便用户查看项目日志,我们提供了另一个脚本"./echoLog.sh"。通过执行"./echoLog.sh"命令,用户可以轻松查看项目的实时日志,快速定位和解决问题。这些Shell脚本的使用将为您的项目管理带来极大的便利和效率提升,让您专注于项目开发和优化。

    财务数据分析模型3.xlsx

    Excel数据看板,Excel办公模板,Excel模板下载,Excel数据统计,数据展示

    杭州电子科技大学数据结构期末样卷参考答案A答案.pdf

    杭州电子科技大学,期末考试资料,计算机专业期末考试试卷,试卷及答案,数据结构。

    基于Python实现的手写数字识别系统源码+数据集.zip

    基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分期末大作业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分期末大作业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分期末大作业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分期末大作业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分期末大作业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 基于Python实现的手写数字识别系统源码+数据集.zip已获导师指导并通过的97分的高分

    垂直起降时刻表matlab代码.zip

    1.版本:matlab2014/2019a/2021a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

Global site tag (gtag.js) - Google Analytics