SpringBoot与Redis缓存

SpringBoot与Redis缓存:

准备

在Docker安装Redis

SpringBoot与Redis缓存_第1张图片

ATw8hQ.png

连接成功

SpringBoot与Redis缓存_第2张图片

对于Redis不熟悉的同学可以在本站搜索Redis的文章阅读。

整合Redis

在pom文件中加入

1
2
3
4
5
 
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
//简化操作KV 都是对象的
public RedisTemplate redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
@ConditionalOnMissingBean
//简化操作字符串的
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}

}

准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.hph.cache;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCacheApplicationTests {

@Autowired
StringRedisTemplate stringRedisTemplate;

@Autowired
RedisTemplate redisTemplate;

/**
* Redis常用的数据类型
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、(有序集合)
* String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)
* stringRedisTemplate.opsForValue()[String(字符串)]
* stringRedisTemplate.opsForList()[List(列表)]
* stringRedisTemplate.opsForSet()[Set(集合)]
* stringRedisTemplate.opsForHash()[Hash(散列)]
* stringRedisTemplate.opsForZSet()[ZSet(有序集合)]
*/
@Test
public void redis() {
stringRedisTemplate.opsForValue().append("msg","hello");

}
}

SpringBoot与Redis缓存_第3张图片

读取数据

更新原有数据

SpringBoot与Redis缓存_第4张图片

1
2
3
4
5
@Test
public void getmesg() {
String msg = stringRedisTemplate.opsForValue().get("msg");
System.out.println(msg);
}

ATyMBn.png

1
2
3
4
5
6
7
@Test
public void listops(){
stringRedisTemplate.opsForList().leftPush("mylist","1");
stringRedisTemplate.opsForList().leftPush("mylist","2");
stringRedisTemplate.opsForList().leftPush("mylist","3");
stringRedisTemplate.opsForList().leftPush("mylist","4");
}

SpringBoot与Redis缓存_第5张图片

1
2
3
4
5
@Test
public void cachObject(){
Employee empById = employeeMapper.getEmpById(1);
redisTemplate.opsForValue().set("emp-001",empById);
}

AT6K2D.png

序列化

报错Employee需要序列化。

1
public class Employee implements Serializable

SpringBoot与Redis缓存_第6张图片

RedisTemplate默认的是JdkSerializationRedisSerializer的序列化器。

1
2
3
4
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}

我们可以使用RedisSerializer的序列化器设置默认的序列化器。

1
2
3
public void setDefaultSerializer(RedisSerializer serializer) {
this.defaultSerializer = serializer;
}

SpringBoot与Redis缓存_第7张图片

自定义序列化器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.hph.cache.config;

import com.hph.cache.bean.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;

@Configuration
public class MyRedisConfig {

@Bean
public RedisTemplate empRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Employee.class);
template.setDefaultSerializer(ser);
return template;
}
}
1
2
3
4
5
6
7
8
@Autowired
RedisTemplate empRedisTemplate;
@Test
public void ObjectToJsonRedis(){
Employee empById = employeeMapper.getEmpById(1);
//默认保存对象使用jdk序列化机制,序列化的数据保存在redis中
empRedisTemplate.opsForValue().set("emp-001",empById);
}

成功

SpringBoot与Redis缓存_第8张图片

好的我们开始运行以下看看使用Redis缓存的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.hph.cache.service;

import com.hph.cache.bean.Employee;
import com.hph.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;

//condition = "#a0>1 第一个参数的值>>1的时候可以进行缓存 除非a0参数是2
@Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")
public Employee getEmp(Integer id) {
System.out.println("查询" + id + "号员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}

/**
* @CachePut即调用方法,又更新缓存数据 修改了数据库的某个数据, 同时更新缓存
* 运行时机:先调用目标方法,现将目标方法的结果缓存起来
*/
@CachePut(value = "emp", key = "#result.id ")
public Employee updateEmp(Employee employee) {
System.out.println("updateEmp" + employee);
employeeMapper.updateEmp(employee);
return employee;
}

/**
* @Cachevict :缓存清除
* key: 指定要清除的数据
*/
//缓存默认清除才足在方法执行之后执行;如果出现异常缓存不会被清除
@CacheEvict(value = "emp", beforeInvocation = true)
public void deleteEmp(Integer id) {
System.out.println("deletEmp" + id);
// employeeMapper.deleteEmpById(id);
int i = 10 / 0;
}
}

流程分析

原理:CacheManager==某一个Cache缓存组件来给实际给缓存中存取得数据,比如我们使用Redis缓存,那么SimpleCacheManager就不匹配进而不能使用,从而使用Redis

ATbUJg.png

ATb6oT.png

引入Redis的starter,容器中保存的是RedisCcheManager;

RedisCacheManager帮我们创建RedisCache作为缓存组件,RedisCache通过Redis缓存数据。

SpringBoot与Redis缓存_第9张图片

我们可以看到SQL只执行了一次。

SpringBoot与Redis缓存_第10张图片

Redis中已经缓存我们查询过的数据。

默认保留数据的K-V都是Object;利用序列化保存可以保存为Json。

引入了redis的stater,cacheManager变为RedisCacheManager;默认创建的RedisCacheManager

1
2
3
4
5
6
7
8
9
10
@Bean
public RedisCacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
cacheManager.setUsePrefix(true);
List cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
}
return this.customizerInvoker.customize(cacheManager);
}

