java11新特性—Nest-Based Access Control(巢狀訪問控制)

宋兵甲發表於2019-03-03

簡介

巢狀是一種訪問控制上下文,它允許多個class同屬一個邏輯程式碼塊,但是被編譯成多個分散的class檔案,它們訪問彼此的私有成員無需通過編譯器新增訪問擴充套件方法。

動機

很多jvm語言支援在一個原始檔中放多個class。這對於使用者是透明的,使用者認為它們在一個class中,所以希望它們共享同一套訪問控制體系。為了達到目的,編譯器需要經常需要通過附加的access bridge擴大private成員的訪問許可權到package。這種bridge和封裝相違背,並且輕微的增加程式的大小,會干擾使用者和工具。所以我們希望一種更直接,更安全,更透明的方式。

一個更大的坑就是反射的時候會有問題。當使用java.lang.reflect.Method.invoke從一個nestmate呼叫另一個nestmate私有方法時會報IllegalAccessError錯誤。這個是讓人不能理解的,因為反射應該和原始碼級訪問擁有相同許可權。

話不多說,看段程式碼

public class JEP181 {

    public static class Nest1 {
        private int varNest1;
        public void f() throws Exception {
            final Nest2 nest2 = new Nest2();
            //這裡沒問題
            nest2.varNest2 = 2;
            final Field f2 = Nest2.class.getDeclaredField("varNest2");
           //這裡在java8環境下會報錯,在java11中是沒問題的
            f2.setInt(nest2, 2);
            System.out.println(nest2.varNest2);
        }
    }

    public static class Nest2 {
        private int varNest2;
    }

    public static void main(String[] args) throws Exception {
        new Nest1().f();
    }
}
複製程式碼
在java11之前,classfile用InnerClasses和EnclosingMethod兩種屬性來幫助編譯器確認原始碼的巢狀關係,每一個巢狀的型別會編譯到自己的class檔案中,在使用上述屬性來連線其他class檔案。這些屬性對於jvm確定巢狀關係上已經足夠了,但是它們不直接適用於訪問控制,並且和java語言繫結的太緊了。

為了提供一種更大的,更廣泛的,不僅僅是java語言的巢狀型別,並且補足訪問控制檢測的不足,引入了兩個新的class檔案屬性。定義了兩種nest member,一種叫nest host(也叫top-level class),它包含一個NestMembers屬性用於確定其他靜態的nest members,其他的就是nest member,它包含一個NestHost屬性用於確定它的nest host。

大家可以看一下上述程式碼的class檔案詳情。

JVM針對巢狀成員的訪問控制

調整了jvm訪問規則,增加了如下條款:
一個field或method R可以被class或interface D訪問,當且僅當如下任一條件為真:

  • … …(原條款不變)
  • R是私有的,並且宣告在另一個class或interface C中,並且C和D是nestmates

C和D是nestmates表名他們肯定有一個相同的host

這個鬆散的訪問規則會作用在如下幾個地方:(這一段我就貼原文了,感覺翻譯過來味道就變了)

  • Resolving fields and methods (JVMS 5.4.3.2, etc.)
  • Resolving method handle constants (JVMS 5.4.3.5)
  • Resolving call site specifiers (JVMS 5.4.3.6)
  • Checking Java language access by instances of java.lang.reflect.AccessibleObject
  • Checking access during queries to java.lang.invoke.MethodHandles.Lookup

針對上述訪問規則的改變,相應的調整位元組碼:

  • invokespecial for private nestmate constructors,
  • invokevirtual for private non-interface, nestmate instance methods,
  • invokeinterface for private interface, nestmate instance methods; and
  • invokestatic for private nestmate, static methods

巢狀類的校驗

巢狀類必須在訪問前校驗。校驗最遲要發生在訪問成員之前,最早可以發生在對class檔案的校驗時,或者在兩者之間,比如JIT時。校驗巢狀關係,需要載入nest host類,為了防止無意義的載入,這一步儘量放到最後做。

為了保證巢狀的完整性,建議禁止修改nest classfile屬性

相關文章