SpringCache学习笔记

1 概述

1.0 Redis访问工具

常用的有Jedis和Lettuce两个访问redis的客户端库,其中Lettuce的性能和并发性要好一些,Spring Boot 默认使用的是 Lettuce 作为 Redis 的客户端。

还有就是Spring data redis框架,在项目中可以通过RedisTemplate访问Redis,RedisTemplate提供了方便访问redis的模板方法。

RedisTemplate和Lettuce 是什么关系?
RedisTemplate 进行 Redis 操作时,实际上是通过 Lettuce 客户端与 Redis 服务器进行通信。
本项目也集成了Spring Cache,Spring Cache是spring的缓存框架,可以集成各种缓存中间件,比如:EhCache、Caffeine、redis。
Spring Cache最终也是通过Lettuce 去访问redis 。
使用Spring Cache的方法很简单,只需要在方法上添加注解即可实现将方法返回数据存入缓存,以及清理缓存等注解的使用。
RedisTemplate适用于灵活操作redis的场景,通过RedisTemplate的API灵活访问Redis。

1.1 SpringCache介绍

Spring Cache就是一个缓存框架。它利用了AOP(将缓存逻辑与服务逻辑解耦),实现了基于注解的缓存功能(声明式缓存),并且进行了合理的抽象,业务代码不用关心底层是使用了什么缓存框架,只需要简单地加一个注解,就能实现缓存功能了。而且Spring Cache也提供了很多默认的配置,用户可以快速将缓存集成到项目中;

2 入门使用

2.1 入门使用

2.1.1 依赖导入

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.1.2 环境配置

#数据库配置
spring:
  redis:
    host: 192.168.200.130     # Redis服务器地址
    database: 1         # Redis数据库索引(默认为0)
    port: 6379          # Redis服务器连接端口
#    password: ld123456  # Redis服务器连接密码(默认为空)

2.1.3 缓存配置类定义

SpringCache抽象出公共的缓存接口,同时面向用户屏蔽了底层实现细节,用户可通过配置缓存管理器来实现缓存方案的替换:
在这里插入图片描述
我们当前以Redis作为SpringCache缓存底层实现为例展开讲解。

@Configuration
public class RedisCacheConfig {
    /**
     * 配置 cacheManager 代替默认的cacheManager (缓存管理器)
     * @param factory RedisConnectionFactory
     * @return  CacheManager
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //仅仅序列化对象的属性,且属性不可为final修饰
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        serializer.setObjectMapper(objectMapper);
        // 配置key value序列化
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
                //关闭控制存储
                .disableCachingNullValues()
                //修改前缀与key的间隔符号,默认是::
                .computePrefixWith(cacheName->cacheName+":");

        //设置特有的Redis配置
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        //定制化的Cache 设置过期时间 eg:以role:开头的缓存存活时间为10s
        cacheConfigurations.put("role",customRedisCacheConfiguration(config,Duration.ofSeconds(10)));
        cacheConfigurations.put("stock",customRedisCacheConfiguration(config,Duration.ofSeconds(3000)));
        cacheConfigurations.put("market",customRedisCacheConfiguration(config,Duration.ofSeconds(300)));
        //构建redis缓存管理器
        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                //Cache事务支持
                .transactionAware()
                .withInitialCacheConfigurations(cacheConfigurations)
                .cacheDefaults(config)
                .build();
        //设置过期时间
        return cacheManager;
    }

    /**
     * 设置RedisConfiguration配置
     * @param config
     * @param ttl
     * @return
     */
    public RedisCacheConfiguration customRedisCacheConfiguration(RedisCacheConfiguration config, Duration ttl) {
        //设置缓存缺省超时时间
        return config.entryTtl(ttl);
    }
}

2.2 SpringCache注解详解

2.2.1 @Cacheable注解

如果缓存中没有:查询数据库,存储缓存,返回结果,

如果缓存中有:直接返回结果

作用:可以用来进行缓存的写入,将结果存储在缓存中,以便于在后续调用的时候可以直接返回缓存中的值,而不必再执行实际的方法。
在这里插入图片描述
最简单的使用方式,注解名称=缓存名称,使用例子如下:

@Service
@CacheConfig(cacheNames = "role")//提取缓存的前缀配置
public class RoleServiceImpl implements IRoleService {

    @Autowired
    private RoleMapper roleMapper;

