Hutool不糊涂(二)

目录

 

●最甜的几块糖(Part 2)

●缓存工具

●JSON工具

●加解密工具

●定时任务

●excel操作

●DFA查找

●糖吃多了有什么坏处?

●小结


●最甜的几块糖(Part 2)

接上一篇,我们继续来看看剩下的几个比较实用的类。

●缓存工具

之前笔者写过一篇利用static实现简易缓存的文章,比较功能有限,例如无法实现缓存的清理等。而Hutool为大家提供了常用的缓存工具,除了不常用到的高级功能以外,例如主从复制,基本够用了,不用额外去学习、集成以及维护第三方的缓存,例如Redis或者memcached。

Hutool提供了以下几种缓存——

1.FIFOCache(先进先出缓存)。元素不停的加入缓存直到缓存满为止,当缓存满时,清理过期缓存对象,清理后依旧满则删除先入的缓存。是用链表来实现的。适用于对加入先后敏感的业务场景,例如售货机商品上架,先放进去的商品先卖掉,这样可以保证生产日期靠前的产品不会积压;不适用于对频率敏感的业务场景,例如不能保证最常用的对象总是被保留。因此适用面比较窄。

2.LFUCache(最少使用率缓存)。当缓存满时清理过期对象,清理后依旧满的情况下清除最少访问(访问计数最小)的对象并将其他对象的访问数减去这个最小访问数,以便新对象进入后可以公平计数。适用于对频率敏感的业务场景,这比较好理解,大部分缓存的目的都是为了留下经常用的对象,不常用的就逐渐排除掉。

3.LRUCache(最久未使用缓存)。当缓存满了,最久未被使用的对象将被移除,是用LinkedHashMap实现的,当缓存对象被使用一次,就取出放入头部。适用于对使用先后敏感的业务场景,同样不适用于对频率敏感的业务场景

4.TimedCache(定时缓存)。为缓存对象设置一个过期时间,到了就移出,与前面三种不同,没有容量限制。适用于对时间段敏感的业务场景,例如缓存交易记录的流水号,交易记录保存3个月;同样不适用于对频率敏感的业务场景

缓存对象的构建采用统一的形式:

 
  1. Long timeout = 60*DateUnit.MINUTE.getMillis();

  2. int capacity = 10;

  3. //构建一个FIFOCache

  4. Cache<String,String> fifoCache = CacheUtil.newFIFOCache(capacity,timeout);

  5. //构建一个LFUCache

  6. Cache<String,String> lfuCache = CacheUtil.newLRUCache(capacity,timeout);

  7. //构建一个LRUCache

  8. Cache<String,String> lruCache = CacheUtil.newLFUCache(capacity,timeout);

  9. //构建一个TimedCache

  10. Cache<String,String> timedCache = CacheUtil.newTimedCache(timeout);

缓存对象的使用:

 
  1. fifoCache.put("key","value");

  2. String value = fifoCache.get("key");

  3. //1秒后到期

  4. fifoCache.put("key2","value2",6000);

  5. String value2 = fifoCache.get("key2");

  6. //获取命中次数与非命中次数

  7. int hitCount = ((FIFOCache<String, String>) fifoCache).getHitCount();

  8. int missCount = ((FIFOCache<String, String>) fifoCache).getMissCount();

●JSON工具

现在第三方开源高效的JSON工具还是蛮多的,比如FastJSON、Gson、Jackson等。Hutool也集成了自己的JSON工具。与FastJSON类似,Hutool的JSONObject类实现了Map接口,JSONArray类实现了List接口,因此可以近乎0学习成本地使用类似FastJSON的API来操作JSON(假设你已经会使用FastJSON)。

下面我们结合代码来比较下FastJSON、Gson以及Hutool的JSON。首先我们看看三者将字符串解析为JsonObject的速度——

