Commons类库
commons: 一套开发源码、免费使用、商业友好的优秀API作为Java自带API的补充,大多数都是一些工具类
包括
- Commons BeanUtils,针对Bean的一个工具集。由于Bean往往是有一堆get和set组成,所以BeanUtils也是在此基础上进行一些包装。
- Commons CLI,这是一个处理命令的工具。比如main方法输入的string[]需要解析。你可以预先定义好参数的规则,然后就可以调用CLI来解析。
- Commons Codec,这个工具是用来编码和解码的,包括Base64,URL,Soundx等等。
- Commons Collections,可以把这个工具看成是java.util的扩展。
- Commons Configuration,这个工具是用来帮助处理配置文件的,支持很多种存储方式
- Commons DbUtils,DbUtils就是把数据库操作单独做一个包这样的工具,以后开发不用再重复这样的工作了。这个工具并不是现在流行的OR-Mapping工具(比如Hibernate),只是简化数据库操作
- Commons FileUpload,文件上传。
- Commons HttpClient,这个工具可以方便通过编程的方式去访问网站
- Commons IO,可以看成是java.io的扩展
- Commons JXPath,JXpath就是基于Java对象的Xpath,也就是用Xpath对Java对象进行查询
- Commons Lang,这个工具包可以看成是对java.lang的扩展。提供了诸如StringUtils, StringEscapeUtils, RandomStringUtils, Tokenizer, WordUtils等工具类
- Commons Logging,提供了log4j
- Commons Math,这个包提供的功能有些和Commons Lang重复,但是这个包更专注于做数学工具,功能更强大
- Commons Net,这个包还是很实用的,封装了很多网络协议
- Commons Validator,用来帮助进行验证的工具。比如验证Email字符串,日期字符串等是否合法。
- Commons virtual File System 提供对各种资源的访问接口。支持的资源类型包括
平时常用的是commons-lang、commons-collection、commons-beanutils、commons-codec、commons-io,Commons Logging。
commons-lang
Lang 主要是一些公共的工具集合,比如对字符、数组的操作等等
1.ArrayUtils
// 有时我们需要将两个数组合并为一个数组,用ArrayUtils就非常方便,示例如下:
private void testArr() {
String[] s1 = new String[] { "1", "2", "3" };
String[] s2 = new String[] { "a", "b", "c" };
String[] s = (String[]) ArrayUtils.addAll(s1, s2);
Stream.of(s).forEach(System.out::println);
String str = ArrayUtils.toString(s);
System.out.println(str + ">>" + str.length());
}
2. StringUtils
private void testStringUtils() {
// 截取FROM之后的内容,并去掉空格
String testTrim = StringUtils.substringAfter("SELECT * FROM PERSON ", "FROM").trim();
System.out.println("截取FROM之后的内容,并去掉空格:" + testTrim);
// 只能正整数.
System.out.println("是否是正整数:" + StringUtils.isNumeric("454534"));
System.out.println("判断是否相等:" + StringUtils.equals("454534", "454534"));
System.out.println("判断忽略大小写之后是否相等:" + StringUtils.equalsIgnoreCase("ABC534", "abc534"));
// 判断是否是空格和null
System.out.println("判断是否是空格和null:" + StringUtils.isBlank(" "));
List<String> list = Lists.newArrayList("a", "b", "c");
// 将数组中的内容以,分隔
System.out.println("将数组中的内容以,分隔:" + StringUtils.join(list, ","));
// 在右边加下字符,使之总长度为6
System.out.println("在右边加下字符,使之总长度为6:" + StringUtils.rightPad("123", 6, '0'));
// 首字母大写
System.out.println("首字母大写:" + StringUtils.capitalize("abc"));
// 删除所有空格
System.out.println("删除所有空格:" + StringUtils.deleteWhitespace(" ab c "));
// 判断是否包含这个字符
System.out.println("判断是否包含这个字符:" + StringUtils.contains("abc", "ba"));
// 表示左边两个字符
System.out.println("表示左边两个字符:" + StringUtils.left("abc", 2));
}
3.ClassUtils
private void testClassUtils() {
System.out.println("短类名:" + ClassUtils.getShortClassName(TestMglApplicationTests.class));
System.out.println("包名:" + ClassUtils.getPackageName(TestMglApplicationTests.class));
}
4.NumberUtils
// math
private void testNumberUtils() {
// 可以识别正负小数等 就别用stringutils里的isnumberic.
System.out.println(NumberUtils.toInt("a", 0));
System.out.println(NumberUtils.isNumber("-2.5"));
System.out.println(NumberUtils.isNumber("2.5"));
}
5.RandomStringUtils
private void testRandomStringUtils() {
// 字母加数字
System.out.println("字母加数字:" + RandomStringUtils.randomAlphanumeric(5));
// 字母
System.out.println("字母:" + RandomStringUtils.randomAlphabetic(5));
// 数字
System.out.println("数字:" + RandomStringUtils.randomNumeric(5));
}
6.StringEscapeUtils
private void testStringEscapeUtils() {
// 转义反转义
System.out.println("转义:" + StringEscapeUtils.escapeHtml("<html>"));
System.out.println("反转义:" + StringEscapeUtils.unescapeHtml("<html>"));
}
7.DateFormatUtils
// 日期格式化
org.apache.commons.lang3.time.DateFormatUtils.format(final Date date, final String pattern)
8.DateUtils
// 是否是同一天(忽略时间)
org.apache.commons.lang3.time.DateUtils.isSameDay(final Date date1, final Date date2)
// 是否同一时间点
org.apache.commons.lang3.time.DateUtils.isSameInstant(final Date date1, final Date date2)
// 解析日期字符串为 Date
org.apache.commons.lang3.time.DateUtils.parseDate(final String str, final String... parsePatterns)
// 日期的加减
org.apache.commons.lang3.time.DateUtils.addYears(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addMonths(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addWeeks(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addDays(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addHours(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addMinutes(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addSeconds(final Date date, final int amount)
org.apache.commons.lang3.time.DateUtils.addMilliseconds(final Date date, final int amount)
commons-collection
Collections 对java.util的扩展封装,处理数据还是挺灵活的。
org.apache.commons.collections – Commons Collections自定义的一组公用的接口和工具类
- bag – 实现Bag接口的一组类
- bidimap – 实现BidiMap系列接口的一组类
- buffer – 实现Buffer接口的一组类
- collection – 实现java.util.Collection接口的一组类
- comparators – 实现java.util.Comparator接口的一组类
- functors – Commons Collections自定义的一组功能类
- iterators – 实现java.util.Iterator接口的一组类
- keyvalue – 实现集合和键/值映射相关的一组类
- list – 实现java.util.List接口的一组类
- map – 实现Map系列接口的一组类
- set – 实现Set系列接口的一组类
private void testOrderedMap() { // 得到集合里按顺序存放的key之后的某一Key OrderedMap map = new LinkedMap(); map.put("FIVE", "5"); map.put("SIX", "6"); map.put("SEVEN", "7"); System.out.println(map.firstKey()); System.out.println(map.nextKey("FIVE")); System.out.println(map.nextKey("SIX")); }
private void testBidiMap() { BidiMap bidi = new TreeBidiMap(); bidi.put("SIX", "6"); // 通过key得到value System.out.println(bidi.get("SIX")); // 通过value得到key System.out.println(bidi.getKey("6")); // 将map里的key和value对调 BidiMap inverse = bidi.inverseBidiMap(); System.out.println(inverse); }
CollectionUtilsprivate void testList() { // 得到两个集合中相同的元素 List<String> list1 = Lists.newArrayList("1", "2", "3"); List<String> list2 = Lists.newArrayList("5", "2", "3"); // 交集 map System.out.println("交集:" + CollectionUtils.intersection(list1, list2)); // 交集,ListUtils System.out.println("交集:" + CollectionUtils.retainAll(list1, list2)); // 并集 System.out.println("并集:" + CollectionUtils.union(list1, list2)); // 减 System.out.println("集合1减集合2:" + CollectionUtils.subtract(list1, list2)); // 加 CollectionUtils.addAll(list1, list2.iterator()); System.out.println("集合1加集合2:" + list1); list2.add(null); System.out.println("集合2加NULL:" + list2); // 不为空才加 list2.forEach(key -> CollectionUtils.addIgnoreNull(list1, key)); System.out.println("不为空才加:" + list1); }
``` 判断两个集合是否有交集 org.apache.commons.collections.CollectionUtils.containsAny(final Collection coll1, final Collection coll2)// 集合判空 org.apache.commons.collections.CollectionUtils.isEmpty(Collection coll) org.apache.commons.collections.CollectionUtils.isNotEmpty(Collection coll)
对 collection 集合元素执行 closure 中定义的操作 org.apache.commons.collections.CollectionUtils.forAllDo(Collection collection, Closure closure)
通过 predicate 判断是否返回 true/false ,可判断某个元素是否存在 org.apache.commons.collections.CollectionUtils.exists(Collection, Predicate)
通过 predicate 对集合元素进行过滤,注意是在原来的集合上操作 org.apache.commons.collections.CollectionUtils.filter(Collection collection, Predicate predicate)
查找符合 predicate 条件的元素,注意是返回新创建的集合 org.apache.commons.collections.CollectionUtils.select(Collection inputCollection, Predicate predicate)
对集合元素进行转换 org.apache.commons.collections.CollectionUtils.transform(Collection collection, Transformer transformer)
注意这些方法:都会执行集合元素的遍历,时间复杂度为 O(n),集合数据量太大要优化请使用其它方法 ps: Predicate , Transformer, Closure 这些不就是所谓的函数式接口嘛
转换为不能修改的集合 org.apache.commons.collections.CollectionUtils.unmodifiableCollection(Collection collection)
**MapUtils**
// Map 判空 org.apache.commons.collections.MapUtils.isEmpty(Map map) org.apache.commons.collections.MapUtils.isNotEmpty(Map map) // Map 取值 避免类型转换和空指针问题 org.apache.commons.collections.MapUtils.getString(final Map map, final Object key) org.apache.commons.collections.MapUtils.getBoolean(final Map map, final Object key) org.apache.commons.collections.MapUtils.getInteger(final Map map, final Object key) org.apache.commons.collections.MapUtils.getString( Map map, Object key, String defaultValue ) // 转换为不能修改的Map org.apache.commons.collections.MapUtils.unmodifiableMap(Map map)
<a name="c14a6"></a>
## commons-beanutils
**BeanUtils** 提供了对于JavaBean进行各种操作, 比如对象,属性复制等等。<br />**1、对象的克隆**
private void testBeanutils() { Book book = new Book(); book.setXh(Lists.newArrayList(“1”, “2”)); book.setName(“论语”); book.setPrice(100); book.setContent(Lists.newArrayList(“己所不欲,勿施于人。”, “不患人之不己知,患不知人也。”)); try { // 克隆,没克隆属性. Book cloneBook = (Book)BeanUtils.cloneBean(book); System.out.println(“克隆:” + cloneBook.toString()); // 不建议用apache赋值,会抛出异常,可用spring提供的不用try.注意参数顺序 Book cloneBook2 = new Book(); org.springframework.beans.BeanUtils.copyProperties(book, cloneBook2); System.out.println(“赋值:” + cloneBook2.toString()); } catch (Exception e) { e.printStackTrace(); } }
**2、 将一个Map对象转化为一个Bean,通过Java的反射机制来做的。**<br />不建议自己去写过多的反射Util,可以从以下工具类方法中去找适合自己的:
- PropertyUtils
- BeanUtils
- ClassUtils
- FieldUtils
- ArtertyClassUtil
- ReflectUtil
private void testMap2Bean() {
// 这个Map对象的key必须与Bean的属性相对应.
Map
<a name="uv2rZ"></a>
## commons-codec
**Codec** 提供了一些公共的编解码实现,比如Base64, Hex, MD5,Phonetic and URLs等等。
System.out.println(Base64.encodeBase64String(“test”.getBytes())); System.out.println(new String(Base64.decodeBase64(“dGVzdA==”), StandardCharsets.UTF_8)); System.out.println(DigestUtils.md5Hex(“asdfasdfasdfasfdasdfasdfasfasd”)); // 可以直接使用spring 提供的 System.out.println(Base64Utils.encodeToString(“absd”.getBytes())); System.out.println(new String(Base64Utils.decodeFromString(“YWJzZA==”), StandardCharsets.UTF_8));
<a name="clnHF"></a>
## common-io
**IO** 对java.io的扩展 操作文件非常方便。
//1.读取Stream
//标准代码:
try (InputStream in = new URL( “http://jakarta.apache.org“ ).openStream()) {
InputStreamReader inR = new InputStreamReader(in);
BufferedReader buf = new BufferedReader(inR);
String line;
while ((line = buf.readLine()) != null) {
System.out.println(line);
}
}
**使用IOUtils**
try (InputStream in = new URL( “http://www.baidu.com").openStream()) {
System.out.println( IOUtils.toString(in, StandardCharsets.UTF_8));
}
//比较两个流是否相等
InputStream in = new URL(“http://www.baidu.com").openStream();
InputStream in2 = new URL(“http://www.baidu.com").openStream();
System.out.println(IOUtils.contentEquals(in, in2));
//将字节从InputStream复制到OutputStream或者是ByteArrayOutputStream
File src = new File(“D:/test.txt”);
InputStream inputStream = new FileInputStream(src);
File dest = new File(“D:/blank.txt”);
OutputStream outputStream = new FileOutputStream(dest);
IOUtils.copy(inputStream, outputStream);
//从输入流中读取字节(通常返回输入流的字节数组的长度)
InputStream in4 = new URL(“http://www.baidu.com").openStream();
byte[] buffer = new byte[100000];
System.out.println(IOUtils.read(in4, buffer));
//获得输入流的内容放回一个List
```
// 关闭流
org.apache.commons.io.IOUtils.closeQuietly(InputStream input)
org.apache.commons.io.IOUtils.closeQuietly(OutputStream output)
// 输入流转字节数组, 需要手动关闭 input
org.apache.commons.io.IOUtils.toByteArray(InputStream input)
// 将字节数组写到输出流,需要手动关闭 output
org.apache.commons.io.IOUtils.write(byte[] data, OutputStream output)
// 从输入流复制到输出流,由于返回值复制的字节数为 int ,流数据需小于2GB,否则返回 -1
org.apache.commons.io.IOUtils.copy(InputStream input, OutputStream output)
// 从输入流复制到输出流,流数据大于2GB
org.apache.commons.io.IOUtils.copyLarge(InputStream input, OutputStream output)
使用FileUtils
File src = new File("D:/test.txt");
List lines = FileUtils.readLines(src, "UTF-8");
System.out.println(lines);
File dest = new File("D:/blank.txt");
//拷贝文件 --这里会覆盖--而非追加
FileUtils.copyFile(src, dest);
//拷贝文件到某一路径
FileUtils.copyFileToDirectory(src, new File("E:/"));
//写字符串到一个文件--此种为覆盖的方法
String string = "Blah blah blah";
FileUtils.writeStringToFile(dest, string, "ISO-8859-1");
// 向文件追加内容.
FileUtils.write(dest, "hhhhhhhhh", StandardCharsets.UTF_8, true);
//删除文件实例
File file = new File( ("E:/test.txt") );
FileUtils.forceDelete(file);
------------------------------------------------------------------------------------
// 打开一个文件流
org.apache.commons.io.FileUtils.openInputStream(File file)
// 列出目录下的文件
org.apache.commons.io.FileUtils.listFiles(File directory, String[] extensions, boolean recursive)
// 删除文件或目录和目录下的所有文件(包含目录)
org.apache.commons.io.FileUtils.forceDelete(File file)
// 清空目录下的所有文件和目录(不包括需要清空的目录,意思是目录下的内容清空)
org.apache.commons.io.FileUtils.cleanDirectory(File dir)
// jvm 正常退出时调用 shutdown hook 执行清除文件操作(一般不用,如果要用,请三思而后行)
org.apache.commons.io.FileUtils.forceDeleteOnExit(File file)
使用FilenameUtils
获取文件扩展名
foo.txt --> "txt" a/b/c.jpg --> "jpg" a/b.txt/c --> "" a/b/c --> "" org.apache.commons.io.FilenameUtils.getExtension(String filename)
删除文件扩展名
foo.txt --> foo a\b\c.jpg --> a\b\c a\b\c --> a\b\c a.b\c --> a.b\c org.apache.commons.io.FilenameUtils.removeExtension(String)
判断是否匹配给定的文件扩展名 ``` FilenameUtils.isExtension(“a.txt”, new String[]{“txt”}) = true FilenameUtils.isExtension(“a/b.doc”, new String[]{“docx”,”doc”}) = true FilenameUtils.isExtension(“a/c”, new String[]{“”}) = true
org.apache.commons.io.FilenameUtils.isExtension(String, String[])
org.apache.commons.io.FilenameUtils.isExtension(String, String)
---
<a name="Cd72S"></a>
# Guava
<a name="tfQqa"></a>
## Guava是什么?
- java开源库,提供用于集合,缓存,支持原语,并发性,常见注解,字符串处理,I/O和验证的实用方法。
- 标准化-由google托管
- 高效、可靠
- 优化-经过高度优化
<a name="HHZ9L"></a>
## 常用类与接口
<a name="bf9ke"></a>
##### 1.集合的创建
// 普通Collection的创建
List
_**不可变集合**_
- 在多线程操作下,是线程安全的。
- 所有不可变集合会比可变集合更有效的利用资源。
- 中途不可改变。
**示例**
//以前
Map
```
Multimap<String,Integer> map = ArrayListMultimap.create();
map.put("aa", 1);
map.put("aa", 2);
System.out.println(map.get("aa")); //[1, 2]
- MultiSet:无序+可重复 count()方法获取重复的次数 增强了可读性+操作简单 创建方式: Multiset set = HashMultiset.create();
- Multimap:key-value key可以重复 创建方式: Multimap
teachers = ArrayListMultimap.create(); - BiMap:双向Map(Bidirectional Map) 键与值都不能重复 创建方式: BiMap
biMap = HashBiMap.create(); Table:双键的Map Map—> Table—>rowKey+columnKey+value //和sql中的联合主键有点像 创建方式: Table
tables = HashBasedTable.create();
2.将集合转换为特定规则的字符串
List<String> list = Lists.newArrayList("aa","bb","cc"); System.out.println(Joiner.on("-").join(list));
3.将String转换为特定的集合
String str = "1-2-3-4-5-6"; List<String> list = Splitter.on("-").splitToList(str);
4.将String转换为map
String str = "xiaoming=11,xiaohong=23"; Map<String,String> map = Splitter.on(",").withKeyValueSeparator("=").split(str);
5.guava还支持多个字符切割,或者特定的正则分隔
String input = "aa.dd,,ff,,."; List<String> result = Splitter.onPattern("[.|,]").omitEmptyStrings().splitToList(input);
其它
// 判断匹配结果 boolean result = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('A', 'Z')).matches('K'); //true // 保留数字文本 String s1 = CharMatcher.digit().retainFrom("abc 123 efg"); //123 // 删除数字文本 String s2 = CharMatcher.digit().removeFrom("abc 123 efg"); //abc efg
6.set的交集, 并集, 差集
HashSet setA = newHashSet(1, 2, 3, 4, 5); HashSet setB = newHashSet(4, 5, 6, 7, 8); SetView union = Sets.union(setA, setB); //union:12345867 SetView difference = Sets.difference(setA, setB); //difference:123678 SetView intersection = Sets.intersection(setA, setB); //intersection:45
7.检查参数
//use java (str !=null && !str.isEmpty()) //use guava if(!Strings.isNullOrEmpty(str)) //use java if (count <= 0) { throw new IllegalArgumentException("must be positive: " + count); } //use guava Preconditions.checkArgument(count > 0, "must be positive: %s", count);
8.计算中间代码的运行时间
Stopwatch stopwatch = Stopwatch.createStarted(); for (int i = 0; i < 10000000; i++) { // watch } long nanos = stopwatch.elapsed(TimeUnit.MILLISECONDS); System.out.println(nanos);
9.文件操作
File from=...; File to=...; Files.copy(from,to); //复制文件 Files.move(File from, File to); //移动文件 URL url = Resources.getResource("abc.xml"); //获取classpath根下的abc.xml文件url
10.Optional
Guava库设计了Optional来解决null含义模糊的问题(1).静态方法
Optional.of(T): 获得一个Optional对象,其内部包含了一个非null的T数据类型实例,若T=null,则立刻报错。
- Optional.absent(): 获得一个Optional对象,其内部包含了空值
- Optional.fromNullable(T): 将一个T的实例转换为Optional对象,T的实例可以不为空,也可以为空[Optional.fromNullable(null)],和Optional.absent()等价。
(2).实例方法
- boolean isPresent(): 果Optional包含的T实例不为null,则返回true;若T实例为null,返回false
- T get(): 返回Optional包含的T实例,该T实例必须不为空;否则,对包含null的Optional实例调用get()会抛出一个IllegalStateException异常
- or(T): ptional实例中包含了传入的T的相同实例,返回Optional包含的该T实例,否则返回输入的T实例作为默认值
- T orNull(): 返回Optional实例中包含的非空T实例,如果Optional中包含的是空值,返回null,逆操作是fromNullable()
Set asSet(): 不可修改的Set,该Set中包含Optional实例中包含的所有非空存在的T实例,且在该Set中,每个T实例都是单态,如果Optional中没有非空存在的T实例,返回的将是一个空的不可修改的Set。
private void testOptional(){ Optional<Long> value = method(); if(value.isPresent()==true){ System.out.println("获得返回值: " + value.get()); }else{ System.out.println("获得返回值: " + value.or(-12L)); } System.out.println("获得返回值 orNull: " + value.orNull()); Optional<Long> valueNoNull = methodNoNull(); if(valueNoNull.isPresent()==true){ Set<Long> set=valueNoNull.asSet(); System.out.println("获得返回值 set 的 size : " + set.size()); System.out.println("获得返回值: " + valueNoNull.get()); }else{ System.out.println("获得返回值: " + valueNoNull.or(-12L)); } System.out.println("获得返回值 orNull: " + valueNoNull.orNull()); } private Optional<Long> method() { return Optional.fromNullable(null); } private Optional<Long> methodNoNull() { return Optional.fromNullable(15L); }
**
Hutool
简介
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自于每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
Hutool名称的由来
Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu”是公司名称的表示,tool表示工具。Hutool谐音“糊涂”,一方面简洁易懂,一方面寓意“难得糊涂”。
Hutool如何改变我们的coding方式
Hutool的目标是使用一个工具方法代替一段复杂代码,从而最大限度的避免“复制粘贴”代码的问题,彻底改变我们写代码的方式。
以计算MD5为例:
- 【以前】打开搜索引擎 -> 搜“Java MD5加密” -> 打开某篇博客-> 复制粘贴 -> 改改好用
- 【现在】引入Hutool -> SecureUtil.md5()
Hutool的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。
包含组件
一个Java基础工具类,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类,同时提供以下组件:
模块 | 介绍 |
---|---|
hutool-aop | JDK动态代理封装,提供非IOC下的切面支持 |
hutool-bloomFilter | 布隆过滤,提供一些Hash算法的布隆过滤 |
hutool-cache | 简单缓存实现 |
hutool-core | 核心,包括Bean操作、日期、各种Util等 |
hutool-cron | 定时任务模块,提供类Crontab表达式的定时任务 |
hutool-crypto | 加密解密模块,提供对称、非对称和摘要算法封装 |
hutool-db | JDBC封装后的数据操作,基于ActiveRecord思想 |
hutool-dfa | 基于DFA模型的多关键字查找 |
hutool-extra | 扩展模块,对第三方封装(模板引擎、邮件、Servlet、二维码、Emoji、FTP、分词等) |
hutool-http | 基于HttpUrlConnection的Http客户端封装 |
hutool-log | 自动识别日志实现的日志门面 |
hutool-script | 脚本执行封装,例如Javascript |
hutool-setting | 功能更强大的Setting配置文件和Properties封装 |
hutool-system | 系统参数调用封装(JVM信息等) |
hutool-json | JSON实现 |
hutool-captcha | 图片验证码实现 |
hutool-poi | 针对POI中Excel和Word的封装 |
hutool-socket | 基于Java的NIO和AIO的Socket封装 |
可以根据需求对每个模块单独引入,也可以通过引入hutool-all
方式引入所有模块。
- hutool-json
处理效率要比阿里巴巴的fastjson效率高,fastjson低版本有安全漏铜
参考资料:https://hutool.cn/
Mvel
Mvel是什么
简单来说是一种强大的表达式解析器。我们可以自己写一些表达式,交给mvel进行解析计算,得到这个表达式计算的值。
比如我们要进行一个加法运算。在java中我们这样写:
int res = 1+1; // 2
若我用mvel则这样写:
Object res = MVEL.eval("1+1"); //2
是不是很吃惊😱。“1+1”就是一个表达式,第一种我们是硬编码实现的计算结果,但是第二种方案,直接给evel函数传递一个表达式字符串,直接能计算出结果。这样如果想计算1-1。直接传人不同的表达式即可。现在要计算’(2+2)*3+5/2’或’2>1?1+1:2+2’。来吧你硬编码试试这些计算?是不是又要多写几行代码,而且不便扩展。
你以为mvel只能做这些了?那就真的是太年轻了。目前mvel支持大量的语法,条件,循环等。还可以支持自定义函数,这就🐂了。那么我们工作中用这东西来干嘛?
- 在自定义数据流转中的使用
数据流转就是不同对象间数据的转换。比如a对象数据通过某些规则转化为b对象数据。这说的是不是数据清洗?对,说的没错,但是数据清洗只是其中的一个具体项罢了。👍,来个图:
对象a ———————-> 转换 —————————-> 对象b
由图可以看出两个对象name和age都是一对一映射,但是目标对象不需要sex字段,但是多了一个出生年的字段,而且是通过年龄计算而来。下面我们就以代码来模拟一下这个转换过程,在这里我对象都用map来定义。
但是代码不够优雅。
// 对象a
HashMap<Object, Object> a = Maps.newHashMap();
a.put("name", "zs");
a.put("age", 10);
a.put("sex", "女");
// 字段映射关系
HashMap<String, String> mapping = Maps.newHashMap();
mapping.put("name", "name");
mapping.put("age", "age");
mapping.put("birthYear", "2020-age");
// 目标对象
HashMap<Object, Object> b = Maps.newHashMap();
// k为目标表字段,v为转换规则
b.forEach((k, v) -> {
Object reValue = MVEL.eval((String)v, a);
b.put(k, reValue);
});
System.out.println("源对象" + a);
System.out.println("目标对象" + b);
将自定义函数注册
static ParserContext context = new ParserContext(); static { //MvelTest是getCurrentYear函数的类 Method[] declaredMethods = MvelTest.class.getDeclaredMethods(); for(Method method : declaredMethods){ context.addImport(method.getName(),method); } }
/** * 获取当前年份方法 * @return */ public static Object getCurrentYear(){ Calendar date = Calendar.getInstance(); String year = String.valueOf(date.get(Calendar.YEAR)); return year; }
重构一下:
// 对象a HashMap<Object, Object> a = Maps.newHashMap(); a.put("name", "zs"); a.put("age", 10); a.put("sex", "女"); // 字段映射关系 HashMap<String, String> mapping = Maps.newHashMap(); mapping.put("name", "name"); mapping.put("age", "age"); mapping.put("birthYear", "getCurrentYear()-age"); // 目标对象 HashMap<Object, Object> b = Maps.newHashMap(); // k为目标表字段,v为转换规则 mapping.forEach((k, v) -> { Object reValue = MVEL.executeExpression(MVEL.compileExpression(v, context), a); b.put(k, reValue); }); System.out.println("源对象" + a); System.out.println("目标对象" + b);
compileExpression
的作用就是将我们的规则进行编译成mvel
可以识别的一个过程birthYear
规则替换为mapping.put("birthYear","getCurrentYear()-age");
执行得到相同的结果。
有了这些我们可以自定义更多的转换规则,还可以借此开发一套用户配置工具,根据用户自己的配置,进行相应的资源映射。得到想要的目标数据。绩效考核项目中的运用
场景:绩效考核系统中有大量的计算指标是要做各式各样的统计计算,且这些指标的计算规则每年都要变
引入计算引擎表达式,大大减少人工维护成本
没有引入之前:绩效系统的计算类
引入之后:
- 指标维护
指标1:
指标2: