赞
踩
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
请移步官网:https://hutool.cn/
不得不说,hutool真的是好用,大大提高开发效率,使代码没有那么臃肿,太妙了,以下只是我在开发中遇到的,并不全
请认准官网最新依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.16</version>
</dependency>
请认准官网最新依赖
implementation 'cn.hutool:hutool-all:5.8.16'
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);
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
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 = 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);
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
从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);
这个工具的用处类似于Apache Commons Lang中的StringUtil
常用的方法例如isBlank、isNotBlank、isEmpty、isNotEmpty、split等
// 将list集合用","拼接每个元素并返回String
String idStr = StrUtil.join(",", ids);
toUnderlineCase(CharSequence str)
将驼峰式命名的字符串转换为下划线方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
例如: helloWorld =》hello_world
toCamelCase(CharSequence name)
将下划线方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。
例如:hello_world =》helloWorld
toSymbolCase(CharSequence str, char symbol)
将驼峰式命名的字符串转换为使用符号连接方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。
商业
计算。保留小数的方法主要有两种:
double te1=123456.123456;
double te2=123456.128456;
Console.log(round(te1,4));//结果:123456.1235
Console.log(round(te2,4));//结果:123456.1285
double te1=123456.123456;
double te2=123456.128456;
Console.log(roundStr(te1,2));//结果:123456.12
Console.log(roundStr(te2,2));//结果:123456.13
针对 DecimalFormat.format进行简单封装。按照固定格式对double或long类型的数字做格式化操作。
long c=299792458;//光速
String format = NumberUtil.decimalFormat(",###", c);//299,792,458
格式中主要以 # 和 0 两种占位符号来指定数字长度。0 表示如果位数不足则以 0 填充,# 表示只要有可能就把数字拉上这个位置。
#
-> 取所有整数部分NumberUtil.range 方法根据范围和步进,生成一个有序整数列表。 NumberUtil.appendRange 将给定范围内的整数添加到已有集合中
在分布式环境中,唯一ID生成应用十分广泛,生成方法也多种多样,Hutool针对一些常用生成策略做了简单封装。
唯一ID生成器的工具类,涵盖了:
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是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();
分布式系统中,有一些需要使用全局唯一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使用全局单例对象。
IdcardUtil现在支持大陆15位、18位身份证,港澳台10位身份证。
工具中主要的方法包括:
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, "内蒙古");
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"
}
String html = "{\"name\":\"Something must have been changed since you leave\"}";
JSONObject jsonObject = JSONUtil.parseObj(html);
jsonObject.getStr("name");
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");
final JSONObject put = JSONUtil.createObj()
.set("aaa", "你好")
.set("键2", "test");
// <aaa>你好</aaa><键2>test</键2>
final String s = JSONUtil.toXmlStr(put);
我们先定义两个较为复杂的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);
存
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);
}
这类方法主要是从JSON文件中读取JSON对象的快捷方法。包括:
除了上面中常用的一些方法,JSONUtil还提供了一些JSON辅助方法:
考虑到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"));
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"));
QrCodeUtil.generate(//
"https://blog.csdn.net/abst122", //二维码内容
QrConfig.create().setImg("e:/logo_small.jpg"), //附带logo
FileUtil.file("e:/qrcodeWithLogo.jpg")//写出到的文件
);
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"));
此工具是针对System.getProperty(name)的封装,通过此工具,可以获取如下信息:
SystemUtil.getJvmSpecInfo();
SystemUtil.getJvmInfo();
SystemUtil.getJavaSpecInfo();
SystemUtil.getJavaInfo();
SystemUtil.getJavaRuntimeInfo();
SystemUtil.getOsInfo();
SystemUtil.getUserInfo();
SystemUtil.getHostInfo();
SystemUtil.getRuntimeInfo();
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());
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
"U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
final JWT jwt = JWTUtil.parseToken(rightToken);
jwt.getHeader(JWTHeader.TYPE);
jwt.getPayload("sub");
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." +
"aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
JWTUtil.verify(token, "123456".getBytes());
算法的验证包括两个方面
// 创建JWT Token
final String token = JWT.create()
.setNotBefore(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 验证算法
JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256("123456".getBytes()));
验证时间
对于时间类载荷,有单独的验证方法,主要包括:
一般时间线是:
(签发时间)---------(生效时间)---------(当前时间)---------(失效时间)
签发时间和生效时间一般没有前后要求,都早于当前时间即可。
final String token = JWT.create()
// 设置签发时间
.setIssuedAt(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 由于只定义了签发时间,因此只检查签发时间
JWTValidator.of(token).validateDate(DateUtil.date());
其实这个才是最常用的
// 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));
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就不会报错了
// 方式一
UserDTO userDTOC = BeanUtil.fillBeanWithMap(userMap, new UserDTO, false);
// 方式二
UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, UserDTO.class, false).newInstance();
判断对象是否为null或者所有属性都为null
Page<OpinionDO> all = opinionRepository.findAll(specification, page);
List<OpinionExcelVO> opinionExcelVOS = BeanUtil.copyToList(all.getContent(), OpinionExcelVO.class);
static boolean isEmpty(Object bean, String… ignoreFieldNames)
判断Bean是否为空对象,空对象表示本身为null或者所有属性都为null
此方法不判断static属性
static boolean isNotEmpty(Object bean, String… ignoreFieldNames)
判断Bean是否为非空对象,非空对象表示本身不为null或者含有非null属性的对象
// 给定类是否为Boolean或者boolean
BooleanUtil.isBoolean(isMember);
// 判读isMember是否是false
BooleanUtil.isFalse(isMember);
// 判读isMember是否是true
BooleanUtil.isTrue(isMember);
// 转换字符串为boolean值
BooleanUtil.toBoolean(valueStr)
需要自己分页时使用page()
// 当前页数,每页总数,需要分页的lisrt
ListUtil.page(int pageNo, int pageSize, List<T> list)
isEmpty、isNotEmpty方法比较常用
判断集合是否为空(包括null和没有元素的集合)。
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);
可以快速构建树结构
// 查询数据 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")); });
@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": [] } ]
@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 } ] } ]
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);
考虑到MySQL等数据库中普通的UTF8编码并不支持Emoji(只有utf8mb4支持),因此对于数据中的Emoji字符进行处理(转换、清除)变成一项必要工作。因此Hutool基于emoji-java库提供了Emoji工具实现。
这里只介绍两个常用的,需要看更多方法请查看hutoolapi文档
// 判断是否包含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());
}
常用方法:
if(!phone){
return Wrapper.fail("输入的手机号存在非法字符,请重新输入。");
}
HttpUtil就是对HttpRequest再次封装,HttpUtil中的get和post工具方法都是HttpRequest对象的封装,因此如果想更加灵活操作Http请求,可以使用HttpRequest。
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"
}
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。
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);
数组的判空类似于字符串的判空,标准是null或者数组长度为0,ArrayUtil中封装了针对原始类型和泛型数组的判空和判非空:
int[] a = {};
int[] b = null;
ArrayUtil.isEmpty(a);
ArrayUtil.isEmpty(b);
int[] a = {1,2};
ArrayUtil.isNotEmpty(a);
Array.newInstance
并不支持泛型返回值,在此封装此方法使之支持泛型返回值。
String[] newArray = ArrayUtil.newArray(String.class, 3);
使用 ArrayUtil.resize
方法生成一个新的重新设置大小的数组。
ArrayUtil.addAll
方法采用可变参数方式,将多个泛型数组合并为一个数组。
数组本身支持clone方法,因此确定为某种类型数组时调用ArrayUtil.clone(T[])
,不确定类型的使用ArrayUtil.clone(T)
,两种重载方法在实现上有所不同,但是在使用中并不能感知出差别。
Integer[] b = {1,2,3};
Integer[] cloneB = ArrayUtil.clone(b);
Assert.assertArrayEquals(b, cloneB);
int[] a = {1,2,3};
int[] clone = ArrayUtil.clone(a);
Assert.assertArrayEquals(a, clone);
ArrayUtil.range
方法有三个重载,这三个重载配合可以实现支持步进的有序数组或者步进为1的有序数组。这种列表生成器在Python中做为语法糖存在。
ArrayUtil.split
方法用于拆分一个byte数组,将byte数组平均分成几等份,常用于消息拆分。
ArrayUtil.filter
方法用于过滤已有数组元素,只针对泛型数组操作,原始类型数组并未提供。 方法中Filter接口用于返回boolean值决定是否保留。
Integer[] a = {1,2,3,4,5,6};
// [2,4,6]
Integer[] filter = ArrayUtil.filter(a, (Editor<Integer>) t -> (t % 2 == 0) ? t : null);
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);
修改元素对象,此方法会修改原数组。
Integer[] a = {1, 2, 3, 4, 5, 6};
// [1, 20, 3, 40, 5, 60]
ArrayUtil.edit(a, t -> (t % 2 == 0) ? t * 10 : t);
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}
ArrayUtil.contains
方法只针对泛型数组,检测指定元素是否在数组中。
在原始类型元素和包装类型中,Java实现了自动包装和拆包,但是相应的数组无法实现,于是便是用ArrayUtil.wrap
和ArrayUtil.unwrap
对原始类型数组和包装类型数组进行转换。
ArrayUtil.isArray
方法封装了obj.getClass().isArray()
。
ArrayUtil.toString
通常原始类型的数组输出为字符串时无法正常显示,于是封装此方法可以完美兼容原始类型数组和包装类型数组的转为字符串操作。
ArrayUtil.join
方法使用间隔符将一个数组转为字符串,比如[1,2,3,4]这个数组转为字符串,间隔符使用“-”的话,结果为 1-2-3-4,join方法同样支持泛型数组和原始类型数组。
ArrayUtil.toArray
方法针对ByteBuffer转数组提供便利。
RandomUtil.randomInt
获得指定范围内的随机数int c = RandomUtil.randomInt(10, 100);
RandomUtil.randomBytes
随机bytes,一般用于密码或者salt生成byte[] c = RandomUtil.randomBytes(10);
RandomUtil.randomEle
随机获得列表中的元素RandomUtil.randomEleSet
随机获得列表中的一定量的不重复元素,返回LinkedHashSetSet<Integer> set = RandomUtil.randomEleSet(CollUtil.newArrayList(1, 2, 3, 4, 5, 6), 2);
RandomUtil.randomString
获得一个随机的字符串(只包含数字和字符)RandomUtil.randomNumbers
获得一个只包含数字的字符串RandomUtil.weightRandom
权重随机生成器,传入带权重的对象,然后根据权重随机获取对象脱敏工具类注解封装
@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;
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);
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。