为了增加解析难度,我们特意采用一个复杂的Json字符串,通过Hutool的文件读取工具从txt中读取,涉及具体的业务数据,此处就不给大家展示了。

 
  1. public class JsonTest {

  2. /**

  3. * 测试三者将字符串、Json对象、JavaBean之间相互转化的用法和性能

  4. */

  5. @Test

  6. public void test(){

  7. System.out.println("--------------String转JsonObject----------------");

  8.  
  9. //读取Json字符串,Hutool提供的文件读取类

  10. FileReader fileReader = new FileReader("复杂Json字符串-组织单元.txt");

  11. String jsonStr = fileReader.readString();

  12.  
  13. //FastJson

  14. Date fastJsonDateTimeStart = DateTime.now().toJdkDate();

  15. JSONObject fastJsonObject = JSONObject.parseObject(jsonStr);

  16. Date fastJsonDateTimeEnd = DateTime.now().toJdkDate();

  17. System.out.printf("FastJson转化JsonObject耗时:%d毫秒\n",DateUtil.between(fastJsonDateTimeStart,fastJsonDateTimeEnd,DateUnit.MS));

  18.  
  19. //Gson

  20. Date gsonDateTimeStart = DateTime.now().toJdkDate();

  21. JsonObject gsonObject = new JsonParser().parse(jsonStr).getAsJsonObject();

  22. Date gsonDateTimeEnd = DateTime.now().toJdkDate();

  23. System.out.printf("Gson转化JsonObject耗时:%d毫秒\n",DateUtil.between(gsonDateTimeStart,gsonDateTimeEnd,DateUnit.MS));

  24.  
  25. //Hutool

  26. Date hutoolJsonDateTimeStart = DateTime.now().toJdkDate();

  27. cn.hutool.json.JSONObject hutoolJsonObject = JSONUtil.parseObj(jsonStr);

  28. Date hutoolJsonDateTimeEnd = DateTime.now().toJdkDate();

  29. System.out.printf("Hutool转化JsonObject耗时:%d毫秒\n",DateUtil.between(hutoolJsonDateTimeStart,hutoolJsonDateTimeEnd,DateUnit.MS));

  30.  
  31. System.out.println("--------------JsonObject转String----------------");

  32.  
  33. //FastJson

  34. fastJsonDateTimeStart = DateTime.now().toJdkDate();

  35. String fastJsonStr = fastJsonObject.toJSONString();

  36. fastJsonDateTimeEnd = DateTime.now().toJdkDate();

  37. System.out.printf("FastJson转化String耗时:%d毫秒\n",DateUtil.between(fastJsonDateTimeStart,fastJsonDateTimeEnd,DateUnit.MS));

  38.  
  39. //Gson

  40. gsonDateTimeStart = DateTime.now().toJdkDate();

  41. String gsonStr = new Gson().toJson(gsonObject);

  42. gsonDateTimeEnd = DateTime.now().toJdkDate();

  43. System.out.printf("Gson转化String耗时:%d毫秒\n",DateUtil.between(gsonDateTimeStart,gsonDateTimeEnd,DateUnit.MS));

  44.  
  45. //Hutool

  46. hutoolJsonDateTimeStart = DateTime.now().toJdkDate();

  47. String hutoolStr = JSONUtil.toJsonStr(hutoolJsonObject);

  48. hutoolJsonDateTimeEnd = DateTime.now().toJdkDate();

  49. System.out.printf("Hutool转化String耗时:%d毫秒\n",DateUtil.between(hutoolJsonDateTimeStart,hutoolJsonDateTimeEnd,DateUnit.MS));

  50.  
  51. System.out.println("--------------JsonObject转JavaBean----------------");

  52.  
  53. //FastJson

  54. fastJsonDateTimeStart = DateTime.now().toJdkDate();

  55. ControlUnitTreeNode fastJsonJavaBean = fastJsonObject.toJavaObject(ControlUnitTreeNode.class);

  56. fastJsonDateTimeEnd = DateTime.now().toJdkDate();

  57. System.out.printf("FastJson转化JavaBean耗时:%d毫秒\n",DateUtil.between(fastJsonDateTimeStart,fastJsonDateTimeEnd,DateUnit.MS));

  58.  
  59. //Gson

  60. gsonDateTimeStart = DateTime.now().toJdkDate();

  61. ControlUnitTreeNode gsonJavaBean = new Gson().fromJson(gsonObject,ControlUnitTreeNode.class);

  62. gsonDateTimeEnd = DateTime.now().toJdkDate();

  63. System.out.printf("Gson转化JavaBean耗时:%d毫秒\n",DateUtil.between(gsonDateTimeStart,gsonDateTimeEnd,DateUnit.MS));

  64.  
  65. //Hutool

  66. hutoolJsonDateTimeStart = DateTime.now().toJdkDate();

  67. ControlUnitTreeNode hutoolJavaBean = hutoolJsonObject.toBean(ControlUnitTreeNode.class);

  68. hutoolJsonDateTimeEnd = DateTime.now().toJdkDate();

  69. System.out.printf("Hutool转化JavaBean耗时:%d毫秒\n",DateUtil.between(hutoolJsonDateTimeStart,hutoolJsonDateTimeEnd,DateUnit.MS));

  70.  
  71. System.out.println("--------------JavaBean转String----------------");

  72.  
  73. //FastJson

  74. fastJsonDateTimeStart = DateTime.now().toJdkDate();

  75. String fastJsonString = JSONObject.toJSONString(fastJsonJavaBean);

  76. fastJsonDateTimeEnd = DateTime.now().toJdkDate();

  77. System.out.printf("FastJson转化String耗时:%d毫秒\n",DateUtil.between(fastJsonDateTimeStart,fastJsonDateTimeEnd,DateUnit.MS));

  78.  
  79. //Gson

  80. gsonDateTimeStart = DateTime.now().toJdkDate();

  81. String gsonString = new Gson().toJson(gsonJavaBean);

  82. gsonDateTimeEnd = DateTime.now().toJdkDate();

  83. System.out.printf("Gson转化String耗时:%d毫秒\n",DateUtil.between(gsonDateTimeStart,gsonDateTimeEnd,DateUnit.MS));

  84.  
  85. //Hutool

  86. hutoolJsonDateTimeStart = DateTime.now().toJdkDate();

  87. String hutoolString = JSONUtil.toJsonStr(hutoolJavaBean);

  88. hutoolJsonDateTimeEnd = DateTime.now().toJdkDate();

  89. System.out.printf("Hutool转化String耗时:%d毫秒\n",DateUtil.between(hutoolJsonDateTimeStart,hutoolJsonDateTimeEnd,DateUnit.MS));

  90.  
  91.  
  92. }

  93. }

