为什么要写RedisUtil这个类

引言:

不知道大家有没有这个疑惑,反正我是有,我学习就有一股打破砂锅问到底的劲儿,明明调用RedisTemplate不就可以和redis交互了吗,为什么还要写RedisUtil这个工具类,是我理解错了吗?

先写个工具类:

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // =============================common============================

    /**
     * 指定缓存失效时间
     */
    public boolean expire(String key, long time, TimeUnit unit) {
        try {
            if (time > 0) {
                redisTemplate
                        .expire(key, time, unit);
            }
            return true;
        } catch (Exception e) {
            e
                    .printStackTrace();
            return false;
        }
    }

    /**
     * 根据key获取过期时间
     */
    public long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    /**
     * 判断key是否存在
     */
    public boolean hasKey(String key) {
        try {
            return redisTemplate.hasKey(key);
        } catch (Exception e) {
            e
                    .printStackTrace();
            return false;
        }
    }

    /**
     * 删除缓存
     */
    public void delete(String... key) {
        if (key != null && key.length > 0) {
            if (key.length == 1) {
                redisTemplate
                        .delete(key[0]);
            } else {
                redisTemplate
                        .delete(java.util.Arrays.asList(key));
            }
        }
    }

    // ============================String=============================

    /**
     * 普通缓存获取
     */
    public Object get(String key) {
        return key == null ? null : redisTemplate.opsForValue().get(key);
    }

    /**
     * 普通缓存放入
     */
    public boolean set(String key, Object value) {
        try {
            redisTemplate
                    .opsForValue().set(key, value);
            return true;
        } catch (Exception e) {
            e
                    .printStackTrace();
            return false;
        }
    }

    /**
     * 普通缓存放入并设置时间
     */
    public boolean set(String key, Object value, long time, TimeUnit unit) {
        try {
            if (time > 0) {
                redisTemplate
                        .opsForValue().set(key, value, time, unit);
            } else {
                set(key, value);
            }
            return true;
        } catch (Exception e) {
            e
                    .printStackTrace();
            return false;
        }
    }

    /**
     * 递增
     */
    public long increment(String key, long delta) {
        if (delta < 0) {
            throw new RuntimeException("递增因子必须大于0");
        }
        return redisTemplate.opsForValue().increment(key, delta);
    }

    // ================================分布式锁================================

    /**
     * 获取分布式锁
     */
    public Boolean tryLock(String key, String value, long timeout, TimeUnit unit) {
        String script = "return redis.call('set', KEYS[1], ARGV[1], 'NX', 'EX', ARGV[2])";
        Object result = redisTemplate.execute((org.springframework.data.redis.connection.RedisConnection connection) ->
                connection
                        .eval(script.getBytes(),
                                org.springframework.data.redis.connection.ReturnType.BOOLEAN,
                                1,
                                key
                                        .getBytes(),
                                value
                                        .getBytes(),
                                String.valueOf(unit.toSeconds(timeout)).getBytes()));
        return result != null && (Boolean) result;
    }

    /**
     * 释放分布式锁
     */
    public Boolean releaseLock(String key, String value) {
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = redisTemplate.execute((org.springframework.data.redis.connection.RedisConnection connection) ->
                connection
                        .eval(script.getBytes(),
                                org.springframework.data.redis.connection.ReturnType.INTEGER,
                                1,
                                key
                                        .getBytes(),
                                value
                                        .getBytes()));
        return result != null && (Long) result > 0;
    }

    // ================================缓存区域================================

    /**
     * 获取缓存区域下的所有key
     */
    public java.util.Set<String> getRegionKeys(String region) {
        return redisTemplate.keys(region + ":*");
    }

    /**
     * 删除缓存区域
     */
    public void deleteRegion(String region) {
        java.util.Set<String> keys = getRegionKeys(region);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate
                    .delete(keys);
        }
    }
}


 

确实直接使用 RedisTemplate 就可以完成所有 Redis 操作,但在实际项目中创建 RedisUtil 这样的工具类有几个重要的原因: 

1. 封装常用操作

RedisUtil 的主要目的是对 RedisTemplate 进行二次封装,提供更简洁、更符合业务需求的 API:

  • 简化调用:将常用的多步操作封装成单方法调用

  • 统一处理:集中处理异常、日志、默认值等

  • 减少重复代码:避免在各个业务类中重复编写相同的 Redis 操作代码

2. 异常统一处理

在你的 RedisUtil 示例中可以看到,它对异常进行了统一处理:

try {
    // Redis操作
    return true;
} catch (Exception e) {
    e.printStackTrace();
    return false;
}

这种处理方式:

  • 避免了业务代码中到处写 try-catch

  • 统一了异常处理逻辑

  • 提供了更友好的返回值(如返回 boolean 而不是抛出异常)

3. 扩展功能

RedisUtil 可以添加 RedisTemplate 没有直接提供的实用功能:

  • 分布式锁:如你示例中的 tryLock 和 releaseLock 方法

  • 缓存区域管理:如 getRegionKeys 和 deleteRegion 方法

  • 业务特定操作:根据项目需求添加特定操作

4. 统一业务逻辑

通过工具类可以:

  • 统一键的命名规则(如自动添加前缀)

  • 统一序列化方式

  • 统一过期时间设置策略

  • 统一缓存空值处理逻辑

5. 代码可维护性

使用工具类的好处:

  • 减少耦合:业务代码不直接依赖 RedisTemplate

  • 易于修改:Redis 操作逻辑变更只需修改工具类

  • 便于测试:可以针对工具类单独测试 Redis 操作

对代码的改进

提供的 RedisUtil 是一个很好的起点,但在生产环境中还可以进一步优化:

1. 使用泛型改进类型安全

public <T> T get(String key, Class<T> clazz) {
    Object value = redisTemplate.opsForValue().get(key);
    return clazz.cast(value);
}

2. 添加更丰富的操作方法

// 批量操作
public <T> void multiSet(Map<String, T> map) {
    redisTemplate.opsForValue().multiSet(map);
}

// 原子操作
public <T> T getAndSet(String key, T value) {
    return (T) redisTemplate.opsForValue().getAndSet(key, value);
}

3. 使用 Spring 的缓存抽象

@Cacheable(value = "users", key = "#userId")
public User getUser(String userId) {
    // 数据库查询
}

@CacheEvict(value = "users", key = "#user.id")
public void updateUser(User user) {
    // 更新逻辑
}

4. 分布式锁改进

public boolean tryLock(String lockKey, String requestId, long expireTime) {
    return redisTemplate.execute((RedisCallback<Boolean>) connection -> {
        RedisSerializer<String> serializer = redisTemplate.getStringSerializer();
        byte[] key = serializer.serialize(lockKey);
        byte[] value = serializer.serialize(requestId);
        Boolean result = connection.set(key, value, 
            Expiration.from(expireTime, TimeUnit.SECONDS), 
            RedisStringCommands.SetOption.SET_IF_ABSENT);
        return result != null && result;
    });
}

总结

直接使用 RedisTemplate使用 RedisUtil
需要记住各种 opsForXxx() 方法提供业务语义明确的方法
需要手动处理异常异常已统一处理
散落在各业务类中集中管理 Redis 操作
灵活性高一致性高
适合简单场景适合中大型项目

所以我们的理解没有错,直接使用 RedisTemplate 确实可以完成所有操作,但在实际项目中,使用 RedisUtil 这样的工具类能带来更好的可维护性、一致性和开发效率。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

暮乘白帝过重山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值