[TOC] # base-spring-boot-starter base-spring-boot-starter 提供了平台通用的模型和工具类,作为公司二方包提供基础组件。 ## 功能 * 加密解密算法 * 公用model类 * 通用工具类 * 通用事件驱动模型 * 通用异常类 * 通用异常通知 * 通用线程池配置 * 通用下载水印 * 统一异常处理 ## maven 配置 ![](https://img.kancloud.cn/b1/ea/b1ea062dcb04a42bc192475723ef2ca5_2534x1153.png) ## 公用model类 用户 角色 菜单 公共模型,提供权限模型的基础工具类。映射数据库中的三张表sys_user,sys_role, sys_menu ![](https://img.kancloud.cn/f5/50/f5502c27f878ce17996383d715ff5a55_1102x984.png) ## 通用工具类 通过各种通用配置,通用工具类简化开发 ### 菜单树 ![](https://img.kancloud.cn/1a/a0/1aa0d217a93ed115881168de94f3aa69_2490x1054.png) ### 获取用户信息 * 获取用户信息工具类1 ![](https://img.kancloud.cn/df/6d/df6df7f5c3442b5a66c46db38e644bb3_2548x1176.png) * 获取用户信息工具类2 ``` com.open.capacity.uaa.common.util.AuthUtils.getLoginAppUser() ``` ### 通用分页对象 ![](https://img.kancloud.cn/65/cf/65cf7d828d61144c973c2830e666f362_2527x1212.png) ### BeanValidator参数校验 ![](https://img.kancloud.cn/f3/3c/f33c4da9d88ada0885426206e654b1b6_1355x1132.png) ### 父子线程异步传递问题 ![](https://img.kancloud.cn/b7/66/b7668cdb5891a8fea1edbe678e1a5910_2429x1000.png) ### Optional工具类 ``` Optional.of(1).ifPresent(o ->{ System.out.println(o); }); System.out.println(Optional.ofNullable(2).orElseGet( ()-> 1)); ``` ### EntityUtils ``` List<Long> userIds = EntityUtils.toList(list, SysUser::getId) ; EntityUtils.toSet(roles, SysRole::getCode); Map<Long, SysMenu> roleMenusMap = EntityUtils.toMap(roleMenus, SysMenu::getId); ``` ### json通用配置 ![](https://img.kancloud.cn/b1/3e/b13ea340361cfd142723a871c883a139_2512x1101.png) ### spring 安全防火墙配置 ![](https://img.kancloud.cn/a9/42/a942a2a29de9e17b062e6a5ca1f6e034_2509x1132.png) ### 白名单配置 ![](https://img.kancloud.cn/4f/21/4f2129dfc88717d199b02faa9c85e586_2085x1212.png) ### spring自定义密码处理器 ![](https://img.kancloud.cn/5c/db/5cdbaee8bae7d01fa6f047cab59eeae7_2531x506.png) ### google 二维码 ![](https://img.kancloud.cn/50/c2/50c22644b513226fe8364d5922f9e213_2560x984.png) ~~~ @Override @SneakyThrows public BufferedImage createQrCode(String deviceId, String secret) { String codeUrl = GoogleOTPAuthUtil.generateTotpURI(deviceId, secret); //生成二维码配置 Map<EncodeHintType,Object> hints = new HashMap<>(); //设置纠错等级 hints.put(EncodeHintType.ERROR_CORRECTION,ErrorCorrectionLevel.L); //编码类型 hints.put(EncodeHintType.CHARACTER_SET,"UTF-8"); BitMatrix bitMatrix = new MultiFormatWriter().encode(codeUrl,BarcodeFormat.QR_CODE,200,200,hints); MatrixToImageConfig matrixToImageConfig = new MatrixToImageConfig(); BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix, matrixToImageConfig); return bufferedImage; } ~~~ ### 白名单配置类 ![](https://img.kancloud.cn/d4/84/d484be4aaf4d44e9aa67be8053a0f485_2222x1004.png) ### 接口级自动加密工具 ![](https://img.kancloud.cn/a2/a3/a2a370782cd7efd92a81c428028792f8_2498x1191.png) ### javers审计比较对象不同 ``` import org.javers.core.Changes; import org.javers.core.Javers; import org.javers.core.JaversBuilder; import org.javers.core.diff.Change; import org.javers.core.diff.Diff; import org.javers.core.diff.changetype.NewObject; import org.javers.core.diff.changetype.ObjectRemoved; import org.javers.core.diff.changetype.ValueChange; import lombok.Data; public class ObjectComparator { public static void main(String[] args) { // 创建Javers实例 Javers javers = JaversBuilder.javers().build(); // 创建要比较的对象 Person person1 = new Person("John", 30); Person person2 = new Person("John", 35,"1"); // 使用Javers比较两个对象 Diff diff = javers.compare(person1, person2); // 输出ValueChange类型的变化 Changes changes = diff.getChanges(); for (Change change : changes) { if ((change instanceof NewObject)) { System.out.println("新增改动: " + change); // change.getAffectedObject().ifPresent(System.out::println); System.out.println("新增"); } if ((change instanceof ObjectRemoved)) { System.out.println("删除改动: " + change); // change.getAffectedObject().ifPresent(System.out::println); } if ((change instanceof ValueChange)) { System.out.println("修改改动: " + change); // change.getAffectedObject().ifPresent(System.out::println); } } } } @Data class Person { private String name; private int age; private String id ; public Person(String name, int age) { this.name = name; this.age = age; } public Person(String name, int age,String id ) { this.name = name; this.age = age; this.id=id ; } } ``` ### hutool 工具类 文档:[https://www.hutool.cn/docs/#/](https://www.hutool.cn/docs/#/) #### 类型转化 ~~~ int a = 1 ; String aString = Convert.toStr(a) ;//整型转String System.out.println(aString); long[] b = {1L,2L,3L,4L,5L} ; String bString = Convert.toStr(b) ;//long数组 转String数组 System.out.println(bString); String[] c = {"1","2","3","4","5"}; Integer[] cArray = Convert.toIntArray(c) ;// String数组转int数组 System.out.println(cArray); String dateString ="2018-05-22 14:09:33" ; Date date = Convert.toDate(dateString) ;//时间转化 System.out.println(date); String quanjiao ="1234567" ; String banjiao =Convert.toSBC(quanjiao) ;//半角转化 System.out.println(banjiao); System.out.println(Convert.toDBC(banjiao)) ;//全角转化 String str = "明文字符串" ; String encodeString = Convert.toHex(str,CharsetUtil.CHARSET_UTF_8) ;//加密 System.out.println(encodeString); String decodeString = Convert.hexToStr(encodeString, CharsetUtil.CHARSET_UTF_8) ; //解密 System.out.println(decodeString); String unicode = Convert.strToUnicode(str) ; System.out.println(unicode); String raw = Convert.unicodeToStr(unicode); System.out.println(raw); String result = Convert.convertCharset(str, CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1) ;//字符集转化 System.out.println(result); String iso = Convert.convertCharset(result, CharsetUtil.ISO_8859_1, CharsetUtil.UTF_8) ; //字符集转化 System.out.println(iso); long date = 72 ; long day = Convert.convertTime(date, TimeUnit.HOURS, TimeUnit.DAYS);//时间转化72小时3天 System.out.println(day); double price = 6449.89; System.out.println(Convert.digitToChinese(price)) ;//金钱转化 ~~~ #### 时间 ~~~ Date date = DateUtil.date(); String format = DateUtil.format(date, "yyyy-MM-dd") ; System.out.println(format); Date beginOfDate = DateUtil.beginOfDay(date) ; Date endOfDate = DateUtil.endOfDay(date) ; System.out.println(beginOfDate); System.out.println(endOfDate); date = DateUtil.date(System.currentTimeMillis()); String now = DateUtil.now(); //yyyy-MM-dd HH:mm:ss String tody = DateUtil.today();//yyyy-MM-dd DateUtil.yesterday(); DateUtil.tomorrow(); DateUtil.lastWeek(); DateUtil.lastMonth(); int age = DateUtil.ageOfNow("1998-04-04" ) ; System.out.println(age); ~~~ #### excel ~~~ List<?> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd", DateUtil.date(), 3.22676575765); List<?> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1", DateUtil.date(), 250.7676); List<?> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2", DateUtil.date(), 0.111); List<?> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3", DateUtil.date(), 35); List<?> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4", DateUtil.date(), 28.00); List<List<?>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5); BigExcelWriter writer= ExcelUtil.getBigWriter("e:"+File.separator+"xxx.xlsx"); // 一次性写出内容,使用默认样式 writer.write(rows); // 关闭writer,释放内存 writer.close(); ExcelReader reader = ExcelUtil.getReader("e:"+File.separator+"xxx.xlsx"); List<Map<String,Object>> readAll = reader.readAll(); readAll.forEach( item -> { System.out.println(item); }); ~~~ ## 通用事件驱动模型 Disruptor是一个高性能的并发框架,专门用于改善事件驱动的系统。它可以在不使用锁的情况下实现高并发,并能够提供一些非常高效的内存读写操作,这使得它在处理高并发的情况下能够获得很高的性能。基于此创建的双event事件模型可以简化开发disruptor的难度,更快速的使用事件驱动解耦项目。 ![](https://img.kancloud.cn/b4/e1/b4e1e376cb1f0d939ff28e8c03fc67bd_1707x691.png) 通过请求异步化带来其他明显优点: * 可以处理更高并发连接数,提高系统整体吞吐量 * 请求解析与业务处理完全分离,职责单一 * 自定义业务线程池,我们可以更容易对其监控,降级等处理 * 可以根据不同业务,自定义不同线程池,相互隔离,不用互相影响 ### 类图关系 ![](https://img.kancloud.cn/be/28/be28bf933555792dc08171951efb7505_1665x849.png) ### 自动装配 ![](https://img.kancloud.cn/62/dd/62dd56a14eef17d2e538816a2ff1f8c0_2502x1055.png) ## 通用异常 平台提供通用异常定义,后续可扩展 ![](https://img.kancloud.cn/21/e2/21e2f3c61b9df7384dd66b8a88bd079d_2267x1042.png) ## 通用异常通知 平台异常主动通知依赖alertmanager组件,根据eventbus方式异常通知配置化。 ![](https://img.kancloud.cn/11/28/1128c78d6428b311d132bd6cfd22f172_1323x574.png) 异常通知alertmanager与企业微信对接 ![](https://img.kancloud.cn/bc/0a/bc0a32b4aa309b917c2670200e113beb_950x416.png) ## 通用线程池配置 ![](https://img.kancloud.cn/7d/c9/7dc959b4371a918a5e99ab5ec1dca3b6_2528x933.png) ## 通用水印 利用poi等组件对word,excel,ppt加水印 ![](https://img.kancloud.cn/b8/55/b855501bc0862f3830474bdbf88a5db1_2502x983.png) 代码详解 ![](https://img.kancloud.cn/65/ff/65ffee199fc0640e654ac7a001c9586e_1403x629.png) 示例代码 ``` public static void main(String[] args) throws FileNotFoundException { { String file = "D:\\file\\自主升级部分功能.docx" ; FileInputStream is = new FileInputStream(file); String waterMark = "管理员,2020-10-10"; WatermarkParam param = WatermarkParam.builder().file(file).inputStream(is).useImage(true).text(waterMark) .fontSize(20).degree(345F).alpha(0.4f).bespread(Boolean.TRUE).color(Color.GRAY).build(); WatermarkUtils.addWatermark(param, new File("D:\\file\\自主升级部分功能1.docx")); IoUtil.close(is); } } ``` 下载水印效果 ![](https://img.kancloud.cn/cf/4c/cf4cb71f338bdf2beca0329107356797_2075x1088.png) ## 统一异常 ### springboot异常 ![](https://img.kancloud.cn/5c/71/5c717a1928e3084338694385a4029845_1320x585.png) ![](https://img.kancloud.cn/e0/61/e061d0591cc123b438f3d2d274783834_2484x1226.png) ### feign异常 ![](https://img.kancloud.cn/d5/65/d565f521349c133db80bdd5c3ceafb8d_2484x1189.png) ## 混沌工程 混沌工程是一种可试验的、基于系统的方法来处理大规模分布式系统中的混乱问题。通过不断试验,了解系统的实际能承受的韧性边界并建立信心,通过不同的试验方法和目的,观察分布式系统的行为和反应。一句话——**以试验的方法尽早揭露系统弱点**。 混沌工程类似于“故障演练”,不局限于测试,而更像是工程实践。混沌试验类似于”探索性测试“,试验本身没有明确是输入和预期结果,通过对系统和服务的干预,来观察系统的”反应“。我们将混沌工程原则融入在试验过程中:在生产环境小规模模拟系统故障并定期自动化执行试验,通过试验结果与正常结果进行比对,观察系统”边界“。 ### 如何引入混沌工程? 在众多服务化改造案例中,Netflix无疑是最成功的公司之一,该公司的很多试验工具也都集成在Spring Cloud中,成为微服务框架的标准。而Chaos Monkey就是Netflix进行混沌试验一个重要工具。 Spring Cloud是时下最流行的分布式微服务架构下的一站式解决方案之一,它方便快速的将Spring Boot的微服务有效的管理起来,并提供了包括负载均衡、全链路监控、服务网关以及众多基于Netflix的开源工具。除此之外,鉴于Netflix在服务化演进中的成功案例,我们来了解下Netflix开源的混沌工程试验框架Chaos Monkey究竟是什么? ### [Chaos Monkey for Spring Boot文档](https://codecentric.github.io/chaos-monkey-spring-boot/) chaos-monkey-spring-boot是专门为Spring Boot打造的Chaos Monkey, ![](https://img.kancloud.cn/e1/d8/e1d8427ee67d707afab3bea7998053d5_1114x487.png) maven位置 ![](https://img.kancloud.cn/49/07/49073ef49d5be684215c73db840de8c8_2332x844.png) ### 加配置 ![](https://img.kancloud.cn/ab/46/ab4695eadaf994954928dca9b068966e_2470x1071.png) 从配置文件中我们可以很容易看到,Chaos Monkey的三种袭击方式——延时、异常和进程终止,同时我们也可以设置一个数值范围,在对服务进行延时攻击时生成随机延时。默认攻击方式为延时攻击,当同时开启异常攻击时,进程攻击则不会发生。Level:N表示第N个请求将被攻击,N=1时,表示每个请求都会被攻击,当同时开启异常攻击时,与N值无关,表示每个请求都将被攻击。 ### 总结 chaos-monkey-spring-boot非常适合用来进行故障演练,暴露服务间调用的问题,好提升系统的健壮性、故障自动恢复能力等。