IT科技

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

时间:2010-12-5 17:23:32  作者:IT科技类资讯   来源:域名  查看:  评论:0
内容摘要:本文转载自微信公众号「Java大厂面试官」,作者laker。转载本文请联系Java大厂面试官公众号。背景前段时间体验了Zuul的groovy Filter,其实现了动态热加载Filter,可以在不重启

本文转载自微信公众号「Java大厂面试官」,从零成作者laker。搭建转载本文请联系Java大厂面试官公众号。脚手架St集

背景

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

需要依赖groovy-all

<dependency>      <groupId>org.codehaus.groovy</groupId>      <artifactId>groovy-all</artifactId>      <version>2.4.12</version> 版本自己去适配哈  </dependency> 

什么是现动 Groovy?

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

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

使用体验

先来体验下实现后的成果

1、服务器托管利用Spring Boot的CommandLineRunner注册SpringBean、GroovyBean

初始化加载项目中RuleFilter的Spring Bean 直接使用@Autowired注解配合List即可获取所有RuleFilter的子类 初始化Groovy动态扫描的监控间隔,目录配置 这里配置的是每5秒检查D:\\laker\\lakernote\\groovy目录下,新增或者修改的文件用于编译加载 初始化也会加载D:\\laker\\lakernote\\groovy目录下文件。 @Component public class GroovyRunner implements CommandLineRunner {      @Autowired     List<RuleFilter> ruleFilterList;     @Override     public void run(String... args) throws Exception {          // 初始化加载项目中RuleFilter的Springbean         RuleFilterLoader.getInstance().initSpringRuleFilter(ruleFilterList);         try {              // 每隔多少秒,扫描目录下的groovy文件             RuleFilterFileManager.init(5, "D:\\laker\\lakernote\\groovy");         } catch (Exception e) {              e.printStackTrace();             throw new RuntimeException();         }     } } 

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

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

@Component public class JavaRule extends RuleFilter {      /**      * 具体规则执行      * @param msg      */     @Override     public void run(String msg) {          System.out.println(" === Java 实现的业务规则 order = 1 , msg = " + msg + " === ");     }     /**      * 该规则是否被执行      * @return      */     @Override     public boolean shouldRun() {          return true;     }     /**      * 该规则执行的顺序      * @return      */     @Override     public int runOrder() {          return 1;     } } 

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

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

public class GroovyRule extends RuleFilter {      @Override     public void run(String msg) {          System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === ");     }     @Override     public boolean shouldRun() {          return true;     }     @Override     public int runOrder() {          return 2;     } } 

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

4、云服务器在合适的位置使用RuleFilterProcessor

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

@RestController @RequestMapping("/groovy") public class GroovyController {      @Autowired     private RuleFilterProcessor ruleFilterProcessor;     @GetMapping()     @ApiOperation("测试groovy的动态加载")     public void transaction(@RequestParam String msg) {          ruleFilterProcessor.runRuleFilters(msg);     } } 

5、启动并验证

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

1). 启动程序

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

结果如下:

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

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

“不用重启服务

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

结果如下:

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

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

public class Groovy2Rule extends RuleFilter {      @Override     public void run(String msg) {          System.out.println(" === Groovy 实现的业务规则 order = " + runOrder() + ", msg = " + msg + " === ");         List<RuleFilter> ruleFilters = RuleFilterLoader.getInstance().getFilters();         for (RuleFilter ruleFilter : ruleFilters) {              System.out.println(ruleFilter.getClass().getName());         }     }     @Override     public boolean shouldRun() {          return true;     }     @Override     public int runOrder() {          return 3;     } } 

不用重启服务

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

结果如下:

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

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

实现

核心的模块如下

RuleFilter :规则过滤器抽象类,用于扩展实现业务规则,供Java和Groovy继承。 RuleFilterLoader :规则过滤器加载器,用于加载基于Spring的RuleFilter实现类和动态编译指定文件基于Groovy的RuleFilter实现类。

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

RuleFilterFileManager : 一个独立线程轮询监听指定目录文件的变化配合RuleFilterLoader ( 规则过滤器加载器)使用。 RuleFilterProcessor: 业务规则处理器核心入口

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

贴上部分核心代码如下:

RuleFilter.java

public abstract class RuleFilter implements IRule, Comparable<RuleFilter> {      abstract public int runOrder();     @Override     public int compareTo(RuleFilter ruleFilter) {          return Integer.compare(this.runOrder(), ruleFilter.runOrder());     }     ... } 

RuleFilterLoader.java

public class RuleFilterLoader {      public boolean putFilter(File file) throws Exception {          String sName = file.getAbsolutePath() + file.getName();         if (filterClassLastModified.get(sName) != null && (file.lastModified() != filterClassLastModified.get(sName))) {              LOG.debug("reloading filter " + sName);             filterRegistry.remove(sName);         }         RuleFilter filter = filterRegistry.get(sName);         if (filter == null) {              Class clazz = compile(file);             if (!Modifier.isAbstract(clazz.getModifiers())) {                  filter = (RuleFilter) clazz.newInstance();                 filterRegistry.put(file.getAbsolutePath() + file.getName(), filter);                 ruleFilters.clear();                 filterClassLastModified.put(sName, file.lastModified());                 return true;             }         }         return false;     }     public List<RuleFilter> getFilters() {          if (CollUtil.isNotEmpty(ruleFilters)) {              return ruleFilters;         }         ruleFilters.addAll(springRuleFilterList);         ruleFilters.addAll(this.filterRegistry.values());         Collections.sort(ruleFilters);         return ruleFilters;     }     private Class compile(File file) throws IOException {          GroovyClassLoader loader = getGroovyClassLoader();         Class groovyClass = loader.parseClass(file);         return groovyClass;     }     GroovyClassLoader getGroovyClassLoader() {          return new GroovyClassLoader();     }     ... } 

RuleFilterFileManager.java

public class RuleFilterFileManager {      public static void init(int pollingIntervalSeconds, String... directories)  {          if (INSTANCE == null) INSTANCE = new RuleFilterFileManager();         INSTANCE.aDirectories = directories;         INSTANCE.pollingIntervalSeconds = pollingIntervalSeconds;         INSTANCE.manageFiles();         INSTANCE.startPoller();     }     void startPoller() {          poller = new Thread("GroovyRuleFilterFileManagerPoller") {              @Override             public void run() {                  while (bRunning) {                      try {                          sleep(pollingIntervalSeconds * 1000);                         manageFiles();                     } catch (Exception e) {                          e.printStackTrace();                     }                 }             }         };         poller.setDaemon(true);         poller.start();     }    void processGroovyFiles(List<File> aFiles) throws Exception, InstantiationException, IllegalAccessException {          for (File file : aFiles) {              RuleFilterLoader.getInstance().putFilter(file);         }     }     void manageFiles() throws Exception, IllegalAccessException, InstantiationException {          List<File> aFiles = getFiles();         processGroovyFiles(aFiles);     }     ... } 

RuleFilterProcessor.java

@Component public class RuleFilterProcessor {      public void runRuleFilters(String msg) {          List<RuleFilter> list = RuleFilterLoader.getInstance().getFilters();         if (list != null) {              list.forEach(ruleFilter -> {                  if (ruleFilter.shouldRun()) {                      ruleFilter.run(msg);                 }             });         }     } } 

总结

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

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

参考:

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

copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap