当前位置: 首页 > 科技观察

从头搭建开发脚手架,集成Groovy与SpringBoot,实现业务规则动态加载

时间:2023-03-21 13:13:24 科技观察

转载本文,请联系Java大厂面试官公众号。背景前段时间体验了Zuul的groovyFilter,它实现了动态热加载Filter,可以在不重启应用的情况下添加和修改自己的业务规则。下面我就仿照Zuul复制一份供我们日常使用。在更改的业务规则中。需要依赖groovy-allorg.codehaus.groovygroovy-all2.4.12自己适配什么是Groovy?类似于Python、perl等灵活的动态语言,但它运行在java平台上,即Groovy的最终代码会被编译成类字节码文件,集成到web应用程序或java应用程序中,用groovy编写程序其实是一个特殊的Java程序,在groovy中可以直接使用java类库。Groovy的另一个好处是它的语法与Java语言非常相似。先使用体验,体验实现后的效果1、在项目中使用SpringBoot的CommandLineRunner注册SpringBean和GroovyBean来初始化加载RuleFilter的SpringBean。直接用@Autowired注解配合List获取RuleFilter的所有子类来初始化Groovy动态扫描的监听。间隔、目录配置这里的配置是每隔5秒查看一次D:\\laker\\lakernote\\groovy目录。新增或修改的文件用于编译、加载和初始化,同时会加载D:\\laker\\lakernote\\groovy目录下的文件。@ComponentpublicclassGroovyRunnerimplementsCommandLineRunner{@AutowiredListruleFilterList;@Overridepublicvoidrun(String...args)throwsException{//初始化SpringbeanRuleFilterLoader.getInstance().initSpringRuleFilter(ruleFilterList);try{//每隔几秒扫描groovy文件RuleFilterFileManager.init(5,"D:\\laker\\lakernote\\groovy");}catch(Exceptione){e.printStackTrace();thrownewRuntimeException();}}}2.项目没有改变规则,Java实现。继承RuleFilter是一个普通的Java类。我们以这种方式实施相同的规则。@ComponentpublicclassJavaRuleextendsRuleFilter{/***具体规则执行*@parammsg*/@Overridepublicvoidrun(Stringmsg){System.out.println("===Java实现业务规则order=1,msg="+msg+"===");}/***规则是否执行*@return*/@OverridepublicbooleanshouldRun(){returntrue;}/***规则执行的顺序*@return*/@OverridepublicintrunOrder(){return1;}}3、项目中经常使用Groovy来实现兼容Groovy的Java语法,可以直接用Java语法编写。publicclassGroovyRuleextendsRuleFilter{@Overridepublicvoidrun(Stringmsg){System.out.println("===Groovy实现的业务规则order="+runOrder()+",msg="+msg+"===");}@OverridepublicbooleanshouldRun(){returntrue;}@OverridepublicintrunOrder(){return2;}}”然后把这个xxx.java文件扔到我们的监控文件夹里就行了。4.在合适的位置使用RuleFilterProcessor这里我写了一个Controller来测试动态加载规则。@RestController@RequestMapping("/groovy")publicclassGroovyController{@AutowiredprivateRuleFilterProcessorruleFilterProcessor;@GetMapping()@ApiOperation("测试groovy的动态加载")publicvoidtransaction(@RequestParamStringmsg){ruleFilterProcessor.runRuleFilters(msgstartandverify);}}5.我分了几个场景来验证如下:1)。启动程序浏览器访问:http://localhost:8080/groovy?msg=laker%20666结果如下:==Java实现的业务规则order=1,msg=laker666======业务rulesimplementedbyGroovyorder=2,msg=laker666===2.)我修改了GroovyRule中的runOrder()并将其更改为0"而无需重新启动服务浏览器访问:http://localhost:8080/groovy?msg=laker%20666结果如下:===Groovy实现的业务规则order=0,msg=laker666======Java实现的业务规则order=1,msg=laker666===3).我补充道一个Groovy2Rule,丢到上面指定的监控文件夹中==");ListruleFilters=RuleFilterLoader.getInstance().getFilters();for(RuleFilterruleFilter:ruleFilters){System.out.println(ruleFilter.getClass().getName());}}@OverridepublicbooleanshouldRun(){returntrue;}@OverridepublicintrunOrder(){return3;}}无需重启服务浏览器访问:http://localhost:8080/groovy?msg=laker%20666结果如下:===Groovy实现的业务规则order=0,msg=laker666======Java实现的业务规则order=1,msg=laker666======Groovy实现的业务rulesorder=3,msg=laker666===com.laker.map.moudle.groovy.javarule.GroovyRulecom.laker.map.moudle.groovy.javarule.JavaRulecom.laker.map.moudle.groovy.Groovy2Rule”这里如果你想在环境中调用SpringBeans可以使用SpringContextUtil实现核心模块如下基于RuleFilter实现类和基于Groovy的RuleFilter实现类动态编译指定文件。存储所有规则过滤器并可以动态加载更改的和新添加的规则。RuleFilterFileManager:一个独立的线程轮询和监视指定目录文件的变化,与RuleFilterLoader(规则过滤器加载器)一起使用。RuleFilterProcessor:业务规则处理器核心入口》这四个核心模块是盗版Zuul的实现。贴上部分核心代码如下:RuleFilter.javapublicabstractclassRuleFilterimplementsIRule,Comparable{abstractpublicintrunOrder();@OverridepublicintcompareTo(RuleFilterruleFilter){returnInteger.compare(this.runOrder(),ruleFilter.runOrder());}...}RuleFilterLoader。javapublicclassRuleFilterLoader{publicbooleanputFilter(Filefile)throwsException{StringsName=file.getAbsolutePath()+file.getName();if(filterClassLastModified.get(sName)!=null&&(file.lastModified()!=filterClassLastModified.get(sName))){LOG.debug("reloadingfilter"+sName);filterRegistry.remove(sName);}RuleFilterfilter=filterRegistry.get(sName);if(filter==null){Classclazz=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());returntrue;}}returnfalse;}publicListgetFilters(){if(CollUtil.isNotEmpty(ruleFilters)){returnruleFilters;}ruleFilters.addAll(springRuleFilterList);ruleFilters.addAll(this.filterRegistry.values());Collections.sort(ruleFilters);returnruleFilters;}privateClasscompile(Filefile)throwsIOException{GroovyClassLoaderloader=getGroovyClassLoader();ClassgroovyClass=loader.parseClass(file);returngroovyClass;}GroovyClassLoadergetGroovyClassLoader(){returnnewGroovyClassLoader();}...}RuleFilterFileManager.javapublicclassRuleFilterFileManager{publicstaticvoidinit(intpollingIntervalSeconds,String...directories){if(INSTANCE==null)INSTANCE=newRuleFilterFileManager();INSTANCE.aDirectories=目录;INSTANCE.pollingIntervalSeconds=pollingIntervalSeconds;INSTANCE.manageFiles();INSTANCE.startPoller();}voidstartPoller(){poller=newThread("GroovyRuleFilterFileManagerPoller"){@Overridepublicvoidrun(){while(bRunning){try{sleep(pollingIntervalSeconds*1000);manageFiles();}catch(Exceptione){e.printStackTrace();}}}};poller.setDaemon(true);poller.start();}voidprocessGroovyFiles(ListaFiles)throwsException,InstantiationException,IllegalAccessException{for(Filefile:aFiles){RuleFilterLoader.getInstance().putFilter(file);}}voidmanageFiles()throwsException,IllegalAccessException,InstantiationException{ListaFiles=getFiles();processGroovyFiles(aFiles);}...}RuleFilterProcessor.java@ComponentpublicclassRuleFilterProcessor{publicvoidrunRuleFilters(Stringmsg){Listlist=RuleFilterstLoader()getFilters();if(list!=null){list.forEach(ruleFilter->{if(ruleFilter.shouldRun()){ruleFilter.run(msg);}});}}}summary在使用中可以看到相当方便,只依赖groovy-all,整体代码结构简单,性能和稳定性没有测试,但这基本上是Zuul的翻版,Zuul在用,应该没有问题。参考:参考Zuul源码,比较简单,建议大家看看。