ruoyi-vue-plus-后端工具篇
# BeanCopyUtils
对象拷贝工具类
核心方法
返回 | 方法 | 返回 |
---|---|---|
<T, V> V | copy(T source, Class<V> desc) | 对象拷贝 |
<T, V> V | copy(T source, V desc) | 对象替换拷贝(相同属性且不为空则覆盖) |
<T, V> List<V> | copyList(List<T> sourceList, Class<V> desc) | 对象列表拷贝 |
<T> Map<String, Object> | copyToMap(T bean) | bean拷贝Map |
<T> T | mapToBean(Map<String, Object> map, Class<T> beanClass) | map拷贝bean |
<T> T | mapToBean(Map<String, Object> map, T bean) | map拷贝bean 对象替换拷贝 |
<T, V> Map<String, V> | mapToMap(Map<String, T> map, Class<V> clazz) | map拷贝map |
源码
点击代码展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class BeanCopyUtils {
/**
* 单对象基于class创建拷贝
*
* @param source 数据来源实体
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> V copy(T source, Class<V> desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
final V target = ReflectUtil.newInstanceIfPossible(desc);
return copy(source, target);
}
/**
* 单对象基于对象创建拷贝
*
* @param source 数据来源实体
* @param desc 转换后的对象
* @return desc
*/
public static <T, V> V copy(T source, V desc) {
if (ObjectUtil.isNull(source)) {
return null;
}
if (ObjectUtil.isNull(desc)) {
return null;
}
BeanCopier beanCopier = BeanCopierCache.INSTANCE.get(source.getClass(), desc.getClass(), null);
beanCopier.copy(source, desc, null);
return desc;
}
/**
* 列表对象基于class创建拷贝
*
* @param sourceList 数据来源实体列表
* @param desc 描述对象 转换后的对象
* @return desc
*/
public static <T, V> List<V> copyList(List<T> sourceList, Class<V> desc) {
if (ObjectUtil.isNull(sourceList)) {
return null;
}
if (CollUtil.isEmpty(sourceList)) {
return CollUtil.newArrayList();
}
return StreamUtils.toList(sourceList, source -> {
V target = ReflectUtil.newInstanceIfPossible(desc);
copy(source, target);
return target;
});
}
/**
* bean拷贝到map
*
* @param bean 数据来源实体
* @return map对象
*/
@SuppressWarnings("unchecked")
public static <T> Map<String, Object> copyToMap(T bean) {
if (ObjectUtil.isNull(bean)) {
return null;
}
return BeanMap.create(bean);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param beanClass bean类
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, Class<T> beanClass) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(beanClass)) {
return null;
}
T bean = ReflectUtil.newInstanceIfPossible(beanClass);
return mapToBean(map, bean);
}
/**
* map拷贝到bean
*
* @param map 数据来源
* @param bean bean对象
* @return bean对象
*/
public static <T> T mapToBean(Map<String, Object> map, T bean) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(bean)) {
return null;
}
BeanMap.create(bean).putAll(map);
return bean;
}
/**
* map拷贝到map
*
* @param map 数据来源
* @param clazz 返回的对象类型
* @return map对象
*/
public static <T, V> Map<String, V> mapToMap(Map<String, T> map, Class<V> clazz) {
if (MapUtil.isEmpty(map)) {
return null;
}
if (ObjectUtil.isNull(clazz)) {
return null;
}
Map<String, V> copyMap = new LinkedHashMap<>(map.size());
map.forEach((k, v) -> copyMap.put(k, copy(v, clazz)));
return copyMap;
}
/**
* BeanCopier属性缓存<br>
* 缓存用于防止多次反射造成的性能问题
*
* @author Looly
* @since 5.4.1
*/
public enum BeanCopierCache {
/**
* BeanCopier属性缓存单例
*/
INSTANCE;
private final SimpleCache<String, BeanCopier> cache = new SimpleCache<>();
/**
* 获得类与转换器生成的key在{@link BeanCopier}的Map中对应的元素
*
* @param srcClass 源Bean的类
* @param targetClass 目标Bean的类
* @param converter 转换器
* @return Map中对应的BeanCopier
*/
public BeanCopier get(Class<?> srcClass, Class<?> targetClass, Converter converter) {
final String key = genKey(srcClass, targetClass, converter);
return cache.get(key, () -> BeanCopier.create(srcClass, targetClass, converter != null));
}
/**
* 获得类与转换器生成的key
*
* @param srcClass 源Bean的类
* @param targetClass 目标Bean的类
* @param converter 转换器
* @return 属性名和Map映射的key
*/
private String genKey(Class<?> srcClass, Class<?> targetClass, Converter converter) {
final StringBuilder key = StrUtil.builder()
.append(srcClass.getName()).append('#').append(targetClass.getName());
if (null != converter) {
key.append('#').append(converter.getClass().getName());
}
return key.toString();
}
}
}
示例代码
点击代码展开
public class TestCopy {
// 对象拷贝
@Test
public void testCopy() {
TestDemo testDemo = new TestDemo();
testDemo.setTestKey("1");
testDemo.setValue("2");
TestDemoVo copy = BeanCopyUtils.copy(testDemo, TestDemoVo.class);
System.out.println("copy = " + copy);
}
// 对象替换拷贝
@Test
public void testCopy2() {
TestDemo testDemo = new TestDemo();
testDemo.setTestKey("1");
testDemo.setValue("2");
TestDemoVo testDemoVo = new TestDemoVo();
testDemoVo.setDeptId(1L);
BeanCopyUtils.copy(testDemo, testDemoVo);
System.out.println("testDemoVo = " + testDemoVo);
}
// 列表拷贝
@Test
public void testCopyList() {
TestDemo testDemo1 = new TestDemo();
TestDemo testDemo2 = new TestDemo();
List<TestDemo> list = Arrays.asList(testDemo1, testDemo2);
List<TestDemoVo> testDemoVos = BeanCopyUtils.copyList(list, TestDemoVo.class);
testDemoVos.forEach(System.out::println);
}
@Test
public void testCopyToMap() {
TestDemo testDemo = new TestDemo();
testDemo.setTestKey("test");
testDemo.setValue("123");
Map<String, Object> stringObjectMap = BeanCopyUtils.copyToMap(testDemo);
stringObjectMap.forEach((K, V) -> System.out.println(K + " : " + V));
// Map To Bean
TestDemoVo testDemoVo = BeanCopyUtils.mapToBean(stringObjectMap, TestDemoVo.class);
System.out.println("testDemoVo = " + testDemoVo);
// Map to Map
// ...
}
}
# CacheUtils
模拟SpringCache注解缓存的使用场景 , 通过CacheUtils实现编程式获取缓存等相关操作
核心方法
返回 | CacheUtils方法 | 说明 |
---|---|---|
Set<Object> | keys(String cacheName) | 获取指定 cacheName 的所有数据 |
<T> T | get(String cacheName, Object key) | 获取 cahceName 中的指定key数据 |
void | put(String cacheName, Object key, Object value) | 保存缓存数据 |
void | evict(String cacheName, Object key) | 删除 cahceName 中的指定key数据 |
void | clear(String cacheName) | 清空缓存数据 |
源码
点击展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@SuppressWarnings(value = {"unchecked"})
public class CacheUtils {
private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class);
/**
* 获取缓存组内所有的KEY
*
* @param cacheNames 缓存组名称
*/
public static Set<Object> keys(String cacheNames) {
RMap<Object, Object> rmap = (RMap<Object, Object>) CACHE_MANAGER.getCache(cacheNames).getNativeCache();
return rmap.keySet();
}
/**
* 获取缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static <T> T get(String cacheNames, Object key) {
Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key);
return wrapper != null ? (T) wrapper.get() : null;
}
/**
* 保存缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
* @param value 缓存值
*/
public static void put(String cacheNames, Object key, Object value) {
CACHE_MANAGER.getCache(cacheNames).put(key, value);
}
/**
* 删除缓存值
*
* @param cacheNames 缓存组名称
* @param key 缓存key
*/
public static void evict(String cacheNames, Object key) {
CACHE_MANAGER.getCache(cacheNames).evict(key);
}
/**
* 清空缓存值
*
* @param cacheNames 缓存组名称
*/
public static void clear(String cacheNames) {
CACHE_MANAGER.getCache(cacheNames).clear();
}
}
# DateUtil
# EncryptUtils
数据加密工具类
加密分类
对称加密 | 非对称加密 | 哈希加密 | |
---|---|---|---|
说明 | 使用相同的秘钥进行 加密/解密 | 两个密钥(公钥/私钥) , 公钥加密 / 私钥解密 | 单向加密 , 不可逆 |
优点 | 简单 , 速度快 , 无需通信双方交互秘钥 | 安全性高 , 难以逆向推到 | 无法破解 |
缺点 | 依赖秘钥保护 , 多用户多数据集难以管理 | 大量算法 , 速度慢 | 不能解密、可能存在哈希碰撞 |
安全性 | 低 | 高 | 高 |
场景 | 大量数据处理 | 秘钥交换、数字签名、... | 身份校验、数据验证 |
类别 | AES、SM4 | SM2 、RSA | MD5 、SHA256 、SM3 |
源码
点击展开
public class EncryptUtils {
/**
* 公钥
*/
public static final String PUBLIC_KEY = "publicKey";
/**
* 私钥
*/
public static final String PRIVATE_KEY = "privateKey";
/**
* Base64加密
*
* @param data 待加密数据
* @return 加密后字符串
*/
public static String encryptByBase64(String data) {
return Base64.encode(data, StandardCharsets.UTF_8);
}
/**
* Base64解密
*
* @param data 待解密数据
* @return 解密后字符串
*/
public static String decryptByBase64(String data) {
return Base64.decodeStr(data, StandardCharsets.UTF_8);
}
/**
* AES加密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* AES加密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByAesHex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* AES解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptByAes(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("AES需要传入秘钥信息");
}
// aes算法的秘钥要求是16位、24位、32位
int[] array = {16, 24, 32};
if (!ArrayUtil.contains(array, password.length())) {
throw new IllegalArgumentException("AES秘钥长度要求为16位、24位、32位");
}
return SecureUtil.aes(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* sm4加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptBase64(data, StandardCharsets.UTF_8);
}
/**
* sm4加密
*
* @param data 待加密数据
* @param password 秘钥字符串
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm4Hex(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).encryptHex(data, StandardCharsets.UTF_8);
}
/**
* sm4解密
*
* @param data 待解密数据
* @param password 秘钥字符串
* @return 解密后字符串
*/
public static String decryptBySm4(String data, String password) {
if (StrUtil.isBlank(password)) {
throw new IllegalArgumentException("SM4需要传入秘钥信息");
}
// sm4算法的秘钥要求是16位长度
int sm4PasswordLength = 16;
if (sm4PasswordLength != password.length()) {
throw new IllegalArgumentException("SM4秘钥长度要求为16位");
}
return SmUtil.sm4(password.getBytes(StandardCharsets.UTF_8)).decryptStr(data, StandardCharsets.UTF_8);
}
/**
* 产生sm2加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateSm2Key() {
Map<String, String> keyMap = new HashMap<>(2);
SM2 sm2 = SmUtil.sm2();
keyMap.put(PRIVATE_KEY, sm2.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, sm2.getPublicKeyBase64());
return keyMap;
}
/**
* sm2公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptBySm2(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm2Hex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("SM2需要传入公钥进行加密");
}
SM2 sm2 = SmUtil.sm2(null, publicKey);
return sm2.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* sm2私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptBySm2(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("SM2需要传入私钥进行解密");
}
SM2 sm2 = SmUtil.sm2(privateKey, null);
return sm2.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
/**
* 产生RSA加解密需要的公钥和私钥
*
* @return 公私钥Map
*/
public static Map<String, String> generateRsaKey() {
Map<String, String> keyMap = new HashMap<>(2);
RSA rsa = SecureUtil.rsa();
keyMap.put(PRIVATE_KEY, rsa.getPrivateKeyBase64());
keyMap.put(PUBLIC_KEY, rsa.getPublicKeyBase64());
return keyMap;
}
/**
* rsa公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Base64编码
*/
public static String encryptByRsa(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptBase64(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa公钥加密
*
* @param data 待加密数据
* @param publicKey 公钥
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByRsaHex(String data, String publicKey) {
if (StrUtil.isBlank(publicKey)) {
throw new IllegalArgumentException("RSA需要传入公钥进行加密");
}
RSA rsa = SecureUtil.rsa(null, publicKey);
return rsa.encryptHex(data, StandardCharsets.UTF_8, KeyType.PublicKey);
}
/**
* rsa私钥解密
*
* @param data 待加密数据
* @param privateKey 私钥
* @return 解密后字符串
*/
public static String decryptByRsa(String data, String privateKey) {
if (StrUtil.isBlank(privateKey)) {
throw new IllegalArgumentException("RSA需要传入私钥进行解密");
}
RSA rsa = SecureUtil.rsa(privateKey, null);
return rsa.decryptStr(data, KeyType.PrivateKey, StandardCharsets.UTF_8);
}
/**
* md5加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptByMd5(String data) {
return SecureUtil.md5(data);
}
/**
* sha256加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySha256(String data) {
return SecureUtil.sha256(data);
}
/**
* sm3加密
*
* @param data 待加密数据
* @return 加密后字符串, 采用Hex编码
*/
public static String encryptBySm3(String data) {
return SmUtil.sm3(data);
}
}
# ExcelUtil
表格处理工具
核心方法 (重载较多自行查阅代码)
方法 | 说明 |
---|---|
importExcel() | Excel 导入方法 |
exportExcel() | Excel 导出方法 |
exportTemplate() | Excel 模板填充 |
源码
点击展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {
/**
* 同步导入(适用于小数据量)
* 一次性读取
* @param is 输入流
* @return 转换后集合
*/
public static <T> List<T> importExcel(InputStream is, Class<T> clazz) {
return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
}
/**
* 使用校验监听器 异步导入 同步返回
*
* @param is 输入流
* @param clazz 对象类型
* @param isValidate 是否 Validator 检验 默认为是
* @return 转换后集合
*/
public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, boolean isValidate) {
DefaultExcelListener<T> listener = new DefaultExcelListener<>(isValidate);
EasyExcel.read(is, clazz, listener).sheet().doRead();
return listener.getExcelResult();
}
/**
* 使用自定义监听器 异步导入 自定义返回
*
* @param is 输入流
* @param clazz 对象类型
* @param listener 自定义监听器
* @return 转换后集合
*/
public static <T> ExcelResult<T> importExcel(InputStream is, Class<T> clazz, ExcelListener<T> listener) {
EasyExcel.read(is, clazz, listener).sheet().doRead();
return listener.getExcelResult();
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param response 响应体
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os, null);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param response 响应体
* @param options 级联下拉选
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, HttpServletResponse response, List<DropDownOptions> options) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os, options);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param merge 是否合并单元格
* @param response 响应体
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os, null);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param merge 是否合并单元格
* @param response 响应体
* @param options 级联下拉选
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge, HttpServletResponse response, List<DropDownOptions> options) {
try {
resetResponse(sheetName, response);
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os, options);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param os 输出流
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os) {
exportExcel(list, sheetName, clazz, false, os, null);
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param os 输出流
* @param options 级联下拉选内容
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, OutputStream os, List<DropDownOptions> options) {
exportExcel(list, sheetName, clazz, false, os, options);
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param merge 是否合并单元格
* @param os 输出流
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, boolean merge,
OutputStream os, List<DropDownOptions> options) {
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.sheet(sheetName);
if (merge) {
// 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true));
}
// 添加下拉框操作
builder.registerWriteHandler(new ExcelDownHandler(options));
builder.doWrite(list);
}
/**
* 单表多数据模板导出 模板格式为 {.属性}
*
* @param filename 文件名
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param response 响应体
*/
public static void exportTemplate(List<Object> data, String filename, String templatePath, HttpServletResponse response) {
try {
resetResponse(filename, response);
ServletOutputStream os = response.getOutputStream();
exportTemplate(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 单表多数据模板导出 模板格式为 {.属性}
*
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param os 输出流
*/
public static void exportTemplate(List<Object> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
if (CollUtil.isEmpty(data)) {
throw new IllegalArgumentException("数据为空");
}
// 单表多数据导出 模板格式为 {.属性}
for (Object d : data) {
excelWriter.fill(d, writeSheet);
}
excelWriter.finish();
}
/**
* 多表多数据模板导出 模板格式为 {key.属性}
*
* @param filename 文件名
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param response 响应体
*/
public static void exportTemplateMultiList(Map<String, Object> data, String filename, String templatePath, HttpServletResponse response) {
try {
resetResponse(filename, response);
ServletOutputStream os = response.getOutputStream();
exportTemplateMultiList(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
}
}
/**
* 多表多数据模板导出 模板格式为 {key.属性}
*
* @param templatePath 模板路径 resource 目录下的路径包括模板文件名
* 例如: excel/temp.xlsx
* 重点: 模板文件必须放置到启动类对应的 resource 目录下
* @param data 模板需要的数据
* @param os 输出流
*/
public static void exportTemplateMultiList(Map<String, Object> data, String templatePath, OutputStream os) {
ClassPathResource templateResource = new ClassPathResource(templatePath);
ExcelWriter excelWriter = EasyExcel.write(os)
.withTemplate(templateResource.getStream())
.autoCloseStream(false)
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
if (CollUtil.isEmpty(data)) {
throw new IllegalArgumentException("数据为空");
}
for (Map.Entry<String, Object> map : data.entrySet()) {
// 设置列表后续还有数据
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
if (map.getValue() instanceof Collection) {
// 多表导出必须使用 FillWrapper
excelWriter.fill(new FillWrapper(map.getKey(), (Collection<?>) map.getValue()), fillConfig, writeSheet);
} else {
excelWriter.fill(map.getValue(), writeSheet);
}
}
excelWriter.finish();
}
/**
* 重置响应体
*/
private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException {
String filename = encodingFilename(sheetName);
FileUtils.setAttachmentResponseHeader(response, filename);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
}
/**
* 解析导出值 0=男,1=女,2=未知
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
* @return 解析后值
*/
public static String convertByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) {
String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[0].equals(value)) {
propertyString.append(itemArray[1] + separator);
break;
}
}
} else {
if (itemArray[0].equals(propertyValue)) {
return itemArray[1];
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 反向解析值 男=0,女=1,未知=2
*
* @param propertyValue 参数值
* @param converterExp 翻译注解
* @param separator 分隔符
* @return 解析后值
*/
public static String reverseByExp(String propertyValue, String converterExp, String separator) {
StringBuilder propertyString = new StringBuilder();
String[] convertSource = converterExp.split(StringUtils.SEPARATOR);
for (String item : convertSource) {
String[] itemArray = item.split("=");
if (StringUtils.containsAny(propertyValue, separator)) {
for (String value : propertyValue.split(separator)) {
if (itemArray[1].equals(value)) {
propertyString.append(itemArray[0] + separator);
break;
}
}
} else {
if (itemArray[1].equals(propertyValue)) {
return itemArray[0];
}
}
}
return StringUtils.stripEnd(propertyString.toString(), separator);
}
/**
* 编码文件名
*/
public static String encodingFilename(String filename) {
return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx";
}
}
# Hutool
官方文档 : https://doc.hutool.cn (opens new window)
Hutool封装了很多Java常用工具类库 , 通过当中的静态方法使用 , 从而降低学习成本提高效率
提示
该工具引入了大量的工具类库 , 因此建议使用单类库形式引入 点击跳转 (opens new window)
官方源码 , 有众多测试类 , 演示这些工具类的使用方式
# JsonUtils
JSON转化工具
- 序列化 toJsonString
- 反序列化 parseObject
核心方法
返回 | 方法 | 说明 |
---|---|---|
<T> | parseObject(String text, Class<T> clazz) | 默认转化(不支持泛型) |
<T> | parseObject(byte[] bytes, Class<T> clazz) | 字节对象转化 |
<T> | parseObject(String text, TypeReference<T> typeReference) | 对象转化 , 支持泛型(集合明确泛型) |
Dict | parseMap(String text) | Map转化 |
List<Dict> | parseArrayMap(String text) | 列表Map转化 |
List<T> | parseArray(String text, Class<T> clazz) | List转化 , 指定泛型 |
源码
点击代码展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class JsonUtils {
// 获取容器Bean对象
private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
public static ObjectMapper getObjectMapper() {
return OBJECT_MAPPER;
}
public static String toJsonString(Object object) {
if (ObjectUtil.isNull(object)) {
return null;
}
try {
return OBJECT_MAPPER.writeValueAsString(object);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
if (ArrayUtil.isEmpty(bytes)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(bytes, clazz);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, typeReference);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static Dict parseMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
} catch (MismatchedInputException e) {
// 类型不匹配说明不是json
return null;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static List<Dict> parseArrayMap(String text) {
if (StringUtils.isBlank(text)) {
return null;
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static <T> List<T> parseArray(String text, Class<T> clazz) {
if (StringUtils.isEmpty(text)) {
return new ArrayList<>();
}
try {
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
示例代码
点击代码展开
@SpringBootTest
public class JsonUtilsTest {
@Test
public void testToJsonString() {
Person person = new Person("小明1", 22);
String jsonString = JsonUtils.toJsonString(person);
System.out.println("jsonString = " + jsonString);
}
@Test
public void testParseObject1(){
String str = "{\"name\":\"小明\",\"age\":12}";
Person person = JsonUtils.parseObject(str, Person.class);
System.out.println("person = " + person);
// Person(name=小明, age=12)
}
// parseObject(String text, Class<T> clazz)
@Test
public void testParseObject2(){
String str = "{\"name\":\"小明\",\"age\":12}";
Person person = JsonUtils.parseObject(str, Person.class);
System.out.println("person = " + person);
// Person(name=小明, age=12)
}
// parseObject(String text, Class<T> clazz)
@Test
public void testParseObject3(){
String str = "{\"name\":\"小明\",\"age\":12}";
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
Person person = JsonUtils.parseObject(bytes, Person.class);
System.out.println("person = " + person);
// Person(name=小明, age=12)
}
// 泛型应用
// parseObject(String text, TypeReference<T> typeReference)
@Test
public void testParseObject4(){
String arr = "[{\"name\":\"小明1\",\"age\":22},{\"name\":\"小明2\",\"age\":12}]";
TypeReference<List<Person>> reference = new TypeReference<List<Person>>() {};
List<Person> peoples = JsonUtils.parseObject(arr, reference);
System.out.println("peoples = " + peoples);
// [Person(name=小明1, age=22), Person(name=小明2, age=12)]
}
// parseMap(String text)
@Test
public void testParseMap(){
String str = "{\"name\":\"小明\",\"age\":12}";
Dict dict = JsonUtils.parseMap(str);
System.out.println("dict = " + dict);
// {name=小明, age=12}
}
// parseArrayMap(String text)
@Test
public void testParseArrayMap() {
String arr = "[{\"name\":\"小明1\",\"age\":22},{\"name\":\"小明2\",\"age\":12}]";
List<Dict> dicts = JsonUtils.parseArrayMap(arr);
System.out.println("dicts = " + dicts);
// [{name=小明1, age=22}, {name=小明2, age=12}]
}
// parseArray(String text, Class<T> clazz)
@Test
public void testParseArray() {
String arr = "[{\"name\":\"小明1\",\"age\":22},{\"name\":\"小明2\",\"age\":12}]";
List<Person> people = JsonUtils.parseArray(arr, Person.class);
System.out.println("people = " + people);
// [Person(name=小明1, age=22), Person(name=小明2, age=12)]
}
}
# LoginHelper
登录鉴权助手 , 通常用于获取当前用户的相关信息
核心方法
返回 | 方法 | 说明 |
---|---|---|
LoginUser | getLoginUser() | 获取 当前用户 |
LoginUser | getLoginUser(String token) | 获取 当前用户 根据token |
Long | getUserId() | 获取 当前用户ID |
Long | getDeptId() | 获取 当前用户 部门ID |
String | getUsername() | 获取 当前用户 名称 |
UserType | getUserType() | 获取 当前用户类型(PC/APP/WX) |
boolean | isAdmin() | 判断 管理员 |
boolean | isAdmin(Long userId) | 判断 管理员 根据用户ID |
源码
点击代码展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class LoginHelper {
public static final String LOGIN_USER_KEY = "loginUser";
public static final String USER_KEY = "userId";
/**
* 登录系统
*
* @param loginUser 登录用户信息
*/
public static void login(LoginUser loginUser) {
loginByDevice(loginUser, null);
}
/**
* 登录系统 基于 设备类型
* 针对相同用户体系不同设备
*
* @param loginUser 登录用户信息
*/
public static void loginByDevice(LoginUser loginUser, DeviceType deviceType) {
SaStorage storage = SaHolder.getStorage();
storage.set(LOGIN_USER_KEY, loginUser);
storage.set(USER_KEY, loginUser.getUserId());
SaLoginModel model = new SaLoginModel();
if (ObjectUtil.isNotNull(deviceType)) {
model.setDevice(deviceType.getDevice());
}
// 自定义分配 不同用户体系 不同 token 授权时间 不设置默认走全局 yml 配置
// 例如: 后台用户30分钟过期 app用户1天过期
// UserType userType = UserType.getUserType(loginUser.getUserType());
// if (userType == UserType.SYS_USER) {
// model.setTimeout(86400).setActiveTimeout(1800);
// } else if (userType == UserType.APP_USER) {
// model.setTimeout(86400).setActiveTimeout(1800);
// }
StpUtil.login(loginUser.getLoginId(), model.setExtra(USER_KEY, loginUser.getUserId()));
StpUtil.getTokenSession().set(LOGIN_USER_KEY, loginUser);
}
/**
* 获取用户(多级缓存)
*/
public static LoginUser getLoginUser() {
LoginUser loginUser = (LoginUser) SaHolder.getStorage().get(LOGIN_USER_KEY);
if (loginUser != null) {
return loginUser;
}
SaSession session = StpUtil.getTokenSession();
if (ObjectUtil.isNull(session)) {
return null;
}
loginUser = (LoginUser) session.get(LOGIN_USER_KEY);
SaHolder.getStorage().set(LOGIN_USER_KEY, loginUser);
return loginUser;
}
/**
* 获取用户基于token
*/
public static LoginUser getLoginUser(String token) {
SaSession session = StpUtil.getTokenSessionByToken(token);
if (ObjectUtil.isNull(session)) {
return null;
}
return (LoginUser) session.get(LOGIN_USER_KEY);
}
/**
* 获取用户id
*/
public static Long getUserId() {
Long userId;
try {
userId = Convert.toLong(SaHolder.getStorage().get(USER_KEY));
if (ObjectUtil.isNull(userId)) {
userId = Convert.toLong(StpUtil.getExtra(USER_KEY));
SaHolder.getStorage().set(USER_KEY, userId);
}
} catch (Exception e) {
return null;
}
return userId;
}
/**
* 获取部门ID
*/
public static Long getDeptId() {
return getLoginUser().getDeptId();
}
/**
* 获取用户账户
*/
public static String getUsername() {
return getLoginUser().getUsername();
}
/**
* 获取用户类型
*/
public static UserType getUserType() {
String loginType = StpUtil.getLoginIdAsString();
return UserType.getUserType(loginType);
}
/**
* 是否为管理员
*
* @param userId 用户ID
* @return 结果
*/
public static boolean isAdmin(Long userId) {
return UserConstants.ADMIN_ID.equals(userId);
}
public static boolean isAdmin() {
return isAdmin(getUserId());
}
}
# QueueUtils
队列工具 , 采用Ression实现队列操作
队列分类以及特点
队列分类 | 结构 | 特点 | 场景 |
---|---|---|---|
普通队列 | 单队列 (先进先出) | - | - |
有界队列 | 单队列 (先进先出) | 限定队列容量 | 停车位场景 |
延迟队列 | 延迟队列 (延迟) / 普通队列 (消费) | 延迟推入普通队列消费 | 订单过期 |
优先队列 | 单队列 (优先先出) | 对象比较器判断优先消费 | vip优先 |
基础架构
点击代码展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 获取客户端实例
*/
public static RedissonClient getClient() {
return CLIENT;
}
// ... 其他队列
}
<a id="普通队列">**普通队列**</a>
::: details 点击代码展开
```java
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class QueueUtils {
private static final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);
/**
* 获取客户端实例
*/
public static RedissonClient getClient() {
return CLIENT;
}
/**
* 添加普通队列数据
*
* @param queueName 队列名
* @param data 数据
*/
public static <T> boolean addQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.offer(data);
}
/**
* 通用获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.poll();
}
/**
* 通用删除队列数据(不支持延迟队列)
*/
public static <T> boolean removeQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 通用销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
return queue.delete();
}
/**
* 订阅阻塞队列(可订阅所有实现类 例如: 延迟 优先 有界 等)
*/
public static <T> void subscribeBlockingQueue(String queueName, Consumer<T> consumer) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
queue.subscribeOnElements(consumer);
}
}
点击代码展开
/**
* 尝试设置 有界队列 容量 用于限制数量
*
* @param queueName 队列名
* @param capacity 容量
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
* 尝试设置 有界队列 容量 用于限制数量
*
* @param queueName 队列名
* @param capacity 容量
* @param destroy 已存在是否销毁
*/
public static <T> boolean trySetBoundedQueueCapacity(String queueName, int capacity, boolean destroy) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
if (boundedBlockingQueue.isExists() && destroy) {
destroyQueue(queueName);
}
return boundedBlockingQueue.trySetCapacity(capacity);
}
/**
* 添加有界队列数据
*
* @param queueName 队列名
* @param data 数据
* @return 添加成功 true 已达到界限 false
*/
public static <T> boolean addBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> boundedBlockingQueue = CLIENT.getBoundedBlockingQueue(queueName);
return boundedBlockingQueue.offer(data);
}
/**
* 有界队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getBoundedQueueObject(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.poll();
}
/**
* 有界队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removeBoundedQueueObject(String queueName, T data) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 有界队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyBoundedQueue(String queueName) {
RBoundedBlockingQueue<T> queue = CLIENT.getBoundedBlockingQueue(queueName);
return queue.delete();
}
点击代码展开
/**
* 添加延迟队列数据 默认毫秒
* 1. 添加延迟队列
* 2. 等待延迟
* 3. 到期进入普通队列
* @param queueName 队列名
* @param data 数据
* @param time 延迟时间
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time) {
addDelayedQueueObject(queueName, data, time, TimeUnit.MILLISECONDS);
}
/**
* 添加延迟队列数据
*
* @param queueName 队列名
* @param data 数据
* @param time 延迟时间
* @param timeUnit 单位
*/
public static <T> void addDelayedQueueObject(String queueName, T data, long time, TimeUnit timeUnit) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.offer(data, time, timeUnit);
}
/**
* 获取一个延迟队列数据 没有数据返回 null
* 直接在延迟队列中获取, 不能获取已到期的信息 .
* 直接获取延迟队列中的数据
* @param queueName 队列名
*/
public static <T> T getDelayedQueueObject(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.poll();
}
/**
* 删除延迟队列数据
* 删除延迟队列中的未过期数据
*/
public static <T> boolean removeDelayedQueueObject(String queueName, T data) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
return delayedQueue.remove(data);
}
/**
* 销毁延迟队列 所有阻塞监听 报错
*/
public static <T> void destroyDelayedQueue(String queueName) {
RBlockingQueue<T> queue = CLIENT.getBlockingQueue(queueName);
RDelayedQueue<T> delayedQueue = CLIENT.getDelayedQueue(queue);
delayedQueue.destroy();
}
点击代码展开
/**
* 添加优先队列数据
*
* @param queueName 队列名
* @param data 数据
*/
public static <T> boolean addPriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> priorityBlockingQueue = CLIENT.getPriorityBlockingQueue(queueName);
return priorityBlockingQueue.offer(data);
}
/**
* 优先队列获取一个队列数据 没有数据返回 null(不支持延迟队列)
*
* @param queueName 队列名
*/
public static <T> T getPriorityQueueObject(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.poll();
}
/**
* 优先队列删除队列数据(不支持延迟队列)
*/
public static <T> boolean removePriorityQueueObject(String queueName, T data) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.remove(data);
}
/**
* 优先队列销毁队列 所有阻塞监听 报错(不支持延迟队列)
*/
public static <T> boolean destroyPriorityQueue(String queueName) {
RPriorityBlockingQueue<T> queue = CLIENT.getPriorityBlockingQueue(queueName);
return queue.delete();
}
# RedisUtils
提供的API变化不大 , 其他方法自行看即可
示例代码
点击代码展开
@SpringBootTest
public class RedisTest {
/**
* 订阅发布消息
*/
@Test
public void subPub() {
String channelKey = "channelKey";
RedisUtils.subscribe(channelKey, String.class, consumer -> {
System.out.println("consumer = " + consumer);
});
RedisUtils.publish(channelKey, "hello world!!");
RedisUtils.publish(channelKey, "yes!");
// 附加通知运行
RedisUtils.publish(channelKey, "ok", consumer -> {
System.out.println("测试执行!");
});
}
/**
* 缓存测试
*/
@Test
public void setCacheObject() {
// 参数 : key, value
// RedisUtils.setCacheObject("name", "张三");
// 参数 : key, value , ttl
RedisUtils.setCacheObject("name", "李四", Duration.ofSeconds(10));
// 参数 : key, value , 保留ttl
RedisUtils.setCacheObject("name", "王五", false);
}
/**
* 设置ttl
*/
@Test
public void testExpired() {
RedisUtils.setCacheObject("name", "小明");
// 10s 过期
RedisUtils.expire("name", 10);
// 1天 过期
RedisUtils.expire("name", Duration.ofDays(1));
}
/**
* 获取对象
*/
@Test
public void testGetObject() {
// 获取值
String name = RedisUtils.getCacheObject("name");
System.out.println("name = " + name);
// 获取TTL
long ttl = RedisUtils.getTimeToLive("name");
System.out.println("ttl = " + ttl);
}
/**
* 删除
*/
@Test
public void testDel() {
// 删除单个对象
boolean name = RedisUtils.deleteObject("name");
System.out.println("name = " + name);
}
/**
* 集合设置
* Set, Map, 同理
*/
@Test
public void test() {
// 区分 obj & list 缓存形式
// 特性 :
// - obj 设置 会覆盖原有的值
// - list 设置 会追加值, 而非覆盖
List<String> list = Arrays.asList("1", "2");
// 对象数组缓存, 通过string (json)
RedisUtils.setCacheObject("list1", list);
// 集合形式缓存, 通过list
RedisUtils.setCacheList("list2", list);
// 获取
List<String> list1 = RedisUtils.getCacheObject("list1");
System.out.println("list1 = " + list1);
List<String> list2 = RedisUtils.getCacheList("list2");
System.out.println("list2 = " + list2);
}
/**
* 原子操作
*/
@Test
public void testAtom() {
RedisUtils.setAtomicValue("num", 1);
long num1 = RedisUtils.getAtomicValue("num");
System.out.println("num1 = " + num1);
long num2 = RedisUtils.incrAtomicValue("num");
System.out.println("num2 = " + num2);
long num3 = RedisUtils.decrAtomicValue("num");
System.out.println("num3 = " + num3);
}
/**
* 测试key存在
*/
@Test
public void testHashKey() {
RedisUtils.setCacheObject("num", null);
// 检查值是否存在
Boolean b = RedisUtils.hasKey("num");
System.out.println("b = " + b);
// 检查键是否存在
boolean b2 = RedisUtils.isExistsObject("num");
System.out.println("b2 = " + b2);
}
}
# ReflectUtils
反射工具类 , 该工具类继承 Hutool库中的ReflectUtil , 拓展重写部分方法
Hutool文档 : https://doc.hutool.cn (opens new window)
源码
点击代码展开
@SuppressWarnings("rawtypes")
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReflectUtils extends ReflectUtil {
private static final String SETTER_PREFIX = "set";
private static final String GETTER_PREFIX = "get";
/**
* 调用Getter方法.
* 支持多级,如:对象名.对象名.方法
*/
@SuppressWarnings("unchecked")
public static <E> E invokeGetter(Object obj, String propertyName) {
Object object = obj;
for (String name : StringUtils.split(propertyName, ".")) {
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(name);
object = invoke(object, getterMethodName);
}
return (E) object;
}
/**
* 调用Setter方法, 仅匹配方法名。
* 支持多级,如:对象名.对象名.方法
*/
public static <E> void invokeSetter(Object obj, String propertyName, E value) {
Object object = obj;
String[] names = StringUtils.split(propertyName, ".");
for (int i = 0; i < names.length; i++) {
if (i < names.length - 1) {
String getterMethodName = GETTER_PREFIX + StringUtils.capitalize(names[i]);
object = invoke(object, getterMethodName);
} else {
String setterMethodName = SETTER_PREFIX + StringUtils.capitalize(names[i]);
Method method = getMethodByName(object.getClass(), setterMethodName);
invoke(object, method, value);
}
}
}
}
ReflectUtil类很大自行查阅
示例代码
点击代码展开
@Slf4j
public class ReflectTest {
/**
* 实例对象
*/
@Data
static class Student {
private String name;
private int age;
private String sex;
private static final int clazz = 1;
private Student() {}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public static String print(Student student) {
return "Student{" + "name='" + student.getName() + '\'' + ", age=" + student.getAge() + ", sex='" + student.getSex() + '\'' + '}';
}
}
public static void main(String[] args) {
//getField();
//getFieldValue();
//setFieldValue();
//getConstructor();
//newInstance();
//getMethod();
//invokeMethod();
overrideMethod();
}
/**
* 反射字段操作
*/
public static void printField(Field field) {
if (field == null) return;
Console.log(" name: {}, type: {}", field.getName(), field.getType());
}
// 反射获取字段
public static void getField() {
System.out.println("============== getFields 获取所有字段");
Field[] fields = ReflectUtils.getFields(Student.class);
for (Field field : fields) printField(field);
System.out.println("============== getFields 过滤字段");
fields = ReflectUtils.getFields(Student.class, field -> field.getName().startsWith("a"));
for (Field field : fields) printField(field);
System.out.println("============== getField 获取指定字段");
Field field = ReflectUtils.getField(Student.class, "age");
printField(field);
}
// 获取实例对象的值
public static void getFieldValue() {
Student student = new Student("bozhu12", 23, "男");
System.out.println("============== getFieldValue 获取指定字段的值");
String value = (String) ReflectUtils.getFieldValue(student, "name");
System.out.println("name: " + value);
System.out.println("============== getStaticFieldValue 获取静态字段值");
int clazz = (int) ReflectUtils.getStaticFieldValue(ReflectUtils.getField(Student.class, "clazz"));
System.out.println("clazz: " + clazz);
System.out.println("============== getFieldsValue 获取所有字段值");
Object[] fieldsValue = ReflectUtils.getFieldsValue(student);
for (Object o : fieldsValue) System.out.println("o = " + o);
}
// 设置实例对象值
public static void setFieldValue() {
Student student = new Student("bozhu12", 23, "男");
System.out.println("============== setFieldValue 设置指定字段的值");
ReflectUtils.setFieldValue(student, "name", "bozhu13");
System.out.println("name: " + student.getName());
System.out.println("============== setFieldValue 设置指定字段的值");
ReflectUtils.setFieldValue(student, ReflectUtils.getField(Student.class, "name"), "bozhu14");
System.out.println("name: " + student.getName());
}
/**
* 构造方法操作
*/
// 打印构造方法
public static void printConstructor(Constructor<?> constructor) {
if (constructor == null) return;
Console.log(" name: {}" + "\n param: {}" + "\n type: {}" + "\n paramCount: {}\n" + constructor.getName(), Arrays.toString(constructor.getParameterTypes()), Arrays.toString(constructor.getTypeParameters()), constructor.getParameterCount());
}
// 获取构造方法基础信息
public static void getConstructor() {
System.out.println("============== getConstructor 获取指定构造方法");
Constructor<?> constructor = ReflectUtils.getConstructor(Student.class, String.class, int.class, String.class);
printConstructor(constructor);
System.out.println("============== getConstructors 获取所有构造方法");
Constructor<?>[] constructors = ReflectUtils.getConstructors(Student.class);
for (Constructor<?> constructor1 : constructors) printConstructor(constructor1);
}
// 实例构造方法
public static void newInstance() {
// 打破私有修饰约束
System.out.println("============== newInstance 实例化对象 (无参)");
Student student = ReflectUtils.newInstance(Student.class);
System.out.println("student = " + student);
System.out.println("============== newInstance 实例化对象 (有参)");
student = ReflectUtils.newInstance(Student.class, "bozhu12", 23, "男");
System.out.println("student = " + student);
System.out.println("============== newInstanceIfPossible 实例化对象");
student = ReflectUtils.newInstanceIfPossible(Student.class);
System.out.println("student = " + student);
}
/**
* 方法操作
*/
public static void printMethod(Method method) {
if (method == null) return;
Console.log(" name: {}" + "\n param: {}" + "\n type: {}" + "\n return: {}\n", method.getName(), Arrays.toString(method.getParameters()), Arrays.toString(method.getParameterTypes()), method.getGenericReturnType().getTypeName());
}
// 获取方法基础信息
public static void getMethod() {
System.out.println("============== getMethod 获取指定方法 (按方法名)");
Method method = ReflectUtils.getMethod(Student.class, "getAge");
printMethod(method);
System.out.println("============== getMethods 获取所有方法");
Method[] methods = ReflectUtils.getMethods(Student.class);
for (Method method1 : methods) printMethod(method1);
System.out.println("============== getMethod 获取指定方法 (按方法名并且支持重载)");
method = ReflectUtils.getMethod(Student.class, "setAge", int.class);
printMethod(method);
System.out.println("============== getMethodByName 获取指定方法 (第一个匹配的)");
method = ReflectUtils.getMethodByName(Student.class, "setAge");
printMethod(method);
}
// 执行方法
public static void invokeMethod() {
Student student = new Student("bozhu12", 23, "男");
System.out.println("============== invoke 执行普通方法 (无参)");
Method method = ReflectUtils.getMethod(Student.class, "getAge");
Object invoke = ReflectUtils.invoke(student, method);
System.out.println("invoke = " + invoke);
System.out.println("============== invoke 执行普通方法 (有参)");
method = ReflectUtils.getMethod(Student.class, "setAge", int.class);
invoke = ReflectUtils.invoke(student, method, 24);
System.out.println("invoke = " + invoke);
System.out.println("student = " + student);
System.out.println("============== invokeStatic 执行静态方法 ");
method = ReflectUtils.getMethodByName(Student.class, "print");
Object res = ReflectUtils.invokeStatic(method, student);
System.out.println("res = " + res);
}
/**
* 重写的方法
*/
public static void overrideMethod() {
System.out.println("============== invokeGetter 调取get方法");
Student student = new Student("bozhu12", 23, "男");
Object o = ReflectUtils.invokeGetter(student, "name");
System.out.println("o = " + o);
System.out.println("============== invokeSetter 调取set方法");
ReflectUtils.invokeSetter(student, "name", "bozhu13");
ReflectUtils.invokeSetter(student, "age", 24);
System.out.println("student = " + student);
}
}
# ServletUtils
客户端工具类 , 该工具类常用于对请求进行相关处理操作 . 该工具类继承 Hutool库中的ServletUtil , 拓展重写部分方法
Hutool文档 : https://doc.hutool.cn (opens new window)
源码
点击代码展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ServletUtils extends ServletUtil {
/**
* 获取String参数
*/
public static String getParameter(String name) {
return getRequest().getParameter(name);
}
/**
* 获取String参数
*/
public static String getParameter(String name, String defaultValue) {
return Convert.toStr(getRequest().getParameter(name), defaultValue);
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name) {
return Convert.toInt(getRequest().getParameter(name));
}
/**
* 获取Integer参数
*/
public static Integer getParameterToInt(String name, Integer defaultValue) {
return Convert.toInt(getRequest().getParameter(name), defaultValue);
}
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name) {
return Convert.toBool(getRequest().getParameter(name));
}
/**
* 获取Boolean参数
*/
public static Boolean getParameterToBool(String name, Boolean defaultValue) {
return Convert.toBool(getRequest().getParameter(name), defaultValue);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String[]> getParams(ServletRequest request) {
final Map<String, String[]> map = request.getParameterMap();
return Collections.unmodifiableMap(map);
}
/**
* 获得所有请求参数
*
* @param request 请求对象{@link ServletRequest}
* @return Map
*/
public static Map<String, String> getParamMap(ServletRequest request) {
Map<String, String> params = new HashMap<>();
for (Map.Entry<String, String[]> entry : getParams(request).entrySet()) {
params.put(entry.getKey(), StringUtils.join(entry.getValue(), StringUtils.SEPARATOR));
}
return params;
}
/**
* 获取request
*/
public static HttpServletRequest getRequest() {
return getRequestAttributes().getRequest();
}
/**
* 获取response
*/
public static HttpServletResponse getResponse() {
return getRequestAttributes().getResponse();
}
/**
* 获取session
*/
public static HttpSession getSession() {
return getRequest().getSession();
}
public static ServletRequestAttributes getRequestAttributes() {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
return (ServletRequestAttributes) attributes;
}
/**
* 将字符串渲染到客户端
*
* @param response 渲染对象
* @param string 待渲染的字符串
*/
public static void renderString(HttpServletResponse response, String string) {
try {
response.setStatus(HttpStatus.HTTP_OK);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().print(string);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 是否是Ajax异步请求
*
* @param request
*/
public static boolean isAjaxRequest(HttpServletRequest request) {
String accept = request.getHeader("accept");
if (accept != null && accept.contains(MediaType.APPLICATION_JSON_VALUE)) {
return true;
}
String xRequestedWith = request.getHeader("X-Requested-With");
if (xRequestedWith != null && xRequestedWith.contains("XMLHttpRequest")) {
return true;
}
String uri = request.getRequestURI();
if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) {
return true;
}
String ajax = request.getParameter("__ajax");
return StringUtils.equalsAnyIgnoreCase(ajax, "json", "xml");
}
public static String getClientIP() {
return getClientIP(getRequest());
}
/**
* 内容编码
*
* @param str 内容
* @return 编码后的内容
*/
public static String urlEncode(String str) {
try {
return URLEncoder.encode(str, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
return StringUtils.EMPTY;
}
}
/**
* 内容解码
*
* @param str 内容
* @return 解码后的内容
*/
public static String urlDecode(String str) {
try {
return URLDecoder.decode(str, Constants.UTF8);
} catch (UnsupportedEncodingException e) {
return StringUtils.EMPTY;
}
}
}
# SpringUtils
Spring工具类 , 该工具类常用于对Bean管理 . 该工具类继承 Hutool库中的StringUtil , 拓展重写部分方法
Hutool文档 : https://doc.hutool.cn (opens new window)
核心方法
返回 | 方法 | 说明 |
---|---|---|
void | registerBean(String beanName, T bean) | 注册Bean |
void | unregisterBean(String beanName) | 注销Bean |
<T> T | getBean(String name) | 获取Bean (重载多种) |
void | publishEvent(Object event) | 发布事件 |
... | ... | ... |
源码
点击代码展开
@Component
public final class SpringUtils extends SpringUtil {
/**
* 如果BeanFactory包含一个与所给名称匹配的bean定义,则返回true
*
* @param name
* @return boolean
*/
public static boolean containsBean(String name) {
return getBeanFactory().containsBean(name);
}
/**
* 判断以给定名字注册的bean定义是一个singleton还是一个prototype。
* 如果与给定名字相应的bean定义没有被找到,将会抛出一个异常(NoSuchBeanDefinitionException)
*
* @param name
* @return boolean
*/
public static boolean isSingleton(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().isSingleton(name);
}
/**
* @param name
* @return Class 注册对象的类型
*/
public static Class<?> getType(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getType(name);
}
/**
* 如果给定的bean名字在bean定义中有别名,则返回这些别名
*
* @param name
*/
public static String[] getAliases(String name) throws NoSuchBeanDefinitionException {
return getBeanFactory().getAliases(name);
}
/**
* 获取aop代理对象
*
* @param invoker
* @return
*/
@SuppressWarnings("unchecked")
public static <T> T getAopProxy(T invoker) {
return (T) AopContext.currentProxy();
}
/**
* 获取spring上下文
*/
public static ApplicationContext context() {
return getApplicationContext();
}
}
# SqlUtil
sql工具类 , 该工具类主要处理SQL语句的校验功能
核心方法
返回 | 方法 | 说明 |
---|---|---|
void | escapeOrderBySql(String value) | 排序查询参数校验 |
void | filterKeyword(String value) | 参数 SQL关键字 校验 |
源码
点击展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class SqlUtil {
/**
* 定义常用的 sql关键字
*/
public static final String SQL_REGEX = "select |insert |delete |update |drop |count |exec |chr |mid |master |truncate |char |and |declare ";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
*/
public static final String SQL_PATTERN = "[a-zA-Z0-9_\\ \\,\\.]+";
/**
* 检查字符,防止注入绕过
*/
public static void escapeOrderBySql(String value) {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
throw new UtilException("参数不符合规范,不能进行查询");
}
}
/**
* 验证 order by 语法是否符合规范
*/
public static boolean isValidOrderBySql(String value) {
return value.matches(SQL_PATTERN);
}
/**
* SQL关键字检查
* 思路: 参数值中包含关键字,抛出异常
*/
public static void filterKeyword(String value) {
if (StringUtils.isEmpty(value)) {
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new UtilException("参数存在SQL注入风险");
}
}
}
}
示例代码
点击展开
public static void main(String[] args) {
// 关键字校验
System.out.println("============== 参数校验");
SqlUtil.filterKeyword(" 9 ");
System.out.println("参数校验完毕 [9]");
// sql注入异常
SqlUtil.filterKeyword(" and 1 = 1 ");
System.out.println("参数校验完毕 [ and 1 = 1 ]");
// 参数校验
System.out.println("============== 排序校验");
SqlUtil.escapeOrderBySql("id");
System.out.println("参数校验完毕 [id]");
SqlUtil.escapeOrderBySql(" id,createTime ");
System.out.println("参数校验完毕 [ id,createTime ]");
// sql注入异常
SqlUtil.escapeOrderBySql(" or 1 = 1");
System.out.println("参数校验完毕 [ or 1 = 1 ]");
}
# StreamUtils
流处理工具
核心方法
返回 | 方法 | 说明 |
---|---|---|
<E, T> List<T> | toList(Collection<E> collection, Function<E, T> function) | 转化为List , 按function结果为值 |
<E, T> Set<T> | toSet(Collection<E> collection, Function<E, T> function) | 转化为Set , 按function结果为值 |
<E, K, V> Map<K, V> | toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) | 转化为Map , 按key结果为键 , 按value结果为值 |
<E> List<E> | filter(Collection<E> collection, Predicate<E> function) | 过滤 , 仅保留function结果为真的值 |
<E> String | join(Collection<E> collection, Function<E, String> function) | 拼接 , 按function结果为值 , 以及指定分隔符 |
<E> List<E> | sorted(Collection<E> collection, Comparator<E> comparing) | 排序 , 按comparing结果值 0 , -1 , 1 区间排序 |
<E, K> Map<K, List<E>> | groupByKey(Collection<E> collection, Function<E, K> key) | 分组 , 按key值作为键 |
... | ... | ... |
源码
点击展开
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class StreamUtils {
/**
* 将collection过滤
*
* @param collection 需要转化的集合
* @param function 过滤方法
* @return 过滤后的list
*/
public static <E> List<E> filter(Collection<E> collection, Predicate<E> function) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection.stream().filter(function).collect(Collectors.toList());
}
/**
* 将collection拼接
*
* @param collection 需要转化的集合
* @param function 拼接方法
* @return 拼接后的list
*/
public static <E> String join(Collection<E> collection, Function<E, String> function) {
return join(collection, function, StringUtils.SEPARATOR);
}
/**
* 将collection拼接
*
* @param collection 需要转化的集合
* @param function 拼接方法
* @param delimiter 拼接符
* @return 拼接后的list
*/
public static <E> String join(Collection<E> collection, Function<E, String> function, CharSequence delimiter) {
if (CollUtil.isEmpty(collection)) {
return StringUtils.EMPTY;
}
return collection.stream().map(function).filter(Objects::nonNull).collect(Collectors.joining(delimiter));
}
/**
* 将collection排序
*
* @param collection 需要转化的集合
* @param comparing 排序方法
* @return 排序后的list
*/
public static <E> List<E> sorted(Collection<E> collection, Comparator<E> comparing) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection.stream().filter(Objects::nonNull).sorted(comparing).collect(Collectors.toList());
}
/**
* 将collection转化为类型不变的map<br>
* <B>{@code Collection<V> ----> Map<K,V>}</B>
*
* @param collection 需要转化的集合
* @param key V类型转化为K类型的lambda方法
* @param <V> collection中的泛型
* @param <K> map中的key类型
* @return 转化后的map
*/
public static <V, K> Map<K, V> toIdentityMap(Collection<V> collection, Function<V, K> key) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, Function.identity(), (l, r) -> l));
}
/**
* 将Collection转化为map(value类型与collection的泛型不同)<br>
* <B>{@code Collection<E> -----> Map<K,V> }</B>
*
* @param collection 需要转化的集合
* @param key E类型转化为K类型的lambda方法
* @param value E类型转化为V类型的lambda方法
* @param <E> collection中的泛型
* @param <K> map中的key类型
* @param <V> map中的value类型
* @return 转化后的map
*/
public static <E, K, V> Map<K, V> toMap(Collection<E> collection, Function<E, K> key, Function<E, V> value) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection.stream().filter(Objects::nonNull).collect(Collectors.toMap(key, value, (l, r) -> l));
}
/**
* 将collection按照规则(比如有相同的班级id)分类成map<br>
* <B>{@code Collection<E> -------> Map<K,List<E>> } </B>
*
* @param collection 需要分类的集合
* @param key 分类的规则
* @param <E> collection中的泛型
* @param <K> map中的key类型
* @return 分类后的map
*/
public static <E, K> Map<K, List<E>> groupByKey(Collection<E> collection, Function<E, K> key) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
.collect(Collectors.groupingBy(key, LinkedHashMap::new, Collectors.toList()));
}
/**
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
* <B>{@code Collection<E> ---> Map<T,Map<U,List<E>>> } </B>
*
* @param collection 需要分类的集合
* @param key1 第一个分类的规则
* @param key2 第二个分类的规则
* @param <E> 集合元素类型
* @param <K> 第一个map中的key类型
* @param <U> 第二个map中的key类型
* @return 分类后的map
*/
public static <E, K, U> Map<K, Map<U, List<E>>> groupBy2Key(Collection<E> collection, Function<E, K> key1, Function<E, U> key2) {
if (CollUtil.isEmpty(collection)) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.groupingBy(key2, LinkedHashMap::new, Collectors.toList())));
}
/**
* 将collection按照两个规则(比如有相同的年级id,班级id)分类成双层map<br>
* <B>{@code Collection<E> ---> Map<T,Map<U,E>> } </B>
*
* @param collection 需要分类的集合
* @param key1 第一个分类的规则
* @param key2 第二个分类的规则
* @param <T> 第一个map中的key类型
* @param <U> 第二个map中的key类型
* @param <E> collection中的泛型
* @return 分类后的map
*/
public static <E, T, U> Map<T, Map<U, E>> group2Map(Collection<E> collection, Function<E, T> key1, Function<E, U> key2) {
if (CollUtil.isEmpty(collection) || key1 == null || key2 == null) {
return MapUtil.newHashMap();
}
return collection
.stream().filter(Objects::nonNull)
.collect(Collectors.groupingBy(key1, LinkedHashMap::new, Collectors.toMap(key2, Function.identity(), (l, r) -> l)));
}
/**
* 将collection转化为List集合,但是两者的泛型不同<br>
* <B>{@code Collection<E> ------> List<T> } </B>
*
* @param collection 需要转化的集合
* @param function collection中的泛型转化为list泛型的lambda表达式
* @param <E> collection中的泛型
* @param <T> List中的泛型
* @return 转化后的list
*/
public static <E, T> List<T> toList(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection)) {
return CollUtil.newArrayList();
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
/**
* 将collection转化为Set集合,但是两者的泛型不同<br>
* <B>{@code Collection<E> ------> Set<T> } </B>
*
* @param collection 需要转化的集合
* @param function collection中的泛型转化为set泛型的lambda表达式
* @param <E> collection中的泛型
* @param <T> Set中的泛型
* @return 转化后的Set
*/
public static <E, T> Set<T> toSet(Collection<E> collection, Function<E, T> function) {
if (CollUtil.isEmpty(collection) || function == null) {
return CollUtil.newHashSet();
}
return collection
.stream()
.map(function)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
}
/**
* 合并两个相同key类型的map
*
* @param map1 第一个需要合并的 map
* @param map2 第二个需要合并的 map
* @param merge 合并的lambda,将key value1 value2合并成最终的类型,注意value可能为空的情况
* @param <K> map中的key类型
* @param <X> 第一个 map的value类型
* @param <Y> 第二个 map的value类型
* @param <V> 最终map的value类型
* @return 合并后的map
*/
public static <K, X, Y, V> Map<K, V> merge(Map<K, X> map1, Map<K, Y> map2, BiFunction<X, Y, V> merge) {
if (MapUtil.isEmpty(map1) && MapUtil.isEmpty(map2)) {
return MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map1)) {
map1 = MapUtil.newHashMap();
} else if (MapUtil.isEmpty(map2)) {
map2 = MapUtil.newHashMap();
}
Set<K> key = new HashSet<>();
key.addAll(map1.keySet());
key.addAll(map2.keySet());
Map<K, V> map = new HashMap<>();
for (K t : key) {
X x = map1.get(t);
Y y = map2.get(t);
V z = merge.apply(x, y);
if (z != null) {
map.put(t, z);
}
}
return map;
}
}
示例代码
点击展开
@Component
@RestController
@Slf4j
@SaIgnore
@RequestMapping("stream")
public class StreamController {
private List<TestDemo> dataList = new ArrayList<>();
public StreamController() {
for (int i = 0; i < 20; i++) {
int num = (int) (Math.random() * 10);
TestDemo testDemo = new TestDemo();
testDemo.setOrderNum(num);
testDemo.setValue(Convert.toStr(i));
testDemo.setTestKey(UUID.randomUUID().toString());
dataList.add(testDemo);
}
}
/**
* 转化应用
*/
@GetMapping("/convert")
public void test() {
System.out.println("============== toList() 处理");
List<String> list = StreamUtils.toList(dataList, TestDemo::getTestKey);
list.forEach(s -> System.out.println("s = " + s));
System.out.println("============== toSet() 处理");
Set<String> set = StreamUtils.toSet(dataList, TestDemo::getTestKey);
set.forEach(s -> System.out.println("s = " + s));
System.out.println("============== toMap() 处理");
Map<String, String> map = StreamUtils.toMap(dataList, TestDemo::getTestKey, TestDemo::getValue);
map.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v));
System.out.println("============== toIdentityMap() 处理");
Map<String, TestDemo> identityMap = StreamUtils.toIdentityMap(dataList, TestDemo::getTestKey);
identityMap.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v));
}
/**
* 基础逻辑应用
*/
@GetMapping("/basics")
public void test2() {
System.out.println("============== filter() 过滤处理 (排除奇数值)");
List<TestDemo> filter = StreamUtils.filter(dataList, ent -> Convert.toInt(ent.getValue()) % 2 == 0);
filter.forEach(ent -> System.out.println("ent = " + ent));
System.out.println("============== join() 拼接处理");
String join = StreamUtils.join(dataList, TestDemo::getTestKey);
System.out.println("join = " + join);
System.out.println("============== sorted() 排序处理 按OrderNum排序");
List<TestDemo> sorted = StreamUtils.sorted(dataList, (o1, o2) -> o1.getOrderNum() - o2.getOrderNum());
sorted.forEach(ent -> System.out.println("ent = " + ent));
}
/**
* 分组应用
*/
@GetMapping("/group")
public void test3() {
System.out.println("============== groupByKey() 分组处理 (按OrderNum分组)");
Map<Integer, List<TestDemo>> groupByKey = StreamUtils.groupByKey(dataList, TestDemo::getOrderNum);
groupByKey.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v));
// 双重分类
System.out.println("============== groupBy2Key() 分组处理 (按OrderNum,getTestKey分组)");
Map<Integer, Map<String, List<TestDemo>>> groupBy2Key = StreamUtils.groupBy2Key(dataList, TestDemo::getOrderNum, TestDemo::getTestKey);
groupBy2Key.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v));
// 双重Map分组
System.out.println("============== group2Map() 分组处理 (按OrderNum,TestKey分组)");
Map<Integer, Map<String, TestDemo>> integerMapMap = StreamUtils.group2Map(dataList, TestDemo::getOrderNum, TestDemo::getTestKey);
integerMapMap.forEach((k, v) -> System.out.println("k = " + k + ", v = " + v));
}
}
# TreeBuildUtils
树构建工具 . 该工具继承 Hutool库中的TreeUtil , 拓展重写部分方法
Hutool文档 : https://doc.hutool.cn (opens new window)
源码
点击展开
TreeBuildUtils类工具类
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TreeBuildUtils extends TreeUtil {
/**
* 根据前端定制差异化字段
*/
public static final TreeNodeConfig DEFAULT_CONFIG = TreeNodeConfig.DEFAULT_CONFIG.setNameKey("label");
public static <T, K> List<Tree<K>> build(List<T> list, NodeParser<T, K> nodeParser) {
if (CollUtil.isEmpty(list)) {
return null;
}
K k = ReflectUtils.invokeGetter(list.get(0), "parentId");
return TreeUtil.build(list, k, DEFAULT_CONFIG, nodeParser);
}
}
TreeUtil工具类
public class TreeUtil {
/**
* 构建单root节点树
*
* @param list 源数据集合
* @return {@link Tree}
* @since 5.7.2
*/
public static Tree<Integer> buildSingle(List<TreeNode<Integer>> list) {
return buildSingle(list, 0);
}
/**
* 树构建
*
* @param list 源数据集合
* @return List
*/
public static List<Tree<Integer>> build(List<TreeNode<Integer>> list) {
return build(list, 0);
}
/**
* 构建单root节点树<br>
* 它会生成一个以指定ID为ID的空的节点,然后逐级增加子节点。
*
* @param <E> ID类型
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @return {@link Tree}
* @since 5.7.2
*/
public static <E> Tree<E> buildSingle(List<TreeNode<E>> list, E parentId) {
return buildSingle(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, new DefaultNodeParser<>());
}
/**
* 树构建
*
* @param <E> ID类型
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @return List
*/
public static <E> List<Tree<E>> build(List<TreeNode<E>> list, E parentId) {
return build(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, new DefaultNodeParser<>());
}
/**
* 构建单root节点树<br>
* 它会生成一个以指定ID为ID的空的节点,然后逐级增加子节点。
*
* @param <T> 转换的实体 为数据源里的对象类型
* @param <E> ID类型
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @param nodeParser 转换器
* @return {@link Tree}
* @since 5.7.2
*/
public static <T, E> Tree<E> buildSingle(List<T> list, E parentId, NodeParser<T, E> nodeParser) {
return buildSingle(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, nodeParser);
}
/**
* 树构建
*
* @param <T> 转换的实体 为数据源里的对象类型
* @param <E> ID类型
* @param list 源数据集合
* @param parentId 最顶层父id值 一般为 0 之类
* @param nodeParser 转换器
* @return List
*/
public static <T, E> List<Tree<E>> build(List<T> list, E parentId, NodeParser<T, E> nodeParser) {
return build(list, parentId, TreeNodeConfig.DEFAULT_CONFIG, nodeParser);
}
/**
* 树构建
*
* @param <T> 转换的实体 为数据源里的对象类型
* @param <E> ID类型
* @param list 源数据集合
* @param rootId 最顶层父id值 一般为 0 之类
* @param treeNodeConfig 配置
* @param nodeParser 转换器
* @return List
*/
public static <T, E> List<Tree<E>> build(List<T> list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
return buildSingle(list, rootId, treeNodeConfig, nodeParser).getChildren();
}
/**
* 构建单root节点树<br>
* 它会生成一个以指定ID为ID的空的节点,然后逐级增加子节点。
*
* @param <T> 转换的实体 为数据源里的对象类型
* @param <E> ID类型
* @param list 源数据集合
* @param rootId 最顶层父id值 一般为 0 之类
* @param treeNodeConfig 配置
* @param nodeParser 转换器
* @return {@link Tree}
* @since 5.7.2
*/
public static <T, E> Tree<E> buildSingle(List<T> list, E rootId, TreeNodeConfig treeNodeConfig, NodeParser<T, E> nodeParser) {
return TreeBuilder.of(rootId, treeNodeConfig)
.append(list, rootId, nodeParser).build();
}
/**
* 树构建,按照权重排序
*
* @param <E> ID类型
* @param map 源数据Map
* @param rootId 最顶层父id值 一般为 0 之类
* @return List
* @since 5.6.7
*/
public static <E> List<Tree<E>> build(Map<E, Tree<E>> map, E rootId) {
return buildSingle(map, rootId).getChildren();
}
/**
* 单点树构建,按照权重排序<br>
* 它会生成一个以指定ID为ID的空的节点,然后逐级增加子节点。
*
* @param <E> ID类型
* @param map 源数据Map
* @param rootId 根节点id值 一般为 0 之类
* @return {@link Tree}
* @since 5.7.2
*/
public static <E> Tree<E> buildSingle(Map<E, Tree<E>> map, E rootId) {
final Tree<E> tree = IterUtil.getFirstNoneNull(map.values());
if (null != tree) {
final TreeNodeConfig config = tree.getConfig();
return TreeBuilder.of(rootId, config)
.append(map)
.build();
}
return createEmptyNode(rootId);
}
/**
* 获取ID对应的节点,如果有多个ID相同的节点,只返回第一个。<br>
* 此方法只查找此节点及子节点,采用递归深度优先遍历。
*
* @param <T> ID类型
* @param node 节点
* @param id ID
* @return 节点
* @since 5.2.4
*/
public static <T> Tree<T> getNode(Tree<T> node, T id) {
if (ObjectUtil.equal(id, node.getId())) {
return node;
}
final List<Tree<T>> children = node.getChildren();
if (null == children) {
return null;
}
// 查找子节点
Tree<T> childNode;
for (Tree<T> child : children) {
childNode = child.getNode(id);
if (null != childNode) {
return childNode;
}
}
// 未找到节点
return null;
}
/**
* 获取所有父节点名称列表
*
* <p>
* 比如有个人在研发1部,他上面有研发部,接着上面有技术中心<br>
* 返回结果就是:[研发一部, 研发中心, 技术中心]
*
* @param <T> 节点ID类型
* @param node 节点
* @param includeCurrentNode 是否包含当前节点的名称
* @return 所有父节点名称列表,node为null返回空List
* @since 5.2.4
*/
public static <T> List<CharSequence> getParentsName(Tree<T> node, boolean includeCurrentNode) {
final List<CharSequence> result = new ArrayList<>();
if (null == node) {
return result;
}
if (includeCurrentNode) {
result.add(node.getName());
}
Tree<T> parent = node.getParent();
CharSequence name;
while (null != parent) {
name = parent.getName();
parent = parent.getParent();
if(null != name || null != parent){
// issue#I795IN,根节点的null不加入
result.add(name);
}
}
return result;
}
/**
* 获取所有父节点ID列表
*
* <p>
* 比如有个人在研发1部,他上面有研发部,接着上面有技术中心<br>
* 返回结果就是:[研发部, 技术中心]
*
* @param <T> 节点ID类型
* @param node 节点
* @param includeCurrentNode 是否包含当前节点的名称
* @return 所有父节点ID列表,node为null返回空List
* @since 5.8.22
*/
public static <T> List<T> getParentsId(Tree<T> node, boolean includeCurrentNode) {
final List<T> result = new ArrayList<>();
if (null == node) {
return result;
}
if (includeCurrentNode) {
result.add(node.getId());
}
Tree<T> parent = node.getParent();
T id;
while (null != parent) {
id = parent.getId();
parent = parent.getParent();
if(null != id || null != parent){
// issue#I795IN,根节点的null不加入
result.add(id);
}
}
return result;
}
/**
* 创建空Tree的节点
*
* @param id 节点ID
* @param <E> 节点ID类型
* @return {@link Tree}
* @since 5.7.2
*/
public static <E> Tree<E> createEmptyNode(E id) {
return new Tree<E>().setId(id);
}
}
示例代码
点击展开
public class TreeBuildTest {
// 构建树
public static List<TreeNode<Integer>> buildTree() {
List<TreeNode<Integer>> list = new ArrayList<>();
TreeNode node = new TreeNode();
node.setName("xxx公司");
node.setId(1);
node.setParentId(0);
list.add(node);
node = new TreeNode();
node.setName("销售部");
node.setId(2);
node.setParentId(1);
list.add(node);
node = new TreeNode();
node.setName("开发部");
node.setId(3);
node.setParentId(1);
list.add(node);
node = new TreeNode();
node.setName("前端组");
node.setId(4);
node.setParentId(3);
list.add(node);
node = new TreeNode();
node.setName("后端组");
node.setId(5);
node.setParentId(3);
list.add(node);
node = new TreeNode();
node.setName("产品组");
node.setId(6);
node.setParentId(2);
list.add(node);
node = new TreeNode();
node.setName("销售组");
node.setId(7);
node.setParentId(2);
list.add(node);
return list;
}
// 构建自定义对象
public static List<TestTree> buildObj() {
List<TestTree> list = new ArrayList<>();
TestTree node = new TestTree();
node.setTreeName("xxx公司");
node.setId(1L);
node.setParentId(0L);
list.add(node);
node = new TestTree();
node.setTreeName("销售部");
node.setId(2L);
node.setParentId(1L);
list.add(node);
node = new TestTree();
node.setTreeName("开发部");
node.setId(3L);
node.setParentId(1L);
list.add(node);
node = new TestTree();
node.setTreeName("前端组");
node.setId(4L);
node.setParentId(3L);
list.add(node);
node = new TestTree();
node.setTreeName("后端组");
node.setId(5L);
node.setParentId(3L);
list.add(node);
node = new TestTree();
node.setTreeName("产品组");
node.setId(6L);
node.setParentId(2L);
list.add(node);
node = new TestTree();
node.setTreeName("销售组");
node.setId(7L);
node.setParentId(2L);
list.add(node);
return list;
}
public static void main(String[] args) {
//buildSingle();
//build();
//getChildren();
getParent();
}
// 构建
public static void buildSingle() {
List<TreeNode<Integer>> treeNodes = buildTree();
// 基础单
Tree<Integer> integerTree = TreeBuildUtils.buildSingle(treeNodes, 0);
System.out.println(integerTree);
System.out.println("=============");
TreeNodeConfig defaultConfig = TreeNodeConfig.DEFAULT_CONFIG;
defaultConfig.setNameKey("label");
Tree<Integer> integerTree2 = TreeBuildUtils.buildSingle(treeNodes, 0, defaultConfig, (originNode, node) -> {
node.setName(originNode.getName());
node.setId(originNode.getId());
node.setParentId(originNode.getParentId());
node.putExtra("extName", originNode.getName());
});
System.out.println(integerTree2);
}
public static void build() {
List<TreeNode<Integer>> treeNodes = buildTree();
List<Tree<Integer>> treeList = TreeBuildUtils.build(treeNodes, 0);
System.out.println(treeList);
System.out.println("============= list<object> 转 tree");
List<TestTree> treeNodes2 = buildObj();
List<Tree<Object>> treeList2 = TreeBuildUtils.build(treeNodes2, (originNode, node) -> {
node.setName(originNode.getTreeName());
node.setId(originNode.getId());
node.setParentId(originNode.getParentId());
});
System.out.println(treeList2);
}
/**
* 获取当前节点的所有子节点
*/
public static void getChildren() {
List<TreeNode<Integer>> treeNodes = buildTree();
Tree<Integer> single = TreeBuildUtils.buildSingle(treeNodes, 0);
Tree<Integer> node = TreeBuildUtils.getNode(single, 5);
System.out.println(node);
}
/**
* 获取当前节点的父节点
*/
public static void getParent() {
List<TreeNode<Integer>> treeNodes = buildTree();
Tree<Integer> single = TreeBuildUtils.buildSingle(treeNodes, 0);
Tree<Integer> node = TreeBuildUtils.getNode(single, 5);
List<CharSequence> parentsName = TreeBuildUtils.getParentsName(node, true);
System.out.println(parentsName);
}
}
# ValidatorUtils
Validator注解 校验工具
源码
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ValidatorUtils {
private static final Validator VALID = SpringUtils.getBean(Validator.class);
public static <T> void validate(T object, Class<?>... groups) {
Set<ConstraintViolation<T>> validate = VALID.validate(object, groups);
if (!validate.isEmpty()) {
throw new ConstraintViolationException("参数校验异常", validate);
}
}
}
示例
点击展开
@SaIgnore
@RequestMapping("/validator")
@RestController
@Slf4j
public class ValidatorTest {
@GetMapping("/demo1")
public void demo1(){
Student student = new Student();
// 注释测试
//student.setName("张三");
ValidatorUtils.validate(student);
}
@Data
static class Student{
@NotBlank(message = "名称 不能为空")
private String name;
}
}
# MeeageUtil
国际化翻译工具
源码
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MessageUtils {
private static final MessageSource MESSAGE_SOURCE = SpringUtils.getBean(MessageSource.class);
/**
* 根据消息键和参数 获取消息 委托给spring messageSource
*
* @param code 消息键
* @param args 参数
* @return 获取国际化翻译值
*/
public static String message(String code, Object... args) {
return MESSAGE_SOURCE.getMessage(code, args, LocaleContextHolder.getLocale());
}
}
# RegionUtils&AddressUtils
区域定位工具&地址工具 , 通过 ip2region (opens new window) 实现离线定位
应用手册 : https://gitee.com (opens new window)
源码
点击展开
RegionUtils工具类
@Slf4j
public class RegionUtils {
private static final Searcher SEARCHER;
static {
String fileName = "/ip2region.xdb";
File existFile = FileUtils.file(FileUtil.getTmpDir() + FileUtil.FILE_SEPARATOR + fileName);
if (!FileUtils.exist(existFile)) {
ClassPathResource fileStream = new ClassPathResource(fileName);
if (ObjectUtil.isEmpty(fileStream.getStream())) {
throw new ServiceException("RegionUtils初始化失败,原因:IP地址库数据不存在!");
}
FileUtils.writeFromStream(fileStream.getStream(), existFile);
}
String dbPath = existFile.getPath();
// 1、从 dbPath 加载整个 xdb 到内存。
byte[] cBuff;
try {
cBuff = Searcher.loadContentFromFile(dbPath);
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败,原因:从ip2region.xdb文件加载内容失败!" + e.getMessage());
}
// 2、使用上述的 cBuff 创建一个完全基于内存的查询对象。
try {
SEARCHER = Searcher.newWithBuffer(cBuff);
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败,原因:" + e.getMessage());
}
}
/**
* 根据IP地址离线获取城市
*/
public static String getCityInfo(String ip) {
try {
ip = ip.trim();
// 3、执行查询
String region = SEARCHER.search(ip);
return region.replace("0|", "").replace("|0", "");
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", ip);
return "未知";
}
}
}
AddressUtils工具类
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class AddressUtils {
// 未知地址
public static final String UNKNOWN = "XX XX";
public static String getRealAddressByIP(String ip) {
if (StringUtils.isBlank(ip)) {
return UNKNOWN;
}
// 内网不查询
ip = StringUtils.contains(ip, "0:0:0:0:0:0:0:1") ? "127.0.0.1" : HtmlUtil.cleanHtmlTag(ip);
if (NetUtil.isInnerIP(ip)) {
return "内网IP";
}
return RegionUtils.getCityInfo(ip);
}
}
示例代码
点击展开
前置说明 : 启用Spring环境加载配置文件 , 调用方法时会进行读取文件进行离线获取
@GetMapping("/demo")
public void demo() {
System.out.println("127.0.0.1 = " + AddressUtils.getRealAddressByIP("127.0.0.1"));
System.out.println("117.134.33.112 = " + AddressUtils.getRealAddressByIP("117.134.33.112"));
System.out.println("60.184.247.1 = " + AddressUtils.getRealAddressByIP("60.184.247.1"));
System.out.println("123.150.2.43 = " + AddressUtils.getRealAddressByIP("123.150.2.43"));
}