结果如图:

可以看出,Json字符串与JsonObeject之间的转换,Hutool提供的工具效率最高;JsonObeject与JavaBean之间的转化,Gson效率最高;JavaBean与Json字符串之间的转化Gson和Hutool效率接近。当然,这只是单条数据的比较,实验还不够严谨,但也可以反映出Hutool基于json.org官方API进行改造的JSON工具表现还是不错的。

●加解密工具

Hutool提供了常用的对称加密(例如:AES、DES等)、非对称加密(例如:RSA、DSA等)、摘要加密(例如:MD5、SHA-1、SHA-256、HMAC等)的加解密工具。

使用示例如下:

 
  1. String beforeStr = "123456";

  2. //md5加密(摘要加密)

  3. Assert.assertEquals("e10adc3949ba59abbe56e057f20f883e",SecureUtil.md5(beforeStr));

  4.  
  5. //AES加密(对称加密)

  6. byte[] key = SecureUtil.generateKey(SymmetricAlgorithm.AES.getValue()).getEncoded();

  7. AES aes = SecureUtil.aes(key);

  8. String encryptStr = aes.encryptHex(beforeStr);

  9. String decryptStr = aes.decryptStr(encryptStr, CharsetUtil.CHARSET_UTF_8);

  10. Assert.assertEquals(beforeStr,decryptStr);

  11.  
  12. //RSA加密(非对称加密)

  13. RSA rsa = SecureUtil.rsa();

  14. //签名,私钥加密,公钥解密

  15. byte[] encrypt = rsa.encrypt(StrUtil.bytes(beforeStr, CharsetUtil.CHARSET_UTF_8), KeyType.PublicKey);

  16. byte[] decrypt = rsa.decrypt(encrypt, KeyType.PrivateKey);

  17. Assert.assertEquals(beforeStr, StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8));

  18. //加密私钥加密,公钥解密

  19. byte[] encrypt2 = rsa.encrypt(StrUtil.bytes(beforeStr, CharsetUtil.CHARSET_UTF_8), KeyType.PrivateKey);

  20. byte[] decrypt2 = rsa.decrypt(encrypt2, KeyType.PublicKey);

  21. Assert.assertEquals(beforeStr, StrUtil.str(decrypt2, CharsetUtil.CHARSET_UTF_8));

