Caffeine custom annotation set expire time for SpringBoot2.X

Spring Boot2.0以后版本,去除了Guava Cache的支持,改而转向更高性能(官方说法)的咖啡因(Caffeine),前面我们学习了自定义注解Redis的失效时间,这次我们将仍以自定义注解方式讨论Caffeine Cache结合Spring-Data-Jpa的失效时间.

自定义注解:

@Target(AnnotationTarget.FUNCTION,AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Inherited
@Cacheable
annotation class CaffeineCacheable(
        val cacheNames: Array<String> = [],
        //val key: String = "",//自动获取参数key值
        val expireTime: Long = 0 //min
)

该注解可加持在类上,在接下来的注解处理器中暂未支持对类注解的拦截,有兴趣可自己玩一下.依旧说明的是,key值在这里暂未设置,Spring Cacheable 将自动获取参数数据,来拼接key值,如有其它需求可自定义处理key值.

注解处理器:

package com.github.caffeine

import com.github.benmanes.caffeine.cache.Caffeine
import org.springframework.beans.factory.InitializingBean
import org.springframework.cache.annotation.AnnotationCacheOperationSource
import org.springframework.cache.caffeine.CaffeineCacheManager
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextAware
import org.springframework.core.annotation.AnnotationUtils
import org.springframework.util.ReflectionUtils
import spring.data.redis.extension.isNotNull
import spring.data.redis.extension.isNull
import java.lang.reflect.Method
import java.util.concurrent.TimeUnit

class CaffeineProcessor: CaffeineCacheManager(),InitializingBean,ApplicationContextAware {

    private var applicationContext: ApplicationContext? = null
    override fun setApplicationContext(applicationContext: ApplicationContext) {
        this.applicationContext = applicationContext
    }

    override fun afterPropertiesSet() {
        parseCacheDuration(applicationContext)
    }

    private fun createdCaffeine(expireTime: Long) = Caffeine.newBuilder().expireAfterAccess(expireTime,TimeUnit.MINUTES)

    private fun buildCaffeineCache(name: String,expireTime: Long){
        super.setCaffeine(this.createdCaffeine(expireTime))
        super.createCaffeineCache(name)
    }
    private fun findCaffeineCacheable(clazz: Class<*>){
        AnnotationCacheOperationSource()
        ReflectionUtils.doWithMethods(clazz, { method ->
            ReflectionUtils.makeAccessible(method)
            val caffeineCache = findCaffeineCache(method)
            
            if (caffeineCache.isNotNull()) {
               
                buildCaffeineCache(caffeineCache.cacheNames[0],caffeineCache.expireTime)
            }
        }, { method ->
            AnnotationUtils.findAnnotation(method, CaffeineCacheable::class.java).isNotNull()
        })
    }
    private fun parseCacheDuration(applicationContext: ApplicationContext?) {
        if (applicationContext.isNull()) return
        val beanNames = applicationContext!!.getBeanNamesForType(Any::class.java)
        beanNames.forEach {
            val clazz = applicationContext!!.getType(it)
            findCaffeineCacheable(clazz)
        }
    }
    private fun findCaffeineCache(method: Method) = AnnotationUtils.findAnnotation(method, CaffeineCacheable::class.java)
}

我在创建Caffeine时使用的是基于时间(Time-Based)定时驱逐策略中的expireAfterAccess,该方法是:在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期,失效单位时间为min;这里简单说明下Caffeine其他另外两种定时驱逐策略:

1.expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期.

2.expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算.

代码中实现InitializingBean接口其实是在学习Redis时get到的技能,其目的是启动SpringBoot自动初始化加载自定义注解处理器.实现拦截方法上的注解,以便获取信息.

在构建Caffeine Cache是暂未设置CacheLoader,在继承了CaffeineCacheManager中未设置的CacheLoader会默认构建

源码:

protected com.github.benmanes.caffeine.cache.Cache<Object, Object> createNativeCaffeineCache(String name) {
		if (this.cacheLoader != null) {
			return this.cacheBuilder.build(this.cacheLoader);
		}
		else {
			return this.cacheBuilder.build();
		}
	}
@Nonnull
  public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
    requireWeightWithWeigher();
    requireNonLoadingCache();

    @SuppressWarnings("unchecked")
    Caffeine<K1, V1> self = (Caffeine<K1, V1>) this;
    return isBounded() || refreshes()
        ? new BoundedLocalCache.BoundedLocalManualCache<>(self)
        : new UnboundedLocalCache.UnboundedLocalManualCache<>(self);
  }

有些文档指出,不设置cacheLoader,并无此key缓存时,Caffeine将会返回null,而在这里Spring会自动将为您的查询结果做缓存;

注解应用:


import com.github.caffeine.CaffeineCacheable
import spring.data.redis.entity.Person

interface PersonCaffeineCacheRepository: BaseRedisCacheRepository<Person,String> {
    @CaffeineCacheable(cacheNames = ["caffeineForName"], expireTime = 1)
    fun findByName(name: String): Person?
}

为了方便将CaffeineCacheable的cacheNames传递给Cacheable在此定义了同一类型.

测试结果:

我们可以看到第一次查询发送了sql语句,而第二次则直接从缓存中获取. 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值