RedisCacheManager操作Redis使用的是RedisTemplate默认使用的是JdkSerializationRedisSerializer

1
2
3
4
5
if (defaultSerializer == null) {

defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}

缓存管理器

接下来我们定制以下缓存管理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.hph.cache.config;

import com.hph.cache.bean.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;

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

//CacheManagerCustomizers可以定制缓存的一些规则。
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate empRedisTemplate){
RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
cacheManager.setUsePrefix(true);
return cacheManager;
}

}

而创建缓存管理器的条件是。

1
2
3
4
5
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisTemplate.class)
@ConditionalOnMissingBean(CacheManager.class) //没有CacheManager的时候创建RedisCacheConfiguration
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

运行结果。

SpringBoot与Redis缓存_第11张图片

我们在创建一个关于Department的缓存查询。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package com.hph.cache.bean;

public class Department {

private Integer id;
private String departmentName;


public Department() {
super();
// TODO Auto-generated constructor stub
}
public Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDepartmentName() {
return departmentName;
}
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
@Override
public String toString() {
return "Department [id=" + id + ", departmentName=" + departmentName + "]";
}

}
1
2
3
4
5
6
7
8
9
10
11
package com.hph.cache.mapper;

import com.hph.cache.bean.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface DeptMapper {
@Select("SELECT * FROM department WHERE id = #{id}")
Department getDeptById(Integer id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.hph.cache.service;

import com.hph.cache.bean.Department;
import com.hph.cache.mapper.DeptMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class DeptService {
@Autowired
DeptMapper deptMapper;

@Cacheable(cacheNames = "dept")
public Department getDeptById(Integer id) {
System.out.println("查询部门" + id);
Department department = deptMapper.getDeptById(id);

return department;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.hph.cache.controller;

import com.hph.cache.bean.Department;
import com.hph.cache.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DeptContorller {

@Autowired
DeptService deptService;

@GetMapping("/dept/{id}")
public Department getDept(@PathVariable("id") Integer id) {
return deptService.getDeptById(id);
}
}

SpringBoot与Redis缓存_第12张图片

SpringBoot与Redis缓存_第13张图片

然而当我们再次请求缓存的时候。

SpringBoot与Redis缓存_第14张图片

5个属性要映射,这是因为我们自定义的那个缓存管理器是属于员工的而不是部门,因此字段对应不上去。同时我们也发现了缓存数据第一次可以存入Redis,第二次从缓存中查询就不能够反序列化回来了。这是因为当我们存储dept的json数据时,CacheManager默认使用的是RedisTemplate操作Redis,只能将Employee数据反序列化。

我们需要配置以下缓存管理器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Bean
public RedisTemplate deptRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer ser = new Jackson2JsonRedisSerializer(Department.class);
template.setDefaultSerializer(ser);
return template;
}

@Bean
public RedisCacheManager deptCacheManager(RedisTemplate deptRedisTemplate){
RedisCacheManager cacheManager = new RedisCacheManager(deptRedisTemplate);
cacheManager.setUsePrefix(true);
return cacheManager;
}

给Employee Dept分别指定缓存类别

1
2
3
4
5
6
7
@CacheConfig(cacheNames = "emp",cacheManager = "employeeCacheManager")
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;
....
}
1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class DeptService {
@Autowired
DeptMapper deptMapper;

@Cacheable(cacheNames = "dept",cacheManager = "deptCacheManager")
public Department getDeptById(Integer id) {
System.out.println("查询部门" + id);
Department department = deptMapper.getDeptById(id);
return department;
}
}

SpringBoot与Redis缓存_第15张图片

在MyRedisConfig中指定一个主类

1
2
3
4
5
6
7
@Primary
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate empRedisTemplate){
RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
cacheManager.setUsePrefix(true);
return cacheManager;
}

我们把Rdis的数据清空下

SpringBoot与Redis缓存_第16张图片

由于默认指定的是employeeCacheManager我们可以在

1
2
3
4
5
@CacheConfig(cacheNames = "emp")EmployeeService上省略。
@Service
public class EmployeeService {
@Autowired
EmployeeMapper employeeMapper;

在实际开发中不应该将employeeCacheManager作为默认指定,应该将RedisCacheManager作为默认指定。@Primary将某个缓存管理器作为默认的

另外我们可以选择编码的方式l来配置缓存管理器。

在DeptService中。

1
2
3
4
5
6
7
8
9
10
11
12
@Qualifier("deptCacheManager")
@Autowired
RedisCacheManager deptCacheManager;

public Department getDeptById(Integer id) {
System.out.println("查询部门" + id);
Department department = deptMapper.getDeptById(id);
//获取某个缓存
Cache dept = deptCacheManager.getCache("dept");
dept.put("缓存操作1", department);
return department;
}

SpringBoot与Redis缓存_第17张图片

你可能感兴趣的:(SpringBoot与Redis缓存)