与Redis相关知识可查看《Redis系列记录》
一、相关配置
1.1 pom.xml
<!-- spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
1.2 application.properties
spring.cache.type=redis
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.lettuce.max-idle=10
# 连接池中的最小空闲连接
spring.redis.lettuce.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000ms
logging.level.com.example.redisdemo=debug
1.3 RedisConfig
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisSerializer fastJson2JsonRedisSerialize(){
return new FastJson2JsonRedisSerialize<Object>(Object.class);
}
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,RedisSerializer fastJson2JsonRedisSerialize){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置Key的序列化采用StringRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//设置值的序列化采用FastJsonRedisSerializer
redisTemplate.setValueSerializer(fastJson2JsonRedisSerialize);
redisTemplate.setHashValueSerializer(fastJson2JsonRedisSerialize);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
// 生成一个默认配置,通过config对象即可对缓存进行自定义配置
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
// 设置缓存的默认过期时间,也是使用Duration设置
config = config.entryTtl(Duration.ofMinutes(5))
// 设置 key为string序列化
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
// 设置value为fastJson序列化
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJson2JsonRedisSerialize()))
// 不缓存空值
.disableCachingNullValues();
// 使用自定义的缓存配置初始化一个cacheManager
return RedisCacheManager
.builder(redisConnectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
}
1.4 FastJson2JsonRedisSerialize
public class FastJson2JsonRedisSerialize<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private Class<T> clazz;
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//如果遇到反序列化autoType is not support错误,请添加并修改一下包名到bean文件路径
//ParserConfig.getGlobalInstance().addAccept("com.example.redisdemo.domain");
}
public FastJson2JsonRedisSerialize(Class clazz){
super();
this.clazz = clazz;
}
/**
* 序列化
* @param t
* @return
* @throws SerializationException
*/
@Override
public byte[] serialize(T t) throws SerializationException {
if (null == t){
return new byte[0];
}
return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
/**
* 反序列化
* @param bytes
* @return
* @throws SerializationException
*/
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (null == bytes || bytes.length <= 0){
return null;
}
String str = new String(bytes,DEFAULT_CHARSET);
return (T) JSON.parseObject(str,clazz);
}
}
二、测试
2.1 User
public class User implements Serializable {
private static final long serialVersionUID = 8391377903506428591L;
private Integer id;
private String username;
private String password;
public User(){
super();
}
public User(Integer id, String username, String password){
this.id = id;
this.username = username;
this.password = password;
}
getter... setter... toString()...
}
2.2 UserService UserServiceImpl
public interface UserService {
public User addUser(User user);
public User getUser(Integer id);
public void delUser(Integer id);
}
这里不用Mapper了,使用Map来模拟一下数据库操作,
@CacheConfig(cacheNames = "user")
@Service("userService")
public class UserServiceImpl implements UserService {
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
private static final Map<String,Object> usersMap = new HashMap<>();
@Override
@CachePut(key = "#user.id")
public User addUser(User user) {
usersMap.put(user.getId().toString(),user);
logger.info("存入数据库【User】= {}",user);
return user;
}
@Override
@Cacheable(key = "#id",unless = "#result == null ")
public User getUser(Integer id){
User user = (User) usersMap.get(id.toString());
logger.info("从数据库获取【User】= {}",user);
return user;
}
@Override
@CacheEvict(key = "#id")
public void delUser(Integer id) {
usersMap.remove(id.toString());
logger.info("从数据库删除【User】");
}
}
2.3 Test
@Test
void testOne() {
userService.addUser(new User(1,"aa","123"));
User user = userService.getUser(1);
logger.debug("【User】={}",user);
}
2.4 结果
这里可以看到只打印了添加日志和最后的查询结果,并没有打印查询日志,所以证明缓存生效。
三、相关知识点与坑
3.1 报com.alibaba.fastjson.JSONException: autoType is not support
解决方法:在FastJson2JsonRedisSerialize 类中添加 这两句代码都可以解决反序列化问题
static {
ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
//如果遇到反序列化autoType is not support错误,请添加并修改一下包名到bean文件路径
//ParserConfig.getGlobalInstance().addAccept("com.example.redisdemo.domain");
}
3.2 Cache 'redisCache' does not allow 'null' values
解决方法:添加unless = "#result == null"
@Cacheable(key = "#id",unless = "#result == null ")
缓存相关注解可查看第(十五)篇:Springboot缓存
四、RedisTemplate
上文中使用注解来完成缓存操作,下文中使用redisTemplate来操作缓存
在测试类中添加:
@Resource(name = "redisTemplate")
private ValueOperations<String,Object> vo;
@Test
void testFour(){
vo.set("java",new User(1,"bb","111"));
User user = (User) vo.get("java");
System.out.println(user);
}
执行: 也是好用的
那么为什么redisTemplate可以直接注入给五种数据类型的XXOperations使用呢?原来有属性编辑器