Java反射不可访问异常:sun.util.calendar.ZoneInfo无法访问排查
问题背景
- 旧环境:Mac + CommandBox v5.0.1+00137(CFEngine:adobe@2023.0.05+330608),网站运行正常
- 新环境:M3 Mac + CommandBox v6.0.0+00787(CFEngine:adobe@2023.0.06+330617),启动开发服务器后,第三方组件
timezone.cfc抛出异常
报错详情
- 核心Java异常:
java.lang.reflect.InaccessibleObjectException: Unable to make public sun.util.calendar.ZoneInfo(java.lang.String,int) accessible: module java.base does not "exports sun.util.calendar" to unnamed module @591fd34d
- 报错位置为
timezone.cfc第87行,相关代码片段:
<!--- the time zone object itself ---> <cfset variables.tzObj = createObject("java","java.util.TimeZone")> <!--- list of all available timezone ids ---> <cfset variables.tzList = listsort(arrayToList(variables.tzObj.getAvailableIDs()), "textnocase")> <!--- default timezone on the server ---> <cfset variables.mytz = variables.tzObj.getDefault().ID>
- 附加现象:
- 输出
variables.tzObj.getDefault()或variables.tzObj.getDefault().ID时,提示[undefined struct element] - 调用
getID()等方法时抛出对象实例化异常:
Object instantiation exception.
An exception occurred while instantiating a Java object. The class must not be an interface or an abstract class. If the class has a constructor that accepts an argument, you must call the constructor explicitly using the init(args) method. Error : ''
解决方案
方案1:添加JVM启动参数开放内部API访问
找到CommandBox对应的服务器配置文件(通常路径为~/.CommandBox/server/{你的服务器名称}/server.json),在jvm.args字段中添加以下参数:
--add-exports java.base/sun.util.calendar=ALL-UNNAMED
该参数允许ColdFusion所在的未命名模块访问java.base模块下的sun.util.calendar包。修改完成后重启CommandBox开发服务器即可生效。
方案2:修改组件代码使用公开API(推荐)
sun.util.calendar.ZoneInfo属于Java内部非公开API,依赖它存在兼容性风险。建议修改timezone.cfc的代码,改用Java官方提供的公开getter方法:
<!--- 替换原第87行代码 ---> <cfset defaultTZ = variables.tzObj.getDefault()> <cfset variables.mytz = defaultTZ.getID()>
通过调用getID()方法而非直接访问ID属性,既符合JavaBean规范,也不会触发模块系统的访问限制,兼容性更强。
方案3:降级CommandBox版本(临时 workaround)
如果暂时无法调整代码或JVM参数,可以将CommandBox降级到v5.x版本,保持与旧环境一致的运行时,规避Java模块系统的限制。
问题根源
CommandBox v6默认使用Java 17及以上版本,而Java 9引入的模块系统(JPMS)默认禁止外部代码访问sun.*系列内部API。旧环境使用的CommandBox v5.x基于Java 8,没有模块系统的限制,因此第三方组件的代码可以正常运行。组件中直接访问内部API的属性,在新Java版本中触发了权限校验异常。
内容的提问来源于stack exchange,提问作者Chris Geirman

