文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

从零搭建开发脚手架Spring Boot 集成Groovy实现动态加载业务规则

2024-12-14 00:32

关注

背景

前段时间体验了Zuul的groovy Filter,其实现了动态热加载Filter,可以在不重启应用的情况下新增、修改自己的业务规则,现在我也来仿照Zuul来山寨一个,用于我们日常多变的业务规则中。

需要依赖groovy-all

  1.  
  2.      org.codehaus.groovy 
  3.      groovy-all 
  4.      2.4.12 版本自己去适配哈 
  5.   

什么是 Groovy?

类似于Python,perl等灵活动态语言,不过它是运行在java平台上,也就是Groovy最后代码会被编译成class字节码文件,集成到web应用或者java应用中,groovy编写的程序其实就是特殊点的Java程序,而且java类库可以在groovy中直接使用。

Groovy 的另一个好处是,它的语法与 Java 语言的语法很相似。

使用体验

先来体验下实现后的成果

1、利用Spring Boot的CommandLineRunner注册SpringBean、GroovyBean

  1. @Component 
  2. public class GroovyRunner implements CommandLineRunner { 
  3.     @Autowired 
  4.     List ruleFilterList; 
  5.     @Override 
  6.     public void run(String... args) throws Exception { 
  7.         // 初始化加载项目中RuleFilter的Springbean 
  8.         RuleFilterLoader.getInstance().initSpringRuleFilter(ruleFilterList); 
  9.         try { 
  10.             // 每隔多少秒,扫描目录下的groovy文件 
  11.             RuleFilterFileManager.init(5, "D:\\laker\\lakernote\\groovy"); 
  12.         } catch (Exception e) { 
  13.             e.printStackTrace(); 
  14.             throw new RuntimeException(); 
  15.         } 
  16.     } 

2、项目内不变的规则以Java实现继承RuleFilter

这个就是普通的Java类,我们把不变的规则以这种方式实现。

  1. @Component 
  2. public class JavaRule extends RuleFilter { 
  3.      
  4.     @Override 
  5.     public void run(String msg) { 
  6.         System.out.println(" === Java 实现的业务规则 order = 1 , msg = " + msg + " === "); 
  7.     } 
  8.      
  9.     @Override 
  10.     public boolean shouldRun() { 
  11.         return true
  12.     } 
  13.  
  14.      
  15.     @Override 
  16.     public int runOrder() { 
  17.         return 1; 
  18.     } 

3、项目内经常变动的以Groovy来实现

groovy兼容Java语法,可以直接用java语法来写。

  1. public class GroovyRule extends RuleFilter { 
  2.     @Override 
  3.     public void run(String msg) { 
  4.  
  5.         System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === "); 
  6.     } 
  7.     @Override 
  8.     public boolean shouldRun() { 
  9.         return true
  10.     } 
  11.     @Override 
  12.     public int runOrder() { 
  13.         return 2; 
  14.     } 

“然后把这个xxx.java文件丢到我们监控的文件夹即可

4、在合适的位置使用RuleFilterProcessor

这里我写了个Controller用来测试动态加载规则。

  1. @RestController 
  2. @RequestMapping("/groovy"
  3. public class GroovyController { 
  4.     @Autowired 
  5.     private RuleFilterProcessor ruleFilterProcessor; 
  6.  
  7.     @GetMapping() 
  8.     @ApiOperation("测试groovy的动态加载"
  9.     public void transaction(@RequestParam String msg) { 
  10.         ruleFilterProcessor.runRuleFilters(msg); 
  11.     } 

5、启动并验证

我分了几个场景验证如下:

1). 启动程序

浏览器访问:http://localhost:8080/groovy?msg=laker%20666

结果如下:

  1. === Java 实现的业务规则 order = 1 , msg = laker 666 ===  
  2. === Groovy 实现的业务规则 order = 2 , msg = laker 666 ===  

2.) 我修改GroovyRule中的runOrder(),把它改为0

“不用重启服务

浏览器访问:http://localhost:8080/groovy?msg=laker%20666

结果如下:

  1. === Groovy 实现的业务规则 order = 0 , msg = laker 666 ===  
  2. === Java 实现的业务规则 order = 1 , msg = laker 666 ===  

3). 我新增一个Groovy2Rule然后丢进上面指定的监控文件夹

  1. public class Groovy2Rule extends RuleFilter { 
  2.     @Override 
  3.     public void run(String msg) { 
  4.         System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === "); 
  5.         List ruleFilters = RuleFilterLoader.getInstance().getFilters(); 
  6.         for (RuleFilter ruleFilter : ruleFilters) { 
  7.             System.out.println(ruleFilter.getClass().getName()); 
  8.         } 
  9.     } 
  10.     @Override 
  11.     public boolean shouldRun() { 
  12.         return true
  13.     } 
  14.  
  15.     @Override 
  16.     public int runOrder() { 
  17.         return 3; 
  18.     } 

不用重启服务

浏览器访问:http://localhost:8080/groovy?msg=laker%20666

结果如下:

  1.  === Groovy 实现的业务规则 order = 0 , msg = laker 666 ===  
  2.  === Java 实现的业务规则 order = 1 , msg = laker 666 ===  
  3.  === Groovy 实现的业务规则 order = 3, msg = laker 666 ===  
  4. com.laker.map.moudle.groovy.javarule.GroovyRule 
  5. com.laker.map.moudle.groovy.javarule.JavaRule 
  6. com.laker.map.moudle.groovy.Groovy2Rule 

“这里如果想调用Spring环境中的bean可以借助SpringContextUtil

实现

核心的模块如下

存储所有的规则过滤器并能动态加载改变的和新增的规则。

“这四个核心模块都是盗版Zuul的实现。

贴上部分核心代码如下:

RuleFilter.java

  1. public abstract class RuleFilter implements IRule, Comparable { 
  2.  
  3.     abstract public int runOrder(); 
  4.  
  5.     @Override 
  6.     public int compareTo(RuleFilter ruleFilter) { 
  7.         return Integer.compare(this.runOrder(), ruleFilter.runOrder()); 
  8.     } 
  9.     ... 

RuleFilterLoader.java

  1. public class RuleFilterLoader { 
  2.     public boolean putFilter(File file) throws Exception { 
  3.         String sName = file.getAbsolutePath() + file.getName(); 
  4.         if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) { 
  5.             LOG.debug("reloading filter " + sName); 
  6.             filterRegistry.remove(sName); 
  7.         } 
  8.         RuleFilter filter = filterRegistry.get(sName); 
  9.         if (filter == null) { 
  10.             Class clazz = compile(file); 
  11.             if (!Modifier.isAbstract(clazz.getModifiers())) { 
  12.                 filter = (RuleFilter) clazz.newInstance(); 
  13.                 filterRegistry.put(file.getAbsolutePath() + file.getName(), filter); 
  14.                 ruleFilters.clear(); 
  15.                 filterClassLastModified.put(sName, file.lastModified()); 
  16.                 return true
  17.             } 
  18.         } 
  19.         return false
  20.     } 
  21.     public List getFilters() { 
  22.         if (CollUtil.isNotEmpty(ruleFilters)) { 
  23.             return ruleFilters; 
  24.         } 
  25.         ruleFilters.addAll(springRuleFilterList); 
  26.         ruleFilters.addAll(this.filterRegistry.values()); 
  27.         Collections.sort(ruleFilters); 
  28.         return ruleFilters; 
  29.     } 
  30.     private Class compile(File file) throws IOException { 
  31.         GroovyClassLoader loader = getGroovyClassLoader(); 
  32.         Class groovyClass = loader.parseClass(file); 
  33.         return groovyClass; 
  34.     } 
  35.     GroovyClassLoader getGroovyClassLoader() { 
  36.         return new GroovyClassLoader(); 
  37.     } 
  38.     ... 

RuleFilterFileManager.java

  1. public class RuleFilterFileManager { 
  2.     public static void init(int pollingIntervalSeconds, String... directories)  { 
  3.         if (INSTANCE == null) INSTANCE = new RuleFilterFileManager(); 
  4.  
  5.         INSTANCE.aDirectories = directories; 
  6.         INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds; 
  7.         INSTANCE.manageFiles(); 
  8.         INSTANCE.startPoller(); 
  9.     } 
  10.     void startPoller() { 
  11.         poller = new Thread("GroovyRuleFilterFileManagerPoller") { 
  12.             @Override 
  13.             public void run() { 
  14.                 while (bRunning) { 
  15.                     try { 
  16.                         sleep(pollingIntervalSeconds * 1000); 
  17.                         manageFiles(); 
  18.                     } catch (Exception e) { 
  19.                         e.printStackTrace(); 
  20.                     } 
  21.                 } 
  22.             } 
  23.         }; 
  24.         poller.setDaemon(true); 
  25.         poller.start(); 
  26.     } 
  27.    void processGroovyFiles(List aFiles) throws Exception, InstantiationException, IllegalAccessException { 
  28.  
  29.         for (File file : aFiles) { 
  30.             RuleFilterLoader.getInstance().putFilter(file); 
  31.         } 
  32.     } 
  33.     void manageFiles() throws Exception, IllegalAccessException, InstantiationException { 
  34.         List aFiles = getFiles(); 
  35.         processGroovyFiles(aFiles); 
  36.     } 
  37.     ... 

RuleFilterProcessor.java

  1. @Component 
  2. public class RuleFilterProcessor { 
  3.     public void runRuleFilters(String msg) { 
  4.         List list = RuleFilterLoader.getInstance().getFilters(); 
  5.         if (list != null) { 
  6.             list.forEach(ruleFilter -> { 
  7.                 if (ruleFilter.shouldRun()) { 
  8.                     ruleFilter.run(msg); 
  9.                 } 
  10.             }); 
  11.         } 
  12.     } 

总结

可以看到使用起来是相当的方便,仅依赖groovy-all,整体代码结构简单。

性能和稳定性未测试,但是这基本就是翻版的Zuul,Zuul都在使用了,应该没什么问题。

参考:

参考了Zuul源码,比较简单,建议大家都去看看。

 

来源:Java大厂面试官内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