本课程我们来看看 Spring Boot 和 Spring Data 模式下面怎么玩?
我们以 Spring Boot 为基础看一下 Redis 在里面是怎么兼容和配置的。
以 Spring Boot 为例分别介绍一下这四种配置方法:
- 基本使用;
- 连接池的使用;
- 高可用连接(master/salve);
- 客户端分片。
如果是 Spring Boot 项目直接添加 spring-boot-starter-data-redis 即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
一旦当我们使用 Spring Boot,其实任何一个 starter 都会引入 spring-boot-autoconfigure 的 jar 包,然后 autoconfigure 就会做很多事情。
我们用 Spring Boot 都知道 starter 的原理(spring-boot-autoconfigure.jar 包里面的 spring.factories 定义了 Spring Boot 默认加载的 AutoConfiguration),因此,打开 spring.factories 文件可以找到 Spring 自动加载了。
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,
org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,
这两个 Configuration 类,我们先打开 RedisAutoConfiguration 的源码 ,来一起看一下里面的关键代码片段。
(1)代码片段一:自动加载 JedisConnectionFactory。
@Bean
@ConditionalOnMissingBean(RedisConnectionFactory.class)
public JedisConnectionFactory redisConnectionFactory()
throws UnknownHostException {
return applyProperties(createJedisConnectionFactory());
}
通过这一段代码可以看到,JedisConnectionFactory 可以自己配置也可以直接用 Spring Boot 给我们提供的默认配置。
(2)代码片段二:查看 createJedisConnectionFactory() 的具体方法。
private JedisConnectionFactory createJedisConnectionFactory() {
//这里会取我们配置文件里面的配置,如果没有配置,new 一个默认连接池
JedisPoolConfig poolConfig = this.properties.getPool() != null
? jedisPoolConfig() : new JedisPoolConfig();
//如果配置了Sentinel就取哨兵的配置直接返回
if (getSentinelConfig() != null) {
return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
}
//如果没有配置中Sentinel,而配置了Cluster切片的配置方法,它就取Cluster的配置方法
if (getClusterConfiguration() != null) {
return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
}
//默认取连接pool的配置方法
return new JedisConnectionFactory(poolConfig);
}
.......
//取配置文件里面的Pool的配置
private JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
RedisProperties.Pool props = this.properties.getPool();
config.setMaxTotal(props.getMaxActive());
config.setMaxIdle(props.getMaxIdle());
config.setMinIdle(props.getMinIdle());
config.setMaxWaitMillis(props.getMaxWait());
return config;
}
.......
//JedisPoolConfig的类默认构造函数
public class JedisPoolConfig extends GenericObjectPoolConfig {
public JedisPoolConfig() {
this.setTestWhileIdle(true);
this.setMinEvictableIdleTimeMillis(60000L);
this.setTimeBetweenEvictionRunsMillis(30000L);
this.setNumTestsPerEvictionRun(-1);
}
}
通过这段代码可以看出来,前面讲到的四种 Jedis 的配置方式,这里默认只支持了三种。而上一节我们讲的第一种单例的基本模式 Spring 不推荐使用,而 Spring 将 Pool 作为默认的配置方法。
可以看出:
其一,三种配置中 Pool(连接池)、Sentinel(哨兵,master/slave)和 Cluster(切片)只能选择一种来配置。
其二,只需要配置我们的配置文件就可以了,剩下的就交给 Spring 的 JedisConnectionFactory。
(3)代码片段三:查看 RedisAutoConfiguration 的关键源码。
@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
......
}
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
/**
* Database index used by the connection factory.
*/
private int database = 0;
/**
* Redis url, which will overrule host, port and password if set.
*/
private String url;
/**
* Redis server host.
*/
private String host = "localhost";
/**
* Login password of the redis server.
*/
private String password;
/**
* Redis server port.
*/
private int port = 6379;
/**
* Enable SSL.
*/
private boolean ssl;
/**
* Connection timeout in milliseconds.
*/
private int timeout;
private Pool pool;
private Sentinel sentinel;
private Cluster cluster;
......
}
这里省略了一些中间的代码,有兴趣的读者可以看一下源码,到这一步,其实已经发现了配置文件应该怎么配置了(PS:字段名字 +spring.redis 前缀就是 application.yml 里面的 key了)。如果使用 Intellij IDEA,当在 application.properties 输入 spring.redis 开头的 key 值的时候会给我们提示这类里面的属性值。
(4)代码片段四: RedisConfiguration 关键源码。
/**
* Standard Redis configuration.
*/
@Configuration
protected static class RedisConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
由以上代码可知,我们可以使用类 RedisTemplate 或者 StringRedisTemplate 来直接操作 Redis 的 client 的相关 API(PS:后面有介绍)。
通过源码分析得出来了和第二节相对应的配置方法,具体如下。
只需要在我们的 application.properties 里面增加如下配置即可。
spring.redis.host=127.0.0.1 #redis服务器地址
spring.redis.port=6379 #端口
spring.redis.timeout=6000 #连接超时时间 毫秒
spring.redis.pool.max-active=8 # 连接池的配置,最大连接激活数
spring.redis.pool.max-idle=8 # 连接池配置,最大空闲数
spring.redis.pool.max-wait=-1 # 连接池配置,最大等待时间
spring.redis.pool.min-idle=0 # 连接池配置,最小空闲活动连接数
调用的地方就可以直接引用 redisTemplate 进行使用了。这时候启动的 pool 的很多设置,如果不配置 pool 的一些相关参数,我们看源码的话,也会发现启动 JedisPoolConfig 里面的默认配置(源码分析里面的代码片段二里面的内容)。
实例 DEMO1:
//例如某个Service里面只需要引用RedisTemplate类即可:
@Autowired
private static RedisTemplate redisTemplate;
//某个service方法中,直接调用redisTemplate操作redis的set集合,储存key和value
public Object cacheAround(String key,String value) throws Throwable {
....
//直接调用redisTemplate操作redis的set集合,储存key和value
redisTemplate.opsForSet().add(key,value);
//这里不需要,关心redisTemplate里面配置的是连接池,还是哨兵,还是cluster。
.....
}
我们可以看 RedisProperties 的 Sentinel 类得出如下配置方式:只需要在 application.properties 里面配置如下内容即可。
spring.redis.sentinel.master= redis_master_name #master 名字
spring.redis.sentinel.nodes= 127.0.0.1:63791,127.0.0.1:63792 #我们配置多个哨兵,用","分割即可
使用的地方保持不变,这就体现了 Spring 的大牛们超强的封装思想,当改变 Redis 的 Server 使用方式的时候,让 redisTemplate 使用的地方不受任何影响,这里体现了 Java 的封装思想(因为我们只需要改变配置文件即可,调用的地方不需要发生任何改变,如上面的实例 DEMO1)。
RedisProperties 的 Cluster 类得出如下配置方式:只需要在 application.properties 里面配置如下内容即可。
spring.redis.cluster.max-redirects=3 # Maximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodes= 127.0.0.1:6379,127.0.0.1:6376,127.0.0.1:6378# Comma-separated list of "host:port" pairs to bootstrap from.
DEMO2:通过 RedisTemplate 直接操作 key/value。
//声明
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template;
//调用方法
template.opsForValue().set("key","value");
DEMO3:通过注入 resdisTemplate 操作 ValueOperations。
//RedisTemplate还提供了对应的*OperationsEditor,用来通过RedisTemplate直接注入对应的Operation。
//声明
@Resource(name = "redisTemplate")
private ValueOperations<String, Object> vOps;
//调用方法
vOps.set("key","value");
DEMO4:通过注解 HashOperations 操作 Hash 数据结构。
//注入HashOperations对象
@Resource(name = "redisTemplate")
private HashOperations<String,String,Object> hashOps;
//具体调用
Map<String,String> map = new HashMap<String, String>();
map.put("value","code");
map.put("key","keyValue");
hashOps.putAll("hashOps",map);
DEM5:通过 template 操作 Hash 数据结构。
//注入RedisTemplate对象
@Resource(name = "redisTemplate")
private RedisTemplate<String, String> template;
//具体调用
Map<String,String> map = new HashMap<String, String>();
map.put("value","code");
map.put("key","keyValue");
template.opsForHash().putAll("hashOps",map);
我们总结一下 Spring Boot 的配置方法:
- 引入 spring-boot-starter-data-redis.jar 包的依赖。
- 修改 application.properties 文件的三种配置方法即可。
- 直接调用 RedisTemplate 进行 Redis 的相关操作。
当我们没有引入 Spring Boot 的时候怎么办?
添加 Spring Data Redis 的 jar 包依赖即可,maven 方式如下:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.2.RELEASE</version>
</dependency>
Bean 和配置文件的加载:写的优雅一点的话,就是将上面 Spring Boot Redis 的源码分析的部分重新实现一遍,定义自己的 RedisAutoConfiguration 和 RedisProperties 及其 Properties(prefix = "spring.redis")。这里不在重复叙述了。
看一下我们要关心的几个重要的类图:
1)JedisConnectionFactory 里面依赖了 JedicConnection 和 JedisPoolConfig、RedisSentineConfiguration、RedisCLusterConfiguration 的三种配置方法。
2)RedisAutoConfiguration 自动加载了 RedisProperties 的配置文件。
3)RedisTemplate 抽象保证了 Redis 相关的 Operations 方法。
4)StringRedisTemplate 继承和扩展了 RedisTemplate,为我们提供了一种扩展思路。
我们主要关心的类 RedisTemplate 的 API 简单介绍:
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作 hash
redisTemplate.opsForList();//操作 list
redisTemplate.opsForSet();//操作 set
redisTemplate.opsForZSet();//操作有序 set
StringRedisTemplate 与 RedisTemplate 的封装的 Reids 操作要比我们第二节讲的自己调用 Jedis 的 API 的方式更优雅了一步。
而 StringRedisTemplate 与 RedisTemplate 对应 API 详细请查看此文档 Spring Data Redis 官方操作手册 ,这里不是本节的重点介绍对象了,不再赘述。
第 N 种做法:这个时候其实可以对上一节介绍的 RedisUtil 的使用用法做改进和优化了,我们不需要自己去管理 Jedis 的相关的东西了,相应的地方我们直接调用 RedisTemplate 就可以解决了很多问题。但是到目前为止还不是最优解,我们接着往后面看,会发现自定义注解这一块也可以不需要。