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

2022-01-13 00:00:00 annotations java

考虑以下代码:

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()));
        }
}

编译和运行按预期工作:

Compiling and running works as expected:

$ javac *.java
$ java -cp . C
[@A()]

但是再考虑一下:

$ rm A.class
$ java -cp . C
[]

我预计它会抛出 ClassNotFoundException,因为缺少 @A.但相反,它会默默地删除注释.

I would've expected it to throw a ClassNotFoundException, since @A is missing. But instead, it silently drops the annotation.

这种行为是否记录在 JLS 中的某个地方,还是 Sun 的 JVM 的怪癖?这样做的理由是什么?

Is this behaviour documented in the JLS somewhere, or is it a quirk of Sun's JVM? What's the rationale for it?

对于 javax.annotation.Nonnull 之类的东西似乎很方便(无论如何,它似乎应该是 @Retention(CLASS) ),但对于许多其他注释似乎它可能会导致运行时发生各种不好的事情.

It seems convenient for things like javax.annotation.Nonnull (which seems like it should've been @Retention(CLASS) anyway), but for many other annotations it seems like it could cause various bad things to happen at runtime.

推荐答案

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

In the earlier public drafts for JSR-175 (annotations), it was discussed if the compiler and runtime should ignore unknown annotations, to provide a looser coupling between the usage and declaration of annotations. A specific example was the use of applications server specific annotations on an EJB to control the deployment configuration. If the same bean should be deployed on a different application server, it would have been convenient if the runtime simply ignored the unknown annotations instead of raising a NoClassDefFoundError.

即使措辞有点模糊,我认为您看到的行为在 JLS 13.5.7: "... 删除注释对 Java 中程序的二进制表示的正确链接没有影响编程语言."我将此解释为好像注释已删除(在运行时不可用),程序仍应链接并运行,这意味着在通过反射访问时会简单地忽略未知注释.

Even if the wording is a little bit vague, I assume that the behaviour you are seeing is specified in JLS 13.5.7: "... removing annotations has no effect on the correct linkage of the binary representations of programs in the Java programming language." I interpret this as if annotations are removed (not available at runtime), the program should still link and run and that this implies that the unknown annotations are simply ignored when accessed through reflection.

Sun 的 JDK 5 的第一个版本没有正确实现这一点,但在 1.5.0_06 中已修复.你可以在bug数据库中找到相关的bug6322301,但是没有指向任何规范,除了声称根据 JSR-175 规范指南,getAnnotations 必须忽略未知注释".

The first release of Sun's JDK 5 did not implement this correctly, but it was fixed in 1.5.0_06. You can find the relevant bug 6322301 in the bug database, but it does not point to any specifications except claiming that "according to the JSR-175 spec lead, unknown annotations must be ignored by getAnnotations".

相关文章