spring boot 集成caffeine实现本地缓存

一、导出依赖

		<!--caffeine-->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>
        
        <!-- 开启springboot 注解式使用缓存功能-->
         <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

二、缓存配置

本人是配置到springboot的启动文件里了,自己可以根据需求自由配置
需要在启动文件里加入@EnableCaching注解,开启注解支持

	@Bean
    @Primary
    public CacheManager cacheManager() {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        List<CaffeineCache> caches = new ArrayList<>();
        for (Caches c : Caches.values()) {
            caches.add(new CaffeineCache(c.name(),
                    Caffeine.newBuilder()
                            .recordStats()
                            .expireAfterWrite(c.getTtl(), TimeUnit.SECONDS)
                            .maximumSize(c.getMaxSize())
                            .softValues()
                          //.weakKeys()   弱引用生成key,容易造成缓存失效
                          //.weakValues()  弱引用生成value,容易造成缓存失效
                            .build())
            );
        }
        cacheManager.setCaches(caches);
        return cacheManager;
    }

    /**
     * 缓存定义
     */
    public enum Caches {
        plugin (1200, 30), // 20分钟
        userToken (1200, 100), //用户 userToken的缓存配置
        customer (1200,50),
        CACHE_3MINUTES(180,50);

        private int ttl;
        private int maxSize;
        Caches(int ttl, int maxSize) {
            this.ttl = ttl;
            this.maxSize = maxSize;
        }
        public int getTtl() {
            return ttl;
        }
        public int getMaxSize() {
            return maxSize;
        }
    }

缓存名称配置

public interface CacheNameTimeConstant {
    String CACHE_DEFAULT =  "CACHE_DEFAULT" ;
    String CACHE_3MINUTES =  "CACHE_3MINUTES" ;
    String plugin = "plugin";
    String userToken = "userToken";
    String customer = "customer";
}

三、使用缓存

	@Cacheable(value = CacheNameTimeConstant.userToken,key = "#root.methodName.#userId",unless = "#result==null")
    public UserTokenPO queryUserByUserId(String userId) {
        log.info("【UserService.queryUserByUserId】未走缓存!userId为 -> {}",userId);
        UserTokenPO userPO = userTokenRepository.getOne(new LambdaQueryWrapper<UserTokenPO>().eq(UserTokenPO::getUserId, userId));

        if (ObjUtil.isNotEmpty(userPO)
                && StrUtil.isNotBlank(userPO.getOpenId())
                && StrUtil.isNotBlank(userPO.getUserKey())) {
            log.info("从db中获取User成功, openId={} calendarId = {} ", userPO.getUserId(), userPO.getCalendarId());
            return userPO;
        }
       }  

四、概念解释

一些概念
名称解释
@Cacheable主要针对方法配置,标注此方法使用缓存(环绕通知)
@CacheEvict清空缓存
@CachePut保证方法被调用后,更新缓存(后置通知)
@EnableCaching在启动文件中,开启基于注解的缓存
@CacheConfig统一配置本类的缓存注解的属性
cache缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache、caffeine等
CacheManager缓存管理器,管理各种缓存(cache)组件
keyGenerator缓存数据时key生成策略
@Cacheable/@CachePut/@CacheEvict 的参数
名称解释
value、cacheNames二者选其一即可。缓存的名称,在 spring 配置中定义,必须指定至少一个。例如:@Cacheable(value=”cacheName”) 或者@Cacheable(value={”cacheName1”,”cacheName2”}
key缓存的 key,spring 官方更推荐显式指定 key 的方式,即指定 @Cacheable 的 key 参数例如:@Cacheable(key=”#id”)
keyGenerator当我们在声明 @Cacheable 时不指定 key 参数,则该缓存名下的所有 key 会使用 KeyGenerator 根据参数 自动生成。spring 有一个默认的 SimpleKeyGenerator ,在 spring boot 自动化配置中,这个会被默认注入。
condition缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存/清除缓存.例如:@Cacheable(value=”userToken”,condition=”#age>2”)
unless否定缓存。当条件结果为TRUE时,就不会缓存。@Cacheable(value=”userToken”,unless=”#”#age>2”)
allEntries(@CacheEvict注解参数)是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存.例如:@CachEvict(value=”userToken”,allEntries=true)
beforeInvocation(@CacheEvict注解参数)是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法,执行抛出异常,则不会清空缓存.例如:@CachEvict(value=”userToken”,beforeInvocation=true)
SpEL表达式数据

当我们要使用root对象的属性作为key时我们也可以将“#root”省略

名称位置描述示例
methodNameroot对象当前被调用的方法名#root.methodname
methodroot对象当前被调用的方法#root.method.name
targetroot对象当前被调用的目标对象实例#root.target
targetClassroot对象当前被调用的目标对象的类#root.targetClass
args[]root对象当前被调用的方法的参数列表#root.args[0]
cachesroot对象当前方法调用使用的缓存列表#root.caches[0].name
Argument Name执行上下文当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数#artsian.id
result执行上下文方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false)#result
SpEL提供的运算符
类型运算符
关系<,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne
算术+,- ,* ,/,%,^
逻辑&&,
条件?: (ternary),?: (elvis)
正则表达式matches
其他类型?.,?[…],![…],1,$[…]

五、缓存失效的原因

  1. 弱引用:在配置CacheManager时,指定了key和vaule为弱引用,导致缓存失效

弱引用与软引用的区别:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。所以被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。
注:虽说优先级很低,但是我在本地跑项目时,每次都不走缓存,改成软引用后正常!
原因:缓存管理器使用了weakKeys()的配置,这就导致了一个问问题,经过代理之后,每次key都匹配不上,因为底层使用的==的方式来进行key的匹配,而不是equals,所以,需要移除该配置。
在这里插入图片描述

2.未加@EnableCaching注解

3.一个方法A调同一个类里的另一个有缓存注解的方法B,这样是不走缓存的。

因为@Cacheable 是使用AOP 代理实现的 ,通过创建内部类来代理缓存方法,这样就会导致一个问题,类内部的方法调用类内部的缓存方法不会走代理,不会走代理,就不能正常创建缓存,所以每次都需要去调用数据库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值