Groovy中如何根据StackTrace元素关联到对应的动态生成脚本(含自定义类的脚本场景)
我理解你的场景:你通过字符串动态生成Groovy脚本,当脚本是无外层类的形式时,栈追踪里的随机文件名刚好和groovyClass.getSimpleName() + ".groovy"一致,能直接关联到脚本;但当脚本里定义了自定义类时,栈追踪文件名还是自动生成的随机串,和自定义类名不匹配,导致无法定位到对应的脚本。这个问题我之前处理过,有几个直接有效的解决方法:
1. 直接使用parseClass的重载方法指定源文件名
这是最直接的方案:GroovyClassLoader提供了parseClass(String text, String fileName)重载方法,允许你手动指定脚本的源文件名。你只需要把期望的文件名(比如MyGroovyClass.groovy)作为第二个参数传入,这样栈追踪里的文件名就会和你指定的一致,完美匹配groovyClass.getSimpleName() + ".groovy"。
示例代码:
String myClassScript = """ public class MyGroovyClass { public int execute() { String myString = null; return myString.size(); } } """; CompilerConfiguration config = new CompilerConfiguration(); config.setTargetByteCode(CompilerConfiguration.JDK8); GroovyClassLoader groovyLoader = new GroovyClassLoader(getClassLoader(), config); // 手动指定源文件名,和自定义类名对应 String targetFileName = "MyGroovyClass.groovy"; Class groovyClass = groovyLoader.parseClass(myClassScript, targetFileName);
当脚本抛出NullPointerException时,栈追踪里的文件名就会是MyGroovyClass.groovy,和你自定义的类名完全对应,直接就能定位到脚本。
2. 自定义脚本名称生成器(批量场景)
如果你的系统需要处理大量动态脚本,想要统一控制命名规则,可以通过CompilerConfiguration自定义ScriptNameGenerator,让Groovy按照你的逻辑生成源文件名:
CompilerConfiguration config = new CompilerConfiguration(); config.setScriptNameGenerator(new ScriptNameGenerator() { @Override String generateScriptName() { // 这里可以根据你的业务逻辑返回自定义名称 // 比如从脚本内容中解析出类名,或者用预先定义的标识 // 示例:假设我们提前知道类名是MyGroovyClass return "MyGroovyClass.groovy"; } }); config.setTargetByteCode(CompilerConfiguration.JDK8); GroovyClassLoader groovyLoader = new GroovyClassLoader(getClassLoader(), config); // 解析脚本 String myClassScript = """public class MyGroovyClass { ... }"""; Class groovyClass = groovyLoader.parseClass(myClassScript);
这种方法适合批量处理的场景,能统一管理所有动态脚本的命名规则,但需要注意:如果脚本里包含多个类,需要在生成器里做更复杂的逻辑来匹配对应的类名。
3. 兜底方案:维护脚本映射表
如果因为某些限制无法使用上面两种方法,你可以在编译脚本时维护一个映射表,把Groovy自动生成的随机文件名和你的自定义脚本标识关联起来。你可以通过groovyClass.getProtectionDomain().getCodeSource().getLocation().getFile()获取到Groovy生成的随机文件名,然后将其和你的自定义类名/脚本ID存入映射表。当出现栈追踪时,就可以通过这个映射表反向查找到对应的脚本。
不过这个方法比较繁琐,需要额外的状态维护,只建议作为前两种方法的兜底。
备注:内容来源于stack exchange,提问作者Hervé Girod

