Java Asm 修改 jdk 源码中的 ThreadPoolExecutor execute 不能引入 不是 jdk 的包下的类吗?

2021-01-25 18:05:10 +08:00
 gengzi

提示 NoClassDefFoundError 。 还是说需要其他方式

1997 次点击
所在节点    Java
9 条回复
wsxyeah
2021-01-25 20:47:15 +08:00
这些东西是用户机器上的,除非你打包一个 jre 进去
gengzi
2021-01-25 23:27:21 +08:00
@wsxyeah 我在启用前使用 Agent,修改 jdk ThreadPoolExecutor execute 的方法,引入的 jar 应该会在运行时加载吧。
ffutop
2021-01-26 09:02:53 +08:00
ThreadPoolExecutor 是被 Bootstrap ClassLoader 加载的。它的加载路径不包括你自定义的 MDCInheritableThreadLocal 。

可以用 -Xbootclasspath 指定 Bootstrap 加载特定 Jar 包
或者在 MANIFEST.MF 用 Boot-Class-Path 声明 Jar 包路径
kingfalse
2021-01-26 09:38:14 +08:00
试试 javassist
fantastM
2021-01-26 10:48:36 +08:00
在启动参数里用 -javaagent 的话,确实是如 #3 说的类加载问题。可以用 arthas 的 sc 、classloader 之类的命令来查看类的加载情况
gengzi
2021-01-26 10:56:34 +08:00
@ffutop @fantastM ok,感谢解答。我去瞅瞅
gengzi
2021-01-26 22:21:26 +08:00
@ffutop @fantastM 麻烦再咨询下,
```
-javaagent:D:/ideaworkspace/baselog.jar -Xbootclasspath/a:D:/ideaworkspace/baselog.jar
```
当这个键入 idea 中,运行 springboot 工程,提示 NoClassDefFoundError 关于项目中的引入的 jar,那这些 jar 我需要怎么引入。
fantastM
2021-01-27 02:59:44 +08:00
简单回答:可以在 agent 中自定义类加载器,避免遵循 Java 类加载器中的双亲委派模型。

详细回答:
JVM 加载类是按照双亲委派模型来执行的,每个类都会优先委托给父类加载器来加载,当父类加载器无法加载类的时候再由子类加载器来加载,因此在 JVM 中加载的类会有一种层级关系。

在你的例子中 `ThreadPoolExecutor` 会由 BootStrap ClassLoader 加载,参数 -javaagent 指定的 baselog.jar 包默认会由 System ClassLoader 加载,所以你一开始描述的问题原因是:`ThreadPoolExecutor` 类在被 BootStrap ClassLoader 加载之后链接的时候,无法找到需要被 System ClassLoader 加载的 baselog.jar 包里的 `MDCInheritableThreadLocal`。(注意这个问题是发生在链接阶段,这也是 JVM 为什么抛了 `NoClassDefFoundError` 而不是 `ClassNotFoundException` 的原因)

然后你将 baselog.jar 包指定为由 BootStrap ClassLoader 加载,这样的话,`ThreadPoolExecutor` 类在被 BootStrap ClassLoader 加载、链接、初始化的时候,就可以找到同样是被 BootStrap ClassLoader 加载的 `MDCInheritableThreadLocal` 了。

你在 #7 里说的事情,我理解为是你在 baselog.jar 包里用到了「项目中的引入的 jar 」(可能是 Spring 之类的吧),这部分包不由是 BootStrap ClassLoader 加载的,可能是 System ClassLoader,也可能是 Tomcat 的 Webapp ClassLoader,所以自然也会有 `ClassNotFoundException` 的问题。解决方案的话,我不建议把这类包也指定为由 BootStrap ClassLoader 来加载,更好的解决方案是自定义类加载机制,破坏一下 Java 类加载器中的双亲委派模型。

具体实现的话,可以借鉴一些分布式追踪 APM 系统,它们在采集应用的数据时候,使用的无侵入式方案也是 javaagent,也会对项目中的一些代码做改造。

一些可供查阅的资料:
https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html
gengzi
2021-01-27 10:07:12 +08:00
@fantastM 听君一席话胜读十年书。昨天在查阅资料时,也考虑使用自定义的类加载器来尝试,我再去看看这块内容。非常感谢这么详细的解答。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/748239

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX