You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java17嵌入Groovy脚本遇java.text包权限异常求助

Java 17升级后Groovy脚本无法访问java.text包类的解决办法

问题背景

我有一个基于Java 8的应用,通过以下方式嵌入Groovy脚本运行:

GroovyClassLoader gcl = new GroovyClassLoader();
Class calcClass = gcl.parseClass(myclass);
GroovyObject calc = (GroovyObject) calcClass.newInstance();
return (String) calc.invokeMethod("evaluateScript", new Object[]{});

该逻辑一直运行正常。

近期将Java版本升级至17后,相同逻辑触发如下异常:

Unable to evaluate smart template script: java.lang.reflect.InaccessibleObjectException: Unable to make java.text.AttributedCharacterIterator java.text.Format.createAttributedCharacterIterator(java.text.AttributedCharacterIterator[]) accessible: module java.base does not "opens java.text" to unnamed module @4ec24700

脚本中使用了java.text包下的SimpleDateFormat,但无法访问该包内的类。

更新:附上脚本中调用的核心代码:

String FRIST(String fromDate, String addDays, String format) {
    if(fromDate==null || "".equals(fromDate))
        return "??.??.????";
    
    java.text.SimpleDateFormat df=new java.text.SimpleDateFormat("dd.MM.yyyy");
    try {
        java.util.Date testDate = df.parse(fromDate)
        java.util.Calendar cal = java.util.Calendar.getInstance(); 
        cal.setTime(testDate);
        if(cal.get(Calendar.YEAR) < 2000)
            cal.add(java.util.Calendar.YEAR, 2000);
        cal.add(java.util.Calendar.DAY_OF_MONTH, Integer.parseInt(addDays));
        testDate = cal.getTime();
        if(format==null) {
            return df.format(testDate);
        } else {
            return new java.text.SimpleDateFormat(format).format(testDate);
        }
    } catch (Throwable t) {
        return "??.??.????"
    }
    
}

解决方案

1. 添加JVM启动参数开放模块访问

Java 9+引入的模块系统默认限制了反射对内部API的访问,你需要在启动应用时添加以下JVM参数,允许未命名模块(Groovy脚本运行在此模块中)访问java.text包的内部成员:

--add-opens java.base/java.text=ALL-UNNAMED

如果后续出现其他包的类似访问问题,可添加对应--add-opens参数扩展权限。

2. 替换为Java 8+推荐的日期API(最优方案)

SimpleDateFormat和Calendar是老旧的日期API,存在线程不安全、设计缺陷等问题。Java 8+推出的java.time包(JSR-310)是模块化设计,在Java 17中可直接访问,无需额外参数,同时功能更完善。修改后的脚本代码如下:

String FRIST(String fromDate, String addDays, String format) {
    if (fromDate == null || fromDate.isEmpty())
        return "??.??.????";
    
    def inputFormatter = java.time.format.DateTimeFormatter.ofPattern("dd.MM.yyyy")
    try {
        LocalDate testDate = LocalDate.parse(fromDate, inputFormatter)
        // 处理年份小于2000的场景
        if (testDate.year < 2000) {
            testDate = testDate.plusYears(2000)
        }
        testDate = testDate.plusDays(Integer.parseInt(addDays))
        
        return format ? testDate.format(java.time.format.DateTimeFormatter.ofPattern(format)) : inputFormatter.format(testDate)
    } catch (Throwable t) {
        return "??.??.????"
    }
}

3. 利用Groovy语法糖简化代码

Groovy对Java日期API有简洁封装,结合java.time可进一步简化代码,同时规避模块访问问题:

String FRIST(String fromDate, String addDays, String format) {
    if (!fromDate) return "??.??.????"
    
    try {
        def testDate = Date.parse("dd.MM.yyyy", fromDate).toLocalDate()
        if (testDate.year < 2000) testDate = testDate.plusYears(2000)
        testDate += addDays.toInteger().days
        
        return format ? testDate.format(format) : testDate.format("dd.MM.yyyy")
    } catch (Throwable t) {
        return "??.??.????"
    }
}

内容的提问来源于stack exchange,提问作者j-dimension

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 17:25:54