由于上游依赖项发生一些变化,我最近不得不将项目从 Java 11 升级到 Java 17。该项目大量使用注释驱动的代码生成,自从升级以来,我遇到了一个奇怪的异常,我无法把我的头绕过去:
java.lang.annotation.AnnotationTypeMismatchException: Incorrectly typed data found for annotation element public abstract pro.projo.interfaces.annotation.Options pro.projo.interfaces.annotation.Interface.options() (Found data of type pro.projo.interfaces.annotation.Options)
at com.sun.tools.javac.model.AnnotationProxyMaker$ValueVisitor$1AnnotationTypeMismatchExceptionProxy.generateException (AnnotationProxyMaker.java:271)
at sun.reflect.annotation.AnnotationInvocationHandler.invoke (AnnotationInvocationHandler.java:89)
at jdk.proxy4.$Proxy31.options (Unknown Source)
at pro.projo.generation.interfaces.InterfaceTemplateProcessor.lambda$getInterfaceConfiguration$5 (InterfaceTemplateProcessor.java:268)
at java.util.stream.ReferencePipeline$3$1.accept (ReferencePipeline.java:197)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining (ArrayList.java:1625)
at java.util.stream.AbstractPipeline.copyInto (AbstractPipeline.java:509)
at java.util.stream.AbstractPipeline.wrapAndCopyInto (AbstractPipeline.java:499)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential (ReduceOps.java:921)
at java.util.stream.AbstractPipeline.evaluate (AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect (ReferencePipeline.java:682)
at pro.projo.generation.interfaces.InterfaceTemplateProcessor.getInterfaceConfiguration (InterfaceTemplateProcessor.java:379)
at pro.projo.generation.interfaces.InterfaceTemplateProcessor.process (InterfaceTemplateProcessor.java:185)
at pro.projo.generation.interfaces.InterfaceTemplateProcessor.process (InterfaceTemplateProcessor.java:162)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor (JavacProcessingEnvironment.java:1023)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.discoverAndRunProcs (JavacProcessingEnvironment.java:939)
at com.sun.tools.javac.processing.JavacProcessingEnvironment$Round.run (JavacProcessingEnvironment.java:1267)
at com.sun.tools.javac.processing.JavacProcessingEnvironment.doProcessing (JavacProcessingEnvironment.java:1382)
at com.sun.tools.javac.main.JavaCompiler.processAnnotations (JavaCompiler.java:1234)
令人困惑的是,预期的类型和实际找到的类型实际上是相同的(它们都是
pro.projo.interfaces.annotation.Options
)。
为了最大限度地提高兼容性,注释处理库本身是用 Java 8 编写的(并相应地使用 @SupportedSourceVersion(RELEASE_8)
和 ElementScanner8
),但它在 Java 9 和 Java 11 项目中运行良好(正如人们所期望的那样)。
在注释处理之外,当我看到似乎抱怨类型正确的异常(例如,
ClassCastException
)时,我的第一反应是检查类加载器问题(即相同的类型,但由不同的类加载器加载)。注释处理器是否有类似的场景?
切换回 Java 11 可以让问题消失,但不幸的是,这不是永久解决方案。
有什么想法可以进一步调试吗?
虽然我不能 100% 确定,并且只能在这个问题上花费有限的时间,但所有迹象确实都表明 JDK bug 8322706 是观察到的异常的根源。 我能够验证 Java 8(Zulu 8.64.0.19-CA-macosx,内部版本 1.8.0_345-b01)、Java 11(GraalVM CE 20.1.0,内部版本 11.0.7+10-)中是否存在该错误。 jvmci-20.1-b02)和 Java 14(Oracle JDK 14,内部版本 14.0.2+12-46)。但是,当使用 Java 17(GraalVM CE 22.1.0,内部版本 17.0.3+7-jvmci-22.1-b06;Zulu 17.48+15-CA,内部版本 17.0.10+7-LTS)和 Java 21 时,确实会发生这种情况。 (GraalVM 21.0.2+13.1,内部版本 21.0.2+13-LTS-jvmci-23.1-b30)。这与 bug 描述中报告的内容一致(除了 Java 14 不受影响,这是新信息)。 该错误列出了三种不同的解决方法,第三种方法(为注释定义中的所有属性添加/重复显式默认值)立即解决了问题。第二个解决方法(冗余导入用于主注释属性值的注释)与我的 IDE 关于不必要导入的警告不一致,也解决了该问题。我没有尝试第一个解决方法(在带注释的类中显式添加所有默认值),因为它会涉及太多代码更改。 因此,就我能够验证的情况来看,错误报告中的信息与我的情况相符。
就调试而言,调试运行(一旦最终正确设置)不会产生太多信息,而无需投入大量额外的精力。正如堆栈跟踪已经表明的那样,所讨论的注释对象是代理对象,因此没有源代码。我能够手动找到负责的
InvocationHandler
的源代码,但最重要的是,应该包含属性值的属性映射包含一个抛出异常的替身。据我所知,最初的问题是另一个对象(也由代理表示)没有预期的类型,很可能是由于同一个类被不同的类加载器加载了两次。
正如 @Holger 在评论中指出的那样,由于显而易见的原因,在编译时使用运行时类型是有问题的(尽管它显然在大多数情况下都有效,而且直到现在我都没有再三考虑)。看看 bug 8322706 最终将如何解决将会很有趣。