文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Groovy系列三 Java SpringBoot 整合 Groovy

2023-10-01 20:36

关注

 

目录

一、概述

一、在Java中使用Groovy:

二、在Groovy中使用Java:

三、几种范式的不同、优缺点

Java调用Groovy的类和方法:

Groovy调用Java的类和方法:

 使用GroovyShell执行Groovy脚本:

使用GroovyClassLoader加载和执行Groovy脚本:

二、实战

第一步、与SpringBoot集成,引入依赖

第二步、编写groovy脚本

第三步、创建测试类使用GroovyShell演示

第四步、查看运行结果

第五步、启动SpringBoot,在Groovy脚本中通过SpringContextUtil获取SpringBoot容器中的Bean

第六步、创建容器中的Bean

第七步、访问代码

第八步、启动、接口测试

三、优化

第一步、创建Groovy脚本,使用GroovyClassLoader实现

第二步、创建Groovy调用Bean

第三步、创建GroovyClassLoader加载类

第四步、创建请求API


上两篇我们系统的学习了Groovy的基础语法和GDK,本篇咱们学习如何将Groovy和Java集成,并且学习集成到SpringBoot项目中。Groovy和Java之间有很好的集成,可以相互调用和使用对方的代码和特性。通过Groovy和Java的集成,可以充分发挥两者的优势,让开发更加灵活和高效。

一、概述

一、在Java中使用Groovy:

  1. 添加Groovy的依赖到Java项目中。
  2. 在Java代码中使用Groovy的类和脚本。Groovy代码可以直接在Java中执行,可以调用Groovy类的方法、访问其属性等。可以使用GroovyShell或GroovyClassLoader来执行Groovy脚本。

二、在Groovy中使用Java:

  1. Groovy天生支持Java,可以直接使用Java类、调用Java方法等。Groovy代码可以与Java代码混合编写。
  2. 在Groovy代码中使用Java类时,不需要额外的导入,直接使用即可。
  3. Groovy还提供了更简洁的语法和更强大的特性,例如闭包、扩展方法、动态类型等,可以更方便地编写代码。

为了实现更好的集成,可以注意以下几点:

三、几种范式的不同、优缺点

在Groovy和Java之间实现集成有多种方式,下面我会描述其中几种常见的方式,以及它们的不同、优缺点。

Java调用Groovy的类和方法:

Groovy调用Java的类和方法:

 使用GroovyShell执行Groovy脚本:

使用GroovyClassLoader加载和执行Groovy脚本:

综上所述,不同的Groovy和Java集成方式具有不同的优缺点。我们可以根据具体需求选择合适的方式。使用Java调用Groovy类和方法以及Groovy调用Java类和方法是最直接、无缝的集成方式。而使用GroovyShell或GroovyClassLoader执行Groovy脚本则更灵活,适用于需要动态执行脚本的场景。

二、实战

那么接下来介绍SpringBoot如何集成Groovy脚本,并应用到实际开发中。

第一步、与SpringBoot集成,引入依赖

   org.codehaus.groovy   groovy-all   3.0.17   pom

第二步、编写groovy脚本

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演示

* // 创建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;}

第四步、查看运行结果

第五步、启动SpringBoot,在Groovy脚本中通过SpringContextUtil获取SpringBoot容器中的Bean

上面几步都是纯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);    }}

第六步、创建容器中的Bean

咱们创建一个“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代码最好是使用缓存管理。

第一步、创建Groovy脚本,使用GroovyClassLoader实现

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)

第二步、创建Groovy调用Bean

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;    }}

第三步、创建GroovyClassLoader加载类

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;    }}

第四步、创建请求API

@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

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