一、概述
1.1介绍
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自于每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
1.2Hutool名称的由来
Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu”是公司名称的表示,tool表示工具。Hutool谐音“糊涂”,一方面简洁易懂,一方面寓意“难得糊
Hutool的目标是使用一个工具方法代替一段复杂代码,从而最大限度的避免“复制粘贴”代码的问题,彻底改变我们写代码的方式。
以计算MD5为例:
- 【以前】打开搜索引擎 -> 搜“Java MD5加密” -> 打开某篇博客-> 复制粘贴 -> 改改好用
- 【现在】引入Hutool -> SecureUtil.md5()
Hutool的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。
1.3包含组件(核心)
一个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-jwt | JSON Web Token (JWT)封装实现 |
1.4官方文档
官网:Hutool — 🍬A set of tools that keep Java sweet.
参考文档:简介 | Hutool
API文档:Document
1.5导入HuTool
HuTool使用非常简单直接在项目中引入如下依赖即可:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
二、常用工具类
2.1Convert
问题
在Java开发中我们要面对各种各样的类型转换问题,尤其是从命令行获取的用户参数、从HttpRequest获取的Parameter等等,这些参数类型多种多样,我们怎么去转换他们呢?常用的办法是先整成String,然后调用XXX.parseXXX方法,还要承受转换失败的风险,不得不加一层try catch,这个小小的过程混迹在业务代码中会显得非常难看和臃肿。
类型转换工具类,用于各种类型数据的转换。
Convert类可以说是一个工具方法类,里面封装了针对Java常见类型的转换,用于简化类型转换。**Convert**类中大部分方法为toXXX,参数为Object,可以实现将任意可能的类型转换为指定类型。同时支持第二个参数defaultValue用于在转换失败时返回一个默认值。
package com.yanyu.redisjedistest.demo;
import cn.hutool.core.convert.Convert;
import lombok.extern.slf4j.Slf4j;
import java.util.Date;
import java.util.List;
@Slf4j
public class hutool1 {
public void covert() {
//转换为字符串
int a = 1;
String aStr = Convert.toStr(a);
log.info(aStr);
//转换为指定类型数组
String[] b = {"1", "2", "3", "4"};
Integer[] bArr = Convert.toIntArray(b);
log.info(bArr.toString());
//转换为日期对象
String dateStr = "2020-09-17";
Date date = Convert.toDate(dateStr);
log.info(date.toString());
//转换为列表
String[] strArr = {"a", "b", "c", "d"};
List<String> strList = Convert.toList(String.class, strArr);
log.info(strList.toString());
}
public static void main(String[] args) {
hutool1 hutool1 = new hutool1();
hutool1.covert();
}
}
2.2DateUtil
日期时间工具类,定义了一些常用的日期时间操作方法。
public void dateUtil() {
//Date、long、Calendar之间的相互转换
//当前时间
Date date = DateUtil.date();
log.info(date.toString());
//Calendar转Date
date = DateUtil.date(Calendar.getInstance());
//时间戳转Date
date = DateUtil.date(System.currentTimeMillis());
//自动识别格式转换
String dateStr = "2020-09-17";
date = DateUtil.parse(dateStr);
//自定义格式化转换
date = DateUtil.parse(dateStr, "yyyy-MM-dd");
//格式化输出日期
String format = DateUtil.format(date, "yyyy-MM-dd");
log.info(format.toString());
//获得年的部分
int year = DateUtil.year(date);
//获得月份,从0开始计数
int month = DateUtil.month(date);
//获取某天的开始、结束时间
Date beginOfDay = DateUtil.beginOfDay(date);
Date endOfDay = DateUtil.endOfDay(date);
//计算偏移后的日期时间
Date newDate = DateUtil.offset(date, DateField.DAY_OF_MONTH, 2);
//计算日期时间之间的偏移量
long betweenDay = DateUtil.between(date, newDate, DateUnit.DAY);
}
2.3ReflectUtil
Java反射工具类,可用于反射获取类的方法及创建对象。
public void reflectUtil() {
//获取某个类的所有方法
Method[] methods = ReflectUtil.getMethods(Dog.class);
//获取某个类的指定方法
Method method = ReflectUtil.getMethod(Dog.class, "getName");
//使用反射来创建对象
Dog dog = ReflectUtil.newInstance(Dog.class);
//反射执行对象的方法
ReflectUtil.invoke(dog, "setName","大黄");
log.info(dog.getName());
}
Dog
Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
private String name;
private Float weight;
}
2.4NumberUtil
public void numberUtil() {
double n1 = 1.234;
double n2 = 1.234;
double result;
//对float、double、BigDecimal做加减乘除操作
result = NumberUtil.add(n1, n2);
log.info(String.valueOf(result));
result = NumberUtil.sub(n1, n2);
log.info(String.valueOf(result));
result = NumberUtil.mul(n1, n2);
log.info(String.valueOf(result));
result = NumberUtil.div(n1, n2);
log.info(String.valueOf(result));
//保留两位小数
BigDecimal roundNum = NumberUtil.round(n1, 2);
log.info(roundNum.toString());
String n3 = "1.234";
//判断是否为数字、整数、浮点数
NumberUtil.isNumber(n3);
NumberUtil.isInteger(n3);
NumberUtil.isDouble(n3);
}
2.5CollUtil
集合操作的工具类,定义了一些常用的集合操作。
public void collUtil() {
//数组转换为列表
String[] array = new String[]{"a", "b", "c", "d", "e"};
List<String> list = CollUtil.newArrayList(array);
//join:数组转字符串时添加连接符号
String joinStr = CollUtil.join(list, ",");
log.info("collUtil join:{}", joinStr);
//将以连接符号分隔的字符串再转换为列表
List<String> splitList = StrUtil.split(joinStr, ',');
log.info("collUtil split:{}", splitList);
//创建新的Map、Set、List
HashMap<Object, Object> newMap = CollUtil.newHashMap();
HashSet<Object> newHashSet = CollUtil.newHashSet();
ArrayList<Object> newList = CollUtil.newArrayList();
//判断列表是否为空
CollUtil.isEmpty(list);
CollUtil.isNotEmpty(list);
}
2.6SecureUtil
public void secureUtil() {
// 加密
String str = "123456";
String key = "secretKey";
byte[] keyBytes = Arrays.copyOf(key.getBytes(), 16);
String encryptedStr = SecureUtil.aes(keyBytes).encryptBase64(str);
log.info("Encrypted string: {}", encryptedStr);
// 解密
String decryptedStr = SecureUtil.aes(keyBytes).decryptStr(encryptedStr);
log.info("Decrypted string: {}", decryptedStr);
}
2.7JSONUtil
JSON 解析工具类,针对 JSONObject 和 JSONArray 的静态快捷方法集合。
public void jsonUtil() {
Dog dog = new Dog();
dog.setName("大黄");
dog.setWeight(5.14f);
//对象转化为JSON字符串
String jsonStr = JSONUtil.parse(dog).toString();
log.info("jsonUtil parse:{}", jsonStr);
//JSON字符串转化为对象
Dog dogBean = JSONUtil.toBean(jsonStr, Dog.class);
log.info("jsonUtil toBean:{}", dogBean);
List<Dog> dogList = new ArrayList<>();
dogList.add(dog);
String jsonListStr = JSONUtil.parse(dogList).toString();
//JSON字符串转化为列表
dogList = JSONUtil.toList(new JSONArray(jsonListStr), Dog.class);
log.info("jsonUtil toList:{}", dogList);
}
2.8RandomUtil
随机工具类,RandomUtil 主要针对 JDK 中 Random 对象做封装。
@Test(description = "RandomUtil使用:随机工具类")
public void randomUtil() {
int result;
String uuid;
//获得指定范围内的随机数
result = RandomUtil.randomInt(1, 100);
log.info("randomInt:{}",StrUtil.toString(result));
//获得随机UUID
uuid = RandomUtil.randomUUID();
log.info("randomUUID:{}", uuid);
}
三、实战exexcel开发
数据库
create table cf_group_user
(
id int unsigned auto_increment comment 'id'
primary key,
group_id int unsigned not null comment '组织id',
nickname char(30) not null comment '姓名',
sex tinyint unsigned not null comment '性别(1:男,2:女)',
born_time datetime not null comment '出生日期',
photo varchar(255) not null comment '形象照片',
home_address varchar(255) not null comment '家庭地址',
mobile int not null comment '联系电话',
post varchar(255) not null comment '职务信息'
)
comment '组织用户信息表' collate = utf8_unicode_ci;
create index auth_id
on cf_group_user (auth_id);
create index group_id
on cf_group_user (group_id);
create index mobile
on cf_group_user (mobile);
实体类
/**
* 组织用户信息表
* @TableName cf_group_user
*/
@TableName(value ="cf_group_user")
@Data
public class GroupUser implements Serializable {
/**
* id
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
* 组织id
*/
private Integer groupId;
/**
* 姓名
*/
@NotBlank(message = "姓名不可为空")
private String nickname;
/**
* 性别(0:默认,1:男,2:女)
*/
@NotBlank(message = "性别不可为空")
private Integer sex;
/**
* 出生日期
*/
@NotBlank(message = "出生日期不可为空")
private Date bornTime;
/**
* 形象照片
*/
@NotBlank(message = "形象照片不可为空")
private String photo;
/**
* 家庭地址
*/
@NotBlank(message = "家庭地址不可为空")
private String homeAddress;
/**
* 联系电话
*/
@NotBlank(message = "联系电话不可为空")
private Integer mobile;
/**
* 实名信息
*/
private String authId;
/**
* 职务信息
*/
@NotNull(message = "职务不可为空")
private String post;
}
在工程中先确定使用的字段构建模板,根据字段构建excel 的模板。这里的示例如下:
//controller层
//返回模板
@RequestMapping("/getExcelTemplate")
public void getExcelTemplate(HttpServletResponse response) {
groupUserService.getExcelTemplate(response);
}
//sevice层
void getExcelTemplate(HttpServletResponse response);
//impl
@Override
public void getExcelTemplate(HttpServletResponse response) {
try {
// 1 读取对象
final ExcelReader reader = ExcelUtil.getReader(ResourceUtil.getStream("templates/group.xlsx"));
List<List<Object>> lists = reader.read();
ExcelWriter writer = ExcelUtil.getWriter(true);
writer.write(lists);
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("group.xlsx", "UTF-8"));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// 2 写出对象
ServletOutputStream outputStream = response.getOutputStream();
// 通过IO写出我们的表格对象
writer.flush(outputStream, true);
writer.close();
IoUtil.close(outputStream);
} catch (IOException e) {
log.error("EducationServiceImpl [export] 输出到响应流失败", e);
throw new APIException("导出Excel异常");
}
}
这里我使用的是三层构建,在controller 层中暴露接口,进行调用,所有的具体实现进行抽象。
//导入信息
@RequestMapping("/importStudent")
public R importStudent(@RequestParam MultipartFile file) {
try {
boolean userInfo = groupUserService.getUserInfo(file);
if(userInfo) return R.success();
} catch (IOException e) {
log.error("EducationController [getEducation] 获取输入流失败", e);
throw new APIException("获取输入流失败");
}
return R.error();
}
//导出信息
@RequestMapping("/export")
public void export(@RequestBody PageVo pageVo, HttpServletResponse response) {
groupUserService.export(pageVo, response);
}
void export(PageVo pageVo, HttpServletResponse response);
boolean getUserInfo(MultipartFile file) throws IOException;
@Override
public void export(PageVo pageVo, HttpServletResponse response) {
// 从数据库查出数据对象封装成map
final List<Map<String, Object>> educationList = this.page(new Page<>(pageVo.getPage(), pageVo.getLimit()), Wrappers.lambdaQuery()).getRecords()
.stream()
// 封装成 Map 并且放入 List
.map(item -> {
final Map<String, Object> map = new LinkedHashMap<>();
// 错误,这里需要根据表中字段名称进行命名
map.put("nickname", item.getNickname());
map.put("sex", item.getSex());
map.put("mobile", item.getMobile());
map.put("bornTime", item.getBornTime());
map.put("homeAddress", item.getHomeAddress());
map.put("post", item.getPost());
return map;
})
.collect(Collectors.toList());
// 准备将数据集合封装成Excel对象
ExcelWriter writer = ExcelUtil.getWriter(true);
// 通过工具类创建writer并且进行别名
writer.addHeaderAlias("nickname", "姓名");
writer.addHeaderAlias("sex", "性别( 0 表示男 , 1 表示 女)");
writer.addHeaderAlias("mobile", "电话");
writer.addHeaderAlias("bornTime", "出生日期");
writer.addHeaderAlias("homeAddress", "家庭地址");
writer.addHeaderAlias("post", "职位");
// 准备将对象写入我们的 List
writer.write(educationList, true);
try {
// 获取我们的输出流
final OutputStream output = response.getOutputStream();
response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode("group.xlsx", "UTF-8"));
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
writer.flush(output, true);
writer.close();
// 这里可以自行关闭资源或者写一个关闭资源的工具类
IoUtil.close(output);
} catch (IOException e) {
log.error("EducationServiceImpl [export] 输出到响应流失败", e);
throw new APIException("导出Excel异常");
}
}
@Override
public boolean getUserInfo(MultipartFile file) throws IOException {
ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
HashMap<String, String> head = new HashMap<>(6);
head.put("姓名","nickname");
head.put("性别( 0 表示男 , 1 表示 女)","sex");
head.put("电话","mobile");
head.put("出生日期", "bornTime");
head.put("家庭地址","homeAddress");
head.put("职位","post");
reader.setHeaderAlias(head);
List<GroupUser> read = reader.read(0, 1, GroupUser.class);
Group group = groupService.getOne(new QueryWrapper<Group>().eq("uid", UserConstant.USER_ID));
for (GroupUser user : read) {
user.setGroupId(group.getId());
//TODO 默认图片
user.setPhoto("https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png");
user.setStatus(1);
user.setCreateTime(new DateTime());
if(!this.save(user))return false;
}
return true;
}