Java 11环境下如何判断对象成员是否允许反射访问?
Java 11反射访问权限判断方案
规则变化的核心原因
Java 9引入的JPMS(Java平台模块系统)重构了访问权限规则,原本全类路径下有效的public可见性被限制在模块内部,只有模块显式导出(exports)或开放(opens)的包,外部模块才能访问对应的类及成员,这就是你遇到的「类和成员都是public但反射调用失败」的根因。
你提供的示例中,sun.security.ssl.SSLSocketFactoryImpl属于java.base模块的内部非导出包,所以哪怕类和getDefaultCipherSuites方法都是public,外部未命名模块也没有访问权限。
通用判断逻辑
判断反射访问权限需要按顺序验证两个维度的规则:
- 传统访问修饰符规则:和Java 8逻辑一致,验证成员修饰符(public/protected/包私有/private)、调用方与目标类的包归属、继承关系、类加载器关系是否匹配。
- 模块访问规则:这是Java 9+新增的必填验证项:
- 若仅访问public成员,需要目标类所在的包已通过
exports指令导出到调用方所在模块(或全局导出) - 若需要访问非public成员,或者要通过
setAccessible(true)突破修饰符限制,需要目标类所在的包已通过opens指令开放到调用方所在模块(或全局开放)
- 若仅访问public成员,需要目标类所在的包已通过
现成API调用方案
Java 9+提供了现成的API可以直接完成权限判断,不需要自己手动实现所有规则:
- 直接判断成员是否可访问:使用
AccessibleObject.canAccess(Object obj)方法,返回值为true即可直接调用反射API
Method m = cls.getMethod("getDefaultCipherSuites"); boolean canInvoke = m.canAccess(sf); if (!canInvoke) { try { m.setAccessible(true); } catch (SecurityException e) { // 无权限修改可访问标识,反射调用会失败 } }
- 模块层面的导出/开放状态判断:使用
Module类的相关API
Module callerModule = Refl.class.getModule(); Module targetModule = cls.getModule(); String targetPackage = cls.getPackageName(); // 验证包是否对调用方模块导出,可正常访问public成员 boolean pkgExported = targetModule.isExported(targetPackage, callerModule); // 验证包是否对调用方模块开放,可setAccessible后访问任意成员 boolean pkgOpened = targetModule.isOpen(targetPackage, callerModule);
示例代码的修复方案
如果你不需要访问实现类的独有方法,直接从公共API类SSLSocketFactory获取Method对象即可正常调用,SSLSocketFactory所在的javax.net.ssl包是java.base模块的导出包,符合模块访问规则:
// 替换获取Method的逻辑即可 Method m = SSLSocketFactory.class.getMethod("getDefaultCipherSuites"); m.invoke(sf);
如果必须访问实现类的成员,需要在JVM启动参数中添加以下配置,将内部包开放给未命名模块:--add-opens java.base/sun.security.ssl=ALL-UNNAMED
内容的提问来源于stack exchange,提问作者Will Lin
相关产品推荐
相关产品推荐

