Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

在现代 Web 应用中,缓存是提升系统性能和减少数据库压力的重要手段。在最基本的SSM开发过程中,Springboot可以方便的将缓存和Redis集成,可以十分便捷的用注解开发。

个人情怀让我使用fastjson。升级到fastjson2后,原本的autotype被废除,不管是官方给出的代码示例还是论坛社区,都没有对泛型数据的支持,对于特定需求使用起来很不方便。

泛型:如果你有一个普通 Java Bean (FileEntity) ,那么List, Map<String, Object> 等就是泛型集合

1 引入依赖

  • Springboot版本采用3.2.3
pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.57</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring6</artifactId>
<version>2.0.57</version>
</dependency>
</dependencies>

2 创建自定义序列器

  • 自定义 FastJSON2 序列化器(支持泛型)
FastJsonRedisSerializer.java
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
package com.example.demo.config;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.TypeReference;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.lang.reflect.Type;

public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

private static final JSONReader.Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(
"com.example.demo.entity.", // 替换为你的实体包名
"java.util.ArrayList",
"java.util.HashMap"
);

private final Type type;

public FastJsonRedisSerializer(Type type) {
this.type = type;
}

public FastJsonRedisSerializer(Class<T> clazz) {
this.type = clazz;
}

@Override
public byte[] serialize(T t) throws SerializationException {
if (t == null) return new byte[0];
return JSON.toJSONBytes(t, JSON.Feature.WriteClassName);
}

@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length == 0) return null;
return JSON.parseObject(bytes, type, AUTO_TYPE_FILTER, JSON.Feature.SupportAutoType);
}
}
  • String 类型专用序列化器
FastJson2StringRedisSerializer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.demo.config;

import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.StandardCharsets;

public class FastJson2StringRedisSerializer implements RedisSerializer<String> {

@Override
public byte[] serialize(String s) throws SerializationException {
return s == null ? new byte[0] : s.getBytes(StandardCharsets.UTF_8);
}

@Override
public String deserialize(byte[] bytes) throws SerializationException {
return bytes == null || bytes.length == 0 ? null : new String(bytes, StandardCharsets.UTF_8);
}
}

3 配置Config

  • 配置 RedisTemplate
RedisConfig.java
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
package com.example.demo.config;

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.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);

FastJsonRedisSerializer<Object> valueSerializer = new FastJsonRedisSerializer<>(Object.class);
FastJson2StringRedisSerializer stringSerializer = new FastJson2StringRedisSerializer();

template.setKeySerializer(stringSerializer); // key: String
template.setHashKeySerializer(stringSerializer); // hashKey: String
template.setValueSerializer(valueSerializer); // value: 泛型对象
template.setHashValueSerializer(valueSerializer); // hashValue: 泛型对象
template.setDefaultSerializer(valueSerializer);
template.setStringSerializer(stringSerializer);
template.afterPropertiesSet();

return template;
}
}
  • 配置 RedisCacheManager(用于 @Cacheable 等注解)
RedisCacheConfig.java
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
package com.example.demo.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.time.Duration;

@Configuration
@EnableCaching
public class RedisCacheConfig {

@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);

RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer))
.entryTtl(Duration.ofMinutes(30)); // 设置缓存过期时间

return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}

到此配置全部完毕

4 Spring Cache 注解详解与使用示例

4.1 @Cacheable

用途:标记方法返回值需被缓存。若缓存存在,则直接返回;否则执行方法体并缓存结果。

1
2
@Cacheable(value = "fileCache", key = "#id")
FileEntity getFileById(UUID id);

4.2 @CachePut

用途:更新缓存,总是执行方法并将结果写入缓存。

1
2
@CachePut(value = "fileCache", key = "#result.id")
FileEntity saveFile(FileEntity file);

4.3 @CacheEvict

用途:清除缓存中的条目。

1
2
@CacheEvict(value = "fileCache", key = "#id")
void deleteFile(UUID id);

清空整个缓存区域:

1
2
@CacheEvict(value = "fileListCache", allEntries = true)
void clearAllFiles();

4.4 @CacheConfig

用途:类级别共享缓存配置

1
2
3
4
5
6
7
@CacheConfig(cacheNames = "fileCache")
public class FileService {
@Cacheable(key = "#id")
public FileEntity getFileById(UUID id) {
return fileMapper.selectById(id);
}
}

4.5 业务层使用示例

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
@Service
public class FileService {

@Autowired
private FileRepository fileRepository;

@Cacheable(value = "fileCache", key = "#id")
public FileEntity getFileById(UUID id) {
return fileRepository.selectById(id);
}

@CachePut(value = "fileCache", key = "#result.id")
public FileEntity saveFile(FileEntity file) {
fileRepository.insert(file);
return file;
}

@CacheEvict(value = "fileCache", key = "#id")
public void deleteFile(UUID id) {
fileRepository.deleteById(id);
}

@Cacheable(value = "fileListCache", key = "'all'")
public List<FileEntity> getAllFiles() {
return fileRepository.selectAll();
}
}

评论