个人公众号(小程序同名): 遇见0和1
Hexo博客: http://vogos.cn
文章目录
前言
官方介绍: Hutool是一个Java工具包,也只是一个工具包,它帮助我们简化每一行代码,减少每一个方法,让Java语言也可以“甜甜的”。Hutool最初是我项目中“util”包的一个整理,后来慢慢积累并加入更多非业务相关功能,并广泛学习其它开源项目精髓,经过自己整理修改,最终形成丰富的开源工具集。Hutool是Hu + tool的自造词,前者致敬我的“前任公司”,后者为工具之意,谐音“糊涂”,寓意追求“万事都作糊涂观,无所谓失,无所谓得”的境界。
个人看法: 网上对它的看法有好有坏,搜索hutool就能看到如下他的生态,那些不推荐的原因我就不在这介绍了(毕竟本人是一个菜🐔,没有这种资格)对于我目前的个人能力来说,它还是非常适合我的,开发时懒得自己写一些工具类或算法加密什么的,hutool也可以帮我减少 util 包中的大量工具类,这里只记录个人常用到的一些工具类,它的浩瀚可自行去官方开拓,当前记录时官方最新版本为v5.6.6
Hutool官网:https://www.hutool.cn
官方文档:https://apidoc.gitee.com/dromara/hutool
添加Hutool的所有依赖
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.3</version>
</dependency>
1、模拟HTTP请求
hutool提供了HttpUtil 和 HttpRequest 两个工具类都可用来模拟发送http请求,这两个用法大同小异这里只拿HttpRequest举例
示例场景: 在项目开发中Swagger可作为后端接口测试的良方,但在和别人对接,或使用别人提供的接口时常常需要后端自己去模拟请求发送,去校验接口
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
/**
* @author 遇见0和1
* @company lingStudy
* @create 2021-06-03 17:04
*/
@Slf4j
public class HttpTest {
/**
* putOnce:一次性Put 键值对,如果key已经存在抛出异常,如果键值中有null值,忽略
* putOpt:在键和值都为非空的情况下put到JSONObject中
*/
@Test
void postTest(){
JSONObject jsonObject = JSONUtil.createObj();// 或 JSONObject jsonObject = new JSONObject();
jsonObject.putOnce("customerTel","17563964456");
jsonObject.putOnce("remarks","备注1:我是HttpRequest测试请求!");
HttpResponse response = HttpRequest.post("http://127.0.0.1:9001/caiyun-record/saveRecord")
//设置请求头(可任意加)
.header("Content-Type", "application/json")
// 添加token
.header("Authorization","eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJ7E3NjJ9.9vgMKLpftjcXxmvViSyJDnBcXrO6c3bLlatwD83frAs")
//请求参数
.body(jsonObject.toString())
.execute();
log.info("请求响应结果:{}",response);
log.info("响应数据:{}",response.body());
}
@Test
void getTest(){
HttpResponse response = HttpRequest.get("http://127.0.0.1:9001/caiyun-record/login?paramData=I0CZZJYUBP9JixsyeDkhRnIfFgyXP-NaP1DiJ8_AWoY1eEeZN5BwF_HMSfb4wl6oE")
.execute();
log.info("get请求返回:{}",response.body());
}
}
2、JSONUtil 序列化
将一个对象序列化,也是后端开发中常遇到的,阿里以前挺火的 fastjson
示例场景: 返回给前端的token中要存对象时可将对象转为字符串,在拦截器中再将该字符串重新转为对象,若在登录后存的是该对象到session中,还可以在拦截器中通过判断session中的值是否为空,来解决后端重启后session丢失的问题。
//对象转字符串
String userInfoStr = JSONUtil.toJsonStr(userInfoDTO);
log.info("对象转字符串userInfoStr:{}", userInfoStr);
//生成token
String jwtToken = TokenUtil.createJwtToken(userInfoStr);
//字符串转对象
UserInfoDTO userInfo = JSONUtil.toBean(jti, UserInfoDTO.class);
request.getSession().setAttribute("userInfo",userInfo);
log.info("重设session后:{}",request.getSession().getAttribute("userInfo"));
List< Map >转List< Entity >
// HuTool
List<Map<String, Object>> userMapList= new ArrayList<>();
// 方法一
List<User> userList = Convert.toList(User.class, userMapList);
// 方法二
List<User> userList = JSONUtil.toList(new JSONArray(userMapList),User.class);
// 同fastjson
List<User> userList = JSON.parseObject(JSON.toJSONString(userMapList),new TypeReference<>() {});
Object转List< Map >
// map是<String,Object>型集合
List<Map> kpiList = JSONUtil.toList(new JSONArray(map.get("kpiList")), Map.class);
// 或
List<Map> kpiList = Convert.toList(Map.class, map.get("kpiList"));
// 强制转换(不推荐)
List<Map<String,Object>> kpiList = (List<Map<String, Object>>) map.get("kpiList");
Object转List< String >
// map是<String,Object>型集合
List<String> ids = JSONUtil.toList(new JSONArray(map.get("ids")), String.class);
拓展记录:
//map.values转list
List<String> headList = new ArrayList<>(headMap.values());
3、BeanUtil:Map与javaBean的转换
示例场景: 面对POST或者一些参数较多的GET请求等,懒得新建参数对象,可用Map来接收前端传递的参数
@ApiOperation(value = "新增记录")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query", dataType = "String", name = "customerTel", value = "客户号码",required = true),
@ApiImplicitParam(paramType = "query", dataType = "String", name = "remarks", value = "备注")
})
@PostMapping("/saveRecord")
public ResultVo<Object> saveRecord(@ApiIgnore @RequestBody Map<String, Object> param){
log.info("新增登记记录 param:{}",param);
try {
HecaiyunRecord hecaiyunRecord = new HecaiyunRecord();
//将map赋值给HecaiyunRecord中与map的key对应的字段
BeanUtil.fillBeanWithMap(param,hecaiyunRecord,false);
UserInfoDTO userInfo = (UserInfoDTO) request.getSession().getAttribute("userInfo");
//用userInfo填充与hecaiyunRecord相同的字段
BeanUtils.copyProperties(userInfo,hecaiyunRecord);
//添加系统日志
sysLogService.saveSysLog(userInfo,request,"add","新增登记");
return recordService.saveRecord(hecaiyunRecord);
} catch (Exception e) {
log.error("新增登记记录失败");
e.printStackTrace();
throw new MyException(StatusEnum.ERROR);
}
}
反过来:将 JavaBean 转为 Map
User user = new User();
user.setAge(3);
user.setName("遇见0和1");
//将 user 转成map
Map<String, Object> map = BeanUtil.beanToMap(user);
//将userInfo对象复制给map,key保留userInfo中属性的驼峰形式不做下划线处理,不忽略userInfo中值为null的属性
BeanUtil.beanToMap(userInfo,map,false,false); //不会清空map (个人常用)
4、Convert 各种数据结构类型转换
// JSONObject转成javaBean
UserInfoDTO userInfoDTO = respData.toBean(UserInfoDTO.class);
// JSONObject转成Map
Map<String, Object> paramMap = Convert.toMap(String.class, Object.class, respData);
// Object转成List
List<String> imgList = Convert.toList(String.class, params.get("imgList"));
5、CaptchaUtil 生成图形验证码
示例场景: 图形验证码在登录时非常常见,Hutool也用几种类型的验证码,这里只举例个人常用的一种。
@ApiOperation(value = "获得图形验证码")
@GetMapping("/getCaptcha")
public void getCaptcha(HttpServletResponse response){
//生成验证码图片(定义图形的宽和高,验证码的位数,干扰线的条数)
//CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 25);
CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(100, 50, 4, 25);
//告诉浏览器输出内容为jpeg类型的图片
response.setContentType("image/jpeg");
//禁止浏览器缓存
response.setHeader("Pragma","No-cache");
try {
ServletOutputStream outputStream = response.getOutputStream();
//图形验证码写出到流,也可以写出到文件如:circleCaptcha.write(“d:/circle25.jpeg”);
circleCaptcha.write(outputStream);
//从图形验证码图片中获取它的字符串验证码(获取字符串验证码要在图形验证码wirte写出后面才行,不然得到的值为null)
String captcha = circleCaptcha.getCode();
request.getSession().setAttribute("captcha",captcha);
log.info("生成的验证码:{}",captcha);
log.info("session id:{}",request.getSession().getId());
//关闭流
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
throw new MyException(StatusEnum.ERROR);
}
}
6、UserAgentUtil
示例场景: 系统操作日志里常常需要判断访问的是什么设备 UserAgentUtil 可以获取到服务的浏览器详细信息,也可以判断访问是不是移动设备。
//自己实现
public String isDevice(HttpServletRequest request){
String requestHeader = request.getHeader("user-agent");
//定义移动端请求的所有可能类型
String[] deviceArray = {"android", "iphone","ipod","ipad", "windows phone","mqqbrowser"};
//将字符串转换为小写
requestHeader = requestHeader.toLowerCase();
for (String device : deviceArray) {
if (requestHeader.contains(device)){
return "移动端";
}
}
return "PC端";
}
//使用UserAgentUtil
public String isDeviceHuTool(HttpServletRequest request){
String requestHeader = request.getHeader("user-agent");
UserAgent userAgent = UserAgentUtil.parse(requestHeader);
if (userAgent.isMobile()){
return "移动端";
}
return "PC端";
}
访问者的ip地址常常也是需要保存的一个数据,个人暂时没找到hutool对这个的支持,方便后面来抄作业,下面放一个搬来的获取ip的方法
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;
/**
* @author 遇见0和1
* @company lingStudy
* @create 2021-03-30 9:31
*/
@Slf4j
public class IPUtils {
/**
* 获取IP地址
*
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StrUtil.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StrUtil.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
log.error("IPUtils ERROR:",e);
}
//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if(!StrUtil.isEmpty(ip) && ip.length() > 15) {
if(ip.indexOf(",") > 0) {
ip = ip.substring(0, ip.indexOf(","));
}
}
return ip;
}
}
7、FileUtil
示例场景: 文件上传下载在项目中也是一个常见并且重要的业务点,Hutool中的FileUtil类对这方面也做了很好的支持。
@Value("${upload-file.img-path}")
private String imgPath;
@ApiOperation(value = "上传图片")
@PostMapping(value = "/uploadImg",headers = "content-type=multipart/form-data")
public ResultVo<Object> uploadFile(@ApiParam(value = "file") @RequestParam(value = "file" ,required = false) MultipartFile file){
if (file == null){
throw new MyException(StatusEnum.BUSINID);
}
if (!FileUtil.exist(imgPath)){
FileUtil.mkdir(imgPath);
}
//String fileName = UUID.randomUUID().toString() + "@" + multipartFile.getOriginalFilename();
log.info("原文件名:{}",file.getOriginalFilename());//包括扩展名
//文件扩展名
String suffix = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf(".") + 1);
String fileName = System.currentTimeMillis() + RandomUtil.randomNumbers(4) + "." +suffix;
try {
File file1 = FileUtil.writeBytes(file.getBytes(), imgPath + fileName);
log.info("file1:{}",file1);
return ResultVo.success();
} catch (IOException e) {
e.printStackTrace();
throw new MyException(StatusEnum.ERROR);
}
}
加密解密
加密分为三种:
- 对称加密(symmetric):例如:AES、DES等
- 非对称加密(asymmetric):例如:RSA、DSA等
- 摘要加密(digest):例如:MD5、SHA-1、SHA-256、HMAC等
对常用到的算法,HuTool提供SecureUtil工具类用于快速实现加解密
// 对称加密
SecureUtil.aes
SecureUtil.des
// 摘要算法
SecureUtil.md5
SecureUtil.sha1
SecureUtil.hmac
SecureUtil.hmacMd5
SecureUtil.hmacSha1
// 非对称加密
SecureUtil.rsa
SecureUtil.dsa
// UUID
SecureUtil.simpleUUID 方法提供无“-”的UUID
// 密钥生成
SecureUtil.generateKey 针对对称加密生成密钥
SecureUtil.generateKeyPair 生成密钥对(用于非对称加密)
SecureUtil.generateSignature 生成签名(用于非对称加密)