Spring注解式缓存

目录

前言:spring+redis集成已完成的前提下​编辑Spring注解式缓存

1. spring注解式缓存使用步骤

  1.1 配置缓存管理器 

  1.2 配置自定义Key生成器CacheKeyGenerator           缓存的Java对象一定要重写hashCode和eqauls

  1.3 启用缓存注解功能

  1.4 在需要的地方进行注解缓存   

2. 缓存注解

  2.1 @CacheConfig        它是一个类级别的注解,允许共享缓存的名称、KeyGenerator、CacheManager和CacheResolver

   2.2 @Cacheable  

        2.3 @CachePut

  2.4 @CacheEvict

3. Spring-Cache key设置

  3.1 基本形式     

  3.2 组合形式

  3.3 对象形式

  3.4 自定义Key生成器

5. redis缓存的一些注意事项

6. 根据用户ID或公司ID进行查询(此想法未测试)


 

前言:spring+redis集成已完成的前提下Spring注解式缓存

 Redis是key-value存储的非关系型数据库。Spring Data Redis包含了多个模板实现,用来完成Redis数据库的数据存取功能



1. spring注解式缓存使用步骤

  1.1 配置缓存管理器
 

    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate" />
        <!--redis缓存数据过期时间单位秒-->
        <property name="defaultExpiration" value="${redis.expiration}" />
        <property name="usePrefix" value="true"/>

        <property name="cachePrefix">
            <bean class="org.springframework.data.redis.cache.DefaultRedisCachePrefix">
                <constructor-arg index="0" value="-cache-"/>
            </bean>
        </property>

    </bean>

  1.2 配置自定义Key生成器CacheKeyGenerator

           缓存的Java对象一定要重写hashCode和eqauls

 <bean id="cacheKeyGenerator" class="com.zking.ssm.redis.CacheKeyGenerator"></bean>

  1.3 启用缓存注解功能

     <cache:annotation-driven cache-manager="redisCacheManager" key-generator="cacheKeyGenerator"/>

  1.4 在需要的地方进行注解缓存
   

2. 缓存注解


  2.1 @CacheConfig  
      它是一个类级别的注解,允许共享缓存的名称、KeyGenerator、CacheManager和CacheResolver

      value:缓存位置的一段名称,不能为空
      key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL

 
  2.2 @Cacheable  


      配置在方法或类上,作用:本方法执行后,先去缓存看有没有数据,如果没有,从数据库中查找出来,给缓存中存一份,返回结果,
      下次本方法执行,在缓存未过期情况下,先在缓存中查找,有的话直接返回,没有的话从数据库查找
      value:缓存位置的一段名称,不能为空
      key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL
      keyGenerator:指定key的生成策略
      condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL

    
      注1:condition是在方法执行前评估, unless是在方法执行后评估. 


      
  2.3 @CachePut


      类似于更新操作,即每次不管缓存中有没有结果,都从数据库查找结果,并将结果更新到缓存,并返回结果
       value    缓存的名称,在 spring 配置文件中定义,必须指定至少一个
      key    缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合
      condition    缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存

  2.4 @CacheEvict


      用来清除用在本方法或者类上的缓存数据(用在哪里清除哪里)
      value:缓存位置的一段名称,不能为空
      key:缓存的key,默认为空,表示使用方法的参数类型及参数值作为key,支持SpEL
      condition:触发条件,满足条件就加入缓存,默认为空,表示全部都加入缓存,支持SpEL
      allEntries:true表示清除value中的全部缓存,默认为false