●定时任务

大家如果使用过定时任务的话,绝大可能用的都是quartz,但相比之下,Hutool封装的定时任务更轻量,并且也具备了常用功能,加之有Cron表达式加持,生产开发绝对没有问题。这里,给大家推荐一个Cron表达式在线生成网站http://cron.qqe2.com/,降低了学习成本。

首先,我们写一个定时任务的方法,类名方法名随意:

 
  1. public class MyTask1 {

  2. public static int count = 0;

  3. public void start(){

  4. System.out.printf("第%d次执行定时任务,当前时间:%s\n",++count,DateTime.now().toString());

  5. }

  6. }

然后,需要在resources目录下config目录下(如果没有的话可自己建立)新建一个setting文件(参见Hutool的上一篇文字)cron.setting。里面配置好需要定时执行的方法以及规则,例如我们每10秒执行一次,每周一到周五执行:

 
  1. # 中括号写到类所在的包,配置项写到类名和方法名

  2. [task]

  3. MyTask1.start = 0/10 0 0 0 0 1/5

最后,就可以启动定时任务了。这里使用了CountDownLatch阻塞观察定时任务运行结果。

 
  1. public class TimerTest {

  2. @Test

  3. public void test() throws InterruptedException {

  4. //设置Cron表达式匹配到秒,不然使用的是Linux的crontab表达式,最小单位是分钟

  5. CronUtil.setMatchSecond(true);

  6. CronUtil.start();

  7. //可以手动停止定时任务

  8. //CronUtil.stop();

  9. CountDownLatch countDownLatch = new CountDownLatch(1);

  10. countDownLatch.await();

  11. }

  12. }

运行结果如下:

●excel操作

笔者接触的项目不少用到了Excel导入导出的功能,Hutool基于Apache的POI库进行封装,提供了简洁操作方法。值得注意的是,此处需要在pom文件中引入Apache POI的依赖,这也是Hutool少数需要引入第三方依赖的地方,这是考虑并非所有Java项目都会用到Excel操作,只有特定统计业务会用到,为了轻量,不主动引入的。

引入的内容如下:

 
  1. <dependency>

  2. <groupId>org.apache.poi</groupId>

  3. <artifactId>poi-ooxml</artifactId>

  4. <version>3.17</version>

  5. <type>pom</type>

  6. </dependency>

我们通过代码来看看具体怎么使用。首先准备一个JavaBean,字段对应excel中的每条记录:

 
  1. public class CarInfo {

  2. private String plateNo;

  3. private String laneName;

  4. private String driveway;

  5. private String direction;

  6. private String plateType;

  7. private String uploadTime;

  8. private int speed;

  9. private double carLength;

  10. private String plateColor;

  11. private String carColor;

  12. private String carType;

  13. private String carColorDeepth;

  14. private String brand;

  15. private String subBrand;

  16. private String productYear;

  17.  
  18. /* 篇幅原因,省略掉构造函数和get、set函数 */

  19. }

excel读取和写入支持xls和xlsx两种格式,看看待读取的文件:

