/**
* groovy 脚本执行
* @auth zzj
*/
@Component
@Slf4j
public class GroovyRunner {
private final String DEFAULT_METHOD = "run";
/** 缓存脚本对象
* TODO 后续需要添加一个计时器清空cache缓存,避免GC无法回收已经不用的对象
*/
private Map<String, GroovyObject> cacheObj = new ConcurrentHashMap();
private Map<String, String> cacheKey = new ConcurrentHashMap<>();
/**
* groovy动态脚本执行, 执行java class 对象
* @param script 要执行脚本的字符串
* @param methodName 执行的方法名
* @param params 方法参数
* @return Object
* @throws Exception
*/
public Object runner(String script, String methodName, Object ...params) throws Exception {
boolean checkJavaScript = checkJavaScript(script);
Checks.checkFalse(checkJavaScript, "脚本文件不是Java class类型");
String cacheObjName = getCacheObjName(script);
String keyName = getJavaScriptName(script);
// bean = SpringContextUtils.getBean(javaScriptBeanName);
GroovyObject obj = getCacheObj(cacheObjName);
if (null == obj) {
ApplicationContext applicationContext = SpringContextUtils.getApplicationContext();
AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
GroovyClassLoader groovyClassLoader = new GroovyClassLoader();
Class aClass = groovyClassLoader.parseClass(script);
// 频繁使用ClassLoader会导致内存泄露,交给容器缓存对象 @zzj 2023.4.20
// 因动态代码修改会重新加载代码,但是Spring 容器重新加载bean 异常困难 后续再研究下 @zzj 2023.4.20
// bean = createSpringBean(aClass, autowireCapableBeanFactory);
// 注入bean
obj = (GroovyObject) aClass.newInstance();
injectSpringBean(obj, autowireCapableBeanFactory);
doClearCacheObj(keyName);
doCacheObj(cacheObjName, obj);
doCacheKey(keyName, cacheObjName);
}
Object run = obj.invokeMethod(methodName, params);
return run;
}
private GroovyObject getCacheObj(String cacheObjName) {
return cacheObj.get(cacheObjName);
}
private void doCacheObj(String cacheObjName, GroovyObject obj) {
cacheObj.put(cacheObjName, obj);
}
private void doCacheKey(String className, String cacheObjName) {
cacheKey.put(className, cacheObjName);
}
private void doClearCacheObj(String className) {
String cacheObjName = cacheKey.get(className);
if (null != cacheObjName) {
GroovyObject groovyObject = cacheObj.get(cacheObjName);
cacheObj.remove(cacheObjName);
groovyObject = null;
}
}
private String getCacheObjName(String script) {
Checks.checkNull(script, "脚本不能为空");
String cacheName = MD5Util.MD5Encode(script, null);
return cacheName;
}
private String getJavaScriptName(String script) {
String regex = "public\\s+class\\s+\\S+";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(script);
boolean b = matcher.find();
if (!b) {
return null;
}
String group = matcher.group();
group = group.replaceAll("public\\s+class\\s+", "");
group = group.replace("[\\{\\}]", "");
return group.trim();
}
private boolean checkJavaScript(String script) {
String regex = "public\\s+class\\s+.+\\s+\\{[\\s\\S]*\\}";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(script);
return matcher.find();
}
private Object createSpringBean(Class aClass, AutowireCapableBeanFactory autowireCapableBeanFactory) {
Object bean = autowireCapableBeanFactory.createBean(aClass, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME, Boolean.TRUE);
return bean;
}
/**
* 注入Spring管理的bean
* @param o
*/
private void injectSpringBean(GroovyObject o, AutowireCapableBeanFactory autowireCapableBeanFactory) {
autowireCapableBeanFactory.autowireBean(o);
}
/**
* groovy动态脚本执行, 默认执行run方法
* @param script 要执行脚本的字符串
* @param params 方法参数
* @return
* @throws Exception
*/
public Object runner(String script, Object ...params) throws Exception {
return runner(script, DEFAULT_METHOD, params);
}
/**
* groovy动态脚本执行, 默认执行run方法
* @param script 要执行脚本的字符串
* @return
* @throws Exception
*/
public Object runner(String script) throws Exception {
return runner(script, DEFAULT_METHOD, null);
}
/**
* groovy脚本执行,通用脚本执行, 支持非class结构代码
* @param script
* @return Object
* @throws Exception
*/
@Deprecated
public Object runnerSimple(String script) throws Exception {
GroovyShell groovyShell = new GroovyShell();
Object evaluate = groovyShell.evaluate(script);
return evaluate;
}
}
Java Grovy 动态代码执行
于 2023-12-19 10:54:58 首次发布