场景:
- 实时计算任务中,采用groovy脚本定义计算逻辑。在执行时动态加载并执行
- 自定义告警插件:业务对于告警文案的展现、告警消息的处理有自定义的诉求,提供一套让业务可基于告警上下文自定义告警处理逻辑的能力,比如决定是否要拦截,告警文案的自定义等
方案:
1、后台界面提供 groovy脚本编写和管理能力
2、GroovyRepository
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>org.codehaus.groovy.all</artifactId>
<version>1.8.9.cloudengine4</version>
</dependency>
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
public class GroovyRepository {
private static final Logger logger = LoggerFactory.getLogger(GroovyRepository.class);
private static final Map<String/*实际脚本内容*/, GroovyObject> cache = new HashMap<>(50);
private static final GroovyClassLoader loader = new GroovyClassLoader();
public static synchronized GroovyObject get(String code) {
//因为这里本身是static的方法,所以需要通过spring上下文 来 获取 service,并执行
InterceptorScriptService interceptorScriptService = SmartBeanContainer.getBean("interceptorScriptService", InterceptorScriptServiceImpl.class);
String script = interceptorScriptService.getInterceptorScriptByCode(code);
// TODO remove,为了方便测试,将脚本维护在了后台参数一份。因为后台的脚本维护页面还没做
RuntimeArgCacheService runtimeArgCacheService = SmartBeanContainer.getBean("runtimeArgCacheService", RuntimeArgCacheServiceImpl.class);
String scriptBackup = runtimeArgCacheService.queryRuntimeArgs(RunTimeArgConstant.TYPE_INTERCEPTOR_INFO, code);
if(StringUtils.isNotBlank(scriptBackup)) {
script = scriptBackup;
}
//------------------------------------------------------------------------------------
GroovyObject object = cache.get(script);
if (object == null) {
Class<?> clazz = loader.parseClass(script);
try {
object = (GroovyObject) clazz.newInstance();
} catch (InstantiationException e) {
logger.error("", e);
} catch (IllegalAccessException e) {
logger.error("", e);
}
if (object != null) {
cache.put(script, object);
}
}
return object;
}
}
3、脚本的执行
//这里可以强转成自己的业务对象
interceptor = (SmartInterceptor) GroovyRepository.get(alarmInterceptor.getCode());
// 依赖注入:groovy脚本里是可能用spring中的各种service的,通过这里来给他注入进去,因为grrovy对象本身不受spring管理,
// 所以需要手动注入
SmartBeanContainer.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(interceptor);
// 极端方法:可以在这里再初始化进去spring当前的上下文,
// 这样在interceptor的实现里 也可以通过applicationContext来获取bean了
interceptor.initApplicationContext(SmartBeanContainer.getApplicationContext());