好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

java 内嵌Groovy动态脚本操作

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

java 有三种方式调用groovy 脚本

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

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

第一种:通过 Groovy Shell来执行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

查看更多关于java 内嵌Groovy动态脚本操作的详细内容...

  阅读:40次