0


SpringCache_概述、Cacheable、更新缓存、删除缓存、从0搭建缓存项目

文章目录

①. Spring Cache概述

  • ①. 如何找到Spring Cache的官方文档

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

  • ②.Spring 从 3.1开始定义了org.springframework.cache.Cache和 org.springframework.cache.Cache Manager接口来统一不同的缓存技术,并支持使用 JCache(JSR-107)注解简化我们开发

  • ③. JSR-107定义了5个核心接口来实现缓存操作,分别是CachingProvider, CacheManager, Cache, Entry和Expiry在这里插入图片描述

  • ④. 每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取

  • ⑤. SpringCache常用注解详解
    注解解释@Cacheable触发将数据保存到缓存的操作@CacheEvict触发将数据从缓存删除的操作@CachePut不影响方法执行更新缓存 双写模式@Caching组合以上多个操作@CacheConfig在类级别共享缓存的相同配置

    ②. 触发缓存入口 - @Cacheable

  • ①. @Cacheable 注解提供了一些参数,用于配置缓存的行为。下面是一些常用的参数:

@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic@interfaceCacheable{@AliasFor("cacheNames")String[]value()default{};@AliasFor("value")String[]cacheNames()default{};Stringkey()default"";StringkeyGenerator()default"";StringcacheManager()default"";StringcacheResolver()default"";Stringcondition()default"";Stringunless()default"";booleansync()defaultfalse;}
  • ②. value/cacheNames属性
  1. 这两个属性代表的意义相同,根据@AliasFor注解就能看出来了。
  2. 这两个属性都是用来指定缓存组件的名称,即将方法的返回结果放在哪个缓存中,属性定义为数组,可以指定多个缓存
@AliasFor("cacheNames")String[]value()default{};@AliasFor("value")String[]cacheNames()default{};
@Cacheable(cacheNames ={CacheEnum.RedisCacheNameExpGroup.AN_MINUTE,CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES})//这两种配置等价@Cacheable(value ="user")//@Cacheable(cacheNames = "user")UsergetUser(Integer id);
@Cacheable(cacheNames =CacheEnum.RedisCacheNameExpGroup.AN_MINUTE, cacheManager =CacheEnum.CacheManager.REDIS_MANAGER,
            key =CacheEnum.RedisCacheKeys.PATROL_BOARD+"+#params.areaCode"+"+#params.staffCode")
  • ③. key属性:可以通过key属性来指定缓存数据所使用的的key,默认使用的是方法调用传过来的参数作为key

在这里插入图片描述在这里插入图片描述

@Cacheable(value ="user",key ="#root.method.name")UsergetUser(Integer id);
  • ④. cacheManager属性:用来指定缓存管理器。针对不同的缓存技术,需要实现不同的 cacheManager,Spring 也为我们定义了如下的一些cacheManger实现

在这里插入图片描述

  • ⑤. unless属性,意为"除非"的意思。即只有unless指定的条件为true时,方法的返回值才不会被缓存。可以在获取到结果后进行判断
@Cacheable(value ="user",unless ="#result==null || #result.size()==0")//当方法返回值为 null 时,就不缓存UsergetUser(Integer id);@Cacheable(value ="user",unless ="#a0 == 1")//如果第一个参数的值是1,结果不缓存UsergetUser(Integer id);
  • ⑥. condition:条件判断属性,用来指定符合指定的条件下才可以缓存。也可以通过SpEL 表达式进行设置。这个配置规则和上面表格中的配置规则是相同的
@Cacheable(value ="user",condition ="#id>0")//传入的 id 参数值>0才进行缓存UsergetUser(Integer id);
  • ⑦. sync该属性用来指定是否使用异步模式,该属性默认值为false,默认为同步模式。异步模式指定sync = true即可,异步模式下unless属性不可用

③. 更新缓存 - CachePut

  • ①. @CachePut:不影响方法执行更新缓存(双写模式) 需要有返回值
  • ②. @CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中
  • ③. @Cacheput注解一般使用在保存,更新方法中
