服务器之家

服务器之家 > 正文

java 内嵌Groovy动态脚本操作

时间:2021-09-29 11:36     来源/作者:cuichunchi

固定的策略有时候还是无法满足千变万化的需求变动,一方面需要支持特定的用户需求,另一方面又得尽可能的复用代码,避免重复开发,这就需要将这部分的特殊的需求剥离出来,采用动态配置规则的方式来实现。

java有三种方式调用groovy脚本

但是真正在实际的服务器环境中,嵌入groovy脚本往往需要满足下面的条件:

  1. 可以直接调用groovy脚本中的方法
  2. 能传递对象到groovy方法中,不仅仅是字符串
  3. 提供脚本缓存机制,不用每次调用脚本的时候,都到磁盘读取
  4. 修改groovy后能实时生效

第一种:通过GroovyShell来执行groovy脚本

第二种:通过GroovyClassLoader动态加载Groovy Class

第三种:使用GroovyScriptEngine脚本引擎加载Groovy脚本

本实例使用第二种动态加载脚本

Groovy的基本语法

由于Groovy脚本语言能够直接编译成java的class字节码,并且还支持java的类库,运行在java虚拟机上,其能够很好的跟java进行交互,因此利用groovy的动态特性,来实现需求频繁变动的且变态的需求,并且不需要重启服务器。

以下是代码测试control类

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.webTest.dynamicGroovy.controller;
import groovy.lang.GroovyObject;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.codehaus.groovy.control.CompilationFailedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
 
import com.base.common.GroovyUtils;
import com.webTest.dynamicGroovy.bean.GroovyBean;
import com.webTest.dynamicGroovy.groovyInterface.CallBackGroovyInvoke;
import com.webTest.dynamicGroovy.service.CallBackGroovy;
 
@Controller
@RequestMapping("/groovyTest")
public class GroovyControl {   
    private Logger logger = Logger.getLogger(GroovyControl.class);     
    @Autowired
    private CallBackGroovy callBackGroovy;     
    @ResponseBody
    @RequestMapping(value="/groovy1.do",method={RequestMethod.GET,RequestMethod.POST})
    public  Object testGroovy(HttpServletRequest request) throws CompilationFailedException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException{
        logger.info("测试动态脚本语言Groovy Begin。。。");
        String name="崔春驰";
        String realPath = request.getSession().getServletContext().getRealPath("groovyFile");
        String groovyNameString = "\\hello.groovy";
        String path = realPath+groovyNameString;
        File file = new File(path);
        
        //获得class,并存放入堆缓存
        Class<?> groovyClass = GroovyUtils.CLASS_LOADER.parseClass("myFirstGroovy",file,true);
        //获取groovyObject的实例
        GroovyObject  groovyObject = (GroovyObject) groovyClass.newInstance();
        //反射执行方法,得到返回信息   传多个参数 new Object[]{bean,request,new HashMap<>()}
        Object invokeResult = groovyObject.invokeMethod("sayHello", name);
        if(invokeResult != null){
            System.out.println(invokeResult.toString());
        }
        //清楚缓存中的map  是为了grrovy脚本变化,清楚堆中缓存,重新类加载
        logger.info("测试动态脚本语言Groovy End。。。");
        return invokeResult;
    }
    
    @ResponseBody
    @RequestMapping(value="/groovy2.do",method={RequestMethod.GET,RequestMethod.POST})
    public  Object testGroovy2(HttpServletRequest request) throws CompilationFailedException, IOException, InstantiationException, IllegalAccessException, ClassNotFoundException{
        logger.info("测试动态脚本语言Groovy2 Begin。。。");
        String realPath = request.getSession().getServletContext().getRealPath("groovyFile");
        String groovyNameString = "\\testGroovyBean.groovy";
        String path = realPath+groovyNameString;
        File file = new File(path);
        
        GroovyBean bean = new GroovyBean();
        bean.setNameString("王小二");bean.setAge(12);
        Map<String, Object> map = new HashMap<>();
        map.put("address", "江苏南京");
        
        //获得class,并存放入堆缓存
        Class<?> groovyClass = GroovyUtils.CLASS_LOADER.parseClass("mySecondGroovy",file,true);
        //获取groovyObject的实例
        CallBackGroovyInvoke  groovyObject = (CallBackGroovyInvoke) groovyClass.newInstance();
        //反射执行方法,得到返回信息
        Object doCallBackVal = groovyObject.doCallBack(bean, request, map);
        
        if(doCallBackVal instanceof GroovyBean){
            GroovyBean bean2  = (GroovyBean) doCallBackVal;
            System.out.println("用户信息---》"+bean2.getNameString() + ":"+bean2.getAge()+"岁");
        }
        System.out.println("统一groovy接口返回数据————————》"+doCallBackVal);
        //清楚缓存中的map  是为了grrovy脚本变化,清楚堆中缓存,重新类加载
        logger.info("测试动态脚本语言Groovy2 End。。。");
        return doCallBackVal;
    }
    
    @ResponseBody
    @RequestMapping(value="/clearGroovyCache.do",method={RequestMethod.GET,RequestMethod.POST})
    public void clearCache(){
        //根据指定key来清楚
        GroovyUtils.CLASS_LOADER.clearCache("myFirstGroovy");
        //清楚所有
        GroovyUtils.CLASS_LOADER.clearCache();
    }
}

