Reflection vs Encapsulation – Stand Off in the Java Module System
反射vs封装—Java模块化系统下的对质
Historically reflection could be used to break into any code that ran in the same JVM. With Java 9 this is going to change. One of the two main goals of the new module system is strong encapsulation; giving modules a safe space into which no code can intrude. These two techniques are clearly at odds so how can this stand off be resolved? After considerable discussions it looks like the recent proposal of open modules would show a way out.
If you’re all down with the module system and what reflection does, you can skip the following back story and jump right into the stand off.
过去,反射功能可以破坏任何运行在同一JVM中的代码的封装。随着Java 9的来临,这一现状将被改变。新模块化系统有两个主要目标,其中之一就是强封装,给各个模块一个安全的,无法入侵的运行空间。这两种技术是如此的的水火难容,那么如何解决这个问题呢?经过相当多的讨论之后,最近提出的开放化模块系统也许会是一个方向。
如果你已经全部了解的模块化系统的概念和反射的功能,那么你可以直接跳过后面的故事,直接进入章节:对质。
Setting the Scene
Let me set the scene of how the module system implements strong encapsulation and how that clashes with reflection.
让我来设置一些场景,解释关于模块化系统如何实现的强封装,以及如何与反射相矛盾。
Module Crash Course
The Java Platform Module Saloon (JPMS) is introducing the concept of modules, which in the end are just regular JARs with a module descriptor. The descriptor is compiled from a module-info.java file that defines a module’s name, its dependencies on other modules, and the packages it makes available:
Java平台模块化平台 (JPMS)介绍了“模块”的概念, 这种“模块”最后也只是一个带着模块描述的JAR包. 这个描述是从module-info.java这个文件编译而来,文件里面定义了模块的名字,模块依赖的其他模块,以及模块提供的可用包:
module some.module {
requires some.other.module;
requires yet.another.module;
exports some.module.package;
exports some.module.other.package;
}
In the context of encapsulation there are two points to take note of:
- Only public types, methods, and fields in exported packages are accessible.
- They are only accessible to modules that require the exporting module.
Here, “being accessible” means that code can be compiled against such elements and that the JVM will allow accessing them at run time. So if code in module a user depends on code in a module owner, all we need to do to make that work is have user require owner and have owner export the packages containing the required types
在封装的代码上下文中有两点值得注意:
- 在要导出(exports)的包中,只有声明为public的类型,方法和字段可以访问.
- 这些导出(exports)的包,只会被在依赖(requires)他们的模块访问.
在这里,“允许访问”意味着可以针对包括的元素进行编译并且JVM可以在运行时访问它们. 所以,如果我们有一个模块 user 依赖模块 owner, 需要我们做的只是在 user 模块中依赖(require) owner 和在 owner 模块中导出(export) 包含着user模块依赖的类型的包:
module user {
requires owner;
}
module owner {
exports owner.api.package;
}
This is the common case and apart from making the dependencies and API explicit and known to the module system all works as we’re used to.
除了写依赖和显示API,在这些我们之前使用的已知模块化系统中,这些都是很常见的情况。
So far everybody’s having fun! Then, in comes Reflection… conversations halt mid-sentence, the piano player stops his tune.
目前为止,大伙都很开心,然后,反射登场… 从人声鼎沸到万籁俱寂。
Reflection
Integer two = 2;
Field value = Integer.class.getDeclaredField("value");
value.setAccessible(true);
value.set(two, 3);
if (1 + 1 != two)
System.out.println("Math died!");
This power drives all kinds of frameworks – starting with JPA providers like Hibernate, coming by testing libraries like JUnit and TestNG, to dependency injectors like Guice, and ending with obsessed class path scanners like Spring – which reflect over our application or test code to work their magic. On the other side we have libraries that need something from the JDK that it would rather not expose (did anybody say sun.misc.Unsafe?). Here as well, reflection was the answer.