纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

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

梁云亮   2021-09-14 我要评论
想了解使用Spring Cache设置缓存条件操作的相关内容吗梁云亮在本文为您仔细讲解Spring Cache缓存条件的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Spring缓存,Cache缓存,设置缓存条件下面大家一起来学习吧

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));
    }
}

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


相关文章

猜您喜欢

  • C 语言常量 C 语言基础之初识 C 语言常量

    想了解C 语言基础之初识 C 语言常量的相关内容吗吞吞吐吐大魔王」在本文为您仔细讲解 C 语言常量的相关知识和一些Code实例欢迎阅读和指正我们先划重点:C,语言常量下面大家一起来学习吧..
  • docker部署code-server docker部署code-server的方法

    想了解docker部署code-server的方法的相关内容吗barwe在本文为您仔细讲解docker部署code-server的相关知识和一些Code实例欢迎阅读和指正我们先划重点:docker部署code-server,docker,code-server下面大家一起来学习吧..

网友评论

Copyright 2020 www.sopisoft.net 【绿软下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式