Java 11 Nest Based Access Control – 基于Java 11 Nest的访问控制

最后修改: 2019年 1月 1日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we will explore nests, the new access control context introduced in Java 11.

在本教程中,我们将探讨nests,即Java 11中引入的新访问控制上下文。

2. Before Java 11

2.在Java 11之前

2.1. Nested Types

2.1.嵌套类型

Java allows classes and interfaces to be nested within each other. These nested types have unrestricted access to each other, including to private fields, methods, and constructors.

Java 允许类和接口相互嵌套在一起。这些嵌套类型可以不受限制地相互访问,包括访问私有字段、方法和构造函数。

Consider the following nested class example:

考虑以下嵌套类的例子。

public class Outer {

    public void outerPublic() {
    }

    private void outerPrivate() {
    }

    class Inner {

        public void innerPublic() {
            outerPrivate();
        }
    }
}

Here, although the method outerPrivate() is private, it is accessible from the method innerPublic().

在这里,尽管方法outerPrivate()private,但它可以从方法innerPublic()中访问。

We can describe a top-level type, plus all types nested within it, as forming a nest. Two members of a nest are described as nestmates.

我们可以将一个顶层类型,加上嵌套在其中的所有类型,描述为形成一个巢。一个巢中的两个成员被描述为巢友。

Thus, in the above example, Outer and Inner together form a nest and are nestmates of each other.

因此,在上述例子中,外侧内侧共同构成一个巢,并且是彼此的巢友。

2.2. Bridge Method

2.2.桥梁法

JVM access rules do not permit private access between nestmates. Ideally, we should get a compilation error for the above example. However, the Java source code compiler permits the access by introducing a level of indirection.

JVM的访问规则不允许nestmates之间的私人访问。理想情况下,对于上述例子,我们应该得到一个编译错误。然而,Java源代码编译器通过引入一个层次的间接性来允许这种访问。

For example, an invocation of a private member is compiled into an invocation of a compiler-generated, package-private, bridging method in the target class, which in turn invokes the intended private method.

例如,对一个私有成员的调用被编译成对目标类中编译器生成的、封装为私有的桥接方法的调用,而该方法又会调用预定的私有方法

This happens behind the scenes. This bridging method slightly increases the size of a deployed application and can confuse users and tools.

这发生在幕后。这种桥接方法稍微增加了部署的应用程序的大小,并可能使用户和工具感到困惑。

2.3. Using Reflection

2.3.使用反射

A further consequence of this is that core reflection also denies access. This is surprising given that reflective invocations should behave the same as source level invocations.

这样做的另一个结果是,核心反射也拒绝访问。这是令人惊讶的,因为反射性调用的行为应该与源级调用的行为相同。

For example, if we try to call the outerPrivate() reflectively from the Inner class:

例如,如果我们试图从Inner类反射性地调用outerPrivate()

public void innerPublicReflection(Outer ob) throws Exception {
    Method method = ob.getClass().getDeclaredMethod("outerPrivate");
    method.invoke(ob);
}

We would get an exception:

我们会得到一个例外。

java.lang.IllegalAccessException: 
Class com.baeldung.Outer$Inner can not access a member of class com.baeldung.Outer with modifiers "private"

Java 11 tries to address these concerns.

Java 11试图解决这些问题。

3. Nest Based Access Control

3.基于鸟巢的访问控制

Java 11 brings the notion of nestmates and the associated access rules within the JVM. This simplifies the job of Java source code compilers.

Java 11在JVM中引入了neestmates的概念和相关的访问规则。这简化了Java源代码编译器的工作。

To achieve this, the class file format now contains two new attributes:

为了实现这一点,现在的类文件格式包含两个新的属性。

  1. One nest member (typically the top-level class) is designated as the nest host. It contains an attribute (NestMembers) to identify the other statically known nest members.
  2. Each of the other nest members has an attribute (NestHost) to identify its nest host.

Thus, for types C and D to be nestmates they must have the same nest host. A type C claims to be a member of the nest hosted by D, if it lists D in its NestHost attribute. The membership is validated if D also lists C in its NestMembers attribute. Also, type D is implicitly a member of the nest that it hosts.

因此,对于类型CD来说,它们必须有相同的巢主,才能成为巢友。如果一个类型C在其NestHost属性中列出D,它就声称是由D托管的巢的成员。如果D在其NestMembers属性中也列出了C,则该成员资格被验证。另外,类型D隐含地是它所托管的巢的成员。

Now there is no need for the compiler to generate the bridge methods.

现在不需要编译器来生成桥接方法了。

Finally, the nest based access control removes the surprising behavior from the core reflection. Therefore, method innerPublicReflection() shown in the previous section will execute without any exceptions.

最后,基于巢穴的访问控制消除了核心反射中令人惊讶的行为。因此,上节所示的方法innerPublicReflection()将在没有任何异常的情况下执行。

4. Nestmate Reflection API

4.Nestmate Reflection API

