เหตุใดคำอธิบายประกอบที่ขาดหายไปจึงไม่ทำให้เกิด ClassNotFoundException ที่รันไทม์


92

พิจารณารหัสต่อไปนี้:

อ. จาวา:

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

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

ค. 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 ที่ไหนสักแห่งหรือเป็นมุมมองของ JVM ของ Sun? เหตุผลของมันคืออะไร?

ดูเหมือนว่าจะสะดวกสำหรับสิ่งต่างๆเช่นjavax.annotation.Nonnull(ซึ่งดูเหมือนว่ามันควรจะเป็นเช่นนั้น@Retention(CLASS)) แต่สำหรับคำอธิบายประกอบอื่น ๆ ดูเหมือนว่ามันอาจทำให้เกิดสิ่งเลวร้ายต่างๆที่รันไทม์ได้

คำตอบ:


92

ในแบบร่างสาธารณะก่อนหน้านี้สำหรับ JSR-175 (คำอธิบายประกอบ) มีการพูดถึงว่าคอมไพลเลอร์และรันไทม์ควรละเว้นคำอธิบายประกอบที่ไม่รู้จักหรือไม่เพื่อให้มีการเชื่อมต่อที่หลวมขึ้นระหว่างการใช้งานและการประกาศคำอธิบายประกอบ ตัวอย่างเฉพาะคือการใช้คำอธิบายประกอบเฉพาะของเซิร์ฟเวอร์แอ็พพลิเคชันบน EJB เพื่อควบคุมคอนฟิกูเรชันการปรับใช้ หากควรปรับใช้ bean เดียวกันบนแอ็พพลิเคชันเซิร์ฟเวอร์อื่นจะสะดวกหากรันไทม์เพียงแค่ละเว้นหมายเหตุประกอบที่ไม่รู้จักแทนที่จะเพิ่ม NoClassDefFoundError

แม้ว่าถ้อยคำจะคลุมเครือเล็กน้อย แต่ฉันถือว่าพฤติกรรมที่คุณเห็นนั้นระบุไว้ใน JLS 13.5.7: "... การลบคำอธิบายประกอบจะไม่มีผลต่อการเชื่อมโยงที่ถูกต้องของการแสดงไบนารีของโปรแกรมในภาษาโปรแกรม Java .” ฉันตีความว่าถ้าคำอธิบายประกอบถูกลบออก (ไม่สามารถใช้งานได้ที่รันไทม์) โปรแกรมควรจะยังคงเชื่อมโยงและเรียกใช้และนั่นหมายความว่าคำอธิบายประกอบที่ไม่รู้จักจะถูกละเว้นเมื่อเข้าถึงผ่านการสะท้อน

JDK 5 รุ่นแรกของ Sun ไม่ได้ใช้สิ่งนี้อย่างถูกต้อง แต่ได้รับการแก้ไขใน 1.5.0_06 คุณสามารถค้นหาจุดบกพร่องที่เกี่ยวข้อง6322301 ได้ในฐานข้อมูลข้อบกพร่อง แต่ไม่ได้ชี้ไปที่ข้อกำหนดใด ๆ ยกเว้นการอ้างว่า "ตามข้อกำหนดของ JSR-175 คำอธิบายประกอบที่ไม่รู้จักต้องถูกละเว้นโดย getAnnotations"


35

การอ้างอิง JLS:

9.6.1.2คำอธิบายประกอบการเก็บรักษาอาจมีเฉพาะในซอร์สโค้ดเท่านั้นหรืออาจอยู่ในรูปแบบไบนารีของคลาสหรืออินเทอร์เฟซ คำอธิบายประกอบที่มีอยู่ในไบนารีอาจมีหรือไม่มีอยู่ในขณะรันไทม์ผ่านไลบรารีสะท้อนแสงของแพลตฟอร์ม Java

คำอธิบายประกอบประเภทคำอธิบายประกอบการเก็บรักษาใช้เพื่อเลือกจากความเป็นไปได้ข้างต้น หากคำอธิบายประกอบ a สอดคล้องกับประเภท T และ T มี (meta-) คำอธิบายประกอบ m ที่สอดคล้องกับคำอธิบายประกอบการเก็บรักษาแล้ว:

  • ถ้า m มีองค์ประกอบที่มีค่าเป็น annotation.RetentionPolicy.SOURCE ดังนั้นคอมไพเลอร์ Java ต้องแน่ใจว่าไม่มีอยู่ในการแทนค่าไบนารีของคลาสหรือส่วนต่อประสานที่ปรากฏ
  • ถ้า m มีอิลิเมนต์ที่มีค่าเป็น annotation.RetentionPolicy.CLASS หรือ annotation.RETentionPolicy.RUNTIME คอมไพเลอร์ Java ต้องแน่ใจว่า a แสดงในการแทนค่าไบนารีของคลาสหรืออินเตอร์เฟสที่ปรากฏขึ้นเว้นแต่ m จะใส่คำอธิบายประกอบการประกาศตัวแปรโลคัล . คำอธิบายประกอบเกี่ยวกับการประกาศตัวแปรโลคัลจะไม่ถูกเก็บไว้ในการแทนค่าไบนารี

ถ้า T ไม่มีคำอธิบายประกอบ (meta-) m ที่สอดคล้องกับ annotation.Retention ดังนั้นคอมไพเลอร์ Java จะต้องปฏิบัติต่อ T ราวกับว่ามันมี meta-annotation m ด้วยองค์ประกอบที่มีค่าเป็น annotation.RetentionPolicy.CLASS

ดังนั้น RetentionPolicy.RUNTIME จึงมั่นใจได้ว่าคำอธิบายประกอบถูกคอมไพล์เป็นไบนารี แต่คำอธิบายประกอบที่มีอยู่ในไบนารีไม่จำเป็นต้องพร้อมใช้งานในขณะรันไทม์


9

หากคุณมีโค้ดที่อ่าน @A และทำอะไรบางอย่างกับโค้ดนั้นจริงๆโค้ดจะมีการพึ่งพาคลาส A และจะส่ง ClassNotFoundException

ถ้าไม่เช่นนั้นก็ไม่มีรหัสที่สนใจเฉพาะเกี่ยวกับ @A ก็เป็นที่ถกเถียงกันอยู่ว่า @A ไม่สำคัญจริงๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.