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í)候,都到磁盤讀取
- 修改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,主要是重寫了,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)文章
maven配置多個(gè)鏡像的實(shí)現(xiàn)方法
這篇文章主要介紹了maven配置多個(gè)鏡像的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06Java實(shí)現(xiàn)FTP批量大文件上傳下載篇1
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)FTP批量大文件上傳下載的基礎(chǔ)篇,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-08-08詳解如何將JAVA程序制作成可以直接執(zhí)行的exe文件
這篇文章主要介紹了詳解如何將JAVA程序制作成可以直接執(zhí)行的exe文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09Java實(shí)現(xiàn)的基于socket通信的實(shí)例代碼
Java實(shí)現(xiàn)的基于socket通信的實(shí)例代碼,需要的朋友可以參考一下2013-03-03