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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3567413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 02:26:57  来源:igfitidea点击:

Why doesn't a missing annotation cause a ClassNotFoundException at runtime?

javaannotations

提问by Matt McHenry

Consider the following code:

考虑以下代码:

A.java:

A.java:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
@interface A{}

C.java:

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()]

But then consider this:

但再考虑一下:

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

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

我原以为它会抛出一个ClassNotFoundException,因为@A它丢失了。但相反,它默默地删除了注释。

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

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

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.

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

采纳答案by jarnbjo

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.

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

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.

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

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 6322301in 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".

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

回答by Guillaume

Quoting the JLS:

引用 JLS:

9.6.1.2 RetentionAnnotations may be present only in the source code, or they may be present in the binary form of a class or interface. An annotation that is present in the binary may or may not be available at run-time via the reflective libraries of the Java platform.

The annotation type annotation.Retention is used to choose among the above possibilities. If an annotation a corresponds to a type T, and T has a (meta-)annotation m that corresponds to annotation.Retention, then:

  • If m has an element whose value is annotation.RetentionPolicy.SOURCE, then a Java compiler must ensure that a is not present in the binary representation of the class or interface in which a appears.
  • If m has an element whose value is annotation.RetentionPolicy.CLASS, or annotation.RetentionPolicy.RUNTIME a Java compiler must ensure that a is represented in the binary representation of the class or interface in which a appears, unless m annotates a local variable declaration. An annotation on a local variable declaration is never retained in the binary representation.

If T does not have a (meta-)annotation m that corresponds to annotation.Retention, then a Java compiler must treat T as if it does have such a meta-annotation m with an element whose value is annotation.RetentionPolicy.CLASS.

9.6.1.2 保留注解可能只存在于源代码中,也可能以类或接口的二进制形式存在。通过 Java 平台的反射库,二进制文件中存在的注释在运行时可能可用,也可能不可用。

注释类型 annotation.Retention 用于在上述可能性中进行选择。如果注释 a 对应于类型 T,并且 T 具有对应于 annotation.Retention 的(元)注释 m,则:

  • 如果 m 有一个值为 annotation.RetentionPolicy.SOURCE 的元素,那么 Java 编译器必须确保 a 不在出现 a 的类或接口的二进制表示中。
  • 如果 m 具有值为 annotation.RetentionPolicy.CLASS 或 annotation.RetentionPolicy.RUNTIME 的元素,Java 编译器必须确保 a 以出现 a 的类或接口的二进制表示形式表示,除非 m 注释局部变量声明. 局部变量声明上的注释永远不会保留在二进制表示中。

如果 T 没有对应于 annotation.Retention 的(元)注释 m,那么 Java 编译器必须将 T 视为它确实具有这样一个元注释 m,其元素值为 annotation.RetentionPolicy.CLASS。

So RetentionPolicy.RUNTIME ensures that the annotation is compiled into the binary but an annotation present in the binary doesn't have to be available at runtime

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

回答by irreputable

if you actually have code that reads @A and does something with it, the code has a dependency on class A, and it will throw ClassNotFoundException.

如果您确实有代码读取@A 并对其进行处理,则该代码依赖于类 A,并且会抛出 ClassNotFoundException。

if not, i.e. no code cares specificly about @A, then it's arguable that @A doesn't really matter.

如果不是,即没有代码特别关心@A,那么@A 并不重要是有争议的。