java 内嵌Groovy动态脚本操作

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

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

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

仅为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Spring中集成Groovy的四种方式(小结)

    groovy是一种动态脚本语言,适用于一些可变.和规则配置性的需求,目前Spring提供ScriptSource接口,支持两种类型,一种是 ResourceScriptSource,另一种是 StaticScriptSource,但是有的场景我们需要把groovy代码放进DB中,所以我们需要扩展这个. ResourceScriptSource:在 resources 下面写groovy类 StaticScriptSource:把groovy类代码放进XML里 DatabaseScriptSour

  • 详解Java执行groovy脚本的两种方式

    记录Java执行groovy脚本的两种方式,简单粗暴: 一种是通过脚本引擎ScriptEngine提供的eval(String)方法执行脚本内容:一种是执行groovy脚本: 二者都通过Invocable来传递参数并获取执行结果: Invocable:脚本引擎的解释器接口,提供invokeFunction和invokeMethod两种传递参数并获取执行结果的方法,Java JDK API文档解释如下: invokeFunction: invokeMethod: 以下为案例: 引入依赖 <depe

  • Java groovy如何提升代码运行效率

    刚开始学groovy,知道了它会先变异成class 文件,然后再用jvm 执行.写了Hello World程序,查看它的编译文件,发现groovy的效率挺低的.不但编译文件的代码多,而且需要依赖很多groovy包,导致了不能够直接使用java 命令运行class文件 比较如下: Java版Hello World,JavaTest.java public class JavaTest { public static void main(String[] args){ System.out.prin

  • Java调用groovy实现原理代码实例

    一.概述 Groovy is a multi-faceted language for the Java platform. Apache Groovy是一种强大的.可选的类型化和动态语言,具有静态类型和静态编译功能,用于Java平台,目的在于通过简洁.熟悉和易于学习的语法提高开发人员的工作效率.它可以与任何Java程序顺利集成,并立即向您的应用程序提供强大的功能,包括脚本编写功能.特定于域的语言编写.运行时和编译时元编程以及函数式编程. Groovy是基于java虚拟机的,执行文件可以是简单的

  • java 内嵌Groovy动态脚本操作

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

  • Golang 动态脚本调研详解

    目录 一.技术背景 1.1 程序的动态链接技术 1.1.1 动态链接库 1.1.2 动态共享对象 1.1.3 非编译语言的动态技术 1.2 Golang 的动态技术 二.Golang 的第三方解释器(Yaegi) 2.1 使用场景 2.1.1 内嵌解释器 2.1.2 动态扩展框架 2.1.3 命令行解释器 2.2 数据交互 2.2.1 数据输入 2.1.2 数据输出 三.实现原理 3.1 AST - 抽象语法树 3.1.1 抽象语法树示例 3.1.2 执行抽象语法树 一.技术背景 1.1 程序的

  • Java动态脚本Groovy

    目录 1.Groovy特性 2.核心涉及 3.Java与Groovy转换 第一步:引入Groovy依赖 第二步:创建interface接口声明方法 第三步:在resources目录下创建.groovy文件 第四步:创建Groovy脚本装载类,动态解析脚本为Class 第五步:读取脚本内容,执行脚本 4.Groovy特性验证 第一步:将之前Groovy脚本数据修改.存于数据库表中,动态加载脚本 第二步:数据库表中:添加.查询Groovy脚本,动态加载执行 第三步:多次修改表数据值,查看执行结果 5

  • PyQt5内嵌浏览器注入JavaScript脚本实现自动化操作的代码实例

    概要 应同学邀请,演示如何使用 PyQt5 内嵌浏览器浏览网页,并注入 Javascript 脚本实现自动化操作. 下面测试的是一个廉价机票预订网站(http://www.flyscoot.com/),关键点如下 使用 QWebEngineView 加载网页,并显示进度. 在默认配置(QWebEngineProfile)中植入 Javascript 内容,这样脚本会在所有打开的网页中执行,不论跳转到哪个网址. Javascript 脚本使用网址中的路径名,判断当前网页位置,从而决定执行哪种操作.

  • Java动态脚本Groovy获取Bean技巧

    目录 一.使用BeanFactoryPostProcessor注入Bean: 第一步:创建实现SpringUtils 接口工具(组件)来获取spring bean 第二步:创建Groovy脚本装载类,动态解析脚本为Class 第三步:读取脚本内容,执行脚本 第四步:在resources目录下创建.groovy文件 第五步:实例化脚本,执行方法  二.使用ApplicationContext注入Bean 第一步:修改项目启动类,获得ApplicationContext 第二步:修改resource

  • 手把手教你 CKEDITOR 4 实现Dialog 内嵌 IFrame操作详解

    本文实例讲述了CKEDITOR 4 实现Dialog 内嵌 IFrame操作.分享给大家供大家参考,具体如下: 在上一篇博文<CKEDITOR 4 扩展插件制作>中,姜哥跟大家探讨了在ckeditor中添加dialog,已经添加dialog的一些控件,最终将控件中的值插入到ckeditor编辑器中的过程.但是实际上,我们更多的应用,还是会在dialog中,添加iframe组件,直接引用外部网页,以实现更为复杂的功能.今天姜哥就和大家一起分享一下,这个开发过程. 继续我们上一个工程中的例子,我们

  • Java 动态模拟操作系统进程调度算法

    目录 实验目的 设备与环境 实验内容 实验结果及分析 部分代码展示  实验目的 通过对进程调度算法的模拟,进一步理解进程的基本概念,加深对进程运行状态和进程调度过程.调度算法的理解. 设备与环境 硬件设备:PC机一台 软件环境:安装Windows操作系统,并安装相关的程序开发环境,如C \C++\Java 等编程语言环境. 实验内容 实验采用了java语言编程模拟N个进程采用动态高优先权优先进程调度算法.该算法就是按照优先权的大小运行进程,如果一个时间片内未运行完,则将优先权数减3后再插入到链表

  • java发送内嵌图片邮件

    整体效果: 发送端:网易邮箱:接收端:qq邮箱. 1.web前端 2.在网易邮箱"已发送"中可以看见通过java代码发送的邮件 3.同样在qq邮箱中也可以看到这样的效果 实现过程: 1.web前端(bootstrap布局) <form action="mailAction!sendMail" method="post" name="mailForm" id="mailFormId"> <u

  • JS动态修改iframe内嵌网页地址的方法

    本文实例讲述了JS动态修改iframe内嵌网页地址的方法.分享给大家供大家参考.具体分析如下: 下面的JS代码通过修改iframe的src属性动态修改iframe的内嵌网页 <!DOCTYPE html> <html> <head> <script> function changeSrc() { document.getElementById("myframe").src="http://google.com"; }

  • 解决Ant Design Modal内嵌Form表单initialValue值不动态更新问题

    场景描述: 如下图所示,点击减免天数会出现一个弹窗, 输入天数后点击确定,保存这个值, 但是我在点第二行的减免天数的时候初始应该是空的, 可是现在显示的是第一行输入的值: <Modal title="减免天数" visible={that.state.visible} onOk={that.handleOk.bind(that)} onCancel={that.handleCancel} > <Form horizontal form={form}> <F

随机推荐