目录 一、概述 一、在Java中使用Groovy: 二、在Groovy中使用Java: 三、几种范式的不同、优缺点 Java调用Groovy的类和方法: Groovy调用Java的类和方法: 使用GroovyShell执行Groovy
目录
使用GroovyClassLoader加载和执行Groovy脚本:
第五步、启动SpringBoot,在Groovy脚本中通过SpringContextUtil获取SpringBoot容器中的Bean
第一步、创建Groovy脚本,使用GroovyClassLoader实现
上两篇我们系统的学习了Groovy的基础语法和GDK,本篇咱们学习如何将Groovy和Java集成,并且学习集成到SpringBoot项目中。Groovy和Java之间有很好的集成,可以相互调用和使用对方的代码和特性。通过Groovy和Java的集成,可以充分发挥两者的优势,让开发更加灵活和高效。
为了实现更好的集成,可以注意以下几点:
在Groovy和Java之间实现集成有多种方式,下面我会描述其中几种常见的方式,以及它们的不同、优缺点。
综上所述,不同的Groovy和Java集成方式具有不同的优缺点。我们可以根据具体需求选择合适的方式。使用Java调用Groovy类和方法以及Groovy调用Java类和方法是最直接、无缝的集成方式。而使用GroovyShell或GroovyClassLoader执行Groovy脚本则更灵活,适用于需要动态执行脚本的场景。
那么接下来介绍springBoot如何集成Groovy脚本,并应用到实际开发中。
org.codehaus.groovy groovy-all 3.0.17 pom
package scriptimport com.example.groovy.GroovyInvokeJavaDemoimport com.example.groovy.groovyshell.ShellGroovyDTOimport com.example.groovy.utils.SpringContextUtildef helloWord() { return "hello groovy"}helloWord()def cal(int a, int b) { ShellGroovyDTO dto = new ShellGroovyDTO() dto.setA(a) dto.setB(b) if (b > 0) { dto.setNum(a + b) } else { dto.setNum(a) } return dto};cal(a, b)class Globals { static String PARAM1 = "静态变量" static int[] arrayList = [1, 2]}def groovyInvokeJavaMethod(int a, int b) { GroovyInvokeJavaDemo groovyInvokeJavaDemo = SprinGContextUtil.getBean("groovyInvokeJavaDemo")// return groovyInvokeJavaDemo.groovyInvokeJava(); return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b);}groovyInvokeJavaMethod(a, b)
* // 创建GroovyShell实例
* // 创建Binding对象,用于传递参数和接收结果
* // 设置参数
* // 执行Groovy脚本
* // 获取结果
package com.example.groovy.groovyshell;import groovy.lang.Binding;import groovy.lang.GroovyShell;import groovy.lang.Script;public class GroovyShellApp { public static void main(String[] args) { String groovyStr = "package script\n" + "\n" + "import com.example.groovy.groovyshell.ShellGroovyDTO\n" + "\n" + "\n" + "\n" + "def helloWord() {\n" + " return \"hello groovy\"\n" + "}\n" + "\n" + "helloWord()\n" + "\n" + "def cal(int a, int b) {\n" + " ShellGroovyDTO dto = new ShellGroovyDTO()\n" + " dto.setA(a)\n" + " dto.setB(b)\n" + " if (b > 0) {\n" + " dto.setNum(a + b)\n" + " } else {\n" + " dto.setNum(a)\n" + " }\n" + " return dto\n" + "};\n" + "\n" + "cal(a , b)"; // 创建GroovyShell实例 GroovyShell shell = new GroovyShell(); Script script = shell.parse(groovyStr); Object helloWord = script.invokeMethod("helloWord", null); System.out.println(helloWord); } // public static void main(String[] args) {//// String groovyStr = "package script\n" +// "\n" +// "import com.example.groovy.groovyshell.ShellGroovyDTO\n" +// "\n" +// "\n" +// "def cal(int a, int b) {\n" +// " ShellGroovyDTO dto = new ShellGroovyDTO()\n" +// " dto.setA(a)\n" +// " dto.setB(b)\n" +// " if (b > 0) {\n" +// " dto.setNum(a + b)\n" +// " } else {\n" +// " dto.setNum(a)\n" +// " }\n" +// " return dto\n" +// "};\n" +// "\n" +// "cal(a, b)";//// // 创建Binding对象,用于传递参数和接收结果// Binding binding = new Binding();//// // 创建GroovyShell实例// GroovyShell shell = new GroovyShell(binding);////// // 设置参数// binding.setVariable("a", 10);// binding.setVariable("b", 20);//// // 执行Groovy脚本// Object result = shell.evaluate(groovyStr);//// // 获取结果// ShellGroovyDTO dto = (ShellGroovyDTO) result;// System.out.println(dto);// }}package com.example.groovy.groovyshell;import lombok.AllArgsConstructor;import lombok.Builder;import lombok.Data;import lombok.NoArgsConstructor;@Data@AllArgsConstructor@NoArgsConstructor@Builderpublic class ShellGroovyDTO { private Integer a; private Integer b; private Integer num;}
上面几步都是纯Java代码中调用Groovy,其实在开发的过程中,我们通常会Groovy和Java代码互调,接下来咱们看看如何使实现Groovy中通过SpringContextUtil获取SpringBoot容器中的Bean并且调用目标方法。
package com.example.groovy.utils;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Service;@Servicepublic class SpringContextUtil implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringContextUtil.applicationContext = applicationContext; } public static ApplicationContext getApplicationContext() { return applicationContext; } public static Object getBean(String name) { return getApplicationContext().getBean(name); } public static T getBean(Class clazz) { return getApplicationContext().getBean(clazz); } public static T getBean(String name, Class clazz) { return getApplicationContext().getBean(name, clazz); }}
咱们创建一个“GroovyInvokeJavaDemo“bean,并交给Spring管理。其中有两个目标方法,一个是需要参数的,一个不需要参数,需要参数的咱们通过Java调用Groovy的时候传入的参数在Groovy中调用Java方法的时候传入。
package com.example.groovy;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Service@Slf4jpublic class GroovyInvokeJavaDemo { public String groovyInvokeJava() { List lits = new ArrayList<>(); log.info("this is SpringBoot class, groovy script invoke this method ..."); return "this is SpringBoot class, groovy script invoke this method ..."; } public String groovyInvokeJavaParam(int a, int b) { List lits = new ArrayList<>(); log.info("this is SpringBoot class, groovy script invoke this method ,param is a:{}, b:{}", a, b); return "this is SpringBoot class, groovy script invoke this method , a:" + a + ", b:" + b; }}
package com.example.groovy;import com.example.groovy.classloader.GroovyClassLoaderRule;import groovy.lang.Binding;import groovy.lang.GroovyShell;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;@RestController@RequestMapping("/groovy")public class GroovyInvokeJavaSpringController { @Resource private GroovyClassLoaderRule groovyClassLoaderRule; @RequestMapping("/groovy-shell/spring") public String groovyInvokeJavaMethodTest() { String groovyStr = "package script\n" + "\n" + "import com.example.groovy.GroovyInvokeJavaDemo\n" + "import com.example.groovy.groovyshell.ShellGroovyDTO\n" + "import com.example.groovy.utils.SpringContextUtil\n" + "\n" + "\n" + "\n" + "def helloWord() {\n" + " return \"hello groovy\"\n" + "}\n" + "\n" + "helloWord()\n" + "\n" + "def cal(int a, int b) {\n" + " ShellGroovyDTO dto = new ShellGroovyDTO()\n" + " dto.setA(a)\n" + " dto.setB(b)\n" + " if (b > 0) {\n" + " dto.setNum(a + b)\n" + " } else {\n" + " dto.setNum(a)\n" + " }\n" + " return dto\n" + "};\n" + "\n" + "cal(a , b)\n" + "\n" + "\n" + "class Globals {\n" + " static String PARAM1 = \"静态变量\"\n" + " static int[] arrayList = [1, 2]\n" + "}\n" + "\n" + "def groovyInvokeJavaMethod(int a, int b) {\n" + " GroovyInvokeJavaDemo groovyInvokeJavaDemo = SpringContextUtil.getBean(\"groovyInvokeJavaDemo\")\n" + "// return groovyInvokeJavaDemo.groovyInvokeJava();\n" + " return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b);\n" + "}\n" + "\n" + "groovyInvokeJavaMethod(a, b)"; Binding binding = new Binding(); binding.setVariable("a", 100); binding.setVariable("b", 100); GroovyShell groovyShell = new GroovyShell(binding); Object evaluate = groovyShell.evaluate(groovyStr); groovyShell.getClassLoader().clearCache(); return (String) evaluate; }}
访问“http://localhost:8080/groovy/groovy-shell/spring”即可看到效果。
以上我们就将Groovy集成到Java中来了,但是上面的代码有很大的问题,主要体现在两个方面:
第一个方面:通过第五步中我们可以看到,在Groovy中是可以获取到SpringBoot容器对象的,能拿到容器对象就可以获取到容器中所有的东西。这虽然很方便、灵活,但是非常的危险。如果没有做好权限控制,Groovy脚本将会成为攻击你系统最有力的武器!
第二个方面:Groovy脚本用不好,会导致OOM,最终服务器宕机。每次调用这个方法都创建了GroovyShell、Script等实例,随着调用次数的增加,必然会出现OOM。
解决方案是通过GroovyClassLoader的clearCache()函数在调用完毕销毁GroovyShell、Script等实例,但是其实这样仅仅是不够的,导致OOM的原因并不止GroovyShell、Script等实例过多,经过查阅资料得知,如果脚本中的Java代码也创建了对象或者new了实例,即使销毁了GroovyShell也不会销毁脚本中的对象。所以Groovy代码最好是使用缓存管理。
GroovyClassLoad_1.groovy
package scriptimport com.example.groovy.GroovyInvokeJavaDemoimport com.example.groovy.groovyshell.ShellGroovyDTOimport com.example.groovy.utils.SpringContextUtildef groovyInvokeJavaMethod(int a, int b) { GroovyInvokeJavaDemo groovyInvokeJavaDemo = SpringContextUtil.getBean("groovyInvokeJavaDemo")// return groovyInvokeJavaDemo.groovyInvokeJava(); return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b)}groovyInvokeJavaMethod(a, b)
GroovyClassLoad_2.groovy
package scriptimport com.example.groovy.GroovyInvokeJavaDemoimport com.example.groovy.groovyshell.ShellGroovyDTOimport com.example.groovy.utils.SpringContextUtildef groovyInvokeJavaMethod(int a, int b) { GroovyInvokeJavaDemo groovyInvokeJavaDemo = SpringContextUtil.getBean("groovyInvokeJavaDemo")// return groovyInvokeJavaDemo.groovyInvokeJava(); return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b)}groovyInvokeJavaMethod(a, b)
package com.example.groovy;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.util.ArrayList;import java.util.List;@Service@Slf4jpublic class GroovyInvokeJavaDemo { public String groovyInvokeJava() { List lits = new ArrayList<>(); log.info("this is SpringBoot class, groovy script invoke this method ..."); return "this is SpringBoot class, groovy script invoke this method ..."; } public String groovyInvokeJavaParam(int a, int b) { List lits = new ArrayList<>(); log.info("this is SpringBoot class, groovy script invoke this method ,param is a:{}, b:{}", a, b); return "this is SpringBoot class, groovy script invoke this method , a:" + a + ", b:" + b; }}
package com.example.groovy.classloader;public interface GroovyClassLoaderRule { String run();}
package com.example.groovy.classloader;import groovy.lang.Binding;import groovy.lang.GroovyClassLoader;import groovy.lang.GroovyObject;import groovy.lang.Script;import lombok.extern.slf4j.Slf4j;import org.apache.groovy.parser.antlr4.util.StringUtils;import org.codehaus.groovy.runtime.InvokerHelper;import org.springframework.stereotype.Service;import javax.annotation.Resource;import java.util.HashMap;import java.util.Map;@Slf4j@Servicepublic class GroovyClassLoaderRuleImpl implements GroovyClassLoaderRule { private static final Map SCRIPT_MAP = new HashMap<>(); private static final GroovyClassLoader CLASS_LOADER = new GroovyClassLoader(); public static GroovyObject loadScript(String key, String rule) { if (SCRIPT_MAP.containsKey(key)) { return SCRIPT_MAP.get(key); } GroovyObject groovyObject = loadScript(rule); SCRIPT_MAP.put(key, groovyObject); return groovyObject; } public static GroovyObject loadScript(String rule) { if (StringUtils.isEmpty(rule)) { return null; } try { Class ruleClazz = CLASS_LOADER.parseClass(rule); if (ruleClazz != null) { log.info("load rule:" + rule + " success!"); GroovyObject groovyObject = (GroovyObject) ruleClazz.newInstance(); return groovyObject; } } catch (Exception e) { log.error(e.getMessage(), e); } finally { CLASS_LOADER.clearCache(); } log.error("load rule error, can not load Script"); return null; } @Override public String run() { // 业务逻辑执行,方便配置 String groovyClassLoader1 = "package script\n" + "\n" + "import com.example.groovy.GroovyInvokeJavaDemo\n" + "import com.example.groovy.groovyshell.ShellGroovyDTO\n" + "import com.example.groovy.utils.SpringContextUtil\n" + "\n" + "\n" + "\n" + "def groovyInvokeJavaMethod(int a, int b) {\n" + " GroovyInvokeJavaDemo groovyInvokeJavaDemo = SpringContextUtil.getBean(\"groovyInvokeJavaDemo\")\n" + "// return groovyInvokeJavaDemo.groovyInvokeJava();\n" + "\n" + " return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b)\n" + "}\n" + "\n" + "groovyInvokeJavaMethod(a, b)"; String groovyClassLoader2 = "package script\n" + "\n" + "import com.example.groovy.GroovyInvokeJavaDemo\n" + "import com.example.groovy.groovyshell.ShellGroovyDTO\n" + "import com.example.groovy.utils.SpringContextUtil\n" + "\n" + "\n" + "\n" + "def groovyInvokeJavaMethod(int a, int b) {\n" + " GroovyInvokeJavaDemo groovyInvokeJavaDemo = SpringContextUtil.getBean(\"groovyInvokeJavaDemo\")\n" + "// return groovyInvokeJavaDemo.groovyInvokeJava();\n" + "\n" + " return groovyInvokeJavaDemo.groovyInvokeJavaParam(a, b)\n" + "}\n" + "\n" + "groovyInvokeJavaMethod(a, b)"; Binding binding = new Binding(); binding.setVariable("a", 300); binding.setVariable("b", 400);// Script classLoader1 = loadScript("groovyClassLoader1", groovyClassLoader1, binding); GroovyObject groovyObject = loadScript("groovyClassLoader2", groovyClassLoader2); Object groovyInvokeJavaMethod = groovyObject.invokeMethod("groovyInvokeJavaMethod", new Object[]{100, 200}); return (String) groovyInvokeJavaMethod; }}
@RequestMapping("/groovy-class-loader/spring")public String groovyClassLoaderRuleTest() { String result = groovyClassLoaderRule.run(); return result;}
第五步、启动验证
至此,咱们的Groovy系列就结束啦,代码大家需要的话可以访问我的GitHub网站获取或者留言,我私信发给大家,希望可以帮助到大家。
https://github.com/576403061lly/groovy
来源地址:https://blog.csdn.net/lly576403061/article/details/131505699
--结束END--
本文标题: Groovy系列三 Java SpringBoot 整合 Groovy
本文链接: https://lsjlt.com/news/422425.html(转载时请注明来源链接)
有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341
2024-04-01
2024-04-03
2024-04-03
2024-01-21
2024-01-21
2024-01-21
2024-01-21
2023-12-23
回答
回答
回答
回答
回答
回答
回答
回答
回答
回答
0