Java 17 更新(6):制裁!我自己私有的 API 你们怎么随便一个人都想用?

2021-10-19 14:27:35 浏览数 (1)

说实话,我们总是用人家 JDK 的内部 API,是不是有点儿欺负人。

  • Java 17 更新(1):更快的 LTS 节奏
  • Java 17 更新(2):没什么存在感的 strictfp, 这回算是回光返照了
  • Java 17 更新(3):随机数生成器来了一波稳稳的增强
  • Java 17 更新(4):这波更新,居然利好 mac 用户
  • Java 17 更新(5):历史包袱有点儿大,JDK 也在删代码啦

今天我们来聊聊 JEP 403: Strongly Encapsulate JDK Internals。这一条对于使用 JDK 内部 API 的应用场景来讲会比较受影响。

JDK 的动作还是很慢的,它给开发者提供了相当长的过渡期。从 Java 9 引入模块化开始,JDK 对于其内部的 API 的访问限制就已经明确开始落地,只是当时我们可以通过配置启动参数 --illegal-access 来继续使用 JDK 的内部 API,其中 Java 9 - Java 15 这个参数默认 permit,Java 16 默认 deny。

不过,现在不可以了。在 Java 17 当中使用 --illegal-access 将会得到以下警告,并且没有任何效果:

代码语言:javascript复制
Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option --illegal-access=permit; support was removed in 17.0

按照提案的说明,被严格限制的这些内部 API 包括:

  • java.* 包下面的部分非 public 类、方法、属性,例如 Classloader 当中的 defineClass 等等。
  • sun.* 下的所有类及其成员都是内部 API。
  • 绝大多数 com.sun.* 、 jdk.* 、org.* 包下面的类及其成员也是内部 API。

举个例子:

代码语言:javascript复制
package com.sun.beans;

...
public final class WeakCache<K, V> {
    private final Map<K, Reference<V>> map = new WeakHashMap<K, Reference<V>>();

    public V get(K key) { ... }

    public void put(K key, V value) { ... }
 ...
}

在 java.desktop 模块下有这么一个类,非常简单,就是对 WeakHashMap 做了个包装。我想要用一下它,我该怎么办呢?

复制一份到我的工程里面。

不是,不是。。。优秀的程序员不应该 CV 代码。。。所以我直接使用它。

啊,不行。那我可以反射呀~ 我可真是个小机灵鬼。这波反射下来真是无人能敌。

代码语言:javascript复制
try {
    var weakCacheClass = Class.forName("com.sun.beans.WeakCache");
    var weakCache = weakCacheClass.getDeclaredConstructor().newInstance();
    var putMethod = weakCacheClass.getDeclaredMethod("put", Object.class, Object.class);
    var getMethod = weakCacheClass.getDeclaredMethod("get", Object.class);
    putMethod.invoke(weakCache, "name", "bennyhuo");
    System.out.println(getMethod.invoke(weakCache, "name"));
} catch (Exception e) {
    e.printStackTrace();
}

满怀欣喜的运行它。。。

代码语言:javascript复制
java.lang.IllegalAccessException: class com.bennyhuo.java17.ReflectionsInternal cannot access class com.sun.beans.WeakCache (in module java.desktop) because module java.desktop does not export com.sun.beans to unnamed module @776ec8df
 at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
 at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
 at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:489)
 at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
 at com.bennyhuo.java17.ReflectionsInternal.useWeakCache(ReflectionsInternal.java:16)
 at com.bennyhuo.java17.ReflectionsInternal.main(ReflectionsInternal.java:10)

en???这让我想起了 Android P,你看这个字母 P,它的发音充满了挑衅,它的形状还有点儿像官方在嘲笑我们

现在 Java 17 也玩这个啊,反射都不行了啊这。。

Java 16 我们可以通过在运行时加入 --illegal-access=permit 来运行,虽然会有一堆警告:

代码语言:javascript复制
# java --illegal-access=permit com.bennyhuo.java17.ReflectionsInternal

Java HotSpot(TM) 64-Bit Server VM warning: Option --illegal-access is deprecated and will be removed in a future release.
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.bennyhuo.java17.ReflectionsInternal (file:/mnt/c/Users/benny/WorkSpace/Mario/SourceCode/Java17UpdatesDemo/src/) to constructor com.sun.beans.WeakCache()
WARNING: Please consider reporting this to the maintainers of com.bennyhuo.java17.ReflectionsInternal
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
bennyhuo

不过正如我们前面所说,Java 17 当中这个参数无效了:

代码语言:javascript复制
# java --illegal-access=permit com.bennyhuo.java17.ReflectionsInternal

Java HotSpot(TM) 64-Bit Server VM warning: Ignoring option --illegal-access=permit; support was removed in 17.0
java.lang.IllegalAccessException: class com.bennyhuo.java17.ReflectionsInternal cannot access class com.sun.beans.WeakCache (in module java.desktop) because module java.desktop does not export com.sun.beans to unnamed module @372f7a8
d
        at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:392)
        at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:674)
        at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:489)
        at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
        at com.bennyhuo.java17.ReflectionsInternal.useWeakCache(ReflectionsInternal.java:16)
        at com.bennyhuo.java17.ReflectionsInternal.main(ReflectionsInternal.java:10)

这就是上帝在关门的时候(Java 9),顺便也提醒我们窗户也马上要关上了,还不赶紧滚出去?然后上帝又花了三年把窗户也关上了(Java 17)。不过,它总算是还留了一个通气孔。。。

Java 17 当中 --add-opens 仍然有效,通过开启它可以让我们的程序在运行时通过反射访问指定的类:

代码语言:javascript复制
--add-opens java.desktop/com.sun.beans=ALL-UNNAMED

所以,上面的代码想要运行,只能:

代码语言:javascript复制
# java --add-opens java.desktop/com.sun.beans=ALL-UNNAMED com.bennyhuo.java17.ReflectionsInternal

bennyhuo

所以这波限制是要来真的,赶快跑吧!

大家也可以参考 受影响的 API 清单(https://cr.openjdk.java.net/~mr/jigsaw/jdk8-packages-strongly-encapsulated) 来规划自己的 JDK 升级。

顺便说一句,著名的 Unsafe 类不在这一波制裁的名单以内,可能是 Unsafe 应用太广泛了吧,而且 Java 官方也没有找到合适的替代品来满足需求,就先放着了(Unsafe 我们在后面访问堆外内存的内容中还会有介绍)。

好啦,关于加强控制内部 API 的限制的更新,我们也就介绍这么多,对大家的影响嘛,应该也不大(只要不升级)。


C 语言是所有程序员应当认真掌握的基础语言,不管你是 Java 还是 Python 开发者,欢迎大家关注我的新课 《C 语言系统精讲》:

0 人点赞