内容简介:使用 yaml+groovy 实现 Java 代码可配置化
背景与目标
在 使用函数接口和枚举实现配置式编程(Java与Scala实现) ,使用了函数接口和枚举实现了配置式编程。读者可先阅读此文,再来阅读本文。
有时,需要将一些业务逻辑,使用配置化的方式抽离出来,供业务专家或外部人员来编辑和修改。这样,就需要将一些代码用脚本的方式实现。在 Java 语言体系中,与Java粘合比较紧密的是Groovy语言,本例中,将使用Groovy实现Java代码的可配置化。
目标: 指定字段集合,可输出指定对象的相应字段的值。实现可配置化目标。
设计思路
使用groovy的语法和脚本实现相应功能,然后集成到Java应用中。
实现
本文的示例代码都可以在工程 https://github.com/shuqin/ALLIN 下的包 zzz.study.groovy 下找到并运行。 记得安装 lombok 插件以及调整运行时到Java8。
依赖JAR包
本文依赖如下Jar包:groovy-all, fastjson, yamlbeans, lombok ,以及 Java8 (函数语法)
<dependency> <groupId>org.codehaus.groovy</groupId> <artifactId>groovy-all</artifactId> <version>2.4.12</version> </dependency> <dependency> <groupId>com.esotericsoftware.yamlbeans</groupId> <artifactId>yamlbeans</artifactId> <version>1.09</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.36</version> </dependency>
从脚本开始
要实现可配置化,显然要进行字段定义。 简单起见,字段通常包含三个要素: 标识、标题、字段逻辑。 采用 yaml + groovy 的方式来实现。放在 src/main/resources/scripts/ 下。 如下所示:
name: studentId title: 学生编号 script: | stu.studentId
name: studentName title: 学生姓名 script: | stu.name
name: studentAble title: 特长 script: | stu.able
字段配置的定义类 :
package zzz.study.groovy; import lombok.Data; /** * Created by shuqin on 17/11/22. */ @Data public class ReportFieldConfig { /** 报表字段标识 */ private String name; /** 报表字段标题 */ private String title; /** 报表字段逻辑脚本 */ private String script; }
配置解析
接下来,需要编写配置解析器,将配置文件内容加载到内存,建立字段映射。 配置化的核心,实际就是建立映射关系。
YamlConfigLoader 实现了单个配置内容的解析。
package zzz.study.groovy; import com.alibaba.fastjson.JSON; import com.esotericsoftware.yamlbeans.YamlReader; import java.util.List; import java.util.stream.Collectors; /** * Created by yuankui on 17/6/13. */ public class YamlConfigLoader { public static ReportFieldConfig loadConfig(String content) { try { YamlReader reader = new YamlReader(content); Object object = reader.read(); return JSON.parseObject(JSON.toJSONString(object), ReportFieldConfig.class); } catch (Exception e) { throw new RuntimeException("load config failed:" + content, e); } } public static List<ReportFieldConfig> loadConfigs(List<String> contents) { return contents.stream().map(YamlConfigLoader::loadConfig).collect(Collectors.toList()); } }
YamlConfigDirLoader 从指定目录下加载所有配置文件,并使用 YamlConfigLoader 建立所有字段的映射关系。实际工程应用中,通常是将配置保存在DB中,并从DB里读取配置。
package zzz.study.groovy; import org.springframework.util.StreamUtils; import java.io.File; import java.io.FileInputStream; import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; /** * Created by shuqin on 17/11/23. */ public class YamlConfigDirLoader { private String dir; public YamlConfigDirLoader(String dir) { this.dir = dir; } public List<ReportFieldConfig> loadConfigs() { File[] files = new File(dir).listFiles(); return Arrays.stream(files).map( file -> { try { String content = StreamUtils.copyToString(new FileInputStream(file), Charset.forName("utf-8")); return YamlConfigLoader.loadConfig(content); } catch (java.io.IOException e) { System.err.println(e.getMessage()); throw new RuntimeException(e); } } ).collect(Collectors.toList()); } }
FieldsConfigLoader 在应用启动的时候,调用 YamlConfigDirLoader 的能力加载所有配置文件。
package zzz.study.groovy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Created by shuqin on 17/11/22. */ public class FieldsConfigLoader { private static Logger logger = LoggerFactory.getLogger(FieldsConfigLoader.class); private static Map<String, ReportFieldConfig> fieldConfigMap = new HashMap<>(); static { try { List<ReportFieldConfig> fieldConfigs = new YamlConfigDirLoader("src/main/resources/scripts/").loadConfigs(); fieldConfigs.forEach( fc -> fieldConfigMap.put(fc.getName(), fc) ); logger.info("fieldConfigs: {}", fieldConfigs); } catch (Exception ex) { logger.error("failed to load fields conf", ex); } } public static ReportFieldConfig getFieldConfig(String name) { return fieldConfigMap.get(name); } }
客户端集成
package zzz.study.groovy; import groovy.lang.Binding; import groovy.lang.GroovyShell; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import zzz.study.function.basic.Person; import zzz.study.function.basic.Student; /** * Created by shuqin on 17/11/23. */ public class StudentOutput { static List<String> fields = Arrays.asList("studentId", "studentName", "studentAble"); public static void main(String[] args) { List<Person> students = getPersons(); List<String> stundentInfos = students.stream().map( p -> getOneStudentInfo(p, fields) ).collect( Collectors.toList()); System.out.println(String.join("\n", stundentInfos)); } private static String getOneStudentInfo(Person p, List<String> fields) { List<String> stuInfos = new ArrayList<>(); fields.forEach( field -> { ReportFieldConfig fieldConfig = FieldsConfigLoader.getFieldConfig(field); Binding binding = new Binding(); binding.setVariable("stu", p); GroovyShell shell = new GroovyShell(binding); Object result = shell.evaluate(fieldConfig.getScript()); //System.out.println("result from groovy script: " + result); stuInfos.add(String.valueOf(result)); } ); return String.join(",", stuInfos); } private static List<Person> getPersons() { Person s1 = new Student("s1", "liming", "Study"); Person s2 = new Student("s2", "xueying", "Piano"); return Arrays.asList(new Person[]{s1, s2}); } }
这里使用了 GroovyShell, Binding 的基本功能来运行 groovy 。虽然例子中只是简单的取属性值,实际上还可以灵活调用传入对象的方法,展示更复杂的业务逻辑。比如 stu.name 还可写成 stu.getName() 。
运行后得到如下结果:
s1,liming,Study s2,xueying,Piano
至此,DEMO 完成。实际工程集成的时候,需要先将所有字段定义的脚本配置加载到内存并解析和缓存起来,在需要的时候直接使用,而不会像demo里每个字段都new一次。
小结
本文使用了yaml+groovy实现了Java代码的可配置化。可配置化的优势是,可以将一些简单的逻辑公开给外部编辑和使用,增强了互操作性;而对于复杂逻辑来说,可配置化代码的调试则会比较麻烦。因此,可配置化的度要掌握好。 配置本身就是代码,只是配置具有公开化的特点。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- React 代码规范配置
- 配置vscode调试java代码
- 配置eslint规范项目代码风格
- Fabric 1.0源代码笔记 之 configtx(配置交易) #ChannelConfig(通道配置)
- 配置即代码:先有鸡还是先有蛋
- Eclipse配置MyBatis代码自动化功能
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
实战Java高并发程序设计
葛一鸣、郭超 / 电子工业出版社 / 2015-10-1 / CNY 69.00
在过去单核CPU时代,单任务在一个时间点只能执行单一程序,随着多核CPU的发展,并行程序开发就显得尤为重要。 《实战Java高并发程序设计》主要介绍基于Java的并行程序设计基础、思路、方法和实战。第一,立足于并发程序基础,详细介绍Java中进行并行程序设计的基本方法。第二,进一步详细介绍JDK中对并行程序的强大支持,帮助读者快速、稳健地进行并行程序开发。第三,详细讨论有关“锁”的优化和提高......一起来看看 《实战Java高并发程序设计》 这本书的介绍吧!