柏竹 柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档

柏竹

奋斗柏竹
首页
后端
前端
  • 应用推荐
关于
友链
  • 分类
  • 标签
  • 归档
  • Java基础

  • JavaWeb

  • 拓展技术

  • 框架技术

  • 数据库

  • 数据结构

  • Spring

  • SpringMVC

  • SpringBoot

  • SpringClound

  • Ruoyi-Vue-Plus

    • ruoyi-vue-plus-基础功能
    • ruoyi-vue-plus-权限控制
    • ruoyi-vue-plus-表格操作
    • ruoyi-vue-plus-缓存功能
    • ruoyi-vue-plus-日志功能
    • ruoyi-vue-plus-线程相关
    • ruoyi-vue-plus-OSS功能
    • ruoyi-vue-plus-代码生成功能
    • ruoyi-vue-plus-多数据源
    • ruoyi-vue-plus-任务调度
    • ruoyi-vue-plus-监控功能
    • ruoyi-vue-plus-国际化
    • ruoyi-vue-plus-XSS功能
    • ruoyi-vue-plus-防重幂&限流 功能
    • ruoyi-vue-plus-推送功能
    • ruoyi-vue-plus-序列化功能
    • ruoyi-vue-plus-数据加密
    • ruoyi-vue-plus-单元测试
    • ruoyi-vue-plus-前端插件
    • ruoyi-vue-plus-前端工具篇
    • ruoyi-vue-plus-部署篇
    • ruoyi-vue-plus-前端篇
    • ruoyi-vue-plus-后端工具篇
      • BeanCopyUtils
      • CacheUtils
      • DateUtil
      • EncryptUtils
      • ExcelUtil
      • Hutool
      • JsonUtils
      • LoginHelper
      • QueueUtils
      • RedisUtils
      • ReflectUtils
      • ServletUtils
      • SpringUtils
      • SqlUtil
      • StreamUtils
      • TreeBuildUtils
      • ValidatorUtils
      • MeeageUtil
      • RegionUtils&AddressUtils
    • ruoyi-vue-plus-框架篇
    • ruoyi-vue-plus-问题解决
  • 后端
  • Ruoyi-Vue-Plus
柏竹
2024-04-21
目录

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)

官方源码 , 有众多测试类 , 演示这些工具类的使用方式

  • gitee (opens new window)
  • github (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"));
}
#ruoyi-vue-plus#工具

← ruoyi-vue-plus-前端篇 ruoyi-vue-plus-框架篇→

最近更新
01
HTTPS自动续签
10-21
02
博客搭建-简化版(脚本)
10-20
03
ruoyi-vue-plus-部署篇
07-13
更多文章>
Theme by Vdoing | Copyright © 2019-2024 | 桂ICP备2022009417号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式