    /**
     * <p>
     * 几个属性:
     * a)cacheNames/value:指定缓存组件的名字
     *      cacheNames = {"role"}可以使用多个参数,是数组的形式,可以指定多个缓存
     * b)key:缓存数据使用的key,可以用他来指定。默认是使用方法参数的值
     *      编写SpEl:   #id  #a0,#po,#argrs[0]  "0"代表参数的索引
     *      #result  方法执行后的返回值
     *      #root.methodName   方法名
     *      key = "#root.methodName+'['+#id+']'"
     * e)condition:指定符合缓存的条件
     *      condition = "#id>0 and #root.methodName eq 'aaa'" 可以多条件判断
     * f)unless: 否定缓存,当unless的条件为true,方法结果不会被缓存,可以获取结果进行判断
     *      unless = "#result==null",结果为null,就不缓存
     */
    @Override
    //@Cacheable(cacheNames = "role", key = "#id",condition = "#id>0",unless = "#result==null")
    @Cacheable(key = "#id",condition = "#id>0",unless = "#result==null")
    public Role findById(Integer id) {
        return roleMapper.selectByPrimaryKey(id);
    }
  
    @Cacheable(key ="#root.method.getName()")//直接引用mehtodname异常
    @Override
    public R findAllRole() {
        List<Role> roleList = roleMapper.findAll();
        return R.ok(roleList);
    }
}

2.2.2 @CacheEvict注解

@CacheEvict:删除缓存的注解,这对删除旧的数据和无用的数据是非常有用的。这里还多了一个参数(allEntries),设置allEntries=true时,可以对整个条目进行批量删除
在这里插入图片描述
示例代码:

    /**
     * .@CacheEvict 缓存清除
     *  key:指定要清除的数据
     */
    @Override
    @CacheEvict(key = "#id")
    public Integer delete(Integer id) {
        return roleMapper.deleteByPrimaryKey(id);
    }

2.2.3 @CachePut注解

@CachePut:当需要更新缓存而不干扰方法的运行时 ,可以使用该注解。也就是说,始终执行该方法,并将结果放入缓存

本质上说,如果存在对应的缓存,则更新覆盖,不存在则添加;
在这里插入图片描述
示例代码:

/**
     * .@CachePut既调用方法、又更新数据,达到同步更新缓存
     * <p>
     * 运行时机:
     * 1、先调用目标方法 ★★★
     * 2、将目标方法的结果缓存起来★★★
     * 条件:存取Id的key要保持一致
     *     key = "#role.id"     传入员工的Id
     *     key = "#result.id"   使用返回员工的Id
     * 注意: @Cacheable不能使用#result
     *      因为 @Cacheable在目标方法执行之前需要得到这个key,所以不能用#result
     */
@Override
@CachePut(key = "#result.id")//更新或者添加缓存---》有则更新,无则添加
public Role update(Role role) {
  roleMapper.updateByPrimaryKey(role);
  return role;
}

2.2.4 @Caching注解

在使用缓存的时候,有可能会同时进行更新和删除,会出现同时使用多个注解的情况.而@Caching可以实现,对于复杂的缓存策略,我们可借助SpEL实现;

Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
在这里插入图片描述
其它:如果表达式想直接引用一个常量值,那么需要 key=“‘xxxx’”

示例代码:

  /**
     * .@Caching 定义复杂缓存规则
     */
    @Override
    @Caching(
            cacheable = {
                    @Cacheable(key = "#role.rolename")
               
            },
            put = {
                    @CachePut(key = "#role.id"),
                    @CachePut(key = "#role.rolecode")
            },
            evict = {
                    @CacheEvict(key = "8")
            }
    )
    public R add(Role role) {
//        role.setId(null);
        try {
            roleMapper.insert(role);
        } catch (Exception e) {
            return R.error();
        }
        return R.ok(role.getId());
    }

2.2.5 注解小结

对于缓存声明,spring的缓存提供了一组java注解:

  • @Cacheable
    • 功能:触发缓存写入,如果缓存中没有,查询数据库,存储缓存,返回结果,如果缓存中有,直接返回结果
    • 应用:查询数据库方法,且查询的数据时热点数据
  • @CacheEvict
    • 功能:触发缓存清除
    • 应用:删除或修改数据库方法
  • @CachePut
    • 功能:缓存写入(不会影响到方法的运行)。有则更新,无则添加
    • 应用:新增到数据库方法
  • @Caching
    • 功能:重新组合要应用于方法的多个缓存操作
    • 应用:上面的注解的组合使用
  • @CacheConfig(cacheNames = “xxx”)
    • 功能:可以提取公共的缓存key的前缀,一般是业务的前缀
    • 应用:作用在类之上

@EnableCaching:开启缓存注解功能
@Cacheable:查询数据时缓存,将方法的返回值进行缓存。
@CacheEvict:用于删除缓存,将一条或多条数据从缓存中删除。
@CachePut:用于更新缓存,将方法的返回值放到缓存中
@Caching:组合多个缓存注解;
@CacheConfig:统一配置@Cacheable中的value值

2.3 SpringCache工作原理

Spring Cache是基于AOP原理,对添加注解@Cacheable的类生成代理对象,在方法执行前查看是否有缓存对应的数据,如果有直接返回数据,如果没有调用源方法获取数据返回,并缓存起来

下面跟踪下源码:
下图是Spring Cache的切面类CacheAspectSupport.java中的private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts)方法
在这里插入图片描述

  • 20
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值