avatar

十六小站

欢迎来到我的个人主页! 期待与您分享我的经验与故事,一起探索技术的无穷可能!

  • 首页
  • NAS专题
  • 关于
Home SpringBoot3.X-2(缓存Redis/memory)
文章

SpringBoot3.X-2(缓存Redis/memory)

Posted 7 days ago Updated 7 days ago
By 十六 已删除用户
47~61 min read

本文主要是实现缓存的集成,由于是单体项目 ,目前整合了内存缓存和Redis缓存两种,可以通过配置来切换。

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.12.1</version>
</dependency>

编写工具接口


import org.springframework.data.redis.core.BoundSetOperations;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author LONG
 */
public interface CacheService {

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    <T> void setCacheObject(final String key, final T value);

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit);

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    boolean expire(final String key, final long timeout);

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功;false=设置失败
     */
    boolean expire(final String key, final long timeout, final TimeUnit unit);

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    long getExpire(final String key);

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    Boolean hasKey(String key);

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    <T> T getCacheObject(final String key);

    /**
     * 删除单个对象
     *
     * @param key
     */
    boolean deleteObject(final String key);

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    boolean deleteObject(final Collection collection);

    /**
     * 缓存List数据
     *
     * @param key 缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    <T> long setCacheList(final String key, final List<T> dataList);
    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    <T> List<T> getCacheList(final String key);

    /**
     * 缓存Set
     *
     * @param key 缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet);

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    <T> Set<T> getCacheSet(final String key);

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    <T> void setCacheMap(final String key, final Map<String, T> dataMap);

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    <T> Map<String, T> getCacheMap(final String key);

    /**
     * 往Hash中存入数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @param value 值
     */
    <T> void setCacheMapValue(final String key, final String hKey, final T value);

    /**
     * 获取Hash中的数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    <T> T getCacheMapValue(final String key, final String hKey);

    /**
     * 获取多个Hash中的数据
     *
     * @param key Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys);

    /**
     * 删除Hash中的某条数据
     *
     * @param key Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    boolean deleteCacheMapValue(final String key, final String hKey);
    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    Collection<String> keys(final String pattern);
}

Redis缓存实现类


import com.ymjing.ymboot.common.cache.CacheService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author LONG
 */
@Service
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "cache.service", name = "type", havingValue = "redis")
public class RedisCache implements CacheService {

    public final RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    @Override
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    @Override
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    @Override
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    @Override
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    @Override
    public long getExpire(final String key) {
        return redisTemplate.getExpire(key);
    }

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    @Override
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    @Override
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    @Override
    public boolean deleteObject(final Collection collection) {
        return redisTemplate.delete(collection) > 0;
    }

    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    @Override
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    @Override
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    @Override
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    @Override
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    @Override
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    @Override
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     */
    @Override
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    @Override
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    @Override
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 删除Hash中的某条数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return 是否成功
     */
    @Override
    public boolean deleteCacheMapValue(final String key, final String hKey) {
        return redisTemplate.opsForHash().delete(key, hKey) > 0;
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    @Override
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }
}

内存缓存实现类


import com.ymjing.ymboot.common.cache.CacheService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

/**
 * @author LONG
 */
@Service
@ConditionalOnProperty(prefix = "cache.service", name = "type", havingValue = "memory")
public class MemoryCache implements CacheService {


