Spring Boot 使用Redis作为默认缓存,且注解支持配置过期时间

本文介绍了如何在Spring Boot项目中利用Redis作为默认缓存,并通过注解方式配置缓存过期时间。首先,进行准备工作,包括搭建项目、引入Redis依赖和配置application.yml。接着,分析源码,特别是@Cacheable注解如何触发缓存过程。然后,展示了自定义CacheManager的实现,以及如何使缓存系统使用这个自定义的管理器。最后,提供了测试代码以验证配置的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

准备工作

  1. 使用spring boot搭建简单的项目
  2. 引入redis的包

    compile('org.springframework.boot:spring-boot-starter-data-redis')
  3. 配置application.yml文件,添加如下内容(redis其他可以先使用默认配置)

    spring:
      cache:
        type: redis
      redis:
        database: 0

源码分析

  • 在需要缓存的方法上面加上@Cacheable
  • 当请求进来的时候会触发CacheInterceptor拦截器,调用invoke方法

    @Override
    public Object invoke(final MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
    
        CacheOperationInvoker aopAllianceInvoker = new CacheOperationInvoker() {
            @Override
            public Object invoke() {
                try {
                    return invocation.proceed();
                }
                catch (Throwable ex) {
                    throw new ThrowableWrapper(ex);
                }
            }
        };
    
        try {
            return execute(aopAllianceInvoker, invocation.getThis(), method, invocation.getArguments());
        }
        catch (CacheOperationInvoker.ThrowableWrapper th) {
            throw th.getOriginal();
        }
    }
  • invoke方法会去调用CacheAspectSupport的execute方法

    protected Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
        // Check whether aspect is enabled (to cope with cases where the AJ is pulled in automatically)
        if (this.initialized) {
            Class<?> targetClass = getTargetClass(target);
            Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
            if (!CollectionUtils.isEmpty(operations)) {
                return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));
            }
        }
    
        return invoker.invoke();
    }
  • 然后会调用到cachemanager的getCache方法

    @Override
    public Cache getCache(String name) {
        Cache cache = this.cacheMap.get(name);
        if (cache != null) {
            return cache;
        }
        else {
            // Fully synchronize now for missing cache creation...
            synchronized (this.cacheMap) {
                cache = this.cacheMap.get(name);
                if (cache == null) {
                    //cache不存在时,会调用该方法取cache,该方法由子类实现
                    cache = getMissingCache(name);
                    if (cache != null) {
                        cache = decorateCache(cache);
                        this.cacheMap.put(name, cache);
                        updateCacheNames(name);
                    }
                }
                return cache;
            }
        }
    }
  • 再看RedisCacheManager类

    @Override
    protected Cache getMissingCache(String name) {
        return this.dynamic ? createCache(name) : null;
    }
    
    @SuppressWarnings("unchecked")
    protected RedisCache createCache(String cacheName) {
        long expiration = computeExpiration(cacheName);
        return new RedisCache(cacheName, (usePrefix ? cachePrefix.prefix(cacheName) : null), redisOperations, expiration,
                cacheNullValues);
    }
    
    // 根据cacheName取过期时间
    protected long computeExpiration(String name) {
        Long expiration = null;
        if (expires != null) {
            expiration = expires.get(name);
        }
        return (expiration != null ? expiration.longValue() : defaultExpiration);
    }

代码实现

  • 分析源码后,我们可以在createCache方法上做文章,实现自己的cacheManager

代码如下:

import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.util.Assert;

/**
 * @author zhangminglei
 */
public class CustomizeRedis extends RedisCacheManager {

    CustomizeRedis(RedisOperations redisOperations) {
        super(redisOperations);
    }

    @Override
    protected RedisCache createCache(String cacheName) {
        Assert.hasText(cacheName, "CacheName must not be null or empty!");

        String[] values = cacheName.split("#");

        long expiration = computeExpiration(values);
        return new RedisCache(values[0], (isUsePrefix() ? getCachePrefix().prefix(cacheName) : null), getRedisOperations(), expiration,
                false);
    }

    private long computeExpiration(String[] values) {
        if (values.length > 1) {
            return Long.parseLong(values[1]);
        }
        // 如果说想使用默认的过期时间而不指定特殊时间,则可以直接@Cacheable(cacheNames="name"),不需要加'#过期时间'了
        return super.computeExpiration(values[0]);
    }

}
  • 然后需要让cache使用我们的自定义CacheManager
// 为redis设定默认的序列化方式为StringRedisSerializer(默认为JdkSerializationRedisSerializer,不太方便查看内容)
@Bean
RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisTemplate redisTemplate = new RedisTemplate();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    redisTemplate.setDefaultSerializer(new StringRedisSerializer());
    return redisTemplate;
}

@Bean
public RedisCacheManager cacheManager(RedisTemplate<Object, Object> redisTemplate) {

    RedisCacheManager cacheManager = new CustomizeRedis(redisTemplate);
    cacheManager.setUsePrefix(false);
    //设置默认的过期时间,单位秒
    cacheManager.setDefaultExpiration(60);

    // 还可以使用下面的方法为指定的key设定过期时间,它将会在computeExpiration方法中用到
//        Map<String, Long> expires = new HashMap<>();
//        expires.put("cacheNameKey", 20L);
//        expires.put("myKey", 40L);
//        cacheManager.setExpires(expires);
    return cacheManager;
}

测试代码

//指定cacheNameKey的缓存45秒后过期
@Cacheable(cacheNames = "cacheNameKey#45", key = "#key")
public String testRedisCache(String key) {
    System.out.println(key);
    return "12345";
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值