解决springCache配置中踩的坑

目录
  • springCache配置中踩的坑
    • 先附上正确的配置
  • springCache配置及一些问题的解决
    • 配置
    • @Cacheable参数
    • @CacheEvict 参数
    • @CachePut 参数

springCache配置中踩的坑

项目基于SpringBoot,使用了SpringCache。

早先在网上找了一份SpringCache的配置,后来由于需要使用到自定义序列化方法,注入一个自定义的序列化类。但是在后来发现自定义的序列化类始终没有调用,后来查看源码后终于发现了原因

先附上正确的配置

  @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory, SessionSerializer serializer) {
        logger.debug("生成缓存管理器");
        logger.debug("注入的序列化工具={}", serializer);
        RedisSerializationContext.SerializationPair pair = RedisSerializationContext.SerializationPair.fromSerializer(serializer);
        logger.debug("生成的cache序列化工具={}", pair);
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();  // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
        config = config.entryTtl(Duration.ofMinutes(10))     // 设置缓存的默认过期时间,也是使用Duration设置
                .serializeValuesWith(pair)
//                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .disableCachingNullValues()
        ;     // 不缓存空值
        logger.debug("初始化完成的config={}", config);
        // 设置一个初始化的缓存空间set集合
        Set<String> cacheNames = new HashSet<>();
        cacheNames.add(CACHE_NAME);
        return RedisCacheManager.builder(new CusTtlRedisCacheWriter(factory))     // 使用自定义的缓存配置初始化一个cacheManager
                .cacheDefaults(config)//这一句必须要最先执行,否则实际运行时使用的是defaultConfig
                .initialCacheNames(cacheNames)
//                .withInitialCacheConfigurations(configMap)
//                .transactionAware()
                .build();
    }

重要在于最后一行return的时候,早先的找到的资料说initialCacheNames方法一定要先执行,否则就会巴拉巴拉~~~,,结果就掉坑了

如果initialCacheNames方法先执行的话,实际上CacheManager里使用的是DefaultConfig,里面的序列化方式也就是Jdk序列化,后面在调用cacheDefaults也没有用了。

所有,cacheDetaults方法一定要先执行

springCache配置及一些问题的解决

配置

1. applicationContext.xml

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:cache="http://www.springframework.org/schema/cache"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">

    <cache:annotation-driven />
  <!-- 定义缓存管理 -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                      p:name="default"/>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                      p:name="activityCache"/>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
                      p:name="awardsCache"/>
            </set>
        </property>
    </bean>

Spring内部默认使用 ConcurrentHashMap 来存储, 配置中的 activityCache awardsCache 就是一个一个的 ConcurrentHashMap 对象的名字. 另外 spring还支持使用 EHCache来存储缓存.

2. 在service的实现类上加上 @Cacheable

//@Service
//public class LotteryActivityServiceImpl implements LotteryActivityService
@Cacheable(value = "activityCache", key = "#shortName")
public LotteryActivity findByShortName(String shortName) {
    log.info("query activity : {} from database.", shortName);
    return activityRepository.findByShortName(shortName);
}

@Cacheable参数

value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

在需要清除缓存的方法上加上@CacheEvict

@CacheEvict(value="activityCache", allEntries = true, beforeInvocation = true)
public void cleanActivityCache(String shortName) {
    log.info("cleaned cache activity : {}", shortName);
}

@CacheEvict 参数

value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @CachEvict(value=”testcache”,key=”#userName”
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”)
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如: @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 例如: @CachEvict(value=”testcache”,beforeInvocation=true)

当需要保证方法被调用,又希望结果被缓存, 可以使用@CachePut

@CachePut(value="accountCache",key="#account.getName()")// 更新 accountCache 缓存
 public Account updateAccount(Account account) {
   return updateDB(account);
 } 

@CachePut 参数

value 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

注解最好加在实现类而不是接口的方法上

Spring recommends that you only annotate concrete classes (and methods of concrete classes) with the @Cache* annotation, as opposed to annotating interfaces. You certainly can place the @Cache* annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies. The fact that Java annotations are not inherited from interfaces means that if you are using class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"), then the caching settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a caching proxy, which would be decidedly bad.

