0%

SpringBoot-缓存

SpringBoot-缓存

几个重要概念&缓存注解

专业术语 解释
Cache 缓存接口,定义缓存操作,实现有RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager 缓存管理器,管理各种缓存(Cache)组件
@Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
@CacheEvict 清空缓存
@CachePut 更新缓存,保证方法被调用,又希望结果被缓存
@EnableCaching 开启基于注解的缓存
keyGenerator 缓存数据时key生成策略
serialize 缓存数据时value序列化策略

缓存使用

1、启动文件配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* @author zephon
* 一、搭建基本环境
* 1. 导入数据文件,创建表
* 2. 创建javabean封装数据
* 3. 整合mybatis操作数据库
* 1.配置数据源信息
* 2.使用注解版mybatis
* 1)使用@Mapperscan指定mapper接口所在的包
* 二、快速使用缓存
* 步骤:
* 1.开启基于注解的缓存
* 2.瓢缓存注解即可
*/
// 使用@EnableCaching开启缓存
@EnableCaching
@MapperScan("com.zephon.mapper")
@SpringBootApplication
public class Springboot01CacheApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01CacheApplication.class, args);
}

}

2、service配置

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// @CacheConfig: 定义公共缓存配置
//@CacheConfig(cacheNames = "user")
@Service
public class UserService {
@Autowired
UserMapper userMapper;

/**
* 将方法运行结果进行缓存
* 以后再要相同的数据,直接从缓存从获取,不用调用方法
* CacheManager管理多个Cache组件,对缓存的真正CRUD操作在Cache组件中,
* 每一个缓存组件有自己唯一的名字
* 几个属性:
* cacheNames/value:指定缓存组件的名字
* key:缓存数据时使用的key,值可以使用SpEL表达式
* keyGenerator:key生成器,可以自己指定key的生成器的组件的id
* key和keyGenerator二选一使用
* cacheManager:指定缓存管理器
* condition:指定符合条件的情况下缓存
* unless:否定缓存;当unless指定的条件为true,方法返回值就不会缓存,可以获取结果进行判断
* sync:是否使用异步模式
*
* @param id
* @return
*/
//@Cacheable(cacheNames = "user",condition = "#id>0",unless = "#result==null")
//@Cacheable(cacheNames = "user",key = "#root.methodName+'['+#id+']'")
// @Cacheable(cacheNames = "user",keyGenerator = "myKeyGenerator")
@Cacheable(cacheNames = "user")
public User getUser(Integer id) {
System.out.println("查询" + id + "员工");
return userMapper.getUserById(id);
}

/**
* @param user
* @return
* @CachePut:既调用方法,又更新缓存数据,同步更新缓存 修改了数据库的某个数据,同时更新缓存
* 运行时机:
* 1、先调用目标方法
* 2、将目标方法的结果缓存
* <p>
* 测试步骤:
* 1. 查询1号用户,查到的结果会放到缓存中:
* key:1 value:username:原值
* 2. 以后查询还是之前的结果
* 3. 更新1号员工:[username:新值]
* 将方法的返回值放进缓存
* key:传入的user对象,值:返回的user对象
* 4. 查询1号用户
* 应该是更新后的用户
* key="#user.id":使用传入参数的员工的id
* key="#result.id":使用返回后的id
* @Cacheable的key是不能使用result的 为什么是没更新前的?[1号员工没有在缓存中更新]
* 解决:使用对应的key
*/
// @CachePut(value = "user")
@CachePut(value = "user", key = "#result.id")
public User update(User user) {
System.out.println("更新" + user.getId() + "员工");
userMapper.updateUser(user);
return user;
}

/**
* @CacheEvict:缓存清除 key:指定要清除的数据
* allEntries = true:指定清除缓存中所有数据
* beforeInvocation = true,默认是false:缓存的清除是否在方法之前执行
* 默认代表在方法执行之后执行,如果出现异常缓存就不清除
*/
@CacheEvict(value = "user", key = "#id")
public void deleteUser(Integer id) {
System.out.println("删除" + id);
// userMapper.deleteUser(id);
}

// @Caching:定义复杂缓存规则
@Caching(
cacheable = {
@Cacheable(value="user",key = "#username")
},
put = {
// 意思是通过用户名查询出对应的用户后,
// 会在缓存中放入另一组将id作为key的值
// 再根据id查询时就会直接从缓存中取值
@CachePut(value = "user",key = "#result.id"),
//@CachePut(value = "user",key="#result.password")
}
)
public User getUserByUsername(String username) {
return userMapper.getUserByUsername(username);
}

}

其中可以自己写keyGenerator,如:

1
2
3
4
5
6
7
8
9
10
11
12
@Configuration
public class MyCacheConfig {
@Bean("myKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator(){
@Override
public Object generate(Object target, Method method, Object... params) {
return method.getName()+"["+ Arrays.asList(params).toString()+"]";
}
};
}
}

缓存注解中使用的SpEL表达式

名字 位置 描述 示例
methodName root object 当前被调用的方法名 #root.methodName
method root object 当前被调用的方法 #root.method.name
target root object 当前被调用的目标对象 #root.target
targetClass root object 当前被调用的目标对象类 #root.targetClass
args root object 当前被调用的方法的参数列表 #root.args[0]
caches root object 当前被调用使用的缓存列表 #root.caches[0].name
argument name evaluation context 方法参数名字,可以直接 #参数名,也可以使用 #p0或#a0的形式,0代表参数的索引 #iban、#a0、#p0
result evaluation context 方法执行后的返回值(仅当方法执行之后的判断有效,如'unless','cache put'的表达式'cache evict'的表达式beforeInvocation=false) #result

整合redis作为缓存

  1. 使用docker安装redis

    1
    2
    docker pull redis
    docker run --name myredis -d -p 6379:6379 redis
  2. 引入redis的starter

    1
    2
    3
    4
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  3. 配置redis

    1
    spring.redis.host=127.0.0.1
  4. 如果是对象,则javabean需要实现Serializable,同时可自定义配置规则

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Configuration
    public class MyRedisConfig {
    @Bean
    public RedisTemplate<Object, User> userRedisTemplate(
    RedisConnectionFactory redisConnectionFactory)
    throws UnknownHostException{
    RedisTemplate<Object,User> template = new RedisTemplate<>();
    template.setConnectionFactory(redisConnectionFactory);
    Jackson2JsonRedisSerializer<User> ser = new Jackson2JsonRedisSerializer<User>(User.class);
    template.setDefaultSerializer(ser);
    return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    Jackson2JsonRedisSerializer<User> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(User.class);
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
    .entryTtl(Duration.ofSeconds(100)) // 设置缓存有效期100s
    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer));
    return RedisCacheManager
    .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
    .cacheDefaults(redisCacheConfiguration).build();
    }
    }

    自定义template使用

    1
    2
    3
    4
    // 1.将数据以json方式保存
    // 1. 自己将对象转json
    // 2. redisTemplate默认的序列化规则
    userRedisTemplate.opsForValue().set("user",userMapper.getUserById(1));