我自己写了一个javaagent来用ASM动态地插装Java类(我没有使用ASM的COMPUTE_MAXS
或COMPUTE_FRAMES
,我自己手动地做)。实际上,我只是试图使用一个大的try-catch块来捕获未捕获的异常或错误,并记录这些事件(我的代码实际上是this question中代码的修订版本)。
但是,当我尝试在开源项目joda-time的测试过程中使用我的javaagent时,出现了以下错误:
org.apache.maven.surefire.testset.TestSetFailedException: org.joda.time.TestAllPackages
at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:116)
at org.apache.maven.surefire.junit.JUnit3Provider.executeTestSet(JUnit3Provider.java:140)
at org.apache.maven.surefire.junit.JUnit3Provider.invoke(JUnit3Provider.java:113)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:379)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:340)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:125)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:413)
Caused by: java.lang.LinkageError: loader (instance of sun/misc/Launcher$AppClassLoader): attempted duplicate class definition for name: "org/joda/time/DateTimeZone"
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at org.joda.time.TestChronology.<clinit>(TestChronology.java:47)
at org.joda.time.TestAll.suite(TestAll.java:37)
at org.joda.time.TestAllPackages.suite(TestAllPackages.java:36)
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 org.apache.maven.surefire.common.junit3.JUnit3Reflector.createInstanceFromSuiteMethod(JUnit3Reflector.java:157)
at org.apache.maven.surefire.common.junit3.JUnit3Reflector.constructTestObject(JUnit3Reflector.java:124)
at org.apache.maven.surefire.junit.JUnitTestSet.execute(JUnitTestSet.java:75)
... 6 more
据我所知,每次sun/misc/Launcher$AppClassLoader
试图加载一个类时,都会进入ClassFileTransformer
的transform
方法。在我修改类并返回修改后的字节数组后,类最终被加载。因此,每个类应该只加载一次。
我进一步尝试打印出类名和在transform
方法开始时加载它的加载器,我发现org/joda/time/DateTimeZone
只出现一次,这与我的理解一致。
所以现在唯一与我的理解不一致的是错误。为什么sun/misc/Launcher$AppClassLoader attempted duplicate class definition
与我的代理?每一个都去了这么soomly当我删除我的-javaagent
选项。
1条答案
按热度按时间2izufjch1#
要计算帧,ASM需要在跳转指令的目标处找到几个类的公共超类。为此,ASM将加载这些类以浏览其层次结构。如果以这种方式加载类,同时在第一次加载时还对该类进行了检测,则该类将在检测后加载,最终将出现此错误。
为了避免这种情况,你可以覆盖ASM的ClassWriter的getCommonSuperClass方法。你需要解析传递给该方法的这些类的类文件,而不是加载它们。如果你想要这种开箱即用的实现,你可以使用Byte Buddy,它公开ASM并以这种方式解析它的ClassWriter。