以下为groovyUtils。

?
1
2
3
4
5
6
7
8
public class GroovyUtils {
 public static GroovyClassLoaderCommon CLASS_LOADER = null;
 static {
  CompilerConfiguration configuration = CompilerConfiguration.DEFAULT;
  configuration.setSourceEncoding("UTF-8");
  CLASS_LOADER = new GroovyClassLoaderCommon(GroovyControl.class.getClassLoader(), configuration);
 }
}

以下为GroovyClassLoaderCommon,继承了GroovyClassLoader,主要是重写了,parseClass方法,并且添加了可以清除sourceCache的map缓存,以及可以remove指定的class信息,用于更改groovy文件后,可以及时响应。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.webTest.dynamicGroovy;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.codehaus.groovy.control.CompilationFailedException;
import org.codehaus.groovy.control.CompilerConfiguration;
public final class GroovyClassLoaderCommon extends GroovyClassLoader{
    
    //这一步可以放入分布式缓存中,如redis中,统一来管理,防止多次对groovy的变动,可以根据缓存开关来及时的更新到jvm中
    private static final Map<String, Object> CACHEMAP_MAP = new ConcurrentHashMap<>();
    
    public GroovyClassLoaderCommon() {
        super();
    }
    
    public GroovyClassLoaderCommon(ClassLoader loader, CompilerConfiguration config){
        super(loader, config);
    }
    
    /**
     *
     * @param file  文件
     * @param shouldCacheSource   是否缓存
     * @return
     * @throws CompilationFailedException
     * @throws FileNotFoundException
     * Class<?>
     * @author 88397658
     * @since
     */
    public Class<?> parseClass(String key,File file,
            boolean shouldCacheSource) throws CompilationFailedException, FileNotFoundException {
        GroovyCodeSource codeSource = new GroovyCodeSource(file);
        codeSource.setCachable(shouldCacheSource);
        if(shouldCacheSource) CACHEMAP_MAP.put(key, codeSource.getName());
        return super.parseClass(codeSource);
    }
    
    /**
     * 清楚缓存
     */
    public void clearCache(){
        synchronized (this) {
            sourceCache.clear();
        }
    }
    /**
     * 清楚指定缓存
     * @param key
     * void
     * @author 88397658
     * @since
     */
    public void clearCache(String key){
        Object value = CACHEMAP_MAP.get(key);
        synchronized (this) {
            if(sourceCache.containsKey(value)) sourceCache.remove(value);
            if(CACHEMAP_MAP.containsKey(key)) CACHEMAP_MAP.remove(key);
        }
    }
}
?
1
2
3
4
5
6
7
8
import java.util.Date;
def sayHello(name){
    println name+"向你说 “你好!!”";
    def date = new Date();
    return "success sayHello()+test  groovy" +date;
}
<!--可以直接调用 -->
sayHello('asda');
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import com.webTest.dynamicGroovy.groovyInterface.CallBackGroovyInvoke;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import com.webTest.dynamicGroovy.bean.GroovyBean;
import org.slf4j.Logger
import org.slf4j.LoggerFactory
 
class testGroovyBean implements CallBackGroovyInvoke{
    public static final Logger LOGGER = LoggerFactory.getLogger(testGroovyBean.class); 
    def doCallBack(GroovyBean bean ,HttpServletRequest request, Map<String, Object> map){
        LOGGER.info("groovy 中记录日志。。。。");
        println "测试接口groovy 获取用户信息:"+bean.getNameString() + " ;年龄:"+bean.getAge();
        println "设置用户信息:";
        bean.setNameString("蛮王");bean.setAge(200);
        def map1 = ['name':'王大锤','sex':'男'];
        map = map+map1+['weight':'160'];
        def str =  otherMethod();
        LOGGER.info(str);
//      return map
        return bean;
    }
    
    def otherMethod(){
        println "在不重启应用的情况下,调用内部其他方法";
        return "我进入了,另一个方法!";
    }
}

以上就是测试的这些,至于其框架使用的map容器作为缓存,因jvmGC不会对该容器进行清除,所以为防止内存溢出,可采用自定义的缓存策略,如基于容量、基于时间、基于java对象引用、基于缓存算法(LRU最近最少使用、LFU最不常用、FIFO先进先出),可不用groovy中的容器,及设置缓存设为false,则不放入容器,然后将其生成的实例可放入分布式缓存中redis即可。

仅为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/cuichunchi/article/details/89454577

标签:

相关文章

热门资讯

2022年最旺的微信头像大全 微信头像2022年最新版图片
2022年最旺的微信头像大全 微信头像2022年最新版图片 2022-01-10
蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整
蜘蛛侠3英雄无归3正片免费播放 蜘蛛侠3在线观看免费高清完整 2021-08-24
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
yue是什么意思 网络流行语yue了是什么梗
yue是什么意思 网络流行语yue了是什么梗 2020-10-11
暖暖日本高清免费中文 暖暖在线观看免费完整版韩国
暖暖日本高清免费中文 暖暖在线观看免费完整版韩国 2021-05-08
返回顶部