使用Spring Cache设置缓存条件操作

目录
  • Spring Cache设置缓存条件
    • 原理
    • @Cacheable的常用属性及说明
    • Root对象
    • @CachePut的常用属性同@Cacheable
  • Cache缓存配置
    • 1、pom.xml
    • 2、Ehcache配置文件
    • 3、配置类
    • 4、示例

Spring Cache设置缓存条件

原理

从Spring3.1开始,Spring框架提供了对Cache的支持,提供了一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的作用。

提供的主要注解有@Cacheable、@CachePut、@CacheEvict和@Caching,具体见下表:

注解 说明
@Cacheable 可以标注在类或方法上:标注在方法上表示该方法支持数据缓存;标在类上表示该类的所有方法都支持数据缓存。 具体功能:在执行方法体之前,检查缓存中是否有相同key值的缓存存在,如果存在对应的缓存,直接返回缓存中的值;如果不存在对应的缓存,则执行相应的方法体获取数据,并将数据存储到缓存中。
@CachePut 可以标注在类或方法上,表示支持数据缓存。 具体功能:在方法执行前不会检查缓存中是否存在相应的缓存,而是每次都会执行方法体,并将方法执行结果存储到缓存中,如果相应key值的缓存存在,则更新key对应的value值。
@CacheEvict 可以标注在类或方法上,用于清除相应key值的缓存。
@Caching 可以标注在类或方法上,它有三个属性cacheable、put、evict分别用于指定@Cacheable、@CachePut和@CacheEvict

当需要在类上或方法上同时使用多个注解时,可以使用@Caching,如:

@Caching(cacheable=@Cacheable("User"), evict = {@CacheEvict("Member"), @CacheEvict(value = "Customer", allEntries = true)})

@Cacheable的常用属性及说明

如下表所示:

@Cacheable属性 说明
key 表示缓存的名称,必须指定且至少要有一个值,比如:@Cacheable(value=“Dept”)或@Cacheable(value={“Dept”,“Depts”})
condition 表示是否需要缓存,默认为空,表示所有情况都会缓存。通过SpEL表达式来指定,若condition的值为true则会缓存,若为false则不会缓存,如@Cacheable(value=“Dept”,key="‘deptno_'+# deptno “,condition=”#deptno<=40")
value 表示缓存的key,支持SpEL表达式,如@Cacheable(value=“Dept”,key="‘deptno_' +#deptno"),可以不指定值,如果不指定,则缺省按照方法的所有参数进行组合。除了上述使用方法参数作为key之外,Spring还提供了一个root对象用来生成key,使用方法如下表所示,其中"#root"可以省略。

Root对象

Root对象 说明
methodName 当前方法名,比如#root.methodName
method 当前方法,比如#root.method.name
target 当前被调用的对象,比如#root.target
targetClass 当前被调用的对象的class,比如#root.targetClass
args 当前方法参数组成的数组,比如#root.args[0]
caches 当前被调用的方法使用的缓存,比如#root.caches[0].name

@CachePut的常用属性同@Cacheable

@CacheEvict的常用属性如下表所示:

@CacheEvict属性 说明
value 表示要清除的缓存名
key 表示需要清除的缓存key值,
condition 当condition的值为true时才清除缓存
allEntries 表示是否需要清除缓存中的所有元素。默认为false,表示不需要,当指定了allEntries为true时,将忽略指定的key。
beforeInvocation 清除操作默认是在方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当该属性值为true时,会在调用该方法之前清除缓存中的指定元素。

示例:设置当 dname 的长度大于3时才缓存

//条件缓存
@ResponseBody
@GetMapping("/getLocByDname")
@Cacheable(cacheNames = "dept", key = "#dname", condition = "#dname.length()>3")
public String getLocByDname(@RequestParam("dname") String dname) {//key动态参数
    QueryWrapper<Dept> queryMapper = new QueryWrapper<>();
    queryMapper.eq("dname", dname);
    Dept dept = deptService.getOne(queryMapper);
    return dept.getLoc();
}

示例:unless 即条件不成立时缓存

#result 代表返回值,意思是当返回码不等于 200 时不缓存,也就是等于 200 时才缓存。

@ResponseBody
@GetMapping("/getDeptByDname")
@Cacheable(cacheNames = "dept", key = "#dname", unless = "#result.code != 200")
public Result<Dept> getDeptByDname(@RequestParam("dname") String dname){//key动态参数
    QueryWrapper<Dept> queryMapper = new QueryWrapper<>();
    queryMapper.eq("dname", dname);
    Dept dept = deptService.getOne(queryMapper);
    if (dept == null)
        return ResultUtil.error(120, "dept is null");
    else
        return ResultUtil.success(dept);
}

