java 内嵌Groovy动态脚本操作

作者:cuichunchi 时间:2022-12-15 05:03:32 

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

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类


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。


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文件后,可以及时响应。


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

import java.util.Date;
def sayHello(name){
println name+"向你说 “你好!!”";
def date = new Date();
return "success sayHello()+test  groovy" +date;
}
<!--可以直接调用 -->
sayHello('asda');

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 "我进入了,另一个方法!";
}
}

来源:https://blog.csdn.net/cuichunchi/article/details/89454577

标签:java,Groovy,脚本
0
投稿

猜你喜欢

  • Android PullToRefreshLayout下拉刷新控件的终结者

    2021-06-22 02:15:00
  • 解决mybatis-plus自动配置的mapper.xml与java接口映射问题

    2023-08-25 04:16:02
  • List转换成DataSet实现代码

    2023-03-02 04:46:01
  • Android RecyclerView缓存复用原理解析

    2022-04-29 10:51:03
  • Android向node.js编写的服务器发送数据并接收请求

    2022-10-12 02:08:21
  • Java实现微信发红包

    2021-07-22 05:11:07
  • Java线程安全的计数器简单实现代码示例

    2023-11-09 15:41:57
  • WPF实现控件拖动的示例代码

    2023-04-01 09:36:15
  • Android实现微信登录的示例代码

    2022-04-04 15:55:56
  • MyBatis的SQL执行结果和客户端执行结果不一致问题排查

    2022-12-30 20:37:22
  • Android 使用PDF.js浏览pdf的方法示例

    2021-12-08 06:11:41
  • java实现简单控制台通讯录

    2023-05-24 14:33:20
  • C#基于Socket的TCP通信实现聊天室案例

    2021-12-19 15:54:13
  • Android自定义控件简单实现侧滑菜单效果

    2023-10-30 16:56:57
  • Java使用通配符实现增强泛型详解

    2021-07-06 17:28:01
  • c# 获取CookieContainer的所有cookies函数代码

    2023-06-17 23:11:30
  • Spring Security 构建rest服务实现rememberme 记住我功能

    2023-03-29 14:42:03
  • C#写入XML文档

    2022-03-21 18:08:33
  • springboot中request和response的加解密实现代码

    2023-05-27 16:46:27
  • Android实现淘宝客户端倒计时界面

    2023-09-18 21:25:09
  • asp之家 软件编程 m.aspxhome.com