@CachePut(value="emp",key ="#result.id")publicEmployeeupdateEmployee(Employee employee){System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmployee(employee);return employee;}

④. 删除缓存 - CacheEvict

  • @CacheEvict:触发将数据从缓存删除的操作
@CacheEvict(cacheNames =CacheEnum.RedisCacheNameExpGroup.ONE_DAY, cacheManager =CacheEnum.CacheManager.REDIS_MANAGER,
            key =CacheEnum.RedisCacheKeys.DISTRICT_DETAIL+"+#params.areaCode"+"+#params.timeRange")publicvoiddistrictDetail(DiagnosisParams params){
        log.info("refresh districtDetail!,areaCode:{},timeRange:{}",params.getAreaCode(),params.getTimeRange());}

⑤. 组合操作- Caching

  • ①. 组合以上多个操作: @Caching不常用
  1. @Caching 注解可以在一个方法或者类上同时指定多个Spring Cache相关的注解。
  2. 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和
  • ②. @CacheEvict。对于一个数据变动,更新多个缓存的场景,可以通过@Caching来实现:
@Caching(cacheable =@Cacheable(cacheNames ="caching", key ="#age"), evict =@CacheEvict(cacheNames ="t4", key ="#age"))publicStringcaching(int age){return"caching: "+ age +"-->"+UUID.randomUUID().toString();}

⑥. 共享缓存配置 - CacheConfig

  • ①. @CacheConfig不常用:@CacheConfig是Spring提供的一个注解,用于统一配置缓存的一些属性,例如缓存名称、缓存管理器等
  • ②. 使用@CacheConfig注解需要注意以下几点:
  1. @CacheConfig可以放在类上,也可以放在方法上。如果放在类上,则该类中所有的缓存方法都会继承该注解的属性。
  2. @CacheConfig的属性可以被缓存方法上的@Cacheable、@CachePut、@CacheEvict等注解覆盖。
  3. @CacheConfig的属性可以通过Spring的EL表达式进行动态配置。
// 在上面的示例中,@CacheConfig注解指定了缓存名称为"myCache",缓存管理器为"myCacheManager"。这些属性会被该类中所有的缓存方法继承// 但是,如果某个缓存方法上使用了@Cacheable、@CachePut、@CacheEvict等注解,并且指定了相同的属性,则该注解的属性会覆盖@CacheConfig注解的属性@CacheConfig(cacheNames ="myCache", cacheManager ="myCacheManager")@ServicepublicclassMyService{@Cacheable(key ="#id")publicMyObjectfindById(Long id){// ...}@CachePut(key ="#myObject.id")publicMyObjectsave(MyObject myObject){// ...}@CacheEvict(key ="#id")publicvoiddeleteById(Long id){// ...}}

⑦. 从0搭建缓存项目

  • ①. 导入依赖
