java 內嵌Groovy動態(tài)腳本操作
固定的策略有時候還是無法滿足千變萬化的需求變動,一方面需要支持特定的用戶需求,另一方面又得盡可能的復用代碼,避免重復開發(fā),這就需要將這部分的特殊的需求剝離出來,采用動態(tài)配置規(guī)則的方式來實現。
java有三種方式調用groovy腳本
但是真正在實際的服務器環(huán)境中,嵌入groovy腳本往往需要滿足下面的條件:
- 可以直接調用groovy腳本中的方法
- 能傳遞對象到groovy方法中,不僅僅是字符串
- 提供腳本緩存機制,不用每次調用腳本的時候,都到磁盤讀取
- 修改groovy后能實時生效
第一種:通過GroovyShell來執(zhí)行groovy腳本
第二種:通過GroovyClassLoader動態(tài)加載Groovy Class
第三種:使用GroovyScriptEngine腳本引擎加載Groovy腳本
本實例使用第二種動態(tài)加載腳本
由于Groovy腳本語言能夠直接編譯成java的class字節(jié)碼,并且還支持java的類庫,運行在java虛擬機上,其能夠很好的跟java進行交互,因此利用groovy的動態(tài)特性,來實現需求頻繁變動的且變態(tài)的需求,并且不需要重啟服務器。
以下是代碼測試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("測試動態(tài)腳本語言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();
//反射執(zhí)行方法,得到返回信息 傳多個參數 new Object[]{bean,request,new HashMap<>()}
Object invokeResult = groovyObject.invokeMethod("sayHello", name);
if(invokeResult != null){
System.out.println(invokeResult.toString());
}
//清楚緩存中的map 是為了grrovy腳本變化,清楚堆中緩存,重新類加載
logger.info("測試動態(tài)腳本語言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("測試動態(tài)腳本語言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();
//反射執(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接口返回數據————————》"+doCallBackVal);
//清楚緩存中的map 是為了grrovy腳本變化,清楚堆中緩存,重新類加載
logger.info("測試動態(tài)腳本語言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中,統(tǒng)一來管理,防止多次對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即可。
僅為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
解決spring cloud gateway 獲取body內容并修改的問題
這篇文章主要介紹了解決spring cloud gateway 獲取body內容并修改的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
SpringBoot使用@RestController處理GET和POST請求的代碼詳解
在Spring?MVC中,@RestController注解的控制器類可以處理多種HTTP請求方法,包括GET和POST,所以本文就給大家詳細介紹了SpringBoot使用@RestController處理GET和POST請求的示例代碼,需要的朋友可以參考下2024-07-07
PowerJob的AliOssService工作流程源碼解讀
這篇文章主要介紹了PowerJob的AliOssServiceg工作流程源碼解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-01-01
SpringBoot如何利用Twilio?Verify發(fā)送驗證碼短信
Twilio提供了一個名為?Twilio?Verify?的服務,專門用于處理驗證碼的發(fā)送和驗證,下面我們就來看看如何使用Twilio?Verify實現發(fā)送驗證碼短信吧2025-03-03
ConditionalOnProperty注解的作用和使用方式
在SpringBoot項目開發(fā)中,@ConditionalOnProperty注解允許根據配置文件中的屬性值來控制配置類是否生效,該注解通過屬性name和havingValue來判斷配置是否注入,如果application.properties中的對應屬性值為空或不匹配havingValue設定值2024-09-09
redis.clients.jedis.exceptions.JedisMovedDataException異常解決
redis.clients.jedis.exceptions.JedisMovedDataException?異常是在使用?Jedis?客戶端與?Redis?集群進行交互時發(fā)生的,下面就來介紹一下解決方法,感興趣的可以了解一下2024-05-05