Cache缓存配置

1、pom.xml

<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- 反射工具类用于手动扫描指定包下的注解,根据defaultCache模块增加ehcache缓存域(非Spring Cache必须)-->
<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
<dependency>
    <groupId>org.reflections</groupId>
    <artifactId>reflections</artifactId>
    <version>0.9.11</version>
</dependency>

2、Ehcache配置文件

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!-- 磁盘缓存位置 -->
    <diskStore path="java.io.tmpdir" />
    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
        maxElementsOnDisk:硬盘最大缓存个数。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
    -->
    <!-- 默认缓存 -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="200000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="600"
            memoryStoreEvictionPolicy="LRU" />
</ehcache>

3、配置类

@Configuration
@EnableCaching
public class CustomConfiguration {
    /**
     * @see org.springframework.cache.interceptor.SimpleKeyGenerator
     * Generate a key based on the specified parameters.
     */
    public static Object generateKey(Object... params) {
        if (params.length == 0) {
            return SimpleKey.EMPTY;
        }
        if (params.length == 1) {
            Object param = params[0];
            if (param != null && !param.getClass().isArray()) {
                return param;
            }
        }
        return new SimpleKey(params);
    }
/**
 * 若将target作为key的一部分时,CGLIB动态代理可能导致重复缓存
 * 注意:返回的key一定要重写hashCode()和toString(),防止key对象不一致导致的缓存无法命中
 * 例如:ehcache 底层存储net.sf.ehcache.store.chm.SelectableConcurrentHashMap#containsKey
 */
    @Bean
    public KeyGenerator customKeyGenerator(){
        return (target, method, params) -> {
            final Object key = generateKey(params);
            StringBuffer buffer = new StringBuffer();
            buffer.append(method.getName());
            buffer.append("::");
            buffer.append(key.toString());
// 注意一定要转为String,否则ehcache key对象可能不一样,导致缓存无法命中
            return buffer.toString();
        };
    }
    /**
     * redis缓存管理器
     */
    @Bean
    @ConditionalOnBean(RedisConfiguration.class)
    @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "redis",
            matchIfMissing = false)
    public CacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
                .entryTtl(Duration.ofMinutes(10));
        return RedisCacheManager
                .builder(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory))
                .cacheDefaults(config).build();
    }
/**
 * ehcache缓存管理器(默认)
 * default XML files {@link net.sf.ehcache.config.ConfigurationFactory#parseConfiguration()}
 */
    @Bean
    @ConditionalOnProperty(prefix = "spring.cache", name = "type", havingValue = "ehcache",
            matchIfMissing = true)
    public CacheManager ehcacheCacheManager() {
        net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.create();
        /**
         * 包扫描查找指定注解并将cacheNames添加到net.sf.ehcache.CacheManager(单例)
         */
        Reflections reflections = new Reflections("com.example.demo.service", new TypeAnnotationsScanner()
                , new SubTypesScanner(), new MethodAnnotationsScanner());
        Set<Class<?>> classesList = reflections.getTypesAnnotatedWith(CacheConfig.class);
        for (Class<?> aClass : classesList) {
            final CacheConfig config = AnnotationUtils.findAnnotation(aClass, CacheConfig.class);
            if (config.cacheNames() != null && config.cacheNames().length > 0) {
                for (String cacheName : config.cacheNames()) {
                    cacheManager.addCacheIfAbsent(cacheName);
                }
            }
        }
        /**
         * 方法级别的注解 @Caching、@CacheEvict、@Cacheable、@CachePut,结合实际业务场景仅扫描@Cacheable即可
         */
        final Set<Method> methods = reflections.getMethodsAnnotatedWith(Cacheable.class);
        for (Method method : methods) {
            final Cacheable cacheable = AnnotationUtils.findAnnotation(method, Cacheable.class);
            if (cacheable.cacheNames() != null && cacheable.cacheNames().length > 0) {
                for (String cacheName : cacheable.cacheNames()) {
                    cacheManager.addCacheIfAbsent(cacheName);
                }
            }
        }
        EhCacheCacheManager ehCacheCacheManager = new EhCacheCacheManager();
        ehCacheCacheManager.setCacheManager(cacheManager);
        return ehCacheCacheManager;
    }
}

4、示例