<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>study2022_xz</artifactId><groupId>com.xiaozhi</groupId><version>0.0.1-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>springboot-swagger</artifactId><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><knife4j.version>2.0.9</knife4j.version><lombok.version>1.18.12</lombok.version><caffeine.version>2.7.0</caffeine.version></properties><dependencies><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>${knife4j.version}</version></dependency><!--web场景--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.80</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--引入caffeine依赖--><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>${caffeine.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--引入guava依赖--><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.1-jre</version></dependency><!--引入redis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><scope>compile</scope></dependency></dependencies></project>
  • ②. 配置类 - 常量分组概念
publicinterfaceCacheEnum{/**
     * cacheTemplate
     */interfaceCacheTemplates{/**
         * 业务用redis
         */StringREDIS_CACHE_TEMPLATE="redisTemplate";}/**
     * cacheManager
     */interfaceCacheManager{/**
         * caffeine Cache Manager
         */StringCAFFEINE_MANAGER="caffeineManager";/**
         * redis Cache Manager
         */StringREDIS_MANAGER="redisCacheManager";}/**
     * redis keys
     */interfaceRedisCacheKeys{StringSSM_USER_KEY="SSM:USER_KEY:";}/**
     * redis exp group
     */interfaceRedisCacheNameExpGroup{StringAN_MINUTE="ssm:anMinute";StringFIVE_MINUTES="ssm:fiveMinute";StringAN_HOUR="ssm:anHour";StringTWO_HOUR="ssm:twoHour";StringFIVE_HOURS="ssm:fiveHour";StringONE_DAY="ssm:oneDay";StringTWO_DAY="ssm:twoDay";}/**
     * caffeine cache names conf
     */@AllArgsConstructor@GetterenumCaffeineCacheNamesConf{SSM_TEST(CaffeineCacheNames.SSM_TEST,100,10000,1,TimeUnit.DAYS),;privateString cacheName;privateint initialCapacity;privateint maximumSize;privateint expsNum;privateTimeUnit timeUnit;}/**
     * Caffeine Cache Names
     */interfaceCaffeineCacheNames{StringSSM_TEST="SSM_TEST";}}
/**
 * caffeine缓存配置,根据{@link CacheEnum.CaffeineCacheNamesConf}配置,
 * 动态设置参数以及自动加载策略
 */@ConfigurationpublicclassCaffeineCacheConfig{@Bean(CacheEnum.CacheManager.CAFFEINE_MANAGER)publicCacheManagercacheManagerWithCaffeine(){SimpleCacheManager caffeineCacheManager =newSimpleCacheManager();
        caffeineCacheManager.setCaches(buildCaffeineCache());return caffeineCacheManager;}//动态设置缓存参数privateList<CaffeineCache>buildCaffeineCache(){ArrayList<CaffeineCache> caches =Lists.newArrayList();CacheEnum.CaffeineCacheNamesConf[] cacheNames =CacheEnum.CaffeineCacheNamesConf.values();for(CacheEnum.CaffeineCacheNamesConf nameCof : cacheNames){
            caches.add(newCaffeineCache(nameCof.getCacheName(),Caffeine.newBuilder().recordStats().initialCapacity(nameCof.getInitialCapacity()).maximumSize(nameCof.getMaximumSize()).expireAfterWrite(nameCof.getExpsNum(), nameCof.getTimeUnit()).build()));}return caches;}}
