Hutool常用工具类
- 介绍
- 安装
- 常用工具类
- 1. 类型转换工具类-Convert
- 2. 日期时间工具-DateUtil
- 3. 日期时间对象-DateTime
- 4. LocalDateTime工具-LocalDateTimeUtil
- 5. 字符串工具-StrUtil
- 6. 数字工具-NumberUtil
- 7. 唯一ID工具-IdUtil
- 8. 身份证工具-IdcardUtil
- 9. JSON工具-JSONUtil
- 10. 二维码工具-QrCodeUtil
- 11. 系统属性调用-SystemUtil
- 12. JWT(Hutool-jwt)
- 13. BeanUtil
- 13. BooleanUtil
- 14. ListUtil
- 15. CollUtil
- 16. TreeUtil 树工具类
- 17. HttpUtil
- 18. EmojiUtil
- 19. PhoneUtil
- 20. HttpRequest
- 21. 加密解密工具的使用
- 22. ArrayUtil
- 23. RandomUtil
- 24. DesensitizedUtil 脱敏工具类
- 25. SensitiveUtil 敏感词工具类
介绍
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
请移步官网:https://hutool.cn/
不得不说,hutool真的是好用,大大提高开发效率,使代码没有那么臃肿,太妙了,以下只是我在开发中遇到的,并不全
安装
1. maven
请认准官网最新依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
2. gradle
请认准官网最新依赖
implementation 'cn.hutool:hutool-all:5.8.16'
常用工具类
1. 类型转换工具类-Convert
Convert类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。Convert类中大部分方法为toXXX,参数为Object,可以实现将任意可能的类型转换为指定类型。同时支持第二个参数defaultValue用于在转换失败时返回一个默认值。
//转换为字符串
int a = 1;
String aStr = Convert.toStr(a);
//转换为指定类型数组
String[] b = { "1", "2", "3", "4" };
Integer[] intArray = Convert.toIntArray(b);
//转换为日期对象
String dateStr = "2022-09-28";
Date date = Convert.toDate(dateStr);
//转换为列表
String[] strArr = {"a", "b", "c", "d"};
List<String> strList = Convert.toList(String.class, strArr);
Object[] a = {"a", "你", "好", "", 1};
List<?> list = Convert.convert(List.class, a);
//从4.1.11开始可以这么用
List<?> list = Convert.toList(a);
2. 日期时间工具-DateUtil
转换
Date、long、Calendar之间的相互转换
//获取当前时间
Date date = DateUtil.date();
//Calendar类型时间转为Date
Date date2 = DateUtil.date(Calendar.getInstance());
//时间戳转为Date
Date date3 = DateUtil.date(System.currentTimeMillis());
//当前时间字符串,格式:yyyy-MM-dd HH:mm:ss
String now = DateUtil.now();
//当前日期字符串,格式:yyyy-MM-dd
String today= DateUtil.today();
// 时间格式转为时间戳
Date date1 = DateUtil.parse("2023-06-21");
long time = date1.getTime();
System.out.println("time = " + time);
字符串转日期
DateUtil.parse方法会自动识别一些常用格式,包括:
yyyy-MM-dd HH:mm:ss
- yyyy/MM/dd HH:mm:ss
- yyyy.MM.dd HH:mm:ss
- yyyy年MM月dd日 HH时mm分ss秒
- yyyy-MM-dd
- yyyy/MM/dd
- yyyy.MM.dd
- HH:mm:ss
- HH时mm分ss秒
- yyyy-MM-dd HH:mm
- yyyy-MM-dd HH:mm:ss.SSS
…
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);
我们也可以使用自定义日期格式转化:
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr, "yyyy-MM-dd");
格式化日期输出
String dateStr = "2017-03-01";
Date date = DateUtil.parse(dateStr);
//结果 2017/03/01
String format = DateUtil.format(date, "yyyy/MM/dd");
//常用格式的格式化,结果:2017-03-01
String formatDate = DateUtil.formatDate(date);
//结果:2017-03-01 00:00:00
String formatDateTime = DateUtil.formatDateTime(date);
//结果:00:00:00
String formatTime = DateUtil.formatTime(date);
获取Date对象的某个部分
Date date = DateUtil.date();
//获得年的部分
DateUtil.year(date);
//获得月份,从0开始计数
DateUtil.month(date);
//获得月份枚举
DateUtil.monthEnum(date);
//.....
开始和结束时间
有的时候我们需要获得每天的开始时间、结束时间,每月的开始和结束时间等等,DateUtil也提供了相关方法:
String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);
//一天的开始,结果:2017-03-01 00:00:00
Date beginOfDay = DateUtil.beginOfDay(date);
//一天的结束,结果:2017-03-01 23:59:59
Date endOfDay = DateUtil.endOfDay(date);
日期时间偏移
日期或时间的偏移指针对某个日期增加或减少分、小时、天等等,达到日期变更的目的。Hutool也针对其做了大量封装
String dateStr = "2017-03-01 22:33:23";
Date date = DateUtil.parse(dateStr);
//结果:2017-03-03 22:33:23
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);
//常用偏移,结果:2017-03-04 22:33:23
DateTime newDate2 = DateUtil.offsetDay(date, 3);
//常用偏移,结果:2017-03-01 19:33:23
DateTime newDate3 = DateUtil.offsetHour(date, -3);
针对当前时间,提供了简化的偏移方法(例如昨天、上周、上个月等):
//昨天
DateUtil.yesterday()
//明天
DateUtil.tomorrow()
//上周
DateUtil.lastWeek()
//下周
DateUtil.nextWeek()
//上个月
DateUtil.lastMonth()
//下个月
DateUtil.nextMonth()
日期时间差
有时候我们需要计算两个日期之间的时间差(相差天数、相差小时数等等),Hutool将此类方法封装为between方法:
String dateStr1 = "2017-03-01 22:33:23";
Date date1 = DateUtil.parse(dateStr1);
String dateStr2 = "2017-04-01 23:33:23";
Date date2 = DateUtil.parse(dateStr2);
//相差一个月,31天
long betweenDay = DateUtil.between(date1, date2, DateUnit.DAY);
格式化时间差
有时候我们希望看到易读的时间差,比如XX天XX小时XX分XX秒,此时使用DateUtil.formatBetween方法:
//Level.MINUTE表示精确到分
String formatBetween = DateUtil.formatBetween(between, Level.MINUTE);
//输出:31天1小时
Console.log(formatBetween);
星座和属相
// "摩羯座"
String zodiac = DateUtil.getZodiac(Month.JANUARY.getValue(), 19);
// "狗"
String chineseZodiac = DateUtil.getChineseZodiac(1994);
其它
//年龄
DateUtil.ageOfNow("1990-01-30");
//是否闰年
DateUtil.isLeapYear(2017);
//获得指定月份的总天数
DateUtil.lengthOfMonth(6,false);
3. 日期时间对象-DateTime
新建对象
DateTime
对象包含众多的构造方法,构造方法支持的参数有:
Date
Calendar
String(日期字符串,第二个参数是日期格式)
long 毫秒数
构建对象有两种方式:DateTime.of()
和new DateTime()
:
Date date = new Date();
//new方式创建
DateTime time = new DateTime(date);
Console.log(time);
//of方式创建
DateTime now = DateTime.now();
DateTime dt = DateTime.of(date);
使用对象
DateTime的成员方法与DateUtil中的静态方法所对应,因为是成员方法,因此可以使用更少的参数操作日期时间。
示例:获取日期成员(年、月、日等)
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);
//年,结果:2017
int year = dateTime.year();
//季度(非季节),结果:Season.SPRING
Season season = dateTime.seasonEnum();
//月份,结果:Month.JANUARY
Month month = dateTime.monthEnum();
//日,结果:5
int day = dateTime.dayOfMonth();
更多成员方法请参阅API文档。
对象的可变性
DateTime对象默认是可变对象(调用offset、setField、setTime方法默认变更自身),但是这种可变性有时候会引起很多问题(例如多个地方共用DateTime对象)。我们可以调用setMutable(false)方法使其变为不可变对象。在不可变模式下,offset、setField方法返回一个新对象,setTime方法抛出异常。
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);
//默认情况下DateTime为可变对象,此时offset == dateTime
DateTime offset = dateTime.offset(DateField.YEAR, 0);
//设置为不可变对象后变动将返回新对象,此时offset != dateTime
dateTime.setMutable(false);
offset = dateTime.offset(DateField.YEAR, 0);
格式化为字符串
调用toString()方法即可返回格式为yyyy-MM-dd HH:mm:ss的字符串,调用toString(String format)可以返回指定格式的字符串。
DateTime dateTime = new DateTime("2017-01-05 12:34:23", DatePattern.NORM_DATETIME_FORMAT);
//结果:2017-01-05 12:34:23
String dateStr = dateTime.toString();
//结果:2017/01/05
4. LocalDateTime工具-LocalDateTimeUtil
从Hutool的5.4.x开始,Hutool加入了针对JDK8+日期API的封装,此工具类的功能包括LocalDateTime和LocalDate的解析、格式化、转换等操作。
日期转换
String dateStr = "2020-01-23T12:23:56";
DateTime dt = DateUtil.parse(dateStr);
// Date对象转换为LocalDateTime
LocalDateTime of = LocalDateTimeUtil.of(dt);
// 时间戳转换为LocalDateTime
of = LocalDateTimeUtil.ofUTC(dt.getTime());
日期字符串解析
// 解析ISO时间
LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
// 解析自定义格式时间
localDateTime = LocalDateTimeUtil.parse("2020-01-23", DatePattern.NORM_DATE_PATTERN);
解析同样支持LocalDate
:
LocalDate localDate = LocalDateTimeUtil.parseDate("2020-01-23");
// 解析日期时间为LocalDate,时间部分舍弃
localDate = LocalDateTimeUtil.parseDate("2020-01-23T12:23:56", DateTimeFormatter.ISO_DATE_TIME);
日期格式化
LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
// "2020-01-23 12:23:56"
String format = LocalDateTimeUtil.format(localDateTime, DatePattern.NORM_DATETIME_PATTERN);
日期偏移
final LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
// 增加一天
// "2020-01-24T12:23:56"
LocalDateTime offset = LocalDateTimeUtil.offset(localDateTime, 1, ChronoUnit.DAYS);
如果是减少时间,offset第二个参数传负数即可:
// "2020-01-22T12:23:56"
offset = LocalDateTimeUtil.offset(localDateTime, -1, ChronoUnit.DAYS);
计算时间间隔
LocalDateTime start = LocalDateTimeUtil.parse("2019-02-02T00:00:00");
LocalDateTime end = LocalDateTimeUtil.parse("2020-02-02T00:00:00");
Duration between = LocalDateTimeUtil.between(start, end);
// 365
between.toDays();
一天的开始和结束
LocalDateTime localDateTime = LocalDateTimeUtil.parse("2020-01-23T12:23:56");
// "2020-01-23T00:00"
LocalDateTime beginOfDay = LocalDateTimeUtil.beginOfDay(localDateTime);
// "2020-01-23T23:59:59.999999999"
LocalDateTime endOfDay = LocalDateTimeUtil.endOfDay(localDateTime);
其它
- static boolean isSameDay(LocalDate date1, LocalDate date2)
比较两个日期是否为同一天 - static boolean isSameDay(LocalDateTime date1, LocalDateTime date2)
比较两个日期是否为同一天 - static boolean isWeekend(LocalDate localDate)
是否为周末(周六或周日) - static boolean isWeekend(LocalDateTime localDateTime)
是否为周末(周六或周日
5. 字符串工具-StrUtil
5.1 常用
这个工具的用处类似于Apache Commons Lang中的StringUtil
常用的方法例如isBlank、isNotBlank、isEmpty、isNotEmpty、split等
// 将list集合用","拼接每个元素并返回String
String idStr = StrUtil.join(",", ids);
5.2 驼峰命名和使用符号连接方式相互转换
-
toUnderlineCase(CharSequence str)
将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
例如: helloWorld =》hello_world -
toCamelCase(CharSequence name)
将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
例如:hello_world =》helloWorld -
toSymbolCase(CharSequence str, char symbol)
将驼峰式命名的字符串转换为使用符号连接方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
6. 数字工具-NumberUtil
加减乘除
- NumberUtil.add 针对数字类型做加法
- NumberUtil.sub 针对数字类型做减法
- NumberUtil.mul 针对数字类型做乘法
- NumberUtil.div 针对数字类型做除法,并提供重载方法用于规定除不尽的情况下保留小数位数和舍弃方式。
以上四种运算都会将double转为BigDecimal后计算,解决float和double类型无法进行精确计算的问题。这些方法常用于商业
计算。
保留小数
保留小数的方法主要有两种:
- NumberUtil.round 方法主要封装BigDecimal中的方法来保留小数,返回BigDecimal,这个方法更加灵活,可以选择四舍五入或者全部舍弃等模式。
double te1=123456.123456;
double te2=123456.128456;
Console.log(round(te1,4));//结果:123456.1235
Console.log(round(te2,4));//结果:123456.1285
- NumberUtil.roundStr 方法主要封装String.format方法,舍弃方式采用四舍五入。
double te1=123456.123456;
double te2=123456.128456;
Console.log(roundStr(te1,2));//结果:123456.12
Console.log(roundStr(te2,2));//结果:123456.13
decimalFormat
针对 DecimalFormat.format进行简单封装。按照固定格式对double或long类型的数字做格式化操作。
long c=299792458;//光速
String format = NumberUtil.decimalFormat(",###", c);//299,792,458
格式中主要以 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。
- 0 -> 取一位整数
- 0.00 -> 取一位整数和两位小数
- 00.000 -> 取两位整数和三位小数
#
-> 取所有整数部分- #.##% -> 以百分比方式计数,并取两位小数
- #.#####E0 -> 显示为科学计数法,并取五位小数
- ,### -> 每三位以逗号进行分隔,例如:299,792,458
- 光速大小为每秒,###米 -> 将格式嵌入文本
是否为数字
- NumberUtil.isNumber 是否为数字
- NumberUtil.isInteger 是否为整数
- NumberUtil.isDouble 是否为浮点数
- NumberUtil.isPrimes 是否为质数
随机数
- NumberUtil.generateRandomNumber 生成不重复随机数 根据给定的最小数字和最大数字,以及随机数的个数,产生指定的不重复的数组。
- NumberUtil.generateBySet 生成不重复随机数 根据给定的最小数字和最大数字,以及随机数的个数,产生指定的不重复的数组。
整数列表
NumberUtil.range 方法根据范围和步进,生成一个有序整数列表。 NumberUtil.appendRange 将给定范围内的整数添加到已有集合中
其它
- NumberUtil.factorial 阶乘
- NumberUtil.sqrt 平方根
- NumberUtil.divisor 最大公约数
- NumberUtil.multiple 最小公倍数
- NumberUtil.getBinaryStr 获得数字对应的二进制字符串
- NumberUtil.binaryToInt 二进制转int
- NumberUtil.binaryToLong 二进制转long
- NumberUtil.compare 比较两个值的大小
- NumberUtil.toStr 数字转字符串,自动并去除尾小数点儿后多余的0
- NumberUtil.isIn(BigDecimal value, BigDecimal minInclude, BigDecimal maxInclude)检查值是否在指定范围内(值,最小值(包含),最大值(包含))
7. 唯一ID工具-IdUtil
在分布式环境中,唯一ID生成应用十分广泛,生成方法也多种多样,Hutool针对一些常用生成策略做了简单封装。
唯一ID生成器的工具类,涵盖了:
- UUID
- ObjectId(MongoDB)
- Snowflake(Twitter)
UUID
UUID全称通用唯一识别码(universally unique identifier),JDK通过java.util.UUID提供了 Leach-Salz 变体的封装。在Hutool中,生成一个UUID字符串方法如下:
//生成的UUID是带-的字符串,类似于:a5c8a5e8-df2b-4706-bea4-08d0939410e3
String uuid = IdUtil.randomUUID();
//生成的是不带-的字符串,类似于:b17f24ff026d40949c85a24f4f375d42
String simpleUUID = IdUtil.simpleUUID();
说明
Hutool重写java.util.UUID的逻辑,对应类为cn.hutool.core.lang.UUID,使生成不带-的UUID字符串不再需要做字符替换,性能提升一倍左右。
ObjectId
ObjectId是MongoDB数据库的一种唯一ID生成策略,是UUID version1的变种,详细介绍可见:服务化框架-分布式Unique ID的生成方法一览。
Hutool针对此封装了cn.hutool.core.lang.ObjectId,快捷创建方法为:
//生成类似:5b9e306a4df4f8c54a39fb0c
String id = ObjectId.next();
//方法2:从Hutool-4.1.14开始提供
String id2 = IdUtil.objectId();
Snowflake
分布式系统中,有一些需要使用全局唯一ID的场景,有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。Twitter的Snowflake 算法就是这种生成器。
使用方法如下:
//参数1为终端ID
//参数2为数据中心ID
Snowflake snowflake = IdUtil.getSnowflake(1, 1);
long id = snowflake.nextId();
//简单使用
long id = IdUtil.getSnowflakeNextId();
String id = snowflake.getSnowflakeNextIdStr();
注意
IdUtil.createSnowflake每次调用会创建一个新的Snowflake对象,不同的Snowflake对象创建的ID可能会有重复,因此请自行维护此对象为单例,或者使用IdUtil.getSnowflake使用全局单例对象。
8. 身份证工具-IdcardUtil
IdcardUtil现在支持大陆15位、18位身份证,港澳台10位身份证。
工具中主要的方法包括:
- isValidCard 验证身份证是否合法
- convert15To18 身份证15位转18位
- getBirthByIdCard 获取生日
- getAgeByIdCard 获取年龄
- getYearByIdCard 获取生日年
- getMonthByIdCard 获取生日月
- getDayByIdCard 获取生日天
- getGenderByIdCard 获取性别
- getProvinceByIdCard 获取省份
String ID_18 = "321083197812162119";
String ID_15 = "150102880730303";
//是否有效
boolean valid = IdcardUtil.isValidCard(ID_18);
boolean valid15 = IdcardUtil.isValidCard(ID_15);
//转换
String convert15To18 = IdcardUtil.convert15To18(ID_15);
Assert.assertEquals(convert15To18, "150102198807303035");//断言
//年龄
DateTime date = DateUtil.parse("2017-04-10");
int age = IdcardUtil.getAgeByIdCard(ID_18, date);
Assert.assertEquals(age, 38);
int age2 = IdcardUtil.getAgeByIdCard(ID_15, date);
Assert.assertEquals(age2, 28);
//生日
String birth = IdcardUtil.getBirthByIdCard(ID_18);
Assert.assertEquals(birth, "19781216");
String birth2 = IdcardUtil.getBirthByIdCard(ID_15);
Assert.assertEquals(birth2, "19880730");
//省份
String province = IdcardUtil.getProvinceByIdCard(ID_18);
Assert.assertEquals(province, "江苏");
String province2 = IdcardUtil.getProvinceByIdCard(ID_15);
Assert.assertEquals(province2, "内蒙古");
9. JSON工具-JSONUtil
JSON字符串创建
JSONUtil.toJsonStr可以将任意对象(Bean、Map、集合等)直接转换为JSON字符串。 如果对象是有序的Map等对象,则转换后的JSON字符串也是有序的。
SortedMap<Object, Object> sortedMap = new TreeMap<Object, Object>() {
private static final long serialVersionUID = 1L;
{
put("attributes", "a");
put("b", "b");
put("c", "c");
}};
JSONUtil.toJsonStr(sortedMap);
结果:
{"attributes":"a","b":"b","c":"c"}
如果我们想获得格式化后的JSON,则:
JSONUtil.toJsonPrettyStr(sortedMap);
结果:
{
"attributes": "a",
"b": "b",
"c": "c"
}
JSON字符串解析
String html = "{\"name\":\"Something must have been changed since you leave\"}";
JSONObject jsonObject = JSONUtil.parseObj(html);
jsonObject.getStr("name");
XML字符串转换为JSON
String s = "<sfzh>123</sfzh><sfz>456</sfz><name>aa</name><gender>1</gender>";
JSONObject json = JSONUtil.parseFromXml(s);
json.get("sfzh");
json.get("name");
JSON转换为XML
final JSONObject put = JSONUtil.createObj()
.set("aaa", "你好")
.set("键2", "test");
// <aaa>你好</aaa><键2>test</键2>
final String s = JSONUtil.toXmlStr(put);
JSON转Bean
我们先定义两个较为复杂的Bean(包含泛型)
@Data
public class ADT {
private List<String> BookingCode;
}
@Data
public class Price {
private List<List<ADT>> ADT;
}
String json = "{\"ADT\":[[{\"BookingCode\":[\"N\",\"N\"]}]]}";
Price price = JSONUtil.toBean(json, Price.class);
//
price.getADT().get(0).get(0).getBookingCode().get(0);
JSON转List
存
List<ShopType> typeList = query().orderByAsc("sort").list();
// 将ShopType转为String
String jsonStr = JSONUtil.toJsonStr(typeList);
// 4.保存到redis
// string类型
stringRedisTemplate.opsForValue().set(CACHE_SHOP_TYPE_KEY,jsonStr);
取
String shopTypeJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_TYPE_KEY);
if (StrUtil.isNotBlank(shopTypeJson)) {
// 2.存在直接返回
List<ShopType> shopTypes = JSONUtil.toList(shopTypeJson, ShopType.class);
return Result.ok(shopTypes);
}
readXXX
这类方法主要是从JSON文件中读取JSON对象的快捷方法。包括:
- readJSON
- readJSONObject
- readJSONArray
其它方法
除了上面中常用的一些方法,JSONUtil还提供了一些JSON辅助方法:
- quote 对所有双引号做转义处理(使用双反斜杠做转义)
- wrap 包装对象,可以将普通任意对象转为JSON对象
- formatJsonStr 格式化JSON字符串,此方法并不严格检查JSON的格式正确与否
10. 二维码工具-QrCodeUtil
引入zxing
考虑到Hutool的非强制依赖性,因此zxing需要用户自行引入:
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.3.3</version>
</dependency>
说明 zxing-3.3.3是此文档编写时的最新版本,理论上你引入的版本应与此版本一致或比这个版本新。
生成二维码
在此我们将Hutool主页的url生成为二维码,微信扫一扫可以看到H5主页哦:
// 生成指定url对应的二维码到文件,宽和高都是300像素
QrCodeUtil.generate("https://blog.csdn.net/abst122", 300, 300, FileUtil.file("d:/qrcode.jpg"));
自定义参数(since 4.1.2)
- 基本参数设定
通过QrConfig可以自定义二维码的生成参数,例如长、宽、二维码的颜色、背景颜色、边距等参数,使用方法如下:
QrConfig config = new QrConfig(300, 300);
// 设置边距,既二维码和背景之间的边距
config.setMargin(3);
// 设置前景色,既二维码颜色(青色)
config.setForeColor(Color.CYAN.getRGB());
// 设置背景色(灰色)
config.setBackColor(Color.GRAY.getRGB());
// 生成二维码到文件,也可以到流
QrCodeUtil.generate("https://blog.csdn.net/abst122", config, FileUtil.file("e:/qrcode.jpg"));
- 附带logo小图标
QrCodeUtil.generate(//
"https://blog.csdn.net/abst122", //二维码内容
QrConfig.create().setImg("e:/logo_small.jpg"), //附带logo
FileUtil.file("e:/qrcodeWithLogo.jpg")//写出到的文件
);
- 调整纠错级别
很多时候,二维码无法识别,这时就要调整纠错级别。纠错级别使用zxing的ErrorCorrectionLevel枚举封装,包括:L、M、Q、H几个参数,由低到高。低级别的像素块更大,可以远距离识别,但是遮挡就会造成无法识别。高级别则相反,像素块小,允许遮挡一定范围,但是像素块更密集。
QrConfig config = new QrConfig();
// 高纠错级别
config.setErrorCorrection(ErrorCorrectionLevel.H);
QrCodeUtil.generate("https://blog.csdn.net/abst122", config, FileUtil.file("e:/qrcodeCustom.jpg"));
识别二维码
// decode -> "https://blog.csdn.net/abst122"
String decode = QrCodeUtil.decode(FileUtil.file("d:/qrcode.jpg"));
11. 系统属性调用-SystemUtil
此工具是针对System.getProperty(name)的封装,通过此工具,可以获取如下信息:
Java Virtual Machine Specification信息
SystemUtil.getJvmSpecInfo();
Java Virtual Machine Implementation信息
SystemUtil.getJvmInfo();
Java Specification信息
SystemUtil.getJavaSpecInfo();
Java Implementation信息
SystemUtil.getJavaInfo();
Java运行时信息
SystemUtil.getJavaRuntimeInfo();
系统信息
SystemUtil.getOsInfo();
用户信息
SystemUtil.getUserInfo();
当前主机网络地址信息
SystemUtil.getHostInfo();
运行时信息,包括内存总大小、已用大小、可用大小等
SystemUtil.getRuntimeInfo();
12. JWT(Hutool-jwt)
JWT工具-JWTUtil
JWT创建
Map<String, Object> map = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("uid", Integer.parseInt("123"));
put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15);
}
};
JWTUtil.createToken(map, "1234".getBytes());
JWT解析
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
"U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
final JWT jwt = JWTUtil.parseToken(rightToken);
jwt.getHeader(JWTHeader.TYPE);
jwt.getPayload("sub");
JWT验证
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." +
"aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
JWTUtil.verify(token, "123456".getBytes());
JWT验证-JWTValidator
验证算法
算法的验证包括两个方面
- 验证header中算法ID和提供的算法ID是否一致
- 调用JWT.verify验证token是否正确
// 创建JWT Token
final String token = JWT.create()
.setNotBefore(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 验证算法
JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256("123456".getBytes()));
验证时间
对于时间类载荷,有单独的验证方法,主要包括:
- 生效时间(JWTPayload#NOT_BEFORE)不能晚于当前时间
- 失效时间(JWTPayload#EXPIRES_AT)不能早于当前时间
- 签发时间(JWTPayload#ISSUED_AT)不能晚于当前时间
一般时间线是:
(签发时间)---------(生效时间)---------(当前时间)---------(失效时间)
签发时间和生效时间一般没有前后要求,都早于当前时间即可。
final String token = JWT.create()
// 设置签发时间
.setIssuedAt(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 由于只定义了签发时间,因此只检查签发时间
JWTValidator.of(token).validateDate(DateUtil.date());
13. BeanUtil
其实这个才是最常用的
bean转bean
// 1. 将user中所有属性赋值给UserDTO已有饿属性
UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
// 2. 忽略某个属性方式一
CopyOptions copyOptions = new CopyOptions();
// 2.1 忽略id
copyOptions.setIgnoreProperties("id");
BeanUtil.copyProperties(templateVO, template, copyOptions);
// 3. 忽略某个属性方式二
BeanUtil.copyProperties(copyOptions, OpinionExcelVO.class,"id");
// 4.忽略某个属性方式三
Commodity newCommodity = new Commodity();
CopyOptions copyOptions = new CopyOptions();
String[] properties = new String[]{"id", "commodityAreaList", "commodityAttributeList", "unitPriceAndPicList", "commodityRemark", "commodityShopList"};
copyOptions.setIgnoreProperties(properties);
BeanUtil.copyProperties(commodity, newCommodity, copyOptions);
// 5.忽略某个属性方式四
BeanUtil.copyProperties(commodity, newCommodity, new String[]{"id", "commodityAreaList", "commodityAttributeList", "unitPriceAndPicList", "commodityRemark", "commodityShopList"});
// 6.忽略null不复制
BeanUtil.copyProperties(request,order, CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true));
bean转map
SubPerson person = new SubPerson();
person.setAge(14);
person.setOpenid("11213232");
person.setName("测试A11");
person.setSubName("sub名字");
Map<String, Object> map = BeanUtil.beanToMap(person);
如果报错
@Data
public class UserDTO {
private Long id;
private String nickName;
private String icon;
}
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO);
这里UserDTO中有Long类型将会抛出异常,不能将Long转换为String,这时,可以手动去转,也可以改为
Map<String, Object> userMap = BeanUtil.beanToMap(userDTO, new HashMap<>(),
CopyOptions.create()
.setIgnoreNullValue(true)
.setFieldValueEditor((fieldName, fieldValue) -> fieldValue.toString()));
将所有属性都转为String就不会报错了
map转bean
// 方式一
UserDTO userDTOC = BeanUtil.fillBeanWithMap(userMap, new UserDTO, false);
// 方式二
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, UserDTO.class, false).newInstance();
isEmpty和isNotEmpty
判断对象是否为null或者所有属性都为null
将集合中每个属性赋值给新对象并加入List
- static List copyToList(Collection<?> collection, Class targetType)
复制集合中的Bean属性
此方法遍历集合中每个Bean,复制其属性后加入一个新的List中。 - static List copyToList(Collection<?> collection, Class targetType, CopyOptions copyOptions)
复制集合中的Bean属性
此方法遍历集合中每个Bean,复制其属性后加入一个新的List中。
Page<OpinionDO> all = opinionRepository.findAll(specification, page);
List<OpinionExcelVO> opinionExcelVOS = BeanUtil.copyToList(all.getContent(), OpinionExcelVO.class);
判断对象属性是否为null
-
static boolean isEmpty(Object bean, String… ignoreFieldNames)
判断Bean是否为空对象,空对象表示本身为null或者所有属性都为null
此方法不判断static属性 -
static boolean isNotEmpty(Object bean, String… ignoreFieldNames)
判断Bean是否为非空对象,非空对象表示本身不为null或者含有非null属性的对象
13. BooleanUtil
// 给定类是否为Boolean或者boolean
BooleanUtil.isBoolean(isMember);
// 判读isMember是否是false
BooleanUtil.isFalse(isMember);
// 判读isMember是否是true
BooleanUtil.isTrue(isMember);
// 转换字符串为boolean值
BooleanUtil.toBoolean(valueStr)
14. ListUtil
需要自己分页时使用page()
// 当前页数,每页总数,需要分页的lisrt
ListUtil.page(int pageNo, int pageSize, List<T> list)
15. CollUtil
isEmpty、isNotEmpty方法比较常用
判断集合是否为空(包括null和没有元素的集合)。
- static <T,S extends T> boolean addIfAbsent(Collection collection, S object)
一个对象不为空且不存在于该集合中时,加入到该集合中 - static Collection addAll(Collection collection, Iterator iterator)
加入全部 - public static boolean isEqualList(final Collection<?> list1, final Collection<?> list2)
判断两个Collection 是否元素和顺序相同,返回true的条件是:- 两个Collection必须长度相同
- 两个Collection元素相同index的对象必须equals,满足Objects.equals(Object, Object)
Set<String> set = new HashSet<>();
set.add("1,23,1");
System.out.println("set = " + set);
List<String> objects = new ArrayList<>();
objects.add("88jjj");
System.out.println("objects = " + objects);
CollUtil.addAll(set, objects);
System.out.println("set = " + set);
16. TreeUtil 树工具类
可以快速构建树结构
// 查询数据
List<CodeCategory> all = list();
CodeCategory uncategorizedNode = new CodeCategory();
uncategorizedNode.setId(-1L); // 假设使用负数作为"未分类"节点的id
uncategorizedNode.setName("未分类");
uncategorizedNode.setParentId(1L); // 父节点是全部
all.add(uncategorizedNode);
TreeNodeConfig config = new TreeNodeConfig();
//config可以配置属性字段名和排序等等
//config.setParentIdKey("parentId");
//config.setDeep(20);//最大递归深度 默认无限制
// 设置权重排序
config.setWeightKey("sort");
List<Tree<Long>> treeNodes = TreeUtil.<CodeCategory, Long>build(all, 0L, config, (codeCategory, tree) -> {
tree.setId(codeCategory.getId());//必填属性
tree.setParentId(codeCategory.getParentId());//必填属性
tree.setName(codeCategory.getName());
// 扩展属性 ...
// 获取每个节的数量
Integer integer = itemMapper.selectCount(Wrappers.<Item>lambdaQuery().eq(Item::getId, "10001"));
tree.putExtra("count",integer);
tree.putExtra("full_id",codeCategory.getFullId());
tree.putExtra("createTime", DateUtil.format(codeCategory.getCreateTime(),"yyyy-MM-dd HH:mm:ss"));
});
1. 方案一:自己写递归
@Data
public class Tree1 {
private String id;
private String name;
private Integer age;
/**
* 上级id
*/
private String superiorId;
private String address;
/**
* 下级数据
*/
private List<Tree1> children;
public Tree1() {
}
public Tree1(String id, String name, Integer age, String superiorId, String address) {
this.id = id;
this.name = name;
this.age = age;
this.superiorId = superiorId;
this.address = address;
}
}
List<Tree1> list = new ArrayList<>();
Collections.addAll(list,
new Tree1("1", "第一级1", 10, "0", "第一级1地址"),
new Tree1("12", "第二级1", 20, "1", "第二级1地址"),
new Tree1("13", "第二级2", 21, "1", "第二级2地址"),
new Tree1("121", "第三级1", 30, "12", "第三级1地址"),
new Tree1("2", "第一级2", 10, "0", "第一级2地址"));
System.out.println(JSONUtil.toJsonStr(TreeUtils.queryTerr("0", list,
"id", "superiorId", "children")));
TreeUtils
/**
* 递归组装树结构
*
* @param superiorId 父级id
* @param list list数据
* @param currentLevelIdName id的名称
* @param superiorIdName 父级id的名称
* @param childrenName 下级集合的名称
* @return 组装完成的数据
* @throws IllegalAccessException
* @throws NoSuchFieldException
*/
public static List<?> queryTerr(Object superiorId, List<?> list,
String currentLevelIdName, String superiorIdName,
String childrenName) throws IllegalAccessException, NoSuchFieldException {
List<Object> children = new ArrayList<>();
for(Object t : list){
Field fieldSuperiorIdName = t.getClass().getDeclaredField(superiorIdName);
fieldSuperiorIdName.setAccessible(true);
Object tSuperior = fieldSuperiorIdName.get(t);
fieldSuperiorIdName.setAccessible(false);
if(superiorId.equals(tSuperior)){
children.add(t);
Field fieldCurrentLevelIdName = t.getClass().getDeclaredField(currentLevelIdName);
fieldCurrentLevelIdName.setAccessible(true);
Object tCuLeIdName = fieldCurrentLevelIdName.get(t);
fieldCurrentLevelIdName.setAccessible(false);
Field fieldEntity = t.getClass().getDeclaredField(childrenName);
fieldEntity.setAccessible(true);
fieldEntity.set(t, queryTerr(tCuLeIdName, list, currentLevelIdName, superiorIdName, childrenName));
fieldEntity.setAccessible(false);
}
}
return children;
}
[
{
"id": "1",
"name": "第一级1",
"age": 10,
"superiorId": "0",
"address": "第一级1地址",
"children": [
{
"id": "12",
"name": "第二级1",
"age": 20,
"superiorId": "1",
"address": "第二级1地址",
"children": [
{
"id": "121",
"name": "第三级1",
"age": 30,
"superiorId": "12",
"address": "第三级1地址",
"children": []
}
]
},
{
"id": "13",
"name": "第二级2",
"age": 21,
"superiorId": "1",
"address": "第二级2地址",
"children": []
}
]
},
{
"id": "2",
"name": "第一级2",
"age": 10,
"superiorId": "0",
"address": "第一级2地址",
"children": []
}
]
2. 方案二:使用TreeUtil
@Data
public class Tree2 {
private Long id;
private String name;
private Integer code;
/**
* 上级id
*/
private Long parentId;
/**
* 下级数据
*/
private List<Tree2> children;
public Tree2(Long id, String name, Integer code, Long parentId) {
this.id = id;
this.name = name;
this.code = code;
this.parentId = parentId;
}
public Tree2() {
}
}
//查询所有数据
List<Tree2> list = new ArrayList<>();
Collections.addAll(list,
new Tree2(1L, "广西壮族自治区", 100000, 0L),
new Tree2(2L, "南宁市", 450000, 1L),
new Tree2(3L, "桂林市", 450000, 1L),
new Tree2(4L, "良庆区", 30, 2L),
new Tree2(5L, "广州省", 30, 0L),
new Tree2(6L, "深圳", 30, 5L),
new Tree2(7L, "广州", 30, 5L),
new Tree2(8L, "桂林甲天下", 10, 3L));
TreeNodeConfig config = new TreeNodeConfig();
//config可以配置属性字段名和排序等等
//config.setParentIdKey("parentId");
//config.setDeep(20);//最大递归深度 默认无限制
// 配置权重排序
config.setWeightKey("name");
List<Tree<Long>> treeNodes = TreeUtil.build(list, 0L, config, (object, tree) -> {
tree.setId(object.getId());//必填属性
tree.setParentId(object.getParentId());//必填属性
tree.setName(object.getName());
// 扩展属性 ...
tree.putExtra("code",object.getCode());
});
System.out.println(JSONUtil.toJsonStr(treeNodes));
}
[
{
"id": 1,
"parentId": 0,
"name": "广西壮族自治区",
"code": 100000,
"children": [
{
"id": 2,
"parentId": 1,
"name": "南宁市",
"code": 450000,
"children": [
{
"id": 4,
"parentId": 2,
"name": "良庆区",
"code": 30
}
]
},
{
"id": 3,
"parentId": 1,
"name": "桂林市",
"code": 450000,
"children": [
{
"id": 8,
"parentId": 3,
"name": "桂林甲天下",
"code": 10
}
]
}
]
},
{
"id": 5,
"parentId": 0,
"name": "广州省",
"code": 30,
"children": [
{
"id": 6,
"parentId": 5,
"name": "深圳",
"code": 30
},
{
"id": 7,
"parentId": 5,
"name": "广州",
"code": 30
}
]
}
]
17. HttpUtil
- 请求并转换为字节数组
template:
url: https//:xxxx.doc
@Value("${template.url}")
String fileUrl;
byte[] bytes = HttpUtil.downloadBytes(fileUrl);
- 请求并下载到本地
@Value("${template.url}")
String fileUrl;
byte[] bytes = HttpUtil.downloadFile(fileUrl,"目标文件或目录",30*1000);
18. EmojiUtil
考虑到MySQL等数据库中普通的UTF8编码并不支持Emoji(只有utf8mb4支持),因此对于数据中的Emoji字符进行处理(转换、清除)变成一项必要工作。因此Hutool基于emoji-java库提供了Emoji工具实现。
这里只介绍两个常用的,需要看更多方法请查看hutoolapi文档
- static boolean containsEmoji(String str) 是否包含Emoji表情的Unicode符
- static String removeAllEmojis(String str) 去除字符串中所有的Emoji Unicode字符
// 判断是否包含Emoji表情的Unicode符
if(EmojiUtil.containsEmoji(request.getName())||EmojiUtil.containsEmoji(request.getTitle())){
return Wrapper.fail("输入参数存在非法字符,请重新输入。");
}
if(EmojiUtil.containsEmoji(request.getName())){
// 去除字符串中所有的Emoji Unicode字符
String resul = EmojiUtil.removeAllEmojis(request.getName());
}
19. PhoneUtil
常用方法:
- static boolean isMobile(CharSequence value)验证是否为手机号码(中国大陆)
- static boolean isMobileHk(CharSequence value)验证是否为手机号码(中国香港)
- static boolean isMobileMo(CharSequence value)验证是否为手机号码(中国澳门)
- static boolean isMobileTw(CharSequence value)验证是否为手机号码(中国台湾)
- static boolean isPhone(CharSequence value)验证是否为座机号码+手机号码(CharUtil中国)+ 400 + 800电话 + 手机号号码(中国香港)
if(!phone){
return Wrapper.fail("输入的手机号存在非法字符,请重新输入。");
}
20. HttpRequest
HttpUtil就是对HttpRequest再次封装,HttpUtil中的get和post工具方法都是HttpRequest对象的封装,因此如果想更加灵活操作Http请求,可以使用HttpRequest。
- 发起post请求
String url="http://example.com/api/user";
HttpRequest request = HttpRequest.post(url)
.header("Content-Type", "application/json"); // 设置请求头
FaceRegisterRequest obj = new FaceRegisterRequest();
obj.setCattle_id("112233");
obj.setCattle_name("1111");
obj.setFace_img("1213");
// 构建请求参数
JSON parse = JSONUtil.parse(obj);
System.out.println("请求参数 = " + parse.toString());
request.body(parse.toString());// 设置请求体
// 发起请求
HttpResponse response = request.execute();
// 获取响应状态
int status = response.getStatus();
System.out.println("status = " + status);
if (status != 200) {
System.out.println("请求错误");
return;
}
// 获取响应body
String body = response.body();
System.out.println("响应body = " + body);
JSONObject jsonObject = JSONUtil.parseObj(body);
String code = jsonObject.get("code").toString();
System.out.println("code = " + code);
String message = jsonObject.get("message").toString();
System.out.println("message = " + message);
String data = jsonObject.get("data").toString();
System.out.println("data = " + data);
}
向http://example.com/api/user
发起了一条POST请求,请求体中包含了JSON格式的数据:
{
"cattle_id":"112233",
"cattle_name":"1111",
"face_img":"1213"
}
- 发起get请求
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("id", 1);
paramMap.put("name", "hutool");
String url = "http://example.com/api";
HttpRequest request = HttpUtil.createGet(url);
request.form(paramMap);
HttpResponse response = request.execute();
String result = response.body();
System.out.println(response.body());
根据代码,实际请求的url为 http://example.com/api?id=1&name=hutool
。其中,参数id的值为1,参数name的值为hutool。
21. 加密解密工具的使用
String data = "Hello, Hutool!";
String key = "1234567890123456";
// 加密
AES aes = SecureUtil.aes(key.getBytes());
String encryptedData = aes.encryptHex(data);
System.out.println("加密后的数据:" + encryptedData);
// 解密
String decryptedData = aes.decryptStr(encryptedData);
System.out.println("解密后的数据:" + decryptedData);
// 密码加密
String hashpw = BCrypt.hashpw("admin" );
user.setPasswrod(hashpw);
System.out.println("hashpw = " + hashpw);
22. ArrayUtil
1 方法
1. 判空
数组的判空类似于字符串的判空,标准是null或者数组长度为0,ArrayUtil中封装了针对原始类型和泛型数组的判空和判非空:
1.1 判断空
int[] a = {};
int[] b = null;
ArrayUtil.isEmpty(a);
ArrayUtil.isEmpty(b);
1.2 判断非空
int[] a = {1,2};
ArrayUtil.isNotEmpty(a);
2. 新建泛型数组
Array.newInstance
并不支持泛型返回值,在此封装此方法使之支持泛型返回值。
String[] newArray = ArrayUtil.newArray(String.class, 3);
3. 调整大小
使用 ArrayUtil.resize
方法生成一个新的重新设置大小的数组。
4. 合并数组
ArrayUtil.addAll
方法采用可变参数方式,将多个泛型数组合并为一个数组。
5. 克隆
数组本身支持clone方法,因此确定为某种类型数组时调用ArrayUtil.clone(T[])
,不确定类型的使用ArrayUtil.clone(T)
,两种重载方法在实现上有所不同,但是在使用中并不能感知出差别。
5.1 泛型数组调用原生克隆
Integer[] b = {1,2,3};
Integer[] cloneB = ArrayUtil.clone(b);
Assert.assertArrayEquals(b, cloneB);
5.2 非泛型数组(原始类型数组)调用第二种重载方法
int[] a = {1,2,3};
int[] clone = ArrayUtil.clone(a);
Assert.assertArrayEquals(a, clone);
6. 有序列表生成
ArrayUtil.range
方法有三个重载,这三个重载配合可以实现支持步进的有序数组或者步进为1的有序数组。这种列表生成器在Python中做为语法糖存在。
7. 拆分数组
ArrayUtil.split
方法用于拆分一个byte数组,将byte数组平均分成几等份,常用于消息拆分。
8. 过滤
ArrayUtil.filter
方法用于过滤已有数组元素,只针对泛型数组操作,原始类型数组并未提供。 方法中Filter接口用于返回boolean值决定是否保留。
8.1 过滤数组,只保留偶数
Integer[] a = {1,2,3,4,5,6};
// [2,4,6]
Integer[] filter = ArrayUtil.filter(a, (Editor<Integer>) t -> (t % 2 == 0) ? t : null);
8.2 对已有数组编辑,获得编辑后的值。
Integer[] a = {1, 2, 3, 4, 5, 6};
// [1, 20, 3, 40, 5, 60]
Integer[] filter = ArrayUtil.filter(a, (Editor<Integer>) t -> (t % 2 == 0) ? t * 10 : t);
9. 编辑
修改元素对象,此方法会修改原数组。
Integer[] a = {1, 2, 3, 4, 5, 6};
// [1, 20, 3, 40, 5, 60]
ArrayUtil.edit(a, t -> (t % 2 == 0) ? t * 10 : t);
10. zip
ArrayUtil.zip
方法传入两个数组,第一个数组为key,第二个数组对应位置为value,此方法在Python中为zip()函数。
String[] keys = {"a", "b", "c"};
Integer[] values = {1,2,3};
Map<String, Integer> map = ArrayUtil.zip(keys, values, true);
//{a=1, b=2, c=3}
11. 是否包含元素
ArrayUtil.contains
方法只针对泛型数组,检测指定元素是否在数组中。
12. 包装和拆包
在原始类型元素和包装类型中,Java实现了自动包装和拆包,但是相应的数组无法实现,于是便是用ArrayUtil.wrap
和ArrayUtil.unwrap
对原始类型数组和包装类型数组进行转换。
13. 判断对象是否为数组
ArrayUtil.isArray
方法封装了obj.getClass().isArray()
。
14. 转为字符串
ArrayUtil.toString
通常原始类型的数组输出为字符串时无法正常显示,于是封装此方法可以完美兼容原始类型数组和包装类型数组的转为字符串操作。
ArrayUtil.join
方法使用间隔符将一个数组转为字符串,比如[1,2,3,4]这个数组转为字符串,间隔符使用“-”的话,结果为 1-2-3-4,join方法同样支持泛型数组和原始类型数组。
15. toArray
ArrayUtil.toArray
方法针对ByteBuffer转数组提供便利。
23. RandomUtil
RandomUtil.randomInt
获得指定范围内的随机数
例如我们想产生一个[10, 100)的随机数,则:
int c = RandomUtil.randomInt(10, 100);
RandomUtil.randomBytes
随机bytes,一般用于密码或者salt生成
byte[] c = RandomUtil.randomBytes(10);
RandomUtil.randomEle
随机获得列表中的元素RandomUtil.randomEleSet
随机获得列表中的一定量的不重复元素,返回LinkedHashSet
Set<Integer> set = RandomUtil.randomEleSet(CollUtil.newArrayList(1, 2, 3, 4, 5, 6), 2);
RandomUtil.randomString
获得一个随机的字符串(只包含数字和字符)RandomUtil.randomNumbers
获得一个只包含数字的字符串RandomUtil.weightRandom
权重随机生成器,传入带权重的对象,然后根据权重随机获取对象
24. DesensitizedUtil 脱敏工具类
脱敏工具类注解封装
@NoArgsConstructor
@AllArgsConstructor
public class DesensitizationSerialize extends JsonSerializer<String> implements ContextualSerializer {
private SensitiveTypeEnum value;
private Integer startInclude;
private Integer endExclude;
@Override
public void serialize(String str, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
switch (value) {
// 自定义类型脱敏
case CUSTOMER:
jsonGenerator.writeString(CharSequenceUtil.hide(str,startInclude,endExclude));
break;
// 中文姓名脱敏
case CHINESE_NAME:
jsonGenerator.writeString(DesensitizedUtil.chineseName(String.valueOf(str)));
break;
// 身份证脱敏
case ID_CARD:
jsonGenerator.writeString(DesensitizedUtil.idCardNum(String.valueOf(str), 1, 2));
break;
// 手机号脱敏
case MOBILE_PHONE:
jsonGenerator.writeString(DesensitizedUtil.mobilePhone(String.valueOf(str)));
break;
// 地址脱敏
case ADDRESS:
jsonGenerator.writeString(DesensitizedUtil.address(String.valueOf(str), 8));
break;
// 邮箱脱敏
case EMAIL:
jsonGenerator.writeString(DesensitizedUtil.email(String.valueOf(str)));
break;
// 密码脱敏
case PASSWORD:
jsonGenerator.writeString(DesensitizedUtil.password(String.valueOf(str)));
break;
// 中国车牌脱敏
case CAR_LICENSE:
jsonGenerator.writeString(DesensitizedUtil.carLicense(String.valueOf(str)));
break;
// 银行卡脱敏
case BANK_CARD:
jsonGenerator.writeString(DesensitizedUtil.bankCard(String.valueOf(str)));
break;
default:
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
if (beanProperty != null) {
// 判断数据类型是否为String类型
if (Objects.equals(beanProperty.getType().getRawClass(), String.class)) {
// 获取定义的注解
SensitiveData desensitization = beanProperty.getAnnotation(SensitiveData.class);
// 为null
if (desensitization == null) {
desensitization = beanProperty.getContextAnnotation(SensitiveData.class);
}
// 不为null
if (desensitization != null) {
// 创建定义的序列化类的实例并且返回,入参为注解定义的type,开始位置,结束位置。
return new DesensitizationSerialize(desensitization.value(), desensitization.startInclude(),
desensitization.endExclude());
}
}
return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
}
return serializerProvider.findNullValueSerializer(null);
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = DesensitizationSerialize.class)
public @interface SensitiveData {
/**
* 脱敏数据类型,只要在CUSTOMER的时候,startInclude和endExclude生效
*/
SensitiveTypeEnum value() default SensitiveTypeEnum.CUSTOMER;
/**
* 开始位置(包含)
*/
int startInclude() default 0;
/**
* 结束位置(不包含)
*/
int endExclude() default 0;
}
public enum SensitiveTypeEnum {
/**
* 中文名
*/
CHINESE_NAME,
/**
* 身份证号
*/
ID_CARD,
/**
* 手机号
*/
MOBILE_PHONE,
/**
* 地址
*/
ADDRESS,
/**
* 电子邮件
*/
EMAIL,
/**
* 密码
*/
PASSWORD,
/**
* 中国大陆车牌,包含普通车辆、新能源车辆
*/
CAR_LICENSE,
/**
* 银行卡
*/
BANK_CARD,
/**
* 自定义
*/
CUSTOMER,
}
使用
响应类中添加注解
//银行卡脱敏
@SensitiveData(SensitiveTypeEnum.BANK_CARD)
private String test;
// 自定义脱敏
@SensitiveData(value = SensitiveTypeEnum.CUSTOMER,startInclude = 2,endExclude = 3)
private String test;
25. SensitiveUtil 敏感词工具类
List<String> sensitiveWords = new ArrayList<>();
sensitiveWords.add("敏感词1");
sensitiveWords.add("敏感词2");
sensitiveWords.add("敏感词3");
SensitiveUtil.init(sensitiveWords);
String text = "这是一段包含敏感词的文本,包含关键字:敏感词1、敏感词2、敏感词3";
String filteredText = SensitiveUtil.sensitiveFilter(text);
System.out.println(filteredText);