为什么缺少注释不会在运行时导致ClassNotFoundException?

时间:2010-08-25 15:14:51

标签: java annotations

请考虑以下代码:

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的某个地方记录的,还是Sun的JVM的怪癖?它的基本原理是什么?

javax.annotation.Nonnull这样的东西似乎很方便(看起来它应该是@Retention(CLASS))但是对于许多其他注释来说,似乎它可能会导致在运行时发生各种不好的事情。

3 个答案:

答案 0 :(得分:83)

在早期的JSR-175(注释)公共草案中,讨论了编译器和运行时是否应该忽略未知注释,以便在注释的使用和声明之间提供更松散的耦合。一个具体示例是在EJB上使用应用程序服务器特定的注释来控制部署配置。如果同一个bean应该部署在不同的应用程序服务器上,那么如果运行时只是忽略了未知的注释而不是引发NoClassDefFoundError,那将会很方便。

即使措辞有点含糊,我也认为你所看到的行为是在JLS 13.5.7中指定的:“...删除注释对正确连接程序的二进制表示没有影响。 Java编程语言。“我将此解释为好像删除了注释(在运行时不可用),程序仍应链接并运行,这意味着在通过反射访问时,将忽略未知注释。

Sun的JDK 5的第一个版本没有正确实现,但它已在1.5.0_06中修复。您可以在bug数据库相关的错误6322301,但它并不指向任何规格,除了声称“根据JSR-175规范领导,未知注释必须由getAnnotations忽略。”

答案 1 :(得分:32)

引用JLS:

  

9.6.1.2保留注释可能仅出现在源代码中,或   它们可能以二进制形式存在   类或接口的。 注释   存在于二进制文件中的可能或   可能无法在运行时通过   Java的反射库   平台。

     

注释类型   annotation.Retention用于选择   在上述可能性中。如果   注释a对应于类型T,   和T有一个(meta-)注释   对应注释。注意,   然后:

     
      
  • 如果m有一个元素,其值为annotation.RetentionPolicy.SOURCE,   那么Java编译器必须确保这一点   a不存在于二进制文件中   代表班级或   a出现的界面。
  •   
  • 如果m有一个元素,其值为annotation.RetentionPolicy.CLASS,或   annotation.RetentionPolicy.RUNTIME a   Java编译器必须确保a   以二进制表示   代表班级或   出现a的界面,除非m   注释局部变量   宣言。关于本地的注释   变量声明永远不会保留   在二进制表示中。
  •   
     

如果T没有(meta-)注释   m对应于   annotation.Retention,然后是Java   编译器必须像对待T一样对待T   有一个这样的元注释   元素的值是   annotation.RetentionPolicy.CLASS。

因此,RetentionPolicy.RUNTIME确保将注释编译为二进制文件,但二进制文件中存在的注释不必在运行时可用

答案 2 :(得分:7)

如果您实际上有代码读取@A并对其执行某些操作,则代码依赖于A类,并且它将抛出ClassNotFoundException。

如果没有,即没有代码特别关注@A,那么可以说@A并不重要。