@EnableCaching@Configuration@Slf4jpublicclassRedisConfigextendsJCacheConfigurerSupport{@Value("${spring.redis.database}")privateint dbIndex;@Value("${spring.redis.host}")privateString host;@Value("${spring.redis.port}")privateint port;@Value("${spring.redis.password}")privateString password;@Value("${spring.redis.timeout}")privateint timeout;@Value("${spring.redis.lettuce.pool.max-active}")privateint redisPoolMaxActive;@Value("${spring.redis.lettuce.pool.max-wait}")privateint redisPoolMaxWait;@Value("${spring.redis.lettuce.pool.max-idle}")privateint redisPoolMaxIdle;@Value("${spring.redis.lettuce.pool.min-idle}")privateint redisPoolMinIdle;publicRedisSerializer<Object>redisSerializer(){ObjectMapper objectMapper =newObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS,true);
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT,true);
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY,true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS,false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE,false);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL,JsonTypeInfo.As.PROPERTY);returnnewGenericJackson2JsonRedisSerializer(objectMapper);}publicLettuceConnectionFactorylettuceConnectionFactory(){RedisStandaloneConfiguration configuration =newRedisStandaloneConfiguration();
        configuration.setHostName(host);
        configuration.setPort(port);
        configuration.setDatabase(dbIndex);if(!ObjectUtils.isEmpty(password)){RedisPassword redisPassword =RedisPassword.of(password);
            configuration.setPassword(redisPassword);}GenericObjectPoolConfig genericObjectPoolConfig =newGenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(redisPoolMaxActive);
        genericObjectPoolConfig.setMinIdle(redisPoolMinIdle);
        genericObjectPoolConfig.setMaxIdle(redisPoolMaxIdle);
        genericObjectPoolConfig.setMaxWaitMillis(redisPoolMaxWait);LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder =LettucePoolingClientConfiguration.builder();
        builder.poolConfig(genericObjectPoolConfig);
        builder.commandTimeout(Duration.ofSeconds(timeout));LettuceConnectionFactory connectionFactory =newLettuceConnectionFactory(configuration, builder.build());
        connectionFactory.afterPropertiesSet();return connectionFactory;}@Bean(CacheEnum.CacheTemplates.REDIS_CACHE_TEMPLATE)publicRedisTemplate<String,Object>redisTemplate(){RedisTemplate<String,Object> redisTemplate =newRedisTemplate();
        redisTemplate.setKeySerializer(newStringRedisSerializer());
        redisTemplate.setHashKeySerializer(newStringRedisSerializer());
        redisTemplate.setHashValueSerializer(redisSerializer());
        redisTemplate.setValueSerializer(redisSerializer());
        redisTemplate.setConnectionFactory(lettuceConnectionFactory());
        redisTemplate.afterPropertiesSet();return redisTemplate;}@Bean(CacheEnum.CacheManager.REDIS_MANAGER)@PrimarypublicCacheManagerredisCacheManager(){RedisCacheConfiguration defConfig =RedisCacheConfiguration.defaultCacheConfig();ImmutableSet.Builder<String> cacheNames =ImmutableSet.builder();ImmutableMap.Builder<String,RedisCacheConfiguration> cacheConfig =ImmutableMap.builder();HashMap<String,Duration> exps =newHashMap<>();
        exps.put(CacheEnum.RedisCacheNameExpGroup.AN_MINUTE,Duration.ofMinutes(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES,Duration.ofMinutes(5));
        exps.put(CacheEnum.RedisCacheNameExpGroup.AN_HOUR,Duration.ofHours(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.TWO_HOUR,Duration.ofHours(2));
        exps.put(CacheEnum.RedisCacheNameExpGroup.FIVE_HOURS,Duration.ofHours(5));
        exps.put(CacheEnum.RedisCacheNameExpGroup.ONE_DAY,Duration.ofDays(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.TWO_DAY,Duration.ofDays(2));for(String cacheName : exps.keySet()){
            defConfig = defConfig.entryTtl(exps.get(cacheName));
            cacheConfig.put(cacheName, defConfig);
            cacheNames.add(cacheName);}returnRedisCacheManager.builder(lettuceConnectionFactory()).initialCacheNames(cacheNames.build()).withInitialCacheConfigurations(cacheConfig.build()).build();}@OverridepublicCacheErrorHandlererrorHandler(){returnnewCacheErrorHandler(){@OverridepublicvoidhandleCacheGetError(RuntimeException exception,Cache cache,Object key){
                log.error("handleCacheGetError!!!->{}", exception.getMessage());}@OverridepublicvoidhandleCachePutError(RuntimeException exception,Cache cache,Object key,Object value){
                log.error("handleCachePutError!!!->{}", exception.getMessage());}@OverridepublicvoidhandleCacheEvictError(RuntimeException exception,Cache cache,Object key){
                log.error("handleCacheEvictError!!!->{}", exception.getMessage());}@OverridepublicvoidhandleCacheClearError(RuntimeException exception,Cache cache){
                log.error("handleCacheClearError!!!->{}", exception.getMessage());}};}}
  • ③. 测试
@GetMapping("/testUser")@Cacheable(cacheNames ={CacheEnum.RedisCacheNameExpGroup.AN_MINUTE,CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES},// 缓存组概念
            cacheManager =CacheEnum.CacheManager.REDIS_MANAGER,// 使用哪个manager管理
            condition ="#a0>2",// 满足条件
            key ="#root.method.name",// key = 当前方法名词
            unless ="#result==null || #result.size()==0")// 值为空无效//key = "'" + CacheEnum.RedisCacheKeys.SSM_USER_KEY + "'" + "+#userId", unless = "#result==null || #result.size()==0")publicList<Integer>testUser(int userId){returnLists.newArrayList(userId);}
标签: 缓存 java 数据库

本文转载自: https://blog.csdn.net/TZ845195485/article/details/133655448
版权归原作者 所得皆惊喜 所有, 如有侵权,请联系我们删除。

“SpringCache_概述、Cacheable、更新缓存、删除缓存、从0搭建缓存项目”的评论:

还没有评论