写在前面
- 同类文章非常多了,而且也不是什么新功能,写它目的仅仅是记录
cache
的使用过程,这里我只介绍我用到的,更全的介绍请自行百度。 - 我是使用
redis
作为缓存工具而非应用程序内存
一、准备工作
redis
服务springBoot
框架- 这里我使用的是
kotlin
,使用java
也是一样的
二、配置redis
缓存
- 需要的
maven
坐标
<!--springboot的redis操作 starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--阿里巴巴的 fastjson,用来序列化存储(非必须)-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
- 缓存的数据结构(故意放到前面说,方便理解后面的配置)
- 缓存由
缓存名、缓存key、缓存值
3部分组成 - 对应理解为
一张数据表、一条数据的主键+索引、实际的数据内容
- 缓存由
-
使用
springBoot
的方式正常配置redis
-
创建一个
redis
的配置器
RedisCacheConfig
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer
import com.ayouran.crawler.common.bo.DetectionBO
import com.ayouran.crawler.common.bo.MilkBO
import com.ayouran.crawler.common.vo.FilterConditionVO
import com.ayouran.crawler.common.vo.MilkDetailsVO
import org.springframework.cache.annotation.CachingConfigurerSupport
import org.springframework.cache.annotation.EnableCaching
import org.springframework.cache.interceptor.KeyGenerator
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Primary
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 org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
import org.springframework.data.redis.serializer.RedisSerializer
import java.lang.reflect.Method
import java.net.UnknownHostException
import java.time.Duration
/**
* Redis 缓存配置类
*/
@Configuration
@EnableCaching
class RedisCacheConfig : CachingConfigurerSupport() {
/**
* 缓存对象集合中,缓存是以 key-value 形式保存的。
* 当不指定缓存的 key 时,SpringBoot 会使用 SimpleKeyGenerator 生成 key。
*/
@Bean
fun wiselyKeyGenerator(): KeyGenerator { // key前缀,用于区分不同项目的缓存,建议每个项目单独设置
val PRE_KEY = "milk_cache"
val sp = ':'
return KeyGenerator { target: Any, method: Method, params: Array<Any> ->
val sb = StringBuilder()
sb.append(PRE_KEY)
sb.append(sp)
sb.append(target.javaClass.simpleName)
sb.append(sp)
sb.append(method.name)
for (obj in params) {
if (null == obj) continue
sb.append(sp)
sb.append(obj.toString())
}
sb.toString()
}
}
/**
* 定义 spring-boot-data-redis的 redisTemplate对象 主要是序列化方式
*/
@Bean
@Throws(UnknownHostException::class)
fun redisTemplate(
redisConnectionFactory: RedisConnectionFactory): RedisTemplate<Any, Any> {
val template = RedisTemplate<Any, Any>()
template.connectionFactory = redisConnectionFactory
//fastjson的序列化器
//FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);
val serializer: Jackson2JsonRedisSerializer<*> = Jackson2JsonRedisSerializer<Any>(Any::class.java)
//设置序列化器
template.defaultSerializer = serializer
return template
}
/**
* 定义 spring-boot-data-redis的 stringRedisTemplate对象 主要是序列化方式
*/
@Bean
@Throws(UnknownHostException::class)
fun stringRedisTemplate(
redisConnectionFactory: RedisConnectionFactory): StringRedisTemplate {
val template = StringRedisTemplate()
//fastjson的序列化器
val serializer: FastJsonRedisSerializer<*> = FastJsonRedisSerializer(Any::class.java)
//Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
template.valueSerializer = serializer
//设置序列化器
template.connectionFactory = redisConnectionFactory
return template
}
/**
* 启用@Cacheable等注解时,redis里面用到的key--value的序列化
* key = new StringRedisSerializer()
* value = new JdkSerializationRedisSerializer()
* 以及缓存的时效
*/
fun redisCacheConfiguration(serializer: RedisSerializer<*>?, duration: Duration): RedisCacheConfiguration {
var configuration = RedisCacheConfiguration.defaultCacheConfig()
if (null != serializer) {
configuration = configuration.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer<Any>(serializer as RedisSerializer<Any>))
}
configuration = configuration.computePrefixWith { cacheName: String -> "milk:$cacheName:" }
configuration = configuration.entryTtl(duration)//设置缓存的时效
return configuration
}
/**
* @Primary 配置默认的缓存管理器
* valueSerializationPair:使用Jackson2JsonRedisSerializer()
* 使用方式:如果不指定cacheManagers属性,就会使用默认的CacheManager
* @Cacheable(value = "cache_1_minutes",keyGenerator = "myKeyGenerator")
* @param redisConnectionFactory
*/
@Primary
@Bean
fun defaultCacheManager(redisConnectionFactory: RedisConnectionFactory): RedisCacheManager {
val serializer: Jackson2JsonRedisSerializer<*> = Jackson2JsonRedisSerializer<Any>(Any::class.java)
val builder = RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration(serializer, Duration.ofDays(30)))
//可以抽取的公共配置
val map = mutableMapOf<String, RedisCacheConfiguration>()
// map["cache_1_minutes"] = redisCacheConfiguration(serializer, Duration.ofMinutes(1))
// map["cache_10_minutes"] = redisCacheConfiguration(serializer, Duration.ofMinutes(10))
// map["cache_1_hour"] = redisCacheConfiguration(serializer, Duration.ofHours(1))
// map["cache_10_hour"] = redisCacheConfiguration(serializer, Duration.ofHours(10))
// map["cache_1_day"] = redisCacheConfiguration(serializer, Duration.ofDays(1))
// map["cache_7_days"] = redisCacheConfiguration(serializer, Duration.ofDays(7))
map["milk:best:milk_cache:MilkServiceImpl:best"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:best:milk_cache:MilkServiceImpl:query"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:best:milk_cache:MilkServiceImpl:queryContrastOptions"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:hotWords:milk_cache:UserServiceImpl:hotWords"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:hotWords:milk_cache:UserServiceImpl:nutritions"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
builder.withInitialCacheConfigurations(map)
return builder.build()
}
/**
* 为PageResult配置一个单独的RedisCacheManager
* 同理,如果Department也想把数据以json的形式放入缓存,再添加一个RedisCacheManager即可
*
*
* 使用方式:@Cacheable(value = "cache_1_minutes",keyGenerator = "myKeyGenerator",cacheManager = "pageResultCacheManager")
* @param redisConnectionFactory
* @return
*/
@Bean
fun pageResultCacheManager(redisConnectionFactory: RedisConnectionFactory): RedisCacheManager { //employee的使用FastJSON的序列化器
val serializer: FastJsonRedisSerializer<*> = FastJsonRedisSerializer(PageResult::class.java)
val builder = RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(redisCacheConfiguration(serializer, Duration.ofDays(30)))
val map = mutableMapOf<String, RedisCacheConfiguration>()
// map["cache_1_minutes"] = redisCacheConfiguration(serializer, Duration.ofMinutes(1))
// map["cache_10_minutes"] = redisCacheConfiguration(serializer, Duration.ofMinutes(10))
// map["cache_1_hour"] = redisCacheConfiguration(serializer, Duration.ofHours(1))
// map["cache_10_hour"] = redisCacheConfiguration(serializer, Duration.ofHours(10))
// map["cache_7_days"] = redisCacheConfiguration(serializer, Duration.ofDays(7))
map["milk:best:milk_cache:MilkServiceImpl:pageEvaluate"] = redisCacheConfiguration(serializer, Duration.ofDays(1))
map["milk:best:milk_cache:MilkServiceImpl:homeSearch"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:best:milk_cache:MilkServiceImpl:search"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
map["milk:hotWords:milk_cache:UserServiceImpl:queryCollection"] = redisCacheConfiguration(serializer, Duration.ofDays(30))
builder.withInitialCacheConfigurations(map)
return builder.build()
}
- 解释一下代码
- 类上的
@Configuration @EnableCaching
这两个注解是必须的 - wiselyKeyGenerator() 用于创建一个默认的
key
生成器,会在redis
存储``key```上体现。 - redisTemplate()、stringRedisTemplate() 用于创建spring-boot-data-redis的默认操作对象模板,主要是指定了序列化方式。
- redisCacheConfiguration() 将
cache
的具体操作抽象成一个方法,其中configuration = configuration.computePrefixWith { cacheName: String -> "milk:$cacheName:" }
仅仅是设置前缀,可以不需要。 - defaultCacheManager() 这是一个默认的缓存管理器,主要是适用于于实现了
Collection<E>
的方法出参(即这个方法的返回参数是一个Collection<E>
实现类 ),原因在于本方法使用的Jackson2JsonRedisSerializer
默认class
是一个Object
对象。 - pageResultCacheManager() 单独为分页方法返回
pageResult
对象配置的一个缓存管理器,主要是是使用了FastJsonRedisSerializer
来指定class
是一个PageResult
对象。 - 如果你有非常多的自定义
Object
对象返回,建议抽象成一个base
对象,那就只需要写一个缓存管理器,或者像我样,每出现一个新的Object
对象返回就定义一个新的缓存管理器 - 需要注意缓存管理器中通过缓存名做了不同的缓存过期时间设置,这个缓存名需要根据你定义的来
- 类上的
三、使用缓存
- 在要使用缓存的类上加上
@CacheConfig(keyGenerator = "wiselyKeyGenerator")
配置默认的缓存键生成方式(即整个类都会默认应用这个生成方式),这个不是必须的,也可以在缓存的地方手动指定 - 下面是一个缓存使用的示例代码
@Cacheable(value = ["pageEvaluate"], key = "#pageRequest.toString()", cacheManager = "pageResultCacheManager")
override fun pageEvaluate(pageRequest: com.ayouran.crawler.common.PageRequest): PageResult<ScoreVO> {
val nmilk = nmilkRepository.findByMilkNo(pageRequest.query!!)
val qTmpUser = QTmpUser.tmpUser
val qScoreRecord = QScoreRecord.scoreRecord
val rredicate = qScoreRecord.brand.eq(nmilk.brand)
.and(qScoreRecord.series.eq(nmilk.series))
.and(qScoreRecord.edition.eq(nmilk.edition))
val order = if (null != pageRequest.sort) qScoreRecord.createAt.desc() else qScoreRecord.praise.desc()
val exprs = arrayOf<Expression<*>>(qTmpUser.head, qTmpUser.nick, qScoreRecord.remark, qScoreRecord.score,
qScoreRecord.praise, qScoreRecord.tags, qScoreRecord.id, qScoreRecord.createAt)
val results = querydslUtlis.getQueryFactory()
.select(*exprs)
.from(qScoreRecord)
.leftJoin(qTmpUser)
.on(qTmpUser.userNo.eq(qScoreRecord.userNo))
.where(ExpressionUtils.allOf(rredicate))
.orderBy(order)
.offset((pageRequest.getPageIndex()) * pageRequest.getPageSize())
.limit(pageRequest.getPageSize())
.fetchResults()
return PageUtlis.retPage(results, querydslUtlis.getList(results.results, exprs, ScoreVO::class.java))
}
- 代码说明
@Cacheable
表示使用缓存注解,会先根据注解的配置来生成redis 缓存键
通过序列化器来尝试获取缓存数据,如果没有命中,就会执行方法体,当方法体正常返回时,将返回数据缓存到redis
。@Cacheable#value = ["pageEvaluate"]
通过这个指定缓存的名称,相当于一个表名。@Cacheable#key = "#pageRequest.toString()"
通过这个自己指定缓存的值,相当于数据表中的索引&主键
,如果不指定这个key
属性,就会使用当前类上指定的@CacheConfig(keyGenerator = "wiselyKeyGenerator")
。@Cacheable#cacheManager = "pageResultCacheManager")
指定缓存管理器,这里就是上面一步针对返回不同的Object
对象定制的缓存管理器。
评论区