简单回答:可以在 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.htmlhttps://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html