利用SPRING管理热加载的GROOVY对象!

作者:paleXu的技术博客

来源:https://palexu.github.io/posts/spring-dynamic-load-groovy-bean?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io


原因


最近做的项目属于数据分析类型,要求数据分析功能做到快速上线。该项目当前使用的语言是Java + Groovy。 使用Groovy的原因很简单,因为 Groovy 脚本支持热加载功能。项目中,简单的数据分析工作,如一些统计、排序、过滤等,都放在Groovy里完成。需要上线新的数据分析功能时,只需要编写一个新的脚本,并热加载到JVM中即可。


现在希望将一些数据源访问、数据预处理的工作也放到 Groovy 脚本中完成,并具有这样的功能:项目在线上稳定运行期间,可以通过修改数据库中的脚本来完成新产品的上线。


解决方案


  • PlanA: Java + 热加载 

  • PlanB: Groovy + 热加载


最终选择的方案是PlanB:让 Groovy 脚本需要具有访问数据源、调用rpc服务等等的能力。核心思路是利用Spring对Groovy脚本进行管理。


1、Groovy 脚本保存在数据库中。定时任务不断轮训数据库检测Groovy脚本的更新时间,若有更新,则读取脚本内容,并解析为Class。 

2、然后利用Spring提供的工具类BeanDefinitionBuilder,生成BeanDefinition。BeanDefinition中保存了Groovy脚本的meta信息,比如对其他类的依赖。 

3、接着,将BeanDefinition放入Spring上下文ApplicationContext中,并调用初始化方法,对bean进行依赖注入。 

4、最后,调用context.getBean(“xxx”)拿到该脚本。


当然,需要注意的细节有很多,比如服务降级、安全控制等,这里就不展开说了。

640?wx_fmt=jpeg


简单实现


Hello.groovy 这是保存在数据库中的Groovy脚本。

 
 
  1. import org.springframework.beans.factory.annotation.Autowired

  2. class Hello {

  3.    @Autowired

  4.    HelloService service;

  5.    HelloService getService() {

  6.        return service

  7.    }

  8.    def run() {

  9.        print(service.hello())

  10.    }

  11. }

HelloService.java 这是项目中已经提供的服务,现实项目中可以是访问数据源等功能。

 
 
  1. import org.springframework.stereotype.Component;

  2. @Component

  3. public class HelloService {

  4.    public String hello() {

  5.        return "now hello";

  6.    }

  7. }

第一步,需要拿到Spring上下文 ApplicationContext。这个有很多种实现,比如继承ApplicationContextAware接口等。

第二步,获取到编译后的脚本,如下。

 
 
  1. //从数据库中获取到脚本内容

  2. String scriptContent = "......";

  3. //编译

  4. Class clazz = new GroovyClassLoader().parseClass(scriptContent);

第三步,将bean放入上下文,并进行依赖注入

 
 
  1. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);

  2. BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();

  3. context.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(beanDefinition, "hello");

  4. beanFactory.registerBeanDefinition("hello", beanDefinition);

第四步,从上下文中获取Groovy脚本

 
 
  1. Hello hello = context.getBean("hello");

  2. hello.run();

  3. //console中应当输出下面内容,此时说明HelloService已经成功注入到groovy脚本中了

  4. //now hello

参考资料

1、Groovy 使 Spring 更出色,第 2 部分 - 在运行时改变应用程序的行为 - 用 Groovy 为 Spring 应用程序添加可动态刷新的 bean 

2、动态注入 Bean 到 Spring 容器 

3、spring 动态注册 bean 王雁 

4、Spring 动态注册 bean 李佳明

-END-

 今日推荐:

推荐一个极客时间的专栏:《深入拆解Java虚拟机》,作者是Oracle高级研究员郑雨迪,在JVM领域也是一位大牛。这个专栏,他会手把手带你入门Java虚拟机,让你系统掌握Java性能分析、调优,修炼进阶必备技能!

限时拼团¥49,原价¥68,立省¥19

扫码即可参与拼团

(很久才做一次降价活动的专栏,可不要错过。2_06.png

640?wx_fmt=jpeg

 近期热文:

关注我

640?wx_fmt=jpeg


点击“阅读原文”,看本号其他精彩内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值