在 SpringBoot 2.x 中,已经将地层的 Jedis 替换为了 Letteuce了。
看下底层依赖,我是使用的是 SpringBoot 2.1.9.RELEASE 这个版本,下面可以可以看到 lettuce 就是基于 netty 实现了。
引入依赖包
org.springframework.boot
spring-boot-starter-data-redis
org.projectlombok
lombok
provided
对应的配置文件 application.properties 内容
# 服务端口
server.port=8080
# redis 配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=
在 @Autowired RedisTemplate 之前,我们先看下 Spring Boot 源码上面的 RedisTemplate 是怎么回事?我们自己打开对应的类文件,RedisAutoConfiguration.java
@Configuration
@ConditionalOnClass({RedisOperations.class})
@EnableConfigurationProperties({RedisProperties.class})
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {}
@Bean
@ConditionalOnMissingBean( // 这里我们是可以自己写 redisTemplate 覆盖
name = {"redisTemplate"}
)
public RedisTemplate
这里我们仔细看下,这里使用了 @ConditionalOnMissingBean 注解,说明如果是程序中存在了对应的 redisTemplate Bean,就不会去加载(其实这个意思是说,我们可以自己去覆盖这个)。
下面会介绍下自己定义,新建一个 RedisConfig.class 类,
package com.wq.redis.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
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 org.springframework.data.redis.serializer.StringRedisSerializer;
import java.util.Objects;
@Configuration
public class RedisConfig {
/**
* 自己定义一个 redisTemplate,并设置相关参数
* @param redisConnectionFactory RedisConnectionFactory
* @return RedisTemplate
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
// 为了我们自己开发方便,一般直接用
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
// Json 序列化配置
// 使用 Jackson2JsonRedisSerializer 替换默认的 JdkSerializationRedisSerializer 来序列化和反序列化 redis的value值
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Objects.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式
// template.setValueSerializer(jackson2JsonRedisSerializer); // value序列化方式采用jackson
template.setValueSerializer(stringRedisSerializer); // value 采用 String 的方式
template.setHashValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson
template.afterPropertiesSet();
return template;
}
}
package com.wq.redis.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class RedisOperator {
@Autowired
private RedisTemplate redisTemplate;
public String getKey(String key){
// 操作 redis 的时候就感觉这里比较奇怪,看下面的截图
return (String) redisTemplate.opsForValue().get(key);
}
public void setKey(String key, String value){
redisTemplate.opsForValue().set(key, value);
}
}
对于redis 的操作,这里其实是封装的一层,需要知道自己操作的是哪种类型,然后使用对应的 API 去操作。
增加一个 封装返回值的类,就在这里使用了 lombok 插件
package com.wq.redis.controller;
import lombok.Data;
@Data
public class ResultModel {
private boolean success = false; // 是否调用成功
private String message = "";
private T body ; // 返回值
public ResultModel(){}
public ResultModel(boolean success, String message){
this.success = success;
this.message = message;
}
public ResultModel(boolean success, String message, T object){
this(success, message);
this.body = object;
}
}
外部访问的入口
package com.wq.redis.controller;
import com.wq.redis.service.RedisOperator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
@CrossOrigin(origins = "*")
public class Router {
@Autowired
private RedisOperator redisOperator;
@GetMapping(value = "/getKey")
public ResultModel getKey(String body) {
ResultModel resultModel = new ResultModel();
System.out.println(body);
try {
String result = redisOperator.getKey(body);
resultModel.setBody(result);
resultModel.setSuccess(true);
resultModel.setMessage("请求成功!");
} catch (Exception e) {
resultModel.setSuccess(false);
resultModel.setMessage(e.getMessage());
resultModel.setBody(new HashMap<>());
}
return resultModel;
}
@RequestMapping("setKey")
public ResultModel setKey(String key, String value){
ResultModel resultModel = new ResultModel();
System.out.println("key:" +key+", value :" + value);
try {
redisOperator.setKey(key, value);
resultModel.setBody("");
resultModel.setSuccess(true);
resultModel.setMessage("请求成功!");
} catch (Exception e) {
resultModel.setSuccess(false);
resultModel.setMessage(e.getMessage());
resultModel.setBody(new HashMap<>());
}
return resultModel;
}
}
先设置一个值
获取刚刚设置的值
我们分析下框架底层是怎么做 set 方法的
我们通过 idea 工具点击 RedisOperator 中的 set 方法,
redisTemplate.opsForValue().set(key, value)
进去会发现,set 方法是在 ValueOperations
然后继续看实现类,会发现就只有一个实现类 DefaultValueOperations.java ,这里会有个 execute 方法,我们继续进去看
这里我们先看下 ValueDeserializingRedisCallback 定义了什么东西
上面有一个 doInRedis 方法,这个就是下面 execute 方法回调的位置,然后这里的 inRedis,就是 DefaultValueOperations 中 set 方法声明的那个匿名内部类中定义的 inRedis 方法。
接着我们一路点击 execute 进来,可以看到下面的这个 execute 方法(这个方法是很多redis 操作最后执行调用的方法,是一个核心的方法),我们刚刚传入对象在这里其实是一个回调方法
最后总结下
通过 RedisTemplate 里面的 execute 方法,调用传入的 RedisCallback的 doInRedis方法完成 对RedisCallback回调,在 ValueDeserializingRedisCallback 类中去回调最终的方法。