为什么在运行时没有缺less注释会导致ClassNotFoundException?
考虑下面的代码:
A.java:
import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) @interface A{}
C.java:
import java.util.*; @A public class C { public static void main(String[] args){ System.out.println(Arrays.toString(C.class.getAnnotations())); } }
按预期编译和运行:
$ javac *.java $ java -cp . C [@A()]
但是,那么考虑一下:
$ rm A.class $ java -cp . C []
我会期望它抛出一个ClassNotFoundException
,因为@A
丢失。 但相反,它默默地放弃了注释。
这种行为是在JLS中logging的,还是Sun的JVM的一个怪癖? 它的基本原理是什么?
看起来javax.annotation.Nonnull
(看起来应该是@Retention(CLASS)
)这样的东西似乎很方便,但是对于许多其他注释来说,它似乎会在运行时造成各种不好的事情。
在较早的JSR-175公共草案(注释)中,讨论了编译器和运行时是否应该忽略未知的注释,以便在注释的使用和声明之间提供一个更宽松的耦合。 一个具体的例子是在EJB上使用应用服务器特定的注释来控制部署configuration。 如果同一个bean应该部署在不同的应用程序服务器上,如果运行时只是忽略了未知的注释而不是引发NoClassDefFoundError,那将是非常方便的。
即使措辞有点模糊,我也假设你所看到的行为在JLS 13.5.7中有规定:“…删除注释对Java程序devise语言中程序的二进制表示的正确连接没有影响“。 我把它解释为注释被移除(在运行时不可用),程序仍然应该链接并运行,这意味着未知的注释在通过reflection访问时被忽略。
Sun的JDK 5的第一个版本没有正确实现,但是它在1.5.0_06中被修复。 您可以在bug数据库中find相关的错误6322301 ,但除了声称“根据JSR-175规范领导,getAnnotations必须忽略未知注释”之外,它并没有指向任何规范。
引用JLS:
9.6.1.2保留注解可能只存在于源代码中,或者可能以类或接口的二进制forms存在。 二进制文件中存在的注释可能在运行时可以通过Java平台的reflection库获得或不可用。
注释types注释。保留用于在上述可能性中进行select。 如果注释a对应于typesT,并且T具有与注释对应的(元注释)m,则保留:
- 如果m有一个值为annotation.RetentionPolicy.SOURCE的元素,则Java编译器必须确保a不出现在出现的类或接口的二进制表示中。
- 如果m有一个值为annotation.RetentionPolicy.CLASS或annotation.RetentionPolicy.RUNTIME的元素,那么Java编译器必须确保a在其中出现a的类或接口的二进制表示中表示,除非m注释了一个局部variables声明。 局部variables声明上的注释永远不会保留在二进制表示中。
如果T没有对应于annotation.Retention的(meta-)注释m,那么Java编译器必须将T看作是具有元素值为annotation.RetentionPolicy.CLASS的元注释m。
因此,RetentionPolicy.RUNTIME确保注释被编译到二进制文件中,但二进制文件中存在的注释不必在运行时可用
如果实际上有代码读取@A并执行某些操作,则代码对类A具有依赖关系,并且会抛出ClassNotFoundException。
如果没有,即没有代码特别关心@A,那么可以说@A并不重要。
注释对他们注释的代码的操作没有直接的影响。
但是,通过使用@Retention(RetentionPolicy.RUNTIME)
,注释在运行时变为可用。
现在,我的猜测是@Retention
不可用,因此被忽略。 这意味着其他注释在运行时不可用。
没有例外,因为默认情况下,注释被忽略。 只有在@Retention
的存在,他们@Retention
考虑。
大概如果你让舒尔@ @Retention
可用,将有一个投诉。 (不知道这个)