关于官网描述的特性,主要的几点如下:
1.支持jedis和lettuce,不支持JRedis和SRP
特别介绍下在2.x版本中,默认是使用lettuce;1.x版本的时候,默认使用的就是Jedis;关于两个的区别:
2.异常的处理转换
3.RedisTemplate提供的是redis操作的高级抽象
4.Pubsub 支持
5.redis的哨兵模式以及集群模式的支持
6.JDK, String, JSON and Spring Object/XML的序列化映射
……………………后面的就不翻译了,英语也不太好,翻译的也不太好………………
org.springframework.boot
spring-boot-starter-parent
2.1.2.RELEASE
1.8
2.9.0
org.springframework.boot
spring-boot-starter-data-redis
io.lettuce
lettuce-core
redis.clients
jedis
org.springframework.boot
spring-boot-starter-web
yml文件的配置
spring:
redis:
database: 0
host: localhost
port: 6379
timeout:
nano: 2
seconds: 2
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
查看redis的配置类org.springframework.boot.autoconfigure.data.redis.RedisProperties,与springboot1.x不同的是
timeout属性由之前的int类型,换成了java8的类型;(下部分,会用源码的形式,查看timeout的类型的改变)
Jedis与Lettuce的连接属性进行了区别;
如果想使用Lettuce,进行整合,只需要把引入spring-boot-starter-data-redis,默认就是Lettuce,只需要把连接池属性改成Lettuce;
如果是集群搭建,配置文件如下
spring:
redis:
cluster:
nodes:
- 192.168.112.129:7000
- 192.168.112.129:7001
- 192.168.112.129:7002
- 192.168.112.129:7003
- 192.168.112.129:7004
- 192.168.112.129:7005
max-redirects: 3
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
在测试过程中,使用redis的客户端(界面化工具),直接获取数据,会遇到序列化的问题;在org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration自动装配类中,实例化了redisTemplate和stringRedisTemplate;
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate
我们简单查看源码,就可以看到序列化的问题;
先看RedisTemplate
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.RedisAccessor#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
boolean defaultUsed = false;
if (defaultSerializer == null) {
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
if (enableDefaultSerializer) {
if (keySerializer == null) {
keySerializer = defaultSerializer;
defaultUsed = true;
}
if (valueSerializer == null) {
valueSerializer = defaultSerializer;
defaultUsed = true;
}
if (hashKeySerializer == null) {
hashKeySerializer = defaultSerializer;
defaultUsed = true;
}
if (hashValueSerializer == null) {
hashValueSerializer = defaultSerializer;
defaultUsed = true;
}
}
if (enableDefaultSerializer && defaultUsed) {
Assert.notNull(defaultSerializer, "default serializer null and not all serializers initialized");
}
if (scriptExecutor == null) {
this.scriptExecutor = new DefaultScriptExecutor<>(this);
}
initialized = true;
}
看代码,就可以看到,在没有指定keySerializer,valueSerializer的情况下,默认使用的是defaultSerializer;
我们在看StringRedisTemplate template = new StringRedisTemplate();构造方法,相信大家也都能看懂,调用了set的方法,把序列化的类,直接使用string的形式
public StringRedisTemplate() {
setKeySerializer(RedisSerializer.string());
setValueSerializer(RedisSerializer.string());
setHashKeySerializer(RedisSerializer.string());
setHashValueSerializer(RedisSerializer.string());
}
所有说,我们对序列化以及反序列化的时候,一般情况下,json串-->POJO对象转换的时候,自己手动转换,可以直接使用StringRedisTemplate;而需要自动转换的时候,可以自定义一个RedisTemplate的实例,使用Jackson2JsonRedisSerializer的形式,序列化valueSerializer,代码如下:
我把key序列了字符换,val用的jackson2JsonRedisSerializer序列化,大家可以根据实际情况来操作;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(
Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setKeySerializer(RedisSerializer.string());
// template.setValueSerializer(RedisSerializer.string());
// template.setHashKeySerializer(RedisSerializer.string());
// template.setHashValueSerializer(RedisSerializer.string());
return template;
}
}
测试代码如下,比较简单,大家自行测试即可
@RunWith(SpringRunner.class)
@SpringBootTest
public class BadgerRedisSingleApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void testRedisTemplate() {
final String key = "obj";
List strs = Arrays.asList("d", "b", "a", "c", "a");
ValueOperations opsForValue = redisTemplate.opsForValue();
opsForValue.set(key, strs);
Object str = opsForValue.get(key);
System.out.println(str);
}
@Test
public void testString() {
final String key = "test";
ValueOperations opsForValue = stringRedisTemplate.opsForValue();
opsForValue.set(key, "test");
String str = opsForValue.get(key);
System.out.println(str);
}
}
至此,redis的整合就完成了,下面对源码进行讲解下,不感兴趣的同学,可以略过了………………
我们先看org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration自动装配类
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
里面的内容,上面已经介绍过了,我们直接看Import导入的JedisConnectionConfiguration的实例,我们使用的Jedis,排除了Lettuce,所以只有JedisConnectionConfiguration可以实例化成功
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
JedisConnectionConfiguration(RedisProperties properties,
ObjectProvider sentinelConfiguration,
ObjectProvider clusterConfiguration,
ObjectProvider builderCustomizers) {
super(properties, sentinelConfiguration, clusterConfiguration);
this.properties = properties;
this.builderCustomizers = builderCustomizers;
}
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return createJedisConnectionFactory();
}
我们先看构造方法,实例的时候自动注入了以下属性,并把properties和builderCustomizers存起来了
RedisProperties properties,:这个就yml配置文件里的属性的类
ObjectProvider
ObjectProvider
ObjectProvider
再看redisConnectionFactory()方法,也是这个类中最重要的部分,为RedisAutoConfiguration自动装配的时候,提供的RedisConnectionFactory 这个连接工厂;
private JedisConnectionFactory createJedisConnectionFactory() {
//获取jedis客户端的配置
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration();
//创建哨兵模式的连接工厂(yml只配置了普通模式,此处不判断不生效)
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), clientConfiguration);
}
//创建集群模式的连接工厂(yml只配置了普通模式,此处不判断不生效)
if (getClusterConfiguration() != null) {
return new JedisConnectionFactory(getClusterConfiguration(),
clientConfiguration);
}
//创建普通模式的连接工厂
return new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
}
我们先看第一行的方法:getJedisClientConfiguration() 获取jedis客户端的配置
private JedisClientConfiguration getJedisClientConfiguration() {
//创建默认的JedisClientConfigurationBuilder对象,并设置useSsl,readTimeout,connectTimeout属性
JedisClientConfigurationBuilder builder = applyProperties(JedisClientConfiguration.builder());
//获取yml文件配置的pool,并配置JedisPoolConfig 设置到poolConfig属性中
RedisProperties.Pool pool = this.properties.getJedis().getPool();
if (pool != null) {
applyPooling(pool, builder);
}
//处理url
if (StringUtils.hasText(this.properties.getUrl())) {
customizeConfigurationFromUrl(builder);
}
//执行定制器
customize(builder);
return builder.build();
}
先看第一个JedisClientConfiguration.builder()方法,构建JedisClientConfigurationBuilder对象
class DefaultJedisClientConfigurationBuilder implements JedisClientConfigurationBuilder,
JedisPoolingClientConfigurationBuilder, JedisSslClientConfigurationBuilder {
private boolean useSsl;
private @Nullable SSLSocketFactory sslSocketFactory;
private @Nullable SSLParameters sslParameters;
private @Nullable HostnameVerifier hostnameVerifier;
private boolean usePooling;
private GenericObjectPoolConfig poolConfig = new JedisPoolConfig();
private @Nullable String clientName;
private Duration readTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private Duration connectTimeout = Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);
private DefaultJedisClientConfigurationBuilder() {}
上述yml配置中spring.timeout.nano =2 以及spring.timeout.seconds =2 ;在applyProperties中,设置了readTimeout 和connectTimeout属性,这个属性默认的就是Duration.ofMillis(Protocol.DEFAULT_TIMEOUT);后面的配置,大家就自己往下看;
我们再看创建JedisConnectionFactory的最后一步,在createJedisConnectionFactory()方法中,创建完JedisClientConfiguration,开始创建new JedisConnectionFactory(getStandaloneConfig(), clientConfiguration);
我们直接看JedisConnectionFactory中的获取连接的方法 Jedis jedis = fetchJedisConnector();
public RedisConnection getConnection() {
if (isRedisClusterAware()) {
return getClusterConnection();
}
Jedis jedis = fetchJedisConnector();
String clientName = clientConfiguration.getClientName().orElse(null);
JedisConnection connection = (getUsePool() ? new JedisConnection(jedis, pool, getDatabase(), clientName)
: new JedisConnection(jedis, null, getDatabase(), clientName));
connection.setConvertPipelineAndTxResults(convertPipelineAndTxResults);
return postProcessConnection(connection);
}
protected Jedis fetchJedisConnector() {
try {
if (getUsePool() && pool != null) {
return pool.getResource();
}
Jedis jedis = createJedis();
// force initialization (see Jedis issue #82)
jedis.connect();
potentiallySetClientName(jedis);
return jedis;
} catch (Exception ex) {
throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);
}
}
private Jedis createJedis() {
if (providedShardInfo) {
return new Jedis(getShardInfo());
}
Jedis jedis = new Jedis(getHostName(), getPort(), getConnectTimeout(), getReadTimeout(), isUseSsl(),
clientConfiguration.getSslSocketFactory().orElse(null), //
clientConfiguration.getSslParameters().orElse(null), //
clientConfiguration.getHostnameVerifier().orElse(null));
Client client = jedis.getClient();
getRedisPassword().map(String::new).ifPresent(client::setPassword);
client.setDb(getDatabase());
return jedis;
}
后面的就可以不看了,拿到Jedis实例后,就可以做相关操作了;在创建实例的时候,传入的getConnectTimeout(),getReadTimeout()参数中,就是上述readTimeout属性Duration的toMillis()方法;获取的结果跟springboot1.x版本中的int类型的,是一致的
private int getConnectTimeout() {
return Math.toIntExact(clientConfiguration.getConnectTimeout().toMillis());
}
好了,redis在springboot2.0中,自动装配就是这样的,有兴趣的,还可以继续深入的看下去