java 內(nèi)嵌Groovy動(dòng)態(tài)腳本操作
固定的策略有時(shí)候還是無(wú)法滿足千變?nèi)f化的需求變動(dòng),一方面需要支持特定的用戶需求,另一方面又得盡可能的復(fù)用代碼,避免重復(fù)開(kāi)發(fā),這就需要將這部分的特殊的需求剝離出來(lái),采用動(dòng)態(tài)配置規(guī)則的方式來(lái)實(shí)現(xiàn)。
java有三種方式調(diào)用groovy腳本
但是真正在實(shí)際的服務(wù)器環(huán)境中,嵌入groovy腳本往往需要滿足下面的條件:
- 可以直接調(diào)用groovy腳本中的方法
- 能傳遞對(duì)象到groovy方法中,不僅僅是字符串
- 提供腳本緩存機(jī)制,不用每次調(diào)用腳本的時(shí)候,都到磁盤(pán)讀取
- 修改groovy后能實(shí)時(shí)生效
第一種:通過(guò)GroovyShell來(lái)執(zhí)行g(shù)roovy腳本
第二種:通過(guò)GroovyClassLoader動(dòng)態(tài)加載Groovy Class
第三種:使用GroovyScriptEngine腳本引擎加載Groovy腳本
本實(shí)例使用第二種動(dòng)態(tài)加載腳本
由于Groovy腳本語(yǔ)言能夠直接編譯成java的class字節(jié)碼,并且還支持java的類庫(kù),運(yùn)行在java虛擬機(jī)上,其能夠很好的跟java進(jìn)行交互,因此利用groovy的動(dòng)態(tài)特性,來(lái)實(shí)現(xiàn)需求頻繁變動(dòng)的且變態(tài)的需求,并且不需要重啟服務(wù)器。
以下是代碼測(cè)試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("測(cè)試動(dòng)態(tài)腳本語(yǔ)言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的實(shí)例
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
//反射執(zhí)行方法,得到返回信息 傳多個(gè)參數(shù) new Object[]{bean,request,new HashMap<>()}
Object invokeResult = groovyObject.invokeMethod("sayHello", name);
if(invokeResult != null){
System.out.println(invokeResult.toString());
}
//清楚緩存中的map 是為了grrovy腳本變化,清楚堆中緩存,重新類加載
logger.info("測(cè)試動(dòng)態(tài)腳本語(yǔ)言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("測(cè)試動(dòng)態(tài)腳本語(yǔ)言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的實(shí)例
CallBackGroovyInvoke groovyObject = (CallBackGroovyInvoke) groovyClass.newInstance();
//反射執(zhí)行方法,得到返回信息
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("統(tǒng)一groovy接口返回?cái)?shù)據(jù)————————》"+doCallBackVal);
//清楚緩存中的map 是為了grrovy腳本變化,清楚堆中緩存,重新類加載
logger.info("測(cè)試動(dòng)態(tài)腳本語(yǔ)言Groovy2 End。。。");
return doCallBackVal;
}
@ResponseBody
@RequestMapping(value="/clearGroovyCache.do",method={RequestMethod.GET,RequestMethod.POST})
public void clearCache(){
//根據(jù)指定key來(lái)清楚
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,主要是重寫(xiě)了,parseClass方法,并且添加了可以清除sourceCache的map緩存,以及可以remove指定的class信息,用于更改groovy文件后,可以及時(shí)響應(yīng)。
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中,統(tǒng)一來(lái)管理,防止多次對(duì)groovy的變動(dòng),可以根據(jù)緩存開(kāi)關(guān)來(lái)及時(shí)的更新到j(luò)vm中
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+"向你說(shuō) “你好!!”";
def date = new Date();
return "success sayHello()+test groovy" +date;
}
<!--可以直接調(diào)用 -->
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 "測(cè)試接口groovy 獲取用戶信息:"+bean.getNameString() + " ;年齡:"+bean.getAge();
println "設(shè)置用戶信息:";
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 "在不重啟應(yīng)用的情況下,調(diào)用內(nèi)部其他方法";
return "我進(jìn)入了,另一個(gè)方法!";
}
}
以上就是測(cè)試的這些,至于其框架使用的map容器作為緩存,因jvmGC不會(huì)對(duì)該容器進(jìn)行清除,所以為防止內(nèi)存溢出,可采用自定義的緩存策略,如基于容量、基于時(shí)間、基于java對(duì)象引用、基于緩存算法(LRU最近最少使用、LFU最不常用、FIFO先進(jìn)先出),可不用groovy中的容器,及設(shè)置緩存設(shè)為false,則不放入容器,然后將其生成的實(shí)例可放入分布式緩存中redis即可。
僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決spring cloud gateway 獲取body內(nèi)容并修改的問(wèn)題
這篇文章主要介紹了解決spring cloud gateway 獲取body內(nèi)容并修改的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
SpringBoot使用@RestController處理GET和POST請(qǐng)求的代碼詳解
在Spring?MVC中,@RestController注解的控制器類可以處理多種HTTP請(qǐng)求方法,包括GET和POST,所以本文就給大家詳細(xì)介紹了SpringBoot使用@RestController處理GET和POST請(qǐng)求的示例代碼,需要的朋友可以參考下2024-07-07
PowerJob的AliOssService工作流程源碼解讀
這篇文章主要介紹了PowerJob的AliOssServiceg工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
SpringBoot如何利用Twilio?Verify發(fā)送驗(yàn)證碼短信
Twilio提供了一個(gè)名為?Twilio?Verify?的服務(wù),專門用于處理驗(yàn)證碼的發(fā)送和驗(yàn)證,下面我們就來(lái)看看如何使用Twilio?Verify實(shí)現(xiàn)發(fā)送驗(yàn)證碼短信吧2025-03-03
ConditionalOnProperty注解的作用和使用方式
在SpringBoot項(xiàng)目開(kāi)發(fā)中,@ConditionalOnProperty注解允許根據(jù)配置文件中的屬性值來(lái)控制配置類是否生效,該注解通過(guò)屬性name和havingValue來(lái)判斷配置是否注入,如果application.properties中的對(duì)應(yīng)屬性值為空或不匹配havingValue設(shè)定值2024-09-09
Activiti開(kāi)發(fā)環(huán)境的配置
本篇文章主要內(nèi)容介紹了Activiti開(kāi)發(fā)環(huán)境的配置,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
redis.clients.jedis.exceptions.JedisMovedDataException異常解決
redis.clients.jedis.exceptions.JedisMovedDataException?異常是在使用?Jedis?客戶端與?Redis?集群進(jìn)行交互時(shí)發(fā)生的,下面就來(lái)介紹一下解決方法,感興趣的可以了解一下2024-05-05
詳解Java實(shí)現(xiàn)JSONArray轉(zhuǎn)Map的三種實(shí)現(xiàn)方式
本文主要介紹了Java實(shí)現(xiàn)JSONArray轉(zhuǎn)Map的三種實(shí)現(xiàn)方式,本文只是自己常用的三種,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

