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
相关产品推荐
相关产品推荐

