- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章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 。
最后此篇关于jdk中动态代理异常处理分析:UndeclaredThrowableException的文章就讲到这里了,如果你想了解更多关于jdk中动态代理异常处理分析:UndeclaredThrowableException的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我对第一个问题的直觉是肯定的。对于第二个问题,我在工作中确实看到,有人使用 JDK8 和 ANT,然后将用 JDK6 编写的旧包编译到 1.6。我真的很困惑。 最佳答案 一般来说,java 向后兼容所
据我所知,在windows中使用JDK有两种方式: 下载JDK安装文件并安装。 下载 JDK 二进制文件。 它们有什么区别? 最佳答案 优点:简单易行,突然间一切正常。 缺点:现在一切都使用新版本 -
我正在安装 HANA Studio,并且已下载 JDK 1.8 和 JDK 1.7。我将 JDK 1.8 用于 Eclipse 和我正在处理的其他一些事情,但是当我尝试通过 SAP HANA 生命周期
JDK 7 的哪些特性(不包括 invokedynamic,因为它不被 java 使用)导致新的类文件版本与 JDK 6 不兼容。似乎所有特性都可以通过编译器生成胶水代码来实现。例如 switch 语
在redhat机器上安装cloudera的库来创建cloudera集群是否必须使用Oracle JDK而不是Open JDK? 最佳答案 在撰写本文时,只有 Oracle JDK 版本经过认证可与 C
下面的语句在 Java 7 中有效吗? Timestamp.valueOf("0000-00-00 00:00:00.000000"); 因为使用 JDK 1.6 构建上述代码效果很好,但在使用 JD
更新 在整个评论中,结果证明我采用的基准测试方法是不正确的,因此结果具有误导性。纠正我的方法后(如已接受的答案),结果正如人们所期望的 - JDK 13 的性能与 JDK 11 一样好。有关更多详细信
我们很快就会从 jdk14 迁移到 jdk16。我们的是桌面应用程序。我需要采取什么措施来确保它在客户端机器上正常工作?现在他们中的一些人使用 JRE4 和一些 JRE6.Server-Solaris
我在/usr/lib/jvm 中有 jdk1.7.0 目录以及其他 open-jdk 版本。我希望我的 Ubuntu 12.04 将此 jdk(jdk1.7.0) 视为其主要 jdk,即我不想使用 o
我认为这可能与 Why does a generic cast of a List to List succeed on Sun JDK 6 but fail to compile on Oracle
代码使用 JDK 8 (1.8.0_212) 编译良好,但使用 JDK 11 (11.0.3) Oracle jdk 和 open jdk (aws corretto) 编译失败 尝试使用 javac
是否可以在 cygwin 上安装任何版本的 Sun JDK 或 Open JDK。 我寻找此选项的原因是:有许多工具(例如 jStack、jMap)在 JDK 的 unix 版本中可用,但在 wind
请确认以上说法? 当他们提到 JDK 时,我需要知道他们指的是什么。 最佳答案 Java Development Kit 是我们通常指的一组创建 Java 应用程序的工具,包括 Java Compil
使用 java -version 给我这个。 java version "1.7.0_80" Java(TM) SE Runtime Environment (build 1.7.0_80-b15)
这个问题在这里已经有了答案: JAVA_HOME should point to a JDK not a JRE (25 个答案) 关闭 4 年前。 您好,感谢您提供的任何帮助。 我刚刚升级到 Ub
没错,自阿里、腾讯之后,华为也终于开源了自家的 JDK——毕昇 JDK! 免费!免费!免费!!! Oracle 要慌了? 毕昇 JDK 毕昇 JDK 是华为内部 OpenJDK 定制版 Hu
关闭。这个问题需要更多focused .它目前不接受答案。 想改进这个问题吗? 更新问题,使其只关注一个问题 editing this post . 关闭去年。 Improve this quest
将 Arquillian 添加到 Maven 构建时,我在 Eclipse 中遇到上述异常: Missing artifact sun.jdk:jconsole:jar:jdk
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 5 年前。 Improve this ques
我正在尝试创建一个 pom,它将: 使用 maven-toolchains-plugin 中的正确 JDK基于 java.version 属性。 根据 maven-toolchains-plugin
我是一名优秀的程序员,十分优秀!