@Component
@CacheConfig(cacheNames = "XXXServiceImpl", keyGenerator = "customKeyGenerator")
public class XXXServiceImpl extends ServiceImpl<XXXMapper, XXXEntity> implements XXXService {
    @CacheEvict(allEntries = true)
    public void evictAllEntries() {}
    @Override
    @Cacheable
    public List<XXXEntity> findById(Long id) {
        return this.baseMapper.selectList(new QueryWrapper<XXXEntity>().lambda()
                .eq(XXXEntity::getId, id));
    }
}

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

(0)

相关推荐

  • Spring缓存注解@Cacheable @CacheEvit @CachePut使用介绍

    目录 I. 项目环境 1. 项目依赖 II. 缓存注解介绍 1. @Cacheable 2. @CachePut 3. @CacheEvict 4. @Caching 5. 异常时,缓存会怎样? 6. 测试用例 7. 小结 III. 不能错过的源码和相关知识点 0. 项目 Spring在3.1版本,就提供了一条基于注解的缓存策略,实际使用起来还是很丝滑的,本文将针对几个常用的注解进行简单的介绍说明,有需要的小伙伴可以尝试一下 本文主要知识点: @Cacheable: 缓存存在,则使用缓存:不存在

  • 解析springboot整合谷歌开源缓存框架Guava Cache原理

    目录 Guava Cache:⾕歌开源缓存框架 Guava Cache使用 使用压测⼯具Jmeter5.x进行接口压力测试: 压测⼯具本地快速安装Jmeter5.x 新增聚合报告:线程组->添加->监听器->聚合报告(Aggregate Report) Guava Cache:⾕歌开源缓存框架 Guava Cache是在内存中缓存数据,相比较于数据库或redis存储,访问内存中的数据会更加高效.Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache: 愿意消耗一些内存空间

  • Spring Cache和EhCache实现缓存管理方式

    目录 1.认识 Spring Cache 2.认识 EhCache 3.创建SpringBoot与MyBatis的整合项目 3.1 创建数据表 3.2 创建项目 4.配置EhCache缓存管理器 4.1 创建 ehcache.xml 配置文件 4.2 配置缓存管理器 4.3 开启缓存功能 5.使用EhCache实现缓存管理 5.1 创建实体类(Entity层) 5.2 数据库映射层(Mapper层) 5.3 业务逻辑层(Service层) 5.4 控制器方法(Controller层) 5.5 显

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

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

  • SpringBoot+SpringCache实现两级缓存(Redis+Caffeine)

    1. 缓存.两级缓存 1.1 内容说明 Spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明 springboot+spring cache:rediscache实现中的缺陷 caffeine简介 spring boot+spring cache实现两级缓存 使用缓存时的流程图 1.2 Sping Cache spring cache是spring-context包中提供的基于注解方式使用的缓存组件,定义了一些标准接口,通过实现这些接口,就可以通过在方法

  • SpringBoot2整合Ehcache组件实现轻量级缓存管理

    目录 一.Ehcache缓存简介 Hibernate缓存 EhCache缓存特点 对比Redis缓存 二.集成SpringBoot框架 1.核心依赖 2.加载配置 3.配置详解 三.注解用法 四.源代码地址 一.Ehcache缓存简介 Hibernate缓存 Hibernate三级缓存机制简介: 一级缓存:基于Session级别分配一块缓存空间,缓存访问的对象信息.Session关闭后会自动清除缓存. 二级缓存:是SessionFactory对象缓存,可以被创建出的多个 Session 对象共享

  • SpringBoot集成cache缓存的实现

    前言 日常开发中,缓存是解决数据库压力的一种方案,通常用于频繁查询的数据,例如新闻中的热点新闻,本文记录springboot中使用cache缓存. 官方文档介绍:https://docs.spring.io/spring-boot/docs/2.1.0.RELEASE/reference/htmlsingle/#boot-features-caching-provider-generic 工程结构 代码编写 pom引入依赖,引入cache缓存,数据库使用mysql,ORM框架用jpa <!--添

  • 使用Spring Cache设置缓存条件操作

    目录 Spring Cache设置缓存条件 原理 @Cacheable的常用属性及说明 Root对象 @CachePut的常用属性同@Cacheable Cache缓存配置 1.pom.xml 2.Ehcache配置文件 3.配置类 4.示例 Spring Cache设置缓存条件 原理 从Spring3.1开始,Spring框架提供了对Cache的支持,提供了一个对缓存使用的抽象,通过在既有代码中添加少量它定义的各种 annotation,即能够达到缓存方法的返回对象的作用. 提供的主要注解有@

  • Spring Cache+Redis缓存数据的实现示例

    目录 1.为什么使用缓存 2.常用的缓存注解 2.1 @Cacheable 2.2 @CacheEvict 2.3.@Cacheput 2.4.@Caching 2.5.@CacheConfig 3.SpringBoot缓存支持 4.项目继承Spring Cache+Redis 4.1 添加依赖 4.2 配置类 4.3 添加redis配置 4.4 接口中使用缓存注解 4.5 缓存效果测试 1.为什么使用缓存   我们知道内存的读取速度远大于硬盘的读取速度.当需要重复地获取相同数据时,一次一次地请

  • 基于Spring Cache实现Caffeine+Redis二级缓存

    目录 一.聊聊什么是硬编码使用缓存? 二.Spring Cache简介 1.Cache接口 2.CacheManager接口 3.常用注解说明 三.使用二级缓存需要思考的一些问题? 四.Caffeine 简介 1.写入缓存策略 2.缓存值的清理策略 3.统计 4.高效的缓存淘汰算法 5.其他说明 五.基于Spring Cache实现二级缓存(Caffeine+Redis) 1.maven引入使用 2.application.yml 3.启动类上增加@EnableCaching 4.在需要缓存的方

  • Spring Cache 集成 Caffeine实现项目缓存的示例

    目录 一.前言 二.缓存注解 三.实战操作 1.依赖引入 2.yaml配置 3.开启缓存 4.模拟方法 5.测试 6.改造 一.前言 Spring Cache本身是Spring框架中一个缓存体系的抽象实现,本身不具备缓存能力,需要配合具体的缓存实现来完成,如Ehcache.Caffeine.Guava.Redis等. 二.缓存注解 @EnableCaching:开启缓存功能 @Cacheable:定义缓存,用于触发缓存 @CachePut:定义更新缓存,触发缓存更新 @CacheEvict:定义

  • spring boot+spring cache实现两级缓存(redis+caffeine)

    spring boot中集成了spring cache,并有多种缓存方式的实现,如:Redis.Caffeine.JCache.EhCache等等.但如果只用一种缓存,要么会有较大的网络消耗(如Redis),要么就是内存占用太大(如Caffeine这种应用内存缓存).在很多场景下,可以结合起来实现一.二级缓存的方式,能够很大程度提高应用的处理效率. 内容说明: 缓存.两级缓存 spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明 spring boot

  • 关于Spring Cache 缓存拦截器( CacheInterceptor)

    目录 Spring Cache 缓存拦截器( CacheInterceptor) spring cache常用的三种缓存操作 具体整个流程是这样的 CacheInterceptor.java 定义Cacheable注解 定义Rediskey.java Cache.java RedisCache.java CacheManager.java AbstractCacheManager.java RedisCacheManager.java 实现CacheInterceptor.java 配置Spri

  • SpringBoot详解整合Spring Cache实现Redis缓存流程

    目录 1.简介 2.常用注解 2.1.@EnableCaching 2.2.@Cacheable 2.3.@CachePut 2.4.@CacheEvict 3.使用Redis当作缓存产品 3.1.坐标导入 3.2.yml配置 3.3.开启注解功能 3.4.使用@Cacheable 3.5.使用@CacheEvict 4.测试 1.简介 Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能. Spring Cache 提供了一层抽象,底层可以切

  • 详解SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)

    引言 ​前两天在写一个实时数据处理的项目,项目要求是 1s 要处理掉 1k 的数据,这时候显然光靠查数据库是不行的,技术选型的时候老大跟我提了一下使用 Layering-Cache 这个开源项目来做缓存框架. ​之间问了一下身边的小伙伴,似乎对这块了解不多.一般也就用用 Redis 来缓存,应该是很少用多级缓存框架来专门性的管理缓存吧. ​趁着这个机会,我多了解了一些关于 SpringBoot 中缓存的相关技术,于是有了这篇文章! 在项目性能需求比较高时,就不能单单依赖数据库访问来获取数据了,必

  • spring cache注解@Cacheable缓存穿透详解

    目录 具体注解是这样的 基于这个思路我把Cache的实现改造了一下 取缓存的get方法实现 测试了一下,发现ok了 最近发现线上监控有个SQL调用量很大,但是方法的调用量不是很大,查看接口实现,发现接口是做了缓存操作的,使用Spring cache缓存注解结合tair实现缓存操作. 但是为啥SQL调用量这么大,难道缓存没有生效.测试发现缓存是正常的,分析了代码发现,代码存在缓存穿透的风险. 具体注解是这样的 @Cacheable(value = "storeDeliveryCoverage&qu

随机推荐