使用Hutool封装好的API进行读写操作:

 
  1. public class ExcelTest {

  2. @Test

  3. public void test(){

  4. //默认读全部行列,还可以通过重载函数,第二个参数指定读取的Sheet

  5. ExcelReader reader1 = ExcelUtil.getReader("车辆信息xlsx.xlsx");

  6. //设置标题别名的目的在于读取的excel标题是中文,一一对应上javabean中的字段。如果excel标题就是字段名,则无需设置别名

  7. reader1.addHeaderAlias("车牌号码","plateNo")

  8. .addHeaderAlias("路口名称","laneName")

  9. .addHeaderAlias("车道","driveway")

  10. .addHeaderAlias("方向","direction")

  11. .addHeaderAlias("车牌类型","plateType")

  12. .addHeaderAlias("过车时间","uploadTime")

  13. .addHeaderAlias("车速(km/h)","speed")

  14. .addHeaderAlias("车长(m)","carLength")

  15. .addHeaderAlias("车牌颜色","plateColor")

  16. .addHeaderAlias("车身颜色","carColor")

  17. .addHeaderAlias("车辆类型","carType")

  18. .addHeaderAlias("车辆颜色深浅","carColorDeepth")

  19. .addHeaderAlias("车辆品牌","brand")

  20. .addHeaderAlias("车辆子品牌","subBrand")

  21. .addHeaderAlias("车辆年款","productYear");

  22. List<CarInfo> carInfosByXlsx = reader1.readAll(CarInfo.class);

  23. reader1.close();

  24.  
  25. //也兼容xls格式

  26. ExcelReader reader2 = ExcelUtil.getReader("车辆信息xls.xls");

  27. reader2.addHeaderAlias("车牌号码","plateNo")

  28. .addHeaderAlias("路口名称","laneName")

  29. .addHeaderAlias("车道","driveway")

  30. .addHeaderAlias("方向","direction")

  31. .addHeaderAlias("车牌类型","plateType")

  32. .addHeaderAlias("过车时间","uploadTime")

  33. .addHeaderAlias("车速(km/h)","speed")

  34. .addHeaderAlias("车长(m)","carLength")

  35. .addHeaderAlias("车牌颜色","plateColor")

  36. .addHeaderAlias("车身颜色","carColor")

  37. .addHeaderAlias("车辆类型","carType")

  38. .addHeaderAlias("车辆颜色深浅","carColorDeepth")

  39. .addHeaderAlias("车辆品牌","brand")

  40. .addHeaderAlias("车辆子品牌","subBrand")

  41. .addHeaderAlias("车辆年款","productYear");

  42. List<CarInfo> carInfosByXls = reader2.readAll(CarInfo.class);

  43. reader2.close();

  44.  
  45. //excel的写出

  46. List<String> row0 = CollUtil.newArrayList("车牌", "颜色", "速度", "车长");

  47. List<String> row1 = CollUtil.newArrayList("浙A54122", "红色", "52", "3.1");

  48. List<String> row2 = CollUtil.newArrayList("浙A10122", "黑色", "60", "3.4");

  49. List<String> row3 = CollUtil.newArrayList("浙A12574", "黑色", "50", "3.35");

  50. List<String> row4 = CollUtil.newArrayList("浙A24762", "蓝色", "55", "3.2");

  51. List<String> row5 = CollUtil.newArrayList("浙A99884", "白色", "58", "3.1");

  52. List<List<String>> rows = CollUtil.newArrayList(row0,row1, row2, row3, row4, row5);

  53.  
  54. ExcelWriter writer = ExcelUtil.getWriter("toXls.xls");

  55. //设置sheet名

  56. writer.renameSheet("车辆信息摘要表");

  57. writer.write(rows);

  58. writer.close();

  59. }

  60. }

●DFA查找

最后给大家介绍一个关键词/敏感词查找的方案,DFA(Deterministic Finite Automaton,确定有穷自动机)算法,用所有关键字构造一棵树,然后用正文遍历这棵树,遍历到叶子节点即表示文章中存在这个关键字。相比使用集合的遍历,速度会快很多。