    // 存储缓存数据
    private static final ConcurrentHashMap<String, Object> CACHE_MAP = new ConcurrentHashMap<>();
    // 存储缓存过期时间
    private static final ConcurrentHashMap<String, Long> EXPIRE_MAP = new ConcurrentHashMap<>();
    // 定时任务执行器
    private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1);

    static {
        // 每秒检查一次过期缓存
        SCHEDULER.scheduleAtFixedRate(()  -> {
            long currentTime = System.currentTimeMillis();
            for (Map.Entry<String, Long> entry : EXPIRE_MAP.entrySet())  {
                if (entry.getValue()  != null && entry.getValue()  < currentTime) {
                    CACHE_MAP.remove(entry.getKey());
                    EXPIRE_MAP.remove(entry.getKey());
                }
            }
        }, 0, 1, TimeUnit.SECONDS);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    @Override
    public <T> void setCacheObject(final String key, final T value) {
        CACHE_MAP.put(key,  value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    @Override
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        CACHE_MAP.put(key,  value);
        expire(key, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis 键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    @Override
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis 键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    @Override
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        if (CACHE_MAP.containsKey(key))  {
            long expireTime = System.currentTimeMillis()  + unit.toMillis(timeout);
            EXPIRE_MAP.put(key,  expireTime);
            return true;
        }
        return false;
    }

    /**
     * 获取有效时间
     *
     * @param key Redis 键
     * @return 有效时间
     */
    @Override
    public long getExpire(final String key) {
        Long expireTime = EXPIRE_MAP.get(key);
        if (expireTime != null) {
            return (expireTime - System.currentTimeMillis())  / 1000;
        }
        return -1;
    }

    /**
     * 判断 key 是否存在
     *
     * @param key 键
     * @return true 存在 false 不存在
     */
    @Override
    public Boolean hasKey(String key) {
        return CACHE_MAP.containsKey(key);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    @Override
    public <T> T getCacheObject(final String key) {
        return (T) CACHE_MAP.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    @Override
    public boolean deleteObject(final String key) {
        if (CACHE_MAP.containsKey(key))  {
            CACHE_MAP.remove(key);
            EXPIRE_MAP.remove(key);
            return true;
        }
        return false;
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    @Override
    public boolean deleteObject(final Collection collection) {
        boolean result = false;
        for (Object key : collection) {
            if (key instanceof String) {
                result |= deleteObject((String) key);
            }
        }
        return result;
    }

    /**
     * 缓存 List 数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的 List 数据
     * @return 缓存的对象
     */
    @Override
    public <T> long setCacheList(final String key, final List<T> dataList) {
        CACHE_MAP.put(key,  dataList);
        return dataList.size();
    }

    /**
     * 获得缓存的 list 对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    @Override
    public <T> List<T> getCacheList(final String key) {
        return (List<T>) CACHE_MAP.get(key);
    }

    /**
     * 缓存 Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    @Override
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        CACHE_MAP.put(key,  dataSet);
        return null;
    }

    /**
     * 获得缓存的 set
     *
     * @param key
     * @return
     */
    @Override
    public <T> Set<T> getCacheSet(final String key) {
        return (Set<T>) CACHE_MAP.get(key);
    }

    /**
     * 缓存 Map
     *
     * @param key
     * @param dataMap
     */
    @Override
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        CACHE_MAP.put(key,  dataMap);
    }

    /**
     * 获得缓存的 Map
     *
     * @param key
     * @return
     */
    @Override
    public <T> Map<String, T> getCacheMap(final String key) {
        return (Map<String, T>) CACHE_MAP.get(key);
    }

    /**
     * 往 Hash 中存入数据
     *
     * @param key   Redis 键
     * @param hKey  Hash 键
     * @param value 值
     */
    @Override
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        Map<String, T> map = getCacheMap(key);
        if (map == null) {
            map = new HashMap<>();
            setCacheMap(key, map);
        }
        map.put(hKey,  value);
    }

    /**
     * 获取 Hash 中的数据
     *
     * @param key  Redis 键
     * @param hKey Hash 键
     * @return Hash 中的对象
     */
    @Override
    public <T> T getCacheMapValue(final String key, final String hKey) {
        Map<String, T> map = getCacheMap(key);
        if (map != null) {
            return map.get(hKey);
        }
        return null;
    }

    /**
     * 获取多个 Hash 中的数据
     *
     * @param key   Redis 键
     * @param hKeys Hash 键集合
     * @return Hash 对象集合
     */
    @Override
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        List<T> result = new ArrayList<>();
        Map<String, T> map = getCacheMap(key);
        if (map != null) {
            for (Object hKey : hKeys) {
                if (hKey instanceof String) {
                    result.add(map.get(hKey));
                }
            }
        }
        return result;
    }

    /**
     * 删除 Hash 中的某条数据
     *
     * @param key  Redis 键
     * @param hKey Hash 键
     * @return 是否成功
     */
    @Override
    public boolean deleteCacheMapValue(final String key, final String hKey) {
        Map<String, ?> map = getCacheMap(key);
        if (map != null) {
            return map.remove(hKey)  != null;
        }
        return false;
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    @Override
    public Collection<String> keys(final String pattern) {
        Pattern regex = Pattern.compile(pattern.replace("*",  ".*"));
        List<String> result = new ArrayList<>();
        for (String key : CACHE_MAP.keySet())  {
            if (regex.matcher(key).matches())  {
                result.add(key);
            }
        }
        return result;
    }

    // 关闭定时任务
    public static void shutdown() {
        SCHEDULER.shutdown();
    }
}

缓存配置


import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.Duration;

/**
 * @author LONG
 */
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    @ConditionalOnProperty(prefix = "cache.service", name = "type", havingValue = "redis")
    public RedisCacheManager cacheManager(RedisConnectionFactory cf) {
        return RedisCacheManager.builder(cf)
                .cacheDefaults(RedisCacheConfiguration.defaultCacheConfig()
                        .entryTtl(Duration.ofMinutes(5))
                        .disableCachingNullValues()
                )
                .build();
    }

    @Bean
    @ConditionalOnProperty(prefix = "cache.service", name = "type", havingValue = "memory")
    public ConcurrentMapCacheManager memoryCacheManager() {
        return new ConcurrentMapCacheManager();
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(cf);
        // 可添加序列化配置
        return template;
    }
}

全局配置和使用

在application.yml配置中添加以下配置

cache:
  service:
    # 缓存类型,目前支持 memory 和 redis
    type: redis

使用方法

可以直接调用工具类使用或者使用注解

//service中使用
@Cacheable(value = "users", key = "#id")
public User getUserById(Integer id) {
    return baseMapper.selectById(5);
}

高手之路
Java
License:  CC BY 4.0
Share

Further Reading

Sep 6, 2025

记一次前端优化(vue2)

由于博主所在公司的前端缺乏高端人员,导致前端代码臃肿,前端页面加载耗时基本上在min级别,于是博主决定进行一次优化 。首先贴上优化前的加载截图 ,明显可以看出 ,资源加载竟然加载了45.2MB,耗时在2.1min。 1. 引入Bundle Size检查打包体积 (1)下载依赖

Sep 6, 2025

SpringBoot3.X-2(缓存Redis/memory)

本文主要是实现缓存的集成,由于是单体项目 ,目前整合了内存缓存和Redis缓存两种,可以通过配置来切换。 引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-sta

Sep 5, 2025

SpringBoot3.X-1(MP+Druid)

本次基于springboot-3.5.5,先附上文档地址:https://docs.spring.io/spring-boot/reference/data/sql.html 初始化springboot项目 项目创建步骤不做记录 集成mybaits-plus</

OLDER

SpringBoot3.X-1(MP+Druid)

NEWER

记一次前端优化(vue2)

Recently Updated

  • 记一次前端优化(vue2)
  • SpringBoot3.X-2(缓存Redis/memory)
  • SpringBoot3.X-1(MP+Druid)
  • Onlyoffice编译
  • K6+Playwright实现并发测试

Trending Tags

Java Docker 前端 中间件 数据库 群晖 unraid

Contents

©2025 十六小站. Some rights reserved.

Using the Halo theme Chirpy