SpringBoot专题学习Part26:Spring Cache整合Redis实现缓存及SpringBoot 2.x新版本自定义CacheManager

Spring Cache默认使用的是ConcurrentMapCacheManager管理器
创建的是ConcurrentMapCache缓存 然后将数据保存在ConcurrentMap中

实际使用缓存中间件的场合会更多 例如Redis EhCache等

Redis是一个高性能的支持网络 可基于内存亦可持久化的日志型 Key-Value数据库

Spring Cache默认使用的是SimpleCacheConfiguration这个配置类
当然 还有其他很多配置类(默认是不开启的
当导入其他相应的缓存场景即可自动开启对应的缓存配置

一、配置

首先 如果机器上还没装Redis 可使用Docker安装Redis

# 拉取最新redis镜像
docker pull docker.mirrors.ustc.edu.cn/library/redis

#启动redis容器
docker run -d -p 6379:6379 --name redis1 docker.mirrors.ustc.edu.cn/library/redis

当然自己安装Redis然后配置也可

然后 在项目中引入redis的starter(启动器):

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

然后在配置文件中配置Redis的主机ip

spring.redis.host=111.111.111.111(主机ip)

当引入了Redis的starter(启动器)之后 RedisAutoConfiguration这个配置类就启动了
底层配置了RedisTemplateStringRedisTemplate 这两个Template类是用于操作Redis的 就类似于JDBC的JdbcTemplate
若要使用 只需注入即可

StringRedisTemplate 顾名思义 是操作字符串的
RedisTemplate是用来操作对象的

二、使用

Redis常用五大数据类型:String字符串 List列表 Set集合 Hash散列 ZSet有序集合
Template都有对应的方法
stringRedisTemplate.opsForValue():操作k-v字符串
stringRedisTemplate.opsForList():操作列表
stringRedisTemplate.opsForSet():操作集合
stringRedisTemplate.opsForHash():操作散列
stringRedisTemplate.opsForZSet():操作有序集合
redisTemplate里也有这些方法

1、字符串操作

// 添加key-value
stringRedisTemplate.opsForValue().append("msg","HelloWorld");
// 通过key获取指定的value
System.out.println(stringRedisTemplate.opsForValue().get("msg"));

// 往list左侧添加
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
stringRedisTemplate.opsForList().leftPush("mylist","3");

具体语法和Redis的是一样的 不过多阐述

2、对象操作

Employee employee = employeeMapper.findEmployeeById(1);
redisTemplate.opsForValue().set("emp1",employee);

此时 会报错:Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [net.zjitc.springboot.bean.Employee]
因为 需要传入可序列化的类 而存入的类不可被序列化 因此报错
需要在实体类上实现Serializable接口 Serializable只是一个声明 代表该类是可以被序列化的 仅此而已

public class Employee implements Serializable {
	
	private Integer id;
	private String lastName;
	private String email;
	private Integer gender;
	private Integer dId;

    ...
}

这样即可将对象正常 存入Redis了

储存乱码问题:

默认保存对象使用的是JDK序列化机制 将序列化后的数据保存到Redis中
因而保存后的数据会多了很多诸如\xAC\xED\x00\x05sr\x00这样的乱码
在这里插入图片描述

平时的使用的话 通常是将数据以JSON的形式保存
有两种方法可以实现转换:

  • 1、自己将对象转为JSON 有很多转换工具可供选择 例如Fastjson
  • 2、使用RedisTemplate默认的序列化规则

在RedisTemplate中 定义了很多序列化器 默认使用的是JdkSerializationRedisSerializer
只需要改变其为JSON的序列化器 即可
自定义一个Redis的配置类:

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);

        // 传入要转换为JSON的类的class
        Jackson2JsonRedisSerializer<Employee> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(jackson2JsonRedisSerializer);

        return template;
    }
}

配置好之后 在类里注入即可使用:

// 注入
@Autowired
RedisTemplate<Object, Employee> empRedisTemplate;

// 使用
Employee employee = employeeMapper.findEmployeeById(1);
empRedisTemplate.opsForValue().set("emp1",employee);

在这里插入图片描述
完美


三、自定义CacheManager

其实原理就是CacheManager缓存管理器创建出Cache缓存组件
然后由Cache缓存组件来进行实际的CRUD操作

引入Redis的starter启动器之后 经过内部的判断 容器中就会自动使用RedisCacheManager(Redis缓存管理器)
RedisCacheManager创建了RedisCache作为缓存组件 该缓存组件是通过操作Redis来缓存数据
而这一切 都是自动配置好的
Just enjoy it

But…

在使用SpringCache缓存抽象的注解来实现缓存时 若引入了Redis的starter(启动器)
则@Cacheable缓存数据会自动储存到Redis中

然鹅 默认还是用的是JDK序列化 储存到Redis中的还是一堆乱码!
那么 如何储存为JSON数据?

在引入了Redis的starter之后 默认使用的CacheManager是RedisCacheManager
默认创建的RedisCacheManager操作Redis的时候使用的是RedisTemplate<Object,Object> 请注意 泛型是Object,Object
而这个默认的RedisTemplate<Object,Object>使用的序列化机制正是JdkSerializationRedisSerializer 它会导致存储(中文)乱码

此时 需要自定义CacheManager
在配置类中往容器里添加一个CacheManager:

// 定制缓存管理器规则
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    //初始化RedisCacheWriter
    RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
    //设置CacheManager的序列化方式为JSON
    RedisSerializer<Object> jsonSerializer = new GenericJackson2JsonRedisSerializer();
    RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair
            .fromSerializer(jsonSerializer);
    RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig()
            .serializeValuesWith(pair);

    //设置默认超时过期时间
    //    defaultCacheConfig.entryTtl(Duration.ofSeconds(30));

    return new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
}

默认的CacheManager生效条件是当容器中没有CacheManager 因此 自己编写的CacheManager一旦添加到容器中 自动会生效
默认会使用前缀 即 默认将注解的cacheNames属性的值作为key的前缀 避免了key重复
在这里插入图片描述


以编码方式使用缓存管理器

不仅可用注解方式 还可单独使用编码方式来操作缓存管理器:
首先要注入缓存管理器 然后直接使用即可(当然 前提是已经自定义了CacheManager 如果你想以JSON格式储存到Redis的话)

// 缓存管理器
@Autowired
CacheManager cacheManager;

public Department getDepartmentById(Integer id)
{
        System.out.println("查询"+id+"部门");
        Department employee = departmentMapper.findEmployeeById(id);

        // 获取一个指定名称的缓存
        Cache dept = cacheManager.getCache("dept");
        // 对指定缓存进行操作 会自动添加前缀
        dept.put(id,employee);

        return employee;
}

在这里插入图片描述


发布了174 篇原创文章 · 获赞 5 · 访问量 24万+

猜你喜欢

转载自blog.csdn.net/Piconjo/article/details/105189645