Spring Boot与缓存

Spring Boot与缓存 笔记

用途:

提升查询效率(商品信息存入缓存。系统先去缓存找,没有再去数据库找,然后把找到的又放入缓存)

存储临时数据(比如验证码)

一、JSR107缓存规范

Java Caching定义了5个核心接口:

CachingProvider管理和控制多个CacheManager;

CacheManager管理和控制多个唯一命名的Cache;

Cache是一个类似Map的数据结构,存储多个key-value对;

Entry是一个存储在Cache中的key-value对;

Expiry是一个存储在Cache中的定义的有效期。

二、Spring缓存抽象

Spring对缓存进行了抽象,更便于使用

Cache:缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConCurrentMapCache等。

CacheManager:缓存管理器,管理各种缓存(Cache)组件

三个基本注解,标注在方法上,简化常见的缓存操作:

@Cacheable:设置缓存。对方法的结果进行缓存(一旦被调用过有数据,就不会再调用)

@CacheEvict:清空缓存

@CachePut:更新缓存(跟@Cacheable区别:方法总会被调用)

@EnableCaching:开启基于注解的缓存

三、缓存实操

(一) 搭建基本环境

1.导入数据库文件,创建出department和employee表
2.创建javaBean封装数据
3.整合MyBatis操作数据库
1.配置数据源信息
2.使用注解版的MyBatis
1)@MapperScan指定需要扫描的mapper接口所在的包

(二) 快速体验缓存

​ 步骤:
​ 1.开启基于注解的缓存@EnableCaching
​ 2.标注缓存注解即可
​ @Cacheable
​ @CacheEvict
​ @CachePut
默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache,将数据保存在ConcurrentMap<Object, Object>中;
开发中使用缓存中间件:redis、memcached、ehcache;

(三) 整合redis作为缓存

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
1.安装redis,使用docker;
2.引入redis的starter
3.配置redis
4.测试缓存
原理:CacheManager==Cache 缓存组件来实际给缓存中存取数据
1)引入redis的starter,容器中保存的是 RedisCacheManager;
2)RedisCacheManager 帮我们创建RedisCache来作为缓存组件,RedisCache通过操作redis缓存数据的
3)默认保存数据 k-v 都是Object,利用序列化保存;
如何保存为json:
1.引入了redis的starter,CacheManager变为 RedisCacheManager
2.默认创建的RedisCacheManager操作redis的时候使用的是 RedisTemplate<Object, Object>
3.RedisTemplate<Object, Object> 是默认使用jdk的序列化机制
4)自定义CacheManager

四、注解讲解

@CacheConfig(cacheNames = "emp")  //抽取缓存的公共配置
@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 将方法的运行结果进行缓存,以后再要相同的数据,直接从缓存中获取,不再调用方法
     *
     * CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字
     * 几个属性:
     *      cacheNames/value:指定缓存的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
     *      key:缓存数据使用的key,可以用它来指定。默认是使用方法参数的值  如:1-方法的返回值
     *          编写SpEl: #id:参数id的值  也可以用  #a0   #p0   #root.args[0]
     *          getEmp[2] : key = "#root.methodName+'['+#id+']'"
     *      keyGenerator:key的生成器;可以自己指定key的生成器的组建的id
     *          key/keyGenerator:二选一使用
     *
     *      cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
     *      condition:指定符合条件的情况下才缓存;
     *          如:condition = "#id>0"
     *          condition = "#a0>1"  第一个参数的值大于1的时候才进行缓存
     *      unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存,可以获取到结果进行判断
     *          如:unless = "#result == null"
     *      sync:是否使用异步模式
     *
     * 原理:
     *      1.自动配置类: CacheAutoConfiguration
     *      2.缓存的配置类  11个
     *
     *      3.哪个配置类默认生效: SimpleCacheConfiguration
     *      4.给容器中注册了一个CacheManager: ConcurrentMapCacheManager
     *      5.可以获取和创建ConcurrentMapCache类型的缓存组件,他的作用 将数据保存在ConcurrentMap中
     *
     *      运行流程:
     *      @Cacheable :
     *      1.方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
     *        (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建
     *      2.去Cache中查找缓存的内容,使用一个key(默认就是方法的参数)
     *        key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key
     *              SimpleKeyGenerator生成key的默认策略:
     *                  如果没有参数:key=new SimpleKey();
     *                  如果有一个参数:key=参数的值
     *                  如果有多个参数:key=new SimpleKey(params);
     *      3.没有查到缓存就调用目标方法;
     *      4.将目标方法返回的结果,放进缓存中;
     *
     *      @Cacheable 标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询,
     *      如果没有就运行方法,并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;
     *
     *      核心:
     *          1)使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
     *          2)key是使用keyGenerator生成的,默认是SimpleKeyGenerator
     *
     * @param id
     * @return
     */
    @Cacheable(/*cacheNames = {"emp"},*/key = "#a0")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

    /**
     * @CachePut :既调用方法,又更新缓存数据;
     * 修改了数据库的某个数据,同时更新缓存
     * 运行时机:
     *  1.先调用目标方法
     *  2.将目标方法的结果缓存起来
     *
     * 测试步骤:
     *  1.查询1号员工,查到的结果会放在缓存中
     *      key:1   value:lastName:张三
     *  2.以后查询还是之前的结果
     *  3.更新1号员工;【lastName:zhangsan;gender:0】
     *      将方法的返回值也放进缓存了;
     *      key:传入的employee对象  值:返回的employee对象
     *  4.查询1号员工
     *      应该是更新后的员工,结果不是,因为key不同
     *      key = "#employee.id",使用传入的参数的员工id
     *      key = "#result.id",使用返回后的id
     *          @Cacheable 的key不能用#result
     *
     */

    @CachePut(/*value = "emp",*/key = "#result.id")
    public Employee updateEmployee(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

    /**
     * @CacheEvict 缓存清除
     * key:指定要清除的数据
     * allEntries = true:指定清除这个缓存中所有的数据
     * beforeInvocation = false:缓存的清除是否在方法之前执行
     *      默认为false,代表是在方法执行之后执行;如果方法出现异常,缓存就不会清除
     *      如果为true,代表是在方法执行之前执行;无论方法是否出现异常,缓存都会清除
     *
     */

    @CacheEvict(/*value="emp",*/key = "#id",beforeInvocation = false)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        //employeeMapper.deleteEmpById(id);
    }

    // @Caching 定义复杂的缓存规则
    @Caching(
        cacheable = {
            @Cacheable(/*value="emp",*/key = "#lastName")
        },
        put = {
            @CachePut(/*value="emp",*/key = "#result.id"),
            @CachePut(/*value="emp",*/key = "#result.email")
        }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

}

五、SpringBoot2.x整合redis缓存自定义序列化的实现

我们使用redis作为缓存中间件时,当我们第一次查询数据的时候,是去数据库查询,然后查到的数据封装到实体类中,实体类会被序列化存入缓存中,当第二次查数据时,会直接去缓存中查找被序列化的数据,然后反序列化被我们获取。

我们在缓存中看到的序列化数据不直观,如果想看到类似json的数据格式,就需要自定义序列化规则。

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }

    @Bean(name = "redisCacheManager")
    @Primary    //将某个缓存管理器作为默认的
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(100)) //设置过期时间
                .disableCachingNullValues()   //禁止缓存null对象
                .computePrefixWith(cacheName -> "sprigboot_cache".concat(":").concat(cacheName).concat(":") )//此处定义了cache key的前缀,避免公司不同项目之间的key名称冲突
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) //定义了key和value的序列化协议,同时hash key和hash value也被定义
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfiguration)
                .build();
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值