场景:
- 实时计算任务中,采用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());
