精华内容
下载资源
问答
  • Groovy解释器以连续传递样式运行Groovy代码,因此执行可以随时暂停并重新启动,而无需与正在解释的程序合作。 有关主要用例,请参见的。 以及我们如何解释Groovy程序
  • springboot集成groovy动态执行代码 springboot版本号:2.2.2.RELEASE 在pom.xml中集成groovy <dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</...

    springboot集成groovy动态执行代码

    springboot版本号:2.2.2.RELEASE

    在pom.xml中集成groovy

    <dependency>
       <groupId>org.codehaus.groovy</groupId>
       <artifactId>groovy-all</artifactId>
       <version>1.8.9</version>
       <scope>compile</scope>
    </dependency>

    一.springboot中执行groovy文件

    main文件直接调用groovy文件,有三种方式

    方式一生成Test.groovy:

    package com.example.demo.groovy
    
    public class Test {
    
        public static String test(String id) {
            return "younger--->" + id;
        }
    }
    

    方式二生成test1.groovy

    package com.example.demo.groovy.groovyscript
    
    def test(id){
        return "test2 id:"+ id;
    }
    

    方法三生成test2.groovy

    package com.example.demo.groovy.groovyscript
    
    output = "test3 id: ${id}, name: ${name}"
    

    通过Java的main方法调用groovy文件,得到执行结束

    package com.example.demo;
    
    import com.example.demo.controller.CalculateController;
    import groovy.lang.Binding;
    import groovy.lang.GroovyClassLoader;
    import groovy.lang.GroovyObject;
    import groovy.lang.Script;
    import groovy.util.GroovyScriptEngine;
    import groovy.util.ResourceException;
    import groovy.util.ScriptException;
    import java.io.File;
    import java.io.IOException;
    
    /**
     * @program: demo
     * @description: 本地main方法执行groovy文件
     * @author: younger
     * @create: 2021-05-07 15:42
     **/
    public class GroovyTest {
    
        public static void main(String[] args) throws IOException, IllegalAccessException, InstantiationException, ResourceException, ScriptException {
            //方式一调用groovy文件
            ClassLoader parent = CalculateController.class.getClassLoader();
            GroovyClassLoader loader = new GroovyClassLoader(parent);
            Class groovyClass = loader.parseClass(new File("src/main/java/com/example/demo/groovy/Test.groovy"));
            //得到groovy对象
            GroovyObject groovyObject= (GroovyObject)groovyClass.newInstance();
            //执行对象的test方法,并传参数-"id---1"
            String result = (String) groovyObject.invokeMethod("test", "id---1");
            System.out.println("result--------->" + result);
    
            //方式二调用groovy文件,找到groovy脚本所在文件夹
            GroovyScriptEngine engine = new GroovyScriptEngine("src/main/java/com/example/demo/groovy/groovyscript");
            //得到Script对象
            Script script = engine.createScript("test1.groovy", new Binding());
            //执行Script对象的test方法,并传参数-"id---1"
            result = (String) script.invokeMethod("test", "id----2");
            System.out.println("result--------->" + result);
    
            //方式三调用groovy文件
    //        GroovyScriptEngine engine = new GroovyScriptEngine("src/main/java/com/example/demo/groovy/groovyscript");
            Binding binding = new Binding();
            //封装参数
            binding.setVariable("id","id---3");
            binding.setVariable("name", "younger");
            //执行test2.groovy脚本
            engine.run("test2.groovy", binding);
            //返回output
            result = binding.getVariable("output").toString();
            System.out.println("result--------->" + result);
        }
    }
    

    二,通过数据库保存groovy内容,动态执行groovy脚本

    数据库mysql,创建groovy规则表:calculate_rule

    CREATE TABLE `calculate_rule` (
      `id` bigint(20) NOT NULL AUTO_INCREMENT,
      `interface_id` varchar(128) NOT NULL COMMENT '接口id',
      `bean_name` varchar(64) NOT NULL COMMENT 'bean_name',
      `calculate_rule` text NOT NULL COMMENT 'groovy脚本内容',
      `calculate_type` varchar(64) NOT NULL COMMENT '状态',
      `status` varchar(16) NOT NULL DEFAULT 'ENABLE' COMMENT 'ENABLE-启用/DISENABLE-停用',
      `extend_info` varchar(4096) DEFAULT NULL,
      `created_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
      `modified_time` timestamp(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COMMENT='calculate rule';
    
    insert into `calculate_rule` (`id`, `interface_id`, `bean_name`, `calculate_rule`, `calculate_type`, `status`, `extend_info`, `created_time`, `modified_time`) values('1','B.integration.A.calculate.reward','rewardCalculateParser','package com.example.demo.groovy.calculate.impl;\nimport com.example.demo.entity.request.CalculateRequest;\nimport com.example.demo.entity.response.CalculateResponse;\nimport com.example.demo.groovy.calculate.CalculateParser\nimport com.example.demo.service.CalculateInterestRuleService;\nimport org.apache.commons.lang3.StringUtils\nimport org.springframework.beans.factory.annotation.Autowired\nimport org.springframework.stereotype.Service;\nimport java.math.RoundingMode;\n/**\n * 计算推广奖金\n */\npublic class RewardCalculateParser implements CalculateParser {\n    @Autowired\n    private CalculateInterestRuleService calculateInterestRuleService;\n    @Override\n    public CalculateResponse parse(CalculateRequest request) {\n		String result = calculateInterestRuleService.test(\"younger\");\n        System.out.println(\"calculateInterestRuleService.test-------->\" + result);\n        Map<String, Object> extendInfo = request.getExtendInfo();\n        String interfaceId = request.getInterfaceId();\n        BigDecimal totalAmount = BigDecimal.ZERO;\n        if (StringUtils.isNotBlank((String) extendInfo.get(\"totalAmount\"))) {\n            totalAmount = new BigDecimal((String) extendInfo.get(\"totalAmount\"));\n        }\n        int refererNumber = 0;\n        if (StringUtils.isNotBlank((String) extendInfo.get(\"refererNumber\"))) {\n            refererNumber = Integer.parseInt((String) extendInfo.get(\"refererNumber\"));\n        }\n        System.out.println(\"进入奖金计算逻辑,总金额为:\" + totalAmount + \",邀请人数为:\" + refererNumber);\n        \n        BigDecimal reward = totalAmount.multiply(new BigDecimal(String.valueOf(refererNumber)))\n                .divide(new BigDecimal(\"100\")).divide(new BigDecimal(\"365\"),4, RoundingMode.HALF_DOWN);\n        CalculateResponse response = new CalculateResponse();\n        response.setInterfaceId(interfaceId);\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"reward\", reward);\n        response.setExtendInfo(map);\n        System.out.println(\"退出奖金计算逻辑,总奖金为:\" + reward);\n        return response;\n    }\n}\n','reward','ENABLE',NULL,'2020-07-06 09:27:58.279144','2021-05-07 17:17:19.771010');
    insert into `calculate_rule` (`id`, `interface_id`, `bean_name`, `calculate_rule`, `calculate_type`, `status`, `extend_info`, `created_time`, `modified_time`) values('2','B.integration.A.calculate.sum','sumCalculateParser','package com.example.demo.groovy.calculate.impl;\n\nimport com.example.demo.entity.request.CalculateRequest;\nimport com.example.demo.entity.response.CalculateResponse;\nimport com.example.demo.groovy.calculate.CalculateParser;\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * 计算推广奖金\n */\npublic class SumCalculateParser implements CalculateParser {\n\n    @Override\n    public CalculateResponse parse(CalculateRequest request) {\n\n        Map<String, Object> extendInfo = request.getExtendInfo();\n\n        String interfaceId = request.getInterfaceId();\n\n        BigDecimal totalAmount = BigDecimal.ZERO;\n        if (StringUtils.isNotBlank((String) extendInfo.get(\"totalAmount\"))) {\n            totalAmount = new BigDecimal((String) extendInfo.get(\"totalAmount\"));\n        }\n\n        int refererNumber = 0;\n        if (StringUtils.isNotBlank((String) extendInfo.get(\"refererNumber\"))) {\n            refererNumber = Integer.parseInt((String) extendInfo.get(\"refererNumber\"));\n        }\n\n\n        System.out.println(\"进入奖金计算逻辑,总金额为:\" + totalAmount + \",邀请人数为:\" + refererNumber);\n        \n        BigDecimal sum = totalAmount.multiply(new BigDecimal(refererNumber));\n        CalculateResponse response = new CalculateResponse();\n\n        response.setInterfaceId(interfaceId);\n        Map<String, Object> map = new HashMap<>();\n        map.put(\"sum\", sum);\n\n        response.setExtendInfo(map);\n\n        System.out.println(\"退出奖金计算逻辑,总奖金为:\" + sum);\n        return response;\n    }\n}','reward','ENABLE',NULL,'2020-07-06 09:27:58.279144','2020-07-06 13:23:51.311882');
    

    原理:把groovy脚本放在表calculate_rule中的calculate_rule,spring-boot启动加载calculate_rule中启用的数据,动态成生spring.xml文件把groovy脚本加载至spring容器中。

    1.创建GroovyDynamicConfiguration类,在spring-boot启动时加载groovy脚本至spring容器中:

    注:请看重点方法

    package com.example.demo.groovy.core;
    
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.example.demo.entity.CalculateRuleDO;
    import com.example.demo.groovy.cache.BeanName;
    import com.example.demo.groovy.cache.BeanNameCache;
    import com.example.demo.groovy.cache.GroovyInfo;
    import com.example.demo.groovy.cache.GroovyInnerCache;
    import com.example.demo.service.CalculateInterestRuleService;
    import groovy.lang.GroovyClassLoader;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.beans.factory.config.BeanPostProcessor;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.beans.factory.xml.ResourceEntityResolver;
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.context.ConfigurableApplicationContext;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.util.Assert;
    import org.springframework.util.CollectionUtils;
    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * @program: demo
     * @description: 动态加载groovy脚本至spring容器中
     * @author: younger
     * @create: 2021-05-07 15:42
     **/
    @Configuration
    public class GroovyDynamicConfiguration implements ApplicationContextAware, InitializingBean {
    
        private ConfigurableApplicationContext applicationContext;
    
        private static final GroovyClassLoader groovyClassLoader = new GroovyClassLoader(GroovyDynamicConfiguration.class.getClassLoader());
    
        @Resource
        private CalculateInterestRuleService calculateInterestRuleService;
    
        @Override
        public void afterPropertiesSet() throws Exception {
            long start = System.currentTimeMillis();
            System.out.println("开始从数据库解析groovy脚本...");
            init();
            long cost = System.currentTimeMillis() - start;
            System.out.println("结束从数据库解析groovy脚本...,耗时:" + cost);
        }
    
        /**
         * 启动spring-boot就加载数据库中groovy脚本至spring容器管理
         */
        private void init() {
            //从mysql中获取groovy脚本规则
            List<CalculateRuleDO> calculateRuleDOS = calculateInterestRuleService.list(new QueryWrapper<CalculateRuleDO>().eq("status", "ENABLE"));
            List<BeanName> beanNameList = new ArrayList<>();
            List<GroovyInfo> groovyInfos = convert(calculateRuleDOS, beanNameList);
            init(groovyInfos, beanNameList);
        }
    
        /**
         * 重点方法
         */
        private void init(List<GroovyInfo> groovyInfos, List<BeanName> beanNameList) {
            if (CollectionUtils.isEmpty(groovyInfos)) {
                return;
            }
            ConfigurationXMLWriter config = new ConfigurationXMLWriter();
            //生成配置文件内容
            addConfiguration(config, groovyInfos);
            //把groovy规则加载至内存
            put2map(groovyInfos, beanNameList);
            //加载至spring容器中
            loadBeanDefinitions(config);
        }
    
    
        /**
         * 重新从mysql加载groovy脚本
         */
        public void refresh() {
            List<CalculateRuleDO> calculateRuleDOS = calculateInterestRuleService.list(new QueryWrapper<CalculateRuleDO>().eq("status", "ENABLE"));
            List<BeanName> beanNameList = new ArrayList<>();
            List<GroovyInfo> groovyInfos = convert(calculateRuleDOS, beanNameList);
            if (CollectionUtils.isEmpty(groovyInfos)) {
                return;
            }
    
            // loadBeanDefinitions 之后才会生效
            destroyBeanDefinition(groovyInfos);
            destroyScriptBeanFactory();
            ConfigurationXMLWriter config = new ConfigurationXMLWriter();
            addConfiguration(config, groovyInfos);
            put2map(groovyInfos, beanNameList);
            loadBeanDefinitions(config);
        }
    
        private List<GroovyInfo> convert(List<CalculateRuleDO> calculateRuleDOS, List<BeanName> beanNameList) {
            List<GroovyInfo> groovyInfos = new LinkedList<>();
    
            if (CollectionUtils.isEmpty(calculateRuleDOS)) {
                return groovyInfos;
            }
    
            for (CalculateRuleDO calculateRuleDO : calculateRuleDOS) {
                GroovyInfo groovyInfo = new GroovyInfo();
                groovyInfo.setClassName(calculateRuleDO.getBeanName());
                groovyInfo.setGroovyContent(calculateRuleDO.getCalculateRule());
                groovyInfos.add(groovyInfo);
    
                BeanName beanName = new BeanName();
                beanName.setInterfaceId(calculateRuleDO.getInterfaceId());
                beanName.setBeanName(calculateRuleDO.getBeanName());
                beanNameList.add(beanName);
            }
    
            return groovyInfos;
        }
    
    
        private void addConfiguration(ConfigurationXMLWriter config, List<GroovyInfo> groovyInfos) {
            for (GroovyInfo groovyInfo : groovyInfos) {
                writeBean(config, groovyInfo);
            }
        }
    
        private void loadBeanDefinitions(ConfigurationXMLWriter config) {
            /**
             * contextString=
             * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
             * <beans xmlns="http://www.springframework.org/schema/beans"
             * xmlns:lang="http://www.springframework.org/schema/lang"
             * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             * default-autowire="byName"
             * xsi:schemaLocation="http://www.springframework.org/schema/beans
             * http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
             * http://www.springframework.org/schema/lang
             * http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
             * <lang:groovy id="rewardCalculateParser" script-source="database:rewardCalculateParser"/>
             * <lang:groovy id="sumCalculateParser" script-source="database:sumCalculateParser"/>
             * </beans>
             * 生成加载至spring容器的xml,为了把groovy对应的对象交给spring容器来管理。
             */
            String contextString = config.getContent();
            if(StringUtils.isBlank(contextString)) {
                return ;
            }
    
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry) this.applicationContext.getBeanFactory());
            beanDefinitionReader.setResourceLoader(this.applicationContext);
            beanDefinitionReader.setBeanClassLoader(applicationContext.getClassLoader());
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this.applicationContext));
            beanDefinitionReader.loadBeanDefinitions(new InMemoryResource(contextString));
    
            String[] postProcessorNames = applicationContext.getBeanFactory().getBeanNamesForType(CustomScriptFactoryPostProcessor.class, true, false);
            for (String postProcessorName : postProcessorNames) {
                applicationContext.getBeanFactory().addBeanPostProcessor((BeanPostProcessor) applicationContext.getBean(postProcessorName));
            }
        }
    
        private void destroyBeanDefinition(List<GroovyInfo> groovyInfos) {
            DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
            for (GroovyInfo groovyInfo : groovyInfos) {
                try {
                    beanFactory.removeBeanDefinition(groovyInfo.getClassName());
                } catch (Exception e) {
                    System.out.println("【Groovy】delete groovy bean definition exception. skip:" + groovyInfo.getClassName());
                }
            }
        }
    
        private void destroyScriptBeanFactory() {
            String[] postProcessorNames = applicationContext.getBeanFactory().getBeanNamesForType(CustomScriptFactoryPostProcessor.class, true, false);
            for (String postProcessorName : postProcessorNames) {
                CustomScriptFactoryPostProcessor processor = (CustomScriptFactoryPostProcessor) applicationContext.getBean(postProcessorName);
                processor.destroy();
            }
        }
    
        private void writeBean(ConfigurationXMLWriter config, GroovyInfo groovyInfo) {
            if (checkSyntax(groovyInfo)) {
                DynamicBean bean = composeDynamicBean(groovyInfo);
                config.write(GroovyConstant.SPRING_TAG, bean);
            }
        }
    
        private boolean checkSyntax(GroovyInfo groovyInfo) {
            try {
                groovyClassLoader.parseClass(groovyInfo.getGroovyContent());
            } catch (Exception e) {
                return false;
            }
            return true;
        }
    
        private DynamicBean composeDynamicBean(GroovyInfo groovyInfo) {
            DynamicBean bean = new DynamicBean();
            String scriptName = groovyInfo.getClassName();
            Assert.notNull(scriptName, "parser className cannot be empty!");
    
            //设置bean的属性,这里只有id和script-source。
            bean.put("id", scriptName);
            bean.put("script-source", GroovyConstant.SCRIPT_SOURCE_PREFIX + scriptName);
            return bean;
        }
    
        private void put2map(List<GroovyInfo> groovyInfos, List<BeanName> beanNameList) {
            GroovyInnerCache.put2map(groovyInfos);
            BeanNameCache.put2map(beanNameList);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = (ConfigurableApplicationContext) applicationContext;
        }
    }
    

    2.调用Controller测试groovy动态脚本

    package com.example.demo.controller;
    
    import java.util.Map;
    import java.util.HashMap;
    import javax.annotation.Resource;
    import com.example.demo.entity.request.CalculateRequest;
    import com.example.demo.entity.response.CalculateResponse;
    import com.example.demo.groovy.calculate.CalculateParser;
    import com.example.demo.groovy.calculate.GroovyParserEngine;
    import com.example.demo.groovy.core.GroovyDynamicConfiguration;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class CalculateController {
    
        @Resource
        private GroovyParserEngine groovyParserEngine;
    
        @Resource
        private GroovyDynamicConfiguration groovyDynamicLoader;
    
        @Resource
        private CalculateParser rewardCalculateParserGroovy;
    
        @RequestMapping("/calculate")
        public Map<String, Object> calculate() {
            String interfaceId = "B.integration.A.calculate.reward";
            Map<String, Object> map = new HashMap<>();
            map.put("totalAmount", "10");
            map.put("refererNumber", "5");
    
            CalculateRequest request = new CalculateRequest();
            request.setInterfaceId(interfaceId);
            request.setExtendInfo(map);
    
            CalculateResponse response = groovyParserEngine.parse(request);
            return response.getExtendInfo();
        }
    
        @RequestMapping("/refresh")
        public void refresh() {
            groovyDynamicLoader.refresh();
        }
    }
    
    package com.example.demo.groovy.calculate;
    
    import com.example.demo.entity.request.CalculateRequest;
    import com.example.demo.entity.response.CalculateResponse;
    
    public interface GroovyParserEngine {
    
        CalculateResponse parse(CalculateRequest request);
    }
    
    package com.example.demo.groovy.calculate.impl;
    
    import com.example.demo.groovy.cache.BeanNameCache;
    import com.example.demo.groovy.calculate.CalculateParser;
    import com.example.demo.groovy.calculate.GroovyParserEngine;
    import com.example.demo.entity.request.CalculateRequest;
    import com.example.demo.entity.response.CalculateResponse;
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    import org.springframework.stereotype.Service;
    
    @Service
    public class GroovyParserEngineImpl implements GroovyParserEngine, ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        @Override
        public CalculateResponse parse(CalculateRequest request) {
            String beanName = BeanNameCache.getByInterfaceId(request.getInterfaceId());
            CalculateParser parser = (CalculateParser) applicationContext.getBean(beanName);
            return parser.parse(request);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    启动项目,调用http://localhost:8888/calculate,测试结果。
    代码下载地址:https://download.csdn.net/download/yangxiang_Younger/18445827

    展开全文
  • groovy执行sql简单示例

    千次阅读 2018-11-07 19:49:17
    这里主要展示groovy为了代码的执行方便,竟然将依赖在脚本中指定。 简直是太方便了。 import groovy.sql.Sql //可以指定maven仓库 @GrabResolver(name = 'aliyun', root = '...

    这里主要展示groovy为了代码的执行方便,竟然将依赖在脚本中指定。
    简直是太方便了。

    import groovy.sql.Sql
    
    //可以指定maven仓库
    @GrabResolver(name = 'aliyun', root = 'http://maven.aliyun.com/nexus/content/groups/public/')
    //jdbc驱动类需要用Class.forName加载
    @GrabConfig(systemClassLoader = true)
    @Grab('mysql:mysql-connector-java:5.1.46')
    class SqlDatabase {
        static Sql setUpDatabase() {
            def url = 'jdbc:mysql://localhost:3306/xxx?useUnicode=true&characterEncoding=UTF-8'
            def user = 'root'
            def password = ''
            def driver = 'com.mysql.jdbc.Driver'
            def sql = Sql.newInstance(url, user, password, driver)
            return sql
        }
    }
    
    def sql = SqlDatabase.setUpDatabase()
    sql.eachRow('SELECT * FROM t_customer limit 0,3') { row ->
      def id = row[0]
      def name = row.cust_name
      println("${id},${name}")
    }
    
    println("""
    '\${a}'
    ${new Date().getYear()}
    """)
    println([1,2,3].collect({it*2}))
    
    展开全文
  • 前言: 1. 不阐述任何业务场景和作用,只提供食用说明书 2. 执行脚本会提升对系统的入侵度,还请谨慎使用,若用户自行加入产品业务逻辑中,与本博文无关,后果自负 ...org.codehaus.groovy</...

    前言:

        1. 不阐述任何业务场景和作用,只提供食用说明书

        2. 执行脚本会提升对系统的入侵度,还请谨慎使用,若用户自行加入产品业务逻辑中,与本博文无关,后果自负


    1. 使用maven仓库引入相关sdk包

    <!-- 动态代码执行 -->
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>2.5.7</version>
        <type>pom</type>
    </dependency>
    
    <!-- Groovy 沙盒 -->
    <dependency>
        <groupId>org.kohsuke</groupId>
        <artifactId>groovy-sandbox</artifactId>
        <version>1.19</version>
    </dependency>

    2. 配置groovy沙盒环境

    import groovy.lang.GroovyShell;
    import io.jsonwebtoken.lang.Collections;
    import lombok.extern.slf4j.Slf4j;
    import org.codehaus.groovy.control.CompilerConfiguration;
    import org.kohsuke.groovy.sandbox.GroovyInterceptor;
    import org.kohsuke.groovy.sandbox.SandboxTransformer;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Component;
    import top.huic.tool.modules.exception.AppException;
    
    import java.util.Map;
    
    /**
     * @author tangzedong.programmer@gamil.com
     * @apiNote groovy沙盒执行环境
     * @since 2019-11-14 16:20
     */
    @Slf4j
    @Component
    public class Groovy {
        private final ApplicationContext context;
    
        public Groovy(ApplicationContext context) {
            this.context = context;
        }
    
        /**
         * 开始执行
         *
         * @param code   执行代码
         * @param params 初始化参数
         * @return 执行结果
         */
        public Object evaluate(String code, Map<String, Object> params) {
            // 初始化执行器,每次执行就初始化一次
            GroovyShell shell = new GroovyShell(new CompilerConfiguration().addCompilationCustomizers(new SandboxTransformer()));
    
            // 初始化沙盒拦截器
            context.getBeansOfType(GroovyInterceptor.class).forEach((k, v) -> v.register());
    
            // 初始化变量
            shell.setVariable("context", context);
            if (!Collections.isEmpty(params)) {
                params.forEach(shell::setVariable);
            }
    
            try {
                // 开始执行
                return shell.evaluate(code);
            } catch (Exception e) {
                log.error("groovy error:", e);
                throw new AppException(e.getMessage());
            }
        }
    }
    

    2.1配置沙盒代码拦截器

    import org.kohsuke.groovy.sandbox.GroovyInterceptor;
    import org.springframework.stereotype.Component;
    
    @Component
    public class NoRunTimeSandboxInterceptor extends GroovyInterceptor {
        @Override
        public Object onStaticCall(GroovyInterceptor.Invoker invoker, Class receiver, String method, Object... args) throws Throwable {
            // 这里是你的逻辑
            return super.onStaticCall(invoker, receiver, method, args);
        }
    }
    

    沙盒环境到这里以及配置完成,关于业务代码就是写类似的js脚本,程序自上而下运行,需要inport相关包,基础的java.lang包是不用引用,这里举个例子

    // 这里是需要被引入的包
    // import java.lang.*;
    
    // 这里写业务逻辑代码,自上而下运行
    System.out.println("hello world");

     

    展开全文
  • GroovyClassLoader是一个Groovy定制的类装载器,负责解析加载Java类中用到的Groovy类,然而,目前该执行器会造成JDK本身的内存泄露。 现象: 由于我们的Java程序通过调度的方式来周期的方式进行运行,而程序中包含...

    Java程序中,当调用Groovy脚本语言来完成某些功能时。常用GroovyClassLoader脚本执行组件。 Groovy GroovyClassLoader ,它会动态地加载一个脚本并执行它。GroovyClassLoader是一个Groovy定制的类装载器,负责解析加载Java类中用到的Groovy类,然而,目前该执行器会造成JDK本身的内存泄露。

    现象:

    由于我们的Java程序通过调度的方式来周期的方式进行运行,而程序中包含使用Groovy脚本的执行。当调度运行一段时间后,发现系统的后台服务会变得特别缓慢,甚至造成服务宕机,无法访问。经过排查发现,我们的Web服务发生了内存泄露,经过使用JDK自带的分析工具jvisualvm,分析程序运行呈现如下现象:

    首先,堆大小一致增加手动执行gc,然而老年代内存依然坚挺,没有下降趋势。正常应用发生oldgc,老年代内存理论上应该被释放掉绝大部分。其次,类视图中已加载的类的数量一致增加,这个也是不正常的现象。

    由此我们可以判断出程序肯定有某处发生了内存泄漏。

    通过加载堆dump发现,AppClassLoader里主要ConcurrentHashMap占用内存最大,而ConcurrentHashMap里主要存放的几乎都是Groovy动态生成的类名

    这个ConcurrentHashMap存放了近600万个Entry

     

    原因:

    AppClassLoaderjava内置类加载器,用来加载用户应用程序的类。里面有一个parallelLockMap,主要用来存储类锁,避免JVM加载同名的类,和提高类加载的并发度。

    GroovyClassLoader如果加载的是无类名的Script,最终会生成一个随机的类名,每次都不一样。导致parallelLockMap不断膨胀, 所以现象在执行脚本频率不算很高的时候一段时间内很难发现内存吃紧。8GLVS甚至几个月都不会有问题。如果像调度程序这种运行方式,很快就会发现内存泄露。

    这应该是一个JDKBUG,只要加载过多的不同类,parallelLockMap就会不断的膨胀,导致memory leak,最终机器就会宕机。这个这个BUG早已经提给官方,但是JDK7JDK8都未修复,而且明确指出不修复。

    https://static.oschina.net/uploads/space/2017/0211/105828_00bE_2001825.png

     

    结论:

    理论上是不合理的,因为这个Map只进不出。既然官方指出不修复,所以无论是Groovy还是通过别的方式动态加载类,尽量使用固定类名。如果类名是随机的,就要控制加载数量了。

    解决办法:

    既然无法修改jdk的类加载方式,那就只能从上层规范使用GroovyClassLoader的使用方式。具体为,原来生成script时,都是直接传入脚本表达式,无论表达式是否重复,都新创建了class,造成了内存泄露。我们可以通过某种业务方式将脚本在内存中存储。通过Key-Value的方式,将脚本生成的classScript对象通过Map方式缓存起来,这样生成的classscript应该是有限的,无论程序运行多少次,都不会存在parallelLockMap无限增加的情况,从而也不会内存泄露。

    展开全文
  • Groovy执行脚本命令shell command

    千次阅读 2018-05-21 12:30:00
    1): 直接执行一个字符串语句,executing a string A string can be executed in the standard java way: def command = """executable arg1 arg2 arg3"""// Create the String def proc = command.execute() // ...
  • 在做一个定时同步数据库任务的时候,想通过在groovy中直接去执行mysqldump命令去执行,发现参数中如果有空格 命令就会错误. 一种解决方案是用命令数组的形式,把字符串的命令拆成数组 def sql = ["sh", "-c", ...
  • Groovy脚本执行

    2020-05-13 22:49:09
    Groovy是一个可以解释执行的语言,在解释执行的情况下具有以下基础语法特征。 groovy xxx.groovy方式运行一个groovy文件. 在解释执行Groovy文件中,有如下特点: 文件运行在一个Script对象中。 def关键字可以省略...
  • groovy集成springboot 1.通过groovy文件执行脚本 2.通过数据库动态执行
  • groovy脚本执行工具.zip

    2020-03-25 23:03:30
    执行测试开发过程中的groovy脚本,无须安装,解压即可使用,直接运行bin目录下的groovyConsole.bat即可。
  • groovy脚本执行与优化

    千次阅读 2019-08-24 15:35:21
    这门动态语言拥有类似Python、Ruby和Smalltalk中的一些特性,可以作为Java平台的脚本语言使用,Groovy代码动态地编译成运行于Java虚拟机(JVM)上的Java字节码,并与其他Java代码和库进行互操作。由于其运行在JVM上...
  • Java中执行Groovy脚本

    千次阅读 2017-06-27 14:15:35
    Groovy脚本执行
  • 项目中存在经常需要往设备中拷贝文件和数据库文件的操作,为便于提高开发效率,使用Groovy脚本编写一段脚本提高开发效率,省去那些繁琐的复制粘贴工作。 方法封装: 几个常用的方法封装: /*********************...
  • 看个例子,我在Groovy项目文件夹所在目录执行dir,得到如下输出: 现在我希望用Groovy代码实现这个dir命令的执行效果,代码如下: def process = "cmd /c dir".execute() println "Found text ${process.text}" ...
  • Java执行Groovy脚本语言

    2020-08-26 11:48:17
    Groovy是什么 Groovy是一个功能强大的、动态的基于JVM的脚本语言。强大在哪里,请看下图。更详细的请参考wiki。 Java中使用Groovy 首先要引入依赖 <dependency> <groupId>org.codehaus....
  • groovy获取shell执行结果和执行状态码

    千次阅读 2021-02-26 20:26:59
    获取执行结果 result = sh(script: "<shell command>", returnStdout: true) 获取执行状态码(0或者非0) excuteCode = sh(script: "<shell command>", returnStatus: true) 参考文章:...
  • 那么可以将这部分业务逻辑改写成Groovy脚本来执行,那么就可以在业务运行过程中动态更改业务规则,达到快速响应。 Case1: Groovy动态编译执行 闲话少说,直接上代码: static Compilable engine; /...
  • 对javax.script包进行讲解,实现支持java动态嵌入执行groovy代码片段
  • 写了一个test方法: @Test public void engineTest() throws ScriptException { ... ScriptEngine engine = manager.getEngineByName("groovy"); System.out.println("============"); if (engine == null)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 32,796
精华内容 13,118
关键字:

groovy执行