APS 系列文章 08

JPMS 强封装:--add-opens 与快速失败

Java 9 引入模块系统(JPMS)后,跨模块访问 class 有了更强的封装约束。动态代理因为要在目标类所在的包内生成字节码,首当其冲。

APS 对这块的态度是:不硬来,快速失败并给出可操作的提示。

类代理怎么定义类

类代理需要在目标类同一个包 里生成子类(这样才能访问包私有成员、合法 extends)。APS 用的是 MethodHandles.privateLookupIn 拿到目标类的私有 Lookup,再定义隐藏类。

当目标类在一个强封装模块 里时------任何没有 open 的包,包括 java.base 里的 java.util 这种------这个 private lookup 会被 JVM 拒绝。于是 proxy() 立刻失败,并抛出一条带 --add-opens 建议的异常。

一个真实报错

下面这段代码尝试代理 java.util.ArrayList:

java 复制代码
try {
    AcceleratedProxy.proxy(ArrayList.class,
            (o, m, a) -> AcceleratedProxy.invokeSuper(o, m, a));
} catch (RuntimeException e) {
    System.out.println(e.getCause().getMessage());
}

输出:

text 复制代码
Cannot access java.util.ArrayList in module java.base (package java.util):
the package is not open to the unnamed module. Add --add-opens
java.base/java.util=ALL-UNNAMED to the JVM arguments, or declare
'opens java.util;' in the module's module-info.java.

这条消息把两件事都告诉你了:为什么失败 (包没 open),以及怎么修。

两种修复方式

方式一:加 JVM 参数 (对 java.base 这种第三方模块常用):

bash 复制代码
java --add-opens java.base/java.util=ALL-UNNAMED ...

方式二:在目标模块声明 open(你自己的模块):

java 复制代码
module my.module {
    opens com.example.internal;   // 把包 open 出来
}

接口代理为什么不受影响

和类代理不同,接口代理用的是 public Lookup ,所以它只支持 public 接口------这和 java.lang.reflect.Proxy 的约束完全一致。非 public 接口代理则走 LookupManager,把生成类定义在接口自己的包里(上一篇讲过)。

所以「JPMS 强封装」这条限制,只针对类代理,接口代理不碰 private lookup。

设计上的取舍

值得点出来的是「快速失败」这个选择。很多库在这里会静默降级(比如偷偷换一个更慢但能工作的路径),结果是用户半年后才发现性能不对。APS 选择当场抛异常 + 明确提示 ,把决策权交给调用方------要么加 --add-opens,要么换接口代理。

这也呼应了本系列第一篇的核心观点:APS 的性能优势来自「直接 INVOKESPECIAL 调用父类方法」,而这要求代理类定义在目标类的包里。JPMS 的强封装直接堵死了这条路,所以必须显式 open,没有中间态。

小结

JPMS 强封装是类代理绕不开的一道坎。APS 的处理是:接口代理不受影响、类代理遇到强封装模块快速失败并给出 --add-opens 提示。理解这一点,你在模块化项目里就能提前规划好哪些包需要 open。

下一篇正式进入原理,回答那个贯穿全系列的问题:invokeSuper 到底是怎么变成 INVOKESPECIAL 的。

本文示例代码见 aps-blog/examples,框架源码与完整文档见 APS 仓库。

相关推荐
文丑颜不良啊2 小时前
LeetCode2161~2180题目解析
java·leetcode
文丑颜不良啊3 小时前
LeetCode2141~2160题目解析
java·leetcode
LabVIEW开发3 小时前
LabVIEW 连接板:28 个端子怎么排,优先级怎么定
java·算法·labview
天天被压力4 小时前
【A股分钟级高频数据完全手册 #07】北交所历史分钟K线接口:京市Pro版分时批量
java·人工智能·python
天天被压力4 小时前
【A股分钟级高频数据完全手册 #08】股票日、分时数据接口对比:普通分时与Pro版差异实测
java·人工智能·python
特立独行的猫A4 小时前
AI 时代,我为什么推荐用 cj-tauri 开发桌面应用
java·后端·python
金銀銅鐵4 小时前
从一个类名到一张类图:用反射自动生成 PlantUML
java·后端·uml
nice_lcj5205 小时前
Java 并发容器入门:ConcurrentHashMap、CopyOnWriteArrayList、阻塞队列
java·面试·并发编程·阻塞队列·concurrenthashmap·copyonwritearraylist
微小的鱼5 小时前
Linux的渲染与显示系统链路
android·java·linux
若亦_6 小时前
Java中关于this的三种用法
java·开发语言·intellij-idea