3. Spring-Cache key设置


  3.1 基本形式

 @Cacheable(value="cacheName", key="#id")
      public ResultDTO method(int id);

      注1:Spring Cacheable注解不缓存null值
           用Cacheable注解时,发现空值,也会被缓存下来。下次另一个系统如果更新了值,这边从缓存取,还是空值,会有问题。
           解决方案:

 @Cacheable(value = "service", key = "#service.serviceId.toString()", unless = "#result == null")
           @Cacheable(value = "service", keyGenerator = RedisKeys.KEY_GENERATOR, unless = "#result.size() == 0")

  3.2 组合形式

  @Cacheable(value="cacheName", key="T(String).valueOf(#name).concat('-').concat(#password))
      public ResultDTO method(int name, String password);

  3.3 对象形式


      @Cacheable(value="cacheName", key="#user.id)
      public ResultDTO method(User user);

      注1:以上三种配置方式中,使用了spEL表达式

  3.4 自定义Key生成器

@Cacheable(value="gomeo2oCache", keyGenerator = "keyGenerator")
      public ResultDTO method(User user);

      spring注解式缓存中的巨坑~~~~~~~
      没有指定key,默认情况下spirng会使用SimpleKeyGenerator生成key,
      而Spring默认的SimpleKeyGenerator是不会将函数名组合进key中的,举个例子:
   

  @Component
      public class CacheTestImpl implements CacheTest {
        @Cacheable("databaseCache")
        public Long test1()
        { return 1L; }
 
        @Cacheable("databaseCache")
        public Long test2()
        { return 2L; }
 
        @Cacheable("databaseCache")
        public Long test3()
        { return 3L; }
 
        @Cacheable("databaseCache")
        public String test4()
        { return "4"; }//注意返回的是字符串“4”
      }


      我们期望的输出是:
      1
      2
      3
      4
      而实际上的输出是:
      1
      1
      1
      此外,原子类型的数组,直接作为key使用也是不会生效的,为了解决上述2个问题,只能通过自定义KeyGenerator解决


      自定义Key生成器CacheKeyGenerator:源码见资料“CacheKeyGenerator.java”,另外此类使用非加密哈希算法MurmurHash

CacheKeyGenerator.java:

package com.zking.ssm.redis;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Array;
import java.lang.reflect.Method;

@Slf4j
public class CacheKeyGenerator implements KeyGenerator {
    // custom cache key
    public static final int NO_PARAM_KEY = 0;
    public static final int NULL_PARAM_KEY = 53;

    @Override
    public Object generate(Object target, Method method, Object... params) {
        StringBuilder key = new StringBuilder();
        key.append(target.getClass().getSimpleName()).append(".").append(method.getName()).append(":");
        if (params.length == 0) {
            key.append(NO_PARAM_KEY);
        } else {
            int count = 0;
            for (Object param : params) {
                if (0 != count) {//参数之间用,进行分隔
                    key.append(',');
                }
                if (param == null) {
                    key.append(NULL_PARAM_KEY);
                } else if (ClassUtils.isPrimitiveArray(param.getClass())) {
                    int length = Array.getLength(param);
                    for (int i = 0; i < length; i++) {
                        key.append(Array.get(param, i));
                        key.append(',');
                    }
                } else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {
                    key.append(param);
                } else {//Java一定要重写hashCode和eqauls
                    key.append(param.hashCode());
                }
                count++;
            }
        }

        String finalKey = key.toString();
        log.debug("using cache key={}", finalKey);
        return finalKey;
    }
}


      (源码46行: Hashing.murmur3_128().hashString),需要引入google guava项目,其pom如下:
   

  <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>27.0.1-jre</version>
      </dependency>


5. redis缓存的一些注意事项


   只应将热数据放到缓存中
   所有缓存信息都应设置过期时间
   缓存过期时间应当分散以避免集中过期
   缓存key应具备可读性
   应避免不同业务出现同名缓存key
   可对key进行适当的缩写以节省内存空间
   选择合适的数据结构
   确保写入缓存中的数据是完整且正确的
   避免使用耗时较长的操作命令,如:keys *
   Redis默认配置中操作耗时超过10ms即视为慢查询   
   一个key对应的数据不应过大

   对于string类型,一个key对应的value大小应控制在10K以内,1K左右更优hash类型,不应超过5000行 避免缓存穿透
   数据库中未查询到的数据,可在Redis中设置特殊标识,以避免因缓存中无数据而导致每次请求均达到数据库缓存层不应抛出异常

   缓存应有降级处理方案,缓存出了问题要能回源到数据库进行处理

   可以进行适当的缓存预热
   对于上线后可能会有大量读请求的应用,在上线之前可预先将数据写入缓存中读的顺序是先缓存,后数据库;写的顺序是先数据库,后缓存

   数据一致性问题
   数据源发生变更时可能导致缓存中数据与数据源中数据不一致,应根据实际业务需求来选择适当的缓存更新策略:

   主动更新:在数据源发生变更时同步更新缓存数据或将缓存数据过期。一致性高,维护成本较高。
   被动删除:根据缓存设置的过期时间有Redis负责数据的过期删除。一致性较低,维护成本较低。

6. 根据用户ID或公司ID进行查询(此想法未测试)

 @Transactional(readOnly = true)
   @Cacheable(value = "service+'By'+service.userId", unless = "#result.size() == 0")
   List<Service> listByUserId(Service service, PageBean pageBean);

 


 

  • 7
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
是的,Spring Framework 提供了多种全局缓存的实现,最常用的是基于 Spring Cache 抽象的缓存实现。Spring Cache 抽象定义了缓存注解缓存操作 API,可以方便地通过声明或编程的方来使用缓存Spring Cache 抽象支持多种缓存实现,包括: 1. ConcurrentMapCache:基于 ConcurrentHashMap 实现的本地缓存实现,适用于单机环境。 2. EhCacheCacheManager:基于 Ehcache 实现的缓存管理器,适用于单机或集群环境。 3. RedisCacheManager:基于 Redis 实现的缓存管理器,适用于单机或集群环境。 4. CaffeineCache:基于 Caffeine 实现的本地缓存实现,适用于单机环境。 等等。 使用 Spring Cache 抽象,只需在方法上添加缓存注解,如 @Cacheable、@CachePut、@CacheEvict 等,即可进行缓存操作。以下是一个使用 Spring Cache 抽象的示例: ```java import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class UserServiceImpl implements UserService { @Override @Cacheable(value = "users", key = "#id") public User getUserById(Long id) { // 从数据库中获取用户信息 return userDao.getUserById(id); } } ``` 在上述示例中,@Cacheable 注解表示该方法结果可以被缓存,value 参数表示缓存名称,key 参数表示缓存键值,可以使用 SpEL 表达来生成。当调用 getUserById 方法时,如果缓存中存在对应的值,则直接返回缓存中的值,否则从数据库中获取,并将结果存入缓存中。 需要注意的是,在使用 Spring Cache 抽象时,需要在 Spring 配置文件中配置缓存管理器和缓存实现,具体配置方可以参考 Spring 官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值