带有@Cache* 注解的方法不能被定义在调用该方法的类里, 比如 UserController要调用 findUserByName(String name), 且该方法有 @Cacheabele注解, 那么该方法就不能被定义在 UserController中

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual caching at runtime even if the invoked method is marked with @Cacheable - considering using the aspectj mode in this case.

@Cache*注解要加在 public 方法上

When using proxies, you should apply the @Cache* annotations only to methods with public visibility. If you do annotate protected, private or package-visible methods with these annotations, no error is raised, but the annotated method does not exhibit the configured caching settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods as it changes the bytecode itself.

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • SpringCache 分布式缓存的实现方法(规避redis解锁的问题)

    简介 spring 从3.1 开始定义 org.springframework.cache.Cache org.springframework.cache.CacheManager 来统一不同的缓存技术 并支持使用JCache(JSR-107)注解简化我们的开发 基础概念 实战使用 整合SpringCache简化缓存开发 常用注解 常用注解 说明 @CacheEvict 触发将数据从缓存删除的操作 (失效模式) @CachePut 不影响方法执行更新缓存 @Caching 组合以上多个操作 @C

  • 浅谈SpringCache与redis集成实现缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性,Redis也成为一种流行的数据缓存工具. 在传统方式下对于缓存的处理代码是非常臃肿的. 例如:我们要把一个查询函数加入缓存功能,大致需要三步. 一.在函数执行前,我们需要先检查缓存中是否存在数据,如果存在则返回缓存数据 二.如果不存在,就需要在数据库的数据查询出来. 三.最后把数据存放在缓存中,当

  • Java Spring-Cache key配置注意事项介绍

    为了提升项目的并发性能,考虑引入本地内存Cache,对:外部数据源访问.Restful API调用.可重用的复杂计算 等3种类型的函数处理结果进行缓存.目前采用的是spring Cache的@Cacheable注解方式,缓存具体实现选取的是Guava Cache. 具体缓存的配置此处不再介绍,重点对于key的配置进行说明: 1.基本形式 @Cacheable(value="cacheName", key"#id") public ResultDTO method(i

  • spring缓存cache的使用详解

    目录 spring缓存cache的使用 springcache配置缓存存活时间 spring缓存cache的使用 在spring配置文件中添加schema和spring对缓存注解的支持: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://w

  • 关于shiro中部分SpringCache失效问题的解决方法

    1.问题抛出 今天在做Springboot和shiro集成时,发现一个严重的问题.部分service的缓存和事务失效,debug代码时,发现这些有问题的service实例都不是代理生成的,所以事务和缓存就失效了(事务和缓存依赖代理类实现).继续查问题,发现这些有问题的service全部被shiro的realm所依赖,所以怀疑是shiro影响了 所以做一下测试: shiro中用到的ResourceService public class LocalRealmService extends Real

  • 解决springCache配置中踩的坑

    目录 springCache配置中踩的坑 先附上正确的配置 springCache配置及一些问题的解决 配置 @Cacheable参数 @CacheEvict 参数 @CachePut 参数 springCache配置中踩的坑 项目基于SpringBoot,使用了SpringCache. 早先在网上找了一份SpringCache的配置,后来由于需要使用到自定义序列化方法,注入一个自定义的序列化类.但是在后来发现自定义的序列化类始终没有调用,后来查看源码后终于发现了原因 先附上正确的配置 @Bea

  • 解决线程池中ThreadGroup的坑

    目录 线程池中ThreadGroup的坑 ThreadGroup是否可行 Executors内部类DefaultThreadFactory ThreadGroup的使用及手写线程池 监听线程异常关闭 如何拿到Thread线程中异常 ThreadGroup 线程池使用 线程池中ThreadGroup的坑 在Java中每一个线程都归属于某个线程组管理的一员,例如在主函数main()主工作流程中产生一个线程,则产生的线程属于main这个线程组管理的一员.简单地说,线程组(ThreadGroup)就是由

  • 解决spring jpa中update的坑

    spring jpa中update遇到的坑 使用jpa 自己编写update语句, 遇到问题: 1.在同一个service事物中,先执行保存,在执行更新,紧接着执行查询--查询结果为更新前的结果. 2.执行自定义update方法结束后执行查询查出结果依然为update前的结果集 解决问题所在: 自定义update并未清空实体缓存.注解@Modifying加参数eg:@Modifying(clearAutomatically = true) jpa更新问题记录 使用jpa 去更新: @Modify

  • Golang时间处理中容易踩的坑分析解决

    目录 简介 类型 时区 小心有坑 时间解析的使用场景 时间操作 获取当前时间 时区设置 时间格式化(时间类型转字符串) 时间类型转时间戳 时间戳转时间类型 时间字符串转时间类型 时间计算 获取时间类型具体内容 时间加减 时间间隔(耗时) 时间取整(向上取整向下取整) 拓展 json时间转换 简介 在各个语言之中都有时间类型的处理,因为这个地球是圆的(我仿佛在讲废话),有多个时区,每个时区的时间不一样,在程序中有必要存在一种方式,或者说一种类型存储时间,还可以通过一系列的方法转换成不同国家的时间.

  • 浅谈在django中使用filter()(即对QuerySet操作)时踩的坑

    代码伺候: 先看如下代码: 例1: message = Message.objects.filter(pk=message_id2) message[0].id = message_id2 message[0].content = content2 message[0].message_type = message_type2 print(message[0].id) print(message[0].content) message[0].save() 可正常从QuerySet中读取数据,并打

  • 解决Vue大括号字符换行踩的坑

    最近遇到这样一个问题,在页面上要显示一段自定义的文本,文本如果较长的话需要换行显示. 在HTML中可以通过<br/>标签换行,也可以通过\n转义字符换行 在Vue中用大括号显示的变量,加<br/>会直接被当成字符串显示出来,加\n显示一个空格,加\r\n显示两个空格,因为{{}}}是文本插值,内部都会按照普通字符串进行处理,可以用v-html进行显示 如下显示name变量 data() { return{ name: '前一部分<br/>后一部分' } } 在元素上显示

  • springboot2.x引入feign踩的坑及解决

    目录 springboot2.x引入feign踩的坑 一.需求 二.什么是feign 三.springboot1.x中feign的使用 四.springboot2.x中feign的使用 feign调用方式比较 一.事发原因 二.方式1介绍 三.方式2介绍 四.调用结果测试 五.两种方式对比 六.小结一下 springboot2.x引入feign踩的坑 一.需求 最近公司项目需求,需要调用第三方服务的接口,所以选用了feign来实现(这里只说springboot2.x的pom引用,没有怎么使用,网

  • 简单聊聊Go for range中容易踩的坑

    目录 前言 1. for+传值 2. for+传址 3.for+闭包 4. for+goroutine 总结 前言 为了让大家更好的理解本期知识点,先介绍以下几个知识点:线性结构.非线性结构.循环.迭代.遍历.递归. 线性结构:数组.队列 非线性结构:树.图 循环(loop):最基础的概念,所有重复的行为都是循环 递归(recursion):在函数内调用自身,将复杂情况逐步转化成基本情况 (数学)迭代(iterate):在多次循环中逐步接近结果 (编程)迭代(iterate):按顺序访问线性结构

  • SpringMVC配置404踩坑记录

    目录 前言 错误 原因 总结和教训 前言 在学习SpringMVC的路上真是开门就遇见拦路虎了,在配置的时候一直是按照硅谷的视频配置的.但是跑起来就一直是404,这个就很搞人心态了.在配置环境时是在晚上11:00然后配置到11:30,而找错误就十分的痛苦了,从当天晚上的12.00一直到半夜1.30还没找出来,第二天又是卡了一天,直到快到第三天的前一个小时可算让我找出来了. 首先在这里先感谢夜星大佬和杨总.夜星大佬远程帮我找错误,杨总帮我提意见!在此90°的鞠躬.同时也希望本篇博客能够帮助更多的小

  • QueryWrapper中查询的坑及解决

    目录 QueryWrapper中查询的坑 首先,先看代码示例 QueryWrapper的查询方法 QueryWrapper QueryWrapper中查询的坑 首先,先看代码示例 List<Entity> list = testService.list(                     new LambdaQueryWrapper<Entity>()                             .eq(ObjectUtils.isNotEmpty(req.get

随机推荐