转载

java11新特性---Nest-Based Access Control(嵌套访问控制)

简介

嵌套是一种访问控制上下文,它允许多个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属性

原文  https://juejin.im/post/5baec7c85188255c5f53fa37
正文到此结束
Loading...