我们假设有这么一个业务场景,需要去查询用户发帖是否含有关键词,如果有,则不让其发帖。我们通过读取一个txt文件来模拟用户发的帖子,主要针对关键词查找做一个演示:

 
  1. public class DFATest {

  2. @Test

  3. public void dfaTest(){

  4. //构造关键词树

  5. WordTree tree = new WordTree();

  6. tree.addWord("阿里");

  7. tree.addWord("腾讯");

  8. tree.addWord("百度");

  9. tree.addWord("苹果");

  10. //读取txt,模拟发帖

  11. FileReader fileReader = new FileReader("hutoolAPI.txt");

  12. String text1 = fileReader.readString();

  13. fileReader = new FileReader("智能化软件开发:程序员与 AI 机器人一起结对编程.txt");

  14. String text2 = fileReader.readString();

  15. fileReader = new FileReader("自动驾驶,或许是秋天,还没到冬天.txt");

  16. String text3 = fileReader.readString();

  17. //DFA关键词匹配

  18. boolean isMatch1 = tree.isMatch(text1);

  19. boolean isMatch2 = tree.isMatch(text2);

  20. boolean isMatch3 = tree.isMatch(text3);

  21. //DFA关键词查找

  22. List<String> matchAll1 = tree.matchAll(text1, -1, true, true);

  23. System.out.printf("匹配到%d个关键词\n",matchAll1.size());

  24. List<String> matchAll2 = tree.matchAll(text2, -1, true, true);

  25. System.out.printf("匹配到%d个关键词\n",matchAll2.size());

  26. List<String> matchAll3 = tree.matchAll(text3, -1, true, true);

  27. System.out.printf("匹配到%d个关键词\n",matchAll3.size());

  28.  
  29. //与正则表达式匹配过程耗时对比

  30. DateTime start1 = DateTime.now();

  31. List<String> matchByDFA = tree.matchAll(text1, -1, true, true);

  32. DateTime end1 = DateTime.now();

  33. System.out.printf("DFA匹配到%d个关键词,耗时:%d毫秒\n",matchByDFA.size(), DateUtil.between(start1.toJdkDate(),end1.toJdkDate(),DateUnit.MS));

  34.  
  35. Pattern pattern=Pattern.compile("(.*阿里.*)|(.*腾讯.*)|(.*百度.*)|(.*苹果.*)");

  36. DateTime start2 = DateTime.now();

  37. Matcher matcher= pattern.matcher(text1);

  38. List<String> matchByReg = new ArrayList<>();

  39. while(matcher.find()){

  40. matchByReg.add(matcher.group());

  41. }

  42. DateTime end2 = DateTime.now();

  43. System.out.printf("正则表达式匹配到%d个关键词,耗时:%d毫秒", matchByReg.size(),DateUtil.between(start2.toJdkDate(),end2.toJdkDate(),DateUnit.MS));

  44. }

  45. }

结果如下:

可以看到,使用DFA比使用正则表达式去做匹配效率更高,特别适用于匹配的原文很大,关键词很多的情况。当然了,可以结合业务需要,匹配出关键词后,将其替换为*,再发帖。

●糖吃多了有什么坏处?

之前一直在说Hutool提供的语法糖的好处——极大提高编程效率,但是回过头来说,如果一直吃糖,很可能导致离开了它就无法编码了,例如前面大量举例使用的传统写法,很可能就会变得手生了。这种情况不只是语法糖的问题,许多框架也存在。随着技术的发展,封装越来越好,程序员已经不需要太了解底层实现就可以直接调用API实现功能,就像springboot,通过官方网站生成工程项目,下载拿来直接就可以用,用惯了可能就不会以前SSH、SSM框架的配置了。希望大家能辩证地看待问题,在合理的范围最大程度去利用好手边的工具。

●小结

以上,对于Hutool的介绍基本就到一段落了,还是很推荐大家使用这个Apache2.0开源协议的工具的,商业许可安全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值