Java 11 provides means to query the new class file attributes using core reflection. The class java.lang.Class contains the following three new methods.

Java 11提供了使用核心反射来查询新的类文件属性的手段。类java.lang.Class包含以下三个新方法。

4.1. getNestHost()

4.1 getNestHost()

This returns the nest host of the nest to which this Class object belongs:

这将返回该对象所属的巢穴的巢主。

@Test
public void whenGetNestHostFromOuter_thenGetNestHost() {
    is(Outer.class.getNestHost().getName()).equals("com.baeldung.Outer");
}

@Test
public void whenGetNestHostFromInner_thenGetNestHost() {
    is(Outer.Inner.class.getNestHost().getName()).equals("com.baeldung.Outer");
}

Both Outer and Inner classes belong to the nest host com.baeldung.Outer.

OuterInner类都属于巢穴主机com.baeldung.Outer

4.2. isNestmateOf()

4.2.isNestmateOf()

This determines if the given Class is a nestmate of this Class object:

这决定了给定的是否是这个对象的近亲。

@Test
public void whenCheckNestmatesForNestedClasses_thenGetTrue() {
    is(Outer.Inner.class.isNestmateOf(Outer.class)).equals(true);
}

4.3. getNestMembers()

4.3.getNestMembers()

This returns an array containing Class objects representing all the members of the nest to which this Class object belongs:

这将返回一个包含Class对象的数组,代表这个Class对象所属的巢的所有成员。

@Test
public void whenGetNestMembersForNestedClasses_thenGetAllNestedClasses() {
    Set<String> nestMembers = Arrays.stream(Outer.Inner.class.getNestMembers())
      .map(Class::getName)
      .collect(Collectors.toSet());

    is(nestMembers.size()).equals(2);

    assertTrue(nestMembers.contains("com.baeldung.Outer"));
    assertTrue(nestMembers.contains("com.baeldung.Outer$Inner"));
}

5. Compilation Details

5.汇编细节

5.1. Bridge Method Before Java 11

5.1.Java 11之前的桥接方法

Let’s dig into the details of the compiler generated bridging method. We can see this by disassembling the resulting class file:

让我们来挖掘一下编译器生成的桥接方法的细节。我们可以通过反汇编生成的类文件来看。

$ javap -c Outer
Compiled from "Outer.java"
public class com.baeldung.Outer {
  public com.baeldung.Outer();
    Code:
       0: aload_0
       1: invokespecial #2                  // Method java/lang/Object."<init>":()V
       4: return

  public void outerPublic();
    Code:
       0: return

  static void access$000(com.baeldung.Outer);
    Code:
       0: aload_0
       1: invokespecial #1                  // Method outerPrivate:()V
       4: return
}

Here, apart from the default constructor and the public method outerPublic(), notice the method access$000(). The compiler generates this as a bridging method.

在这里,除了默认的构造函数和公共方法outerPublic()之外,注意到方法access$000()。编译器将其生成为一个桥接方法。

The innerPublic() goes through this method to call the outerPrivate():

innerPublic()通过这个方法来调用outerPrivate()

$ javap -c Outer\$Inner
Compiled from "Outer.java"
class com.baeldung.Outer$Inner {
  final com.baeldung.Outer this$0;

  com.baeldung.Outer$Inner(com.baeldung.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void innerPublic();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       4: invokestatic  #3                  // Method com/baeldung/Outer.access$000:(Lcom/baeldung/Outer;)V
       7: return
}

Notice the comment at line #19. Here, innerPublic() calls the bridge method access$000().

注意第19行的注释。这里,innerPublic()调用桥接方法access$000()

5.2. Nestmates with Java 11

5.2.与Java 11的关系

The Java 11 compiler would generate the following disassembled Outer class file:

Java 11编译器将生成以下反汇编的Outer类文件。

$ javap -c Outer
Compiled from "Outer.java"
public class com.baeldung.Outer {
  public com.baeldung.Outer();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void outerPublic();
    Code:
       0: return
}

Notice that there isn’t a compiler generated bridging method. Also, the Inner class can now make a direct call to the outerPrivate() method:

注意,没有一个编译器生成的桥接方法。另外,Inner类现在可以直接调用outerPrivate()方法。

$ javap -c Outer\$Inner.class 
Compiled from "Outer.java"
class com.baeldung.Outer$Inner {
  final com.baeldung.Outer this$0;

  com.baeldung.Outer$Inner(com.baeldung.Outer);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."<init>":()V
       9: return

  public void innerPublic();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:Lcom/baeldung/Outer;
       4: invokevirtual #3                  // Method com/baeldung/Outer.outerPrivate:()V
       7: return
}

6. Conclusion

6.结论

In this article, we explored the nest based access control introduced in Java 11.

在这篇文章中,我们探讨了Java 11中引入的基于巢穴的访问控制。

As usual, code snippets can be found over on GitHub.

像往常一样,代码片段可以在GitHub上找到over