0


SpringCache

Spring Cache简介

Spring Cache 是 Spring 提供的一整套的缓存解决方案。虽然它本身并没有提供缓存的实现,但是它提供了一整套的接口和代码规范、配置、注解等,这样它就可以整合各种缓存方案了,比如 Redis、Ehcache,我们也就不用关心操作缓存的细节。

Cache接口它包含了缓存的各种操作方式,同时还提供了各种xxxCache缓存的实现,比如 RedisCache 针对Redis,EhCacheCache 针对 EhCache,ConcurrentMapCache 针对 ConCurrentMap。

Spring Cache使用效果

每次调用某方法,而此方法又是带有缓存功能时,Spring 框架就会检查

  1. 指定参数

的那个方法是否已经被调用过,如果之前调用过,就从缓存中取之前调用的结果;如果没有调用过,则再调用一次这个方法,并缓存结果,然后再返回结果,那下次调用这个方法时,就可以直接从缓存中获取结果了。

Spring Cache自定义CacheManager配置和过期时间

  1. package com.huing.blog.config;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  4. import com.fasterxml.jackson.databind.MapperFeature;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import com.fasterxml.jackson.databind.SerializationFeature;
  7. import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
  8. import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  9. import org.springframework.cache.interceptor.KeyGenerator;
  10. import org.springframework.context.annotation.Bean;
  11. import org.springframework.context.annotation.Configuration;
  12. import org.springframework.context.annotation.Primary;
  13. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  14. import org.springframework.data.redis.cache.RedisCacheManager;
  15. import org.springframework.data.redis.connection.RedisConnectionFactory;
  16. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  17. import org.springframework.data.redis.serializer.RedisSerializationContext;
  18. import org.springframework.util.StringUtils;
  19. import java.lang.reflect.Method;
  20. import java.time.Duration;
  21. /**
  22. * Cache注解类,配置了过期时间和序列化
  23. *
  24. * @Author huing
  25. * @Create 2022-07-15 15:42
  26. */
  27. @Configuration
  28. public class CacheConfig {
  29. @Bean
  30. public RedisCacheManager cacheManager1Minute(RedisConnectionFactory connectionFactory){
  31. RedisCacheConfiguration config = instanceConig(60L);
  32. return RedisCacheManager.builder(connectionFactory)
  33. .cacheDefaults(config)
  34. .transactionAware()
  35. .build();
  36. }
  37. @Bean
  38. @Primary // 默认的,没有指定采用默认的
  39. public RedisCacheManager cacheManager1Hour(RedisConnectionFactory connectionFactory){
  40. RedisCacheConfiguration config = instanceConig(3600L);
  41. return RedisCacheManager.builder(connectionFactory)
  42. .cacheDefaults(config)
  43. .transactionAware()
  44. .build();
  45. }
  46. @Bean
  47. public RedisCacheManager cacheManager1Day(RedisConnectionFactory connectionFactory){
  48. RedisCacheConfiguration config = instanceConig(3600 * 24L);
  49. return RedisCacheManager.builder(connectionFactory)
  50. .cacheDefaults(config)
  51. .transactionAware()
  52. .build();
  53. }
  54. private RedisCacheConfiguration instanceConig(long ttl) {
  55. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
  56. ObjectMapper objectMapper = new ObjectMapper();
  57. objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  58. objectMapper.registerModule(new JavaTimeModule());
  59. //去掉各种@JsonSerialize注解的解析
  60. objectMapper.configure(MapperFeature.USE_ANNOTATIONS,false);
  61. //只针对非空的值进行序列化
  62. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  63. //将类型序列化到属性json字符串中
  64. objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
  65. ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
  66. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  67. return RedisCacheConfiguration.defaultCacheConfig()
  68. .entryTtl(Duration.ofSeconds(ttl))
  69. .disableCachingNullValues() //禁止缓存null的值
  70. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
  71. }
  72. }

Spring Cache自定义KeyGenerator

CacheConfig:

  1. /**
  2. * 自定义缓存Key规则
  3. * @return
  4. */
  5. @Bean
  6. public KeyGenerator springCacheCustomKeyGenerator(){
  7. return new KeyGenerator() {
  8. @Override
  9. public Object generate(Object o, Method method, Object... objects) {
  10. String key = o.getClass().getSimpleName() + "_" + method.getName() + "_" + StringUtils.arrayToDelimitedString(objects,"_");
  11. System.out.println(key);
  12. return key;
  13. }
  14. };
  15. }

缓存注解

注解描述@Cacheable在调用方法前,首先去缓存中找方法的返回值,如果能找到,则返回缓存的值,否则就执行这个方法,并将返回值放到缓存中。@CachePut在方法调用前不会去缓存中找,无论如何都会执行方法,执行后将缓存数据放入缓存中。@CacheEvict清理缓存中的一个或多个记录@Caching能够同时应用多个缓存注解@CacheConfig在类级别共享相同的缓存的配置
其核心主要是@Cacheable和@CacheEvict。使用@Cacheable标记的方法在执行后,Spring Cache将缓存其返回结果,而使用@CacheEvict标记的方法会在方法执行前或者执行后移除Spring Cache中的某些元素。

Cacheable注解

  • 标记在一个方法上,也可以标记在一个类上
  • 缓存标注对象的返回结果,标注在方法上缓存该方法的返回值,标注在类上缓存该类所有方法的返回值
  • value缓存名称,可以有多个
  • key缓存的key规则,可以使用springEL表达式,默认是方法参数组合
  • condition缓存条件,使用springEL编写,返回true才缓存

需要注意的是:一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的

示例:

  1. /**
  2. * 最新文章
  3. *
  4. * @return
  5. */
  6. @PostMapping("new")
  7. @Cacheable(value = {"newArticle"}, key = "#root.methodName",cacheManager = "cacheManager1Minute")
  8. public Result newArticle() {
  9. return articleService.newArticle(limit);
  10. }

其中使用root对象来生成key:
属性名称描述示例methodName当前方法名#root.methodNamemethod当前方法#root.method.nametargett当前被调用的对象#root.targettargetClass当前被调用的对象的class#root.targetClassargs当前方法参数组成的数组#root.args[0]caches当前被调用的方法使用的Cache#root.caches[0].name
使用root对象属性作为key时可以省略“#root”

CachePut注解

  • 根据方法的请求参数对其结果进行缓存,每次都会触发真实方法的调用
  • value缓存名称,可以有多个
  • key缓存的key规则,可以使用springEL表达式,默认是方法参数组合
  • condition缓存条件,使用springEL编写,返回true才缓存

与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

可以标注在类上和方法上。使用@CachePut时我们可以指定的属性跟@Cacheable是一样的。

CacheEvict注解

  • 从缓存中移除相应数据,触发缓存删除的操作
  • value缓存名称,可以有多个
  • key缓存的key规则,可以使用springEL表达式,默认是方法参数组合
  • beforeinvocation = false(默认) - 缓存的清除是否在方法执行之前执行,默认代表缓存清除操作是在方法执行之后执行;- 如果出现异常缓存就不会清除
  • beforeinvocation = true - 代表清除缓存操作是在方法之前执行,无论方法是否出现异常,缓存都会清除
  • condition清除缓存条件,使用springEL编写,返回true才缓存
  • allEntries = false(默认) - 清除指定key的缓存
  • allEntries = true - 忽略key,清除value缓存中的所有元素

示例:

  1. /**
  2. * 写文章
  3. *
  4. * @param articleParm
  5. * @return
  6. */
  7. @PostMapping("publish")
  8. @CacheEvict(value = {"ArticleById"},key = "#articleParm.id",condition = "#articleParm.id != null") //如果文章id不为空(即编辑时),删除文章对应缓存缓存
  9. public Result publish(@RequestBody ArticleParm articleParm) {
  10. return articleService.publish(articleParm);
  11. }

当articleParm.id != null时,删除缓存名为ArticleById中的key为"#articleParm.id"的元素。

Caching注解

  • 组合多个Cache注解使用
  • 允许同一方法上使用多个嵌套的@Cacheable、@CachePut和@CacheEvict注解
  • 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和@CacheEvict

示例:

  1. /**
  2. * 写文章
  3. *
  4. * @param articleParm
  5. * @return
  6. */
  7. @PostMapping("publish")
  8. @Caching(
  9. evict = {
  10. @CacheEvict(value = {"ArticleById"},key = "#articleParm.id",condition = "#articleParm.id != null"), //如果文章id不为空(即编辑时),删除文章对应缓存缓存
  11. @CacheEvict(value = "ArticleList",allEntries = true) //删除ArticleList下的所有缓存
  12. }
  13. )
  14. public Result publish(@RequestBody ArticleParm articleParm) {
  15. return articleService.publish(articleParm);
  16. }

在线博客系统使用SpringCache实现缓存和缓存更新

实现效果

在博客项目中,使用SpringCache,首先我们要实现的效果是:

对文章列表、文章详情和最新文章的接口进行缓存处理,同时,当有新文章发布或者文章被作者编辑后,对相应的缓存内容进行更新。

pop依赖

  1. <!-- cache-->
  2. <dependency>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-cache</artifactId>
  5. </dependency>

application.properties配置文件

  1. #cache 配置文件指定缓存类型
  2. spring.cache.type=redis

启动类开启缓存注解

  1. package com.huing.blog;
  2. import org.springframework.boot.SpringApplication;
  3. import org.springframework.boot.autoconfigure.SpringBootApplication;
  4. import org.springframework.cache.annotation.EnableCaching;
  5. /**
  6. * @author huing
  7. * @create 2022-07-03 10:01
  8. */
  9. @SpringBootApplication
  10. @EnableCaching
  11. public class BlogApp {
  12. public static void main(String[] args) {
  13. SpringApplication.run(BlogApp.class,args);
  14. }
  15. }

CacheConfig配置文件

  1. package com.huing.blog.config;
  2. import com.fasterxml.jackson.annotation.JsonInclude;
  3. import com.fasterxml.jackson.annotation.JsonTypeInfo;
  4. import com.fasterxml.jackson.databind.MapperFeature;
  5. import com.fasterxml.jackson.databind.ObjectMapper;
  6. import com.fasterxml.jackson.databind.SerializationFeature;
  7. import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
  8. import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.context.annotation.Configuration;
  11. import org.springframework.context.annotation.Primary;
  12. import org.springframework.data.redis.cache.RedisCacheConfiguration;
  13. import org.springframework.data.redis.cache.RedisCacheManager;
  14. import org.springframework.data.redis.connection.RedisConnectionFactory;
  15. import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
  16. import org.springframework.data.redis.serializer.RedisSerializationContext;
  17. import java.time.Duration;
  18. /**
  19. * Cache注解类,配置了过期时间和序列化
  20. *
  21. * @Author huing
  22. * @Create 2022-07-15 15:42
  23. */
  24. @Configuration
  25. public class CacheConfig {
  26. @Bean
  27. public RedisCacheManager cacheManager1Minute(RedisConnectionFactory connectionFactory){
  28. RedisCacheConfiguration config = instanceConig(60L);
  29. return RedisCacheManager.builder(connectionFactory)
  30. .cacheDefaults(config)
  31. .transactionAware()
  32. .build();
  33. }
  34. @Bean
  35. @Primary // 默认的,没有指定采用默认的
  36. public RedisCacheManager cacheManager1Hour(RedisConnectionFactory connectionFactory){
  37. RedisCacheConfiguration config = instanceConig(3600L);
  38. return RedisCacheManager.builder(connectionFactory)
  39. .cacheDefaults(config)
  40. .transactionAware()
  41. .build();
  42. }
  43. @Bean
  44. public RedisCacheManager cacheManager1Day(RedisConnectionFactory connectionFactory){
  45. RedisCacheConfiguration config = instanceConig(3600 * 24L);
  46. return RedisCacheManager.builder(connectionFactory)
  47. .cacheDefaults(config)
  48. .transactionAware()
  49. .build();
  50. }
  51. private RedisCacheConfiguration instanceConig(long ttl) {
  52. Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
  53. ObjectMapper objectMapper = new ObjectMapper();
  54. objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
  55. objectMapper.registerModule(new JavaTimeModule());
  56. //去掉各种@JsonSerialize注解的解析
  57. objectMapper.configure(MapperFeature.USE_ANNOTATIONS,false);
  58. //只针对非空的值进行序列化
  59. objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
  60. //将类型序列化到属性json字符串中
  61. objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
  62. ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
  63. jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
  64. return RedisCacheConfiguration.defaultCacheConfig()
  65. .entryTtl(Duration.ofSeconds(ttl))
  66. .disableCachingNullValues() //禁止缓存null的值
  67. .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
  68. }
  69. }

缓存注解实现

对ArticleController控制层进行操作示例

文章列表缓存

  1. /**
  2. * 首页,文章处理
  3. *
  4. * @param pageParams
  5. * @return
  6. */
  7. @PostMapping()
  8. //加上此注解,代表对此接口记录日志
  9. @LogAnnotation(module = "文章", operation = "获取文章列表")
  10. //对文章列表进行缓存,key为列表的页码数,使用默认的过期时间(一小时)
  11. @Cacheable(value = {"ArticleList"}, key = "#pageParams.page")
  12. public Result<List<ArticleVo>> listArticle(@RequestBody PageParams pageParams) {
  13. return articleService.listArticle(pageParams);
  14. }

最新文章缓存

  1. /**
  2. * 最新文章
  3. *
  4. * @return
  5. */
  6. @PostMapping("new")
  7. @Cacheable(value = {"newArticle"}, key = "#root.methodName",cacheManager = "cacheManager1Minute")
  8. public Result newArticle() {
  9. return articleService.newArticle(limit);
  10. }

文章详情缓存

  1. /**
  2. * 根据文章id查询文章详情
  3. *
  4. * @param articleId
  5. * @return
  6. */
  7. @PostMapping("view/{id}")
  8. @Cacheable(value = {"ArticleById"}, key = "#articleId")
  9. public Result findArticleById(@PathVariable("id") Long articleId) {
  10. return articleService.findArticleById(articleId);
  11. }

缓存更新

当对缓存内容进行变更时,删除对应的缓存,实现缓存更新

  1. /**
  2. * 写文章
  3. *
  4. * @param articleParm
  5. * @return
  6. */
  7. // @RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
  8. // 而最常用的使用请求体传参的无疑是POST请求了,所以使用@RequestBody接收数据时,一般都用POST方式进行提交。
  9. @PostMapping("publish")
  10. // @CacheEvict(value = {"ArticleById"},key = "#articleParm.id",condition = "#articleParm.id != null") //如果文章id不为空(即编辑时),删除文章对应缓存缓存
  11. @Caching(
  12. evict = {
  13. @CacheEvict(value = {"ArticleById"},key = "#articleParm.id",condition = "#articleParm.id != null"), //如果文章id不为空(即编辑时),删除文章对应缓存缓存
  14. @CacheEvict(value = "ArticleList",allEntries = true) //删除ArticleList下的所有缓存
  15. }
  16. )
  17. public Result publish(@RequestBody ArticleParm articleParm) {
  18. return articleService.publish(articleParm);
  19. }

这样我们就实现了:

对文章列表、文章详情和最新文章的接口进行缓存处理,同时,当有新文章发布或者文章被作者编辑后,对相应的缓存内容进行更新。

标签: java spring redis

本文转载自: https://blog.csdn.net/qq_56851614/article/details/125822041
版权归原作者 hxung 所有, 如有侵权,请联系我们删除。

“SpringCache”的评论:

还没有评论