固定的策略有时候还是无法满足千变万化的需求变动,一方面需要支持特定的用户需求,另一方面又得尽可能的复用代码,避免重复开发,这就需要将这部分的特殊的需求剥离出来,采用动态配置规则的方式来实现。
java有三种方式调用groovy脚本
但是真正在实际的服务器环境中,嵌入groovy脚本往往需要满足下面的条件:
- 可以直接调用groovy脚本中的方法
- 能传递对象到groovy方法中,不仅仅是字符串
- 提供脚本缓存机制,不用每次调用脚本的时候,都到磁盘读取
- 修改groovy后能实时生效
第一种:通过GroovyShell来执行groovy脚本
第二种:通过GroovyClassLoader动态加载Groovy Class
第三种:使用GroovyScriptEngine脚本引擎加载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