SpringBoot整合Redis缓存

一、前言

之前分享过关于Redis集群如何搭建的,最近有小伙伴儿说分享一下Redis如何使用SpringBoot的整合,项目中经常使用的就是Redis,本次整合的是Redis单机版,后续会分享Redis集群版本,请多提出自己的意见。

二、开始整合

1.首先需要搭建Redis服务器,可参考之前的博客()

2019/04/26/Redis的搭建(win和linux版)

2.搭建SpringBoot工程,参考之前的博客

SpringBoot工程搭建

3.在pom文件加入redis的依赖和Json相关依赖

1
2
3
4
5
6
7
8
9
10
<!-- Redis 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>

4.创建项目结构,结构如下图所示

5.项目全局配置文件application.properties文件配置Redis的相关配置

如下问全部的配置,因为我的redis没有设置密码,故password被我注释掉了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#项目启动端口
server.port=8088
#应用上下文
server.context-path = /SpringBootRedis
#redis相关配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
#spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=1000

6.配置redis的配置文件,使用RedisTemplate进行操作,需要将数据的进行序列化,才不会出现乱码

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
package com.yang.config.redis;

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;

/**
* Redis配置类
*/
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
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);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}

7.编写redis工具类,供开发使用,注入RedisTemplate进行操作

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
41
42
43
44
45
46
47
48
package com.yang.config.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import java.util.concurrent.TimeUnit;

/**
* redis 工具,存取
*/
@Configuration
public class RedisUtil {

@Autowired
private RedisTemplate<String,Object> redisTemplate;

/**
* 存数据
* @param key
* @param value
*/
public void set(String key , Object value){
redisTemplate.opsForValue().set(key,value);
}

/**
* 存数据
* @param key 键
* @param value 值
* @param exp 过期时间 默认秒
* @param s 使用 时还是分还是秒,默认秒
*/
public void set(String key , Object value , int exp,TimeUnit s){
if( s==null ){
s = TimeUnit.SECONDS;
}
redisTemplate.opsForValue().set(key,value,exp, s);
}

/**
* 获取数据
* @param key
* @return
*/
public <T>T get(String key){
return (T)redisTemplate.opsForValue().get(key);
}

}

8.编写RedisController,编写set和get,使用请求来存储redis的值和获取redis的值

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
package com.yang.controller;

import com.yang.config.redis.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/redis")
public class RedisController {

@Autowired
private RedisUtil redisUtil;

/**
* redis测试
* @return
*/
@GetMapping("/get")
public String get(@RequestParam(value="key") String key){
return redisUtil.get(key).toString();
}
/**
* redis测试
* @return
*/
@GetMapping("/put")
public String put(@RequestParam(value="key") String key,@RequestParam(value="value") String value){
redisUtil.set(key,value);
return "success";
}
}

9.使用浏览器访问接口保存数据,并查询数据

三、加载机制剖析

1.经过查看,Redis的包中,包含RedisAutoConfiguration这个配置类,使用了Configuration注解,SpringBoot会自动加载

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.boot.autoconfigure.data.redis;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Cluster;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Pool;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties.Sentinel;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnection;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPoolConfig;

@Configuration
@ConditionalOnClass({JedisConnection.class, RedisOperations.class, Jedis.class})
@EnableConfigurationProperties({RedisProperties.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}

@Configuration
protected static class RedisConfiguration {
protected RedisConfiguration() {
}

@Bean
@ConditionalOnMissingBean(
name = {"redisTemplate"}
)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate();
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;
}
}

@Configuration
@ConditionalOnClass({GenericObjectPool.class})
protected static class RedisConnectionConfiguration {
private final RedisProperties properties;
private final RedisSentinelConfiguration sentinelConfiguration;
private final RedisClusterConfiguration clusterConfiguration;

public RedisConnectionConfiguration(RedisProperties properties, ObjectProvider<RedisSentinelConfiguration> sentinelConfiguration, ObjectProvider<RedisClusterConfiguration> clusterConfiguration) {
this.properties = properties;
this.sentinelConfiguration = (RedisSentinelConfiguration)sentinelConfiguration.getIfAvailable();
this.clusterConfiguration = (RedisClusterConfiguration)clusterConfiguration.getIfAvailable();
}

@Bean
@ConditionalOnMissingBean({RedisConnectionFactory.class})
public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException {
return this.applyProperties(this.createJedisConnectionFactory());
}

protected final JedisConnectionFactory applyProperties(JedisConnectionFactory factory) {
this.configureConnection(factory);
if (this.properties.isSsl()) {
factory.setUseSsl(true);
}

factory.setDatabase(this.properties.getDatabase());
if (this.properties.getTimeout() > 0) {
factory.setTimeout(this.properties.getTimeout());
}

return factory;
}

private void configureConnection(JedisConnectionFactory factory) {
if (StringUtils.hasText(this.properties.getUrl())) {
this.configureConnectionFromUrl(factory);
} else {
factory.setHostName(this.properties.getHost());
factory.setPort(this.properties.getPort());
if (this.properties.getPassword() != null) {
factory.setPassword(this.properties.getPassword());
}
}

}

private void configureConnectionFromUrl(JedisConnectionFactory factory) {
String url = this.properties.getUrl();
if (url.startsWith("rediss://")) {
factory.setUseSsl(true);
}

try {
URI uri = new URI(url);
factory.setHostName(uri.getHost());
factory.setPort(uri.getPort());
if (uri.getUserInfo() != null) {
String password = uri.getUserInfo();
int index = password.lastIndexOf(":");
if (index >= 0) {
password = password.substring(index + 1);
}

factory.setPassword(password);
}

} catch (URISyntaxException var6) {
throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url, var6);
}
}

protected final RedisSentinelConfiguration getSentinelConfig() {
if (this.sentinelConfiguration != null) {
return this.sentinelConfiguration;
} else {
Sentinel sentinelProperties = this.properties.getSentinel();
if (sentinelProperties != null) {
RedisSentinelConfiguration config = new RedisSentinelConfiguration();
config.master(sentinelProperties.getMaster());
config.setSentinels(this.createSentinels(sentinelProperties));
return config;
} else {
return null;
}
}
}

protected final RedisClusterConfiguration getClusterConfiguration() {
if (this.clusterConfiguration != null) {
return this.clusterConfiguration;
} else if (this.properties.getCluster() == null) {
return null;
} else {
Cluster clusterProperties = this.properties.getCluster();
RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
if (clusterProperties.getMaxRedirects() != null) {
config.setMaxRedirects(clusterProperties.getMaxRedirects().intValue());
}

return config;
}
}

private List<RedisNode> createSentinels(Sentinel sentinel) {
List<RedisNode> nodes = new ArrayList();
String[] var3 = StringUtils.commaDelimitedListToStringArray(sentinel.getNodes());
int var4 = var3.length;

for(int var5 = 0; var5 < var4; ++var5) {
String node = var3[var5];

try {
String[] parts = StringUtils.split(node, ":");
Assert.state(parts.length == 2, "Must be defined as 'host:port'");
nodes.add(new RedisNode(parts[0], Integer.valueOf(parts[1]).intValue()));
} catch (RuntimeException var8) {
throw new IllegalStateException("Invalid redis sentinel property '" + node + "'", var8);
}
}

return nodes;
}

private JedisConnectionFactory createJedisConnectionFactory() {
JedisPoolConfig poolConfig = this.properties.getPool() != null ? this.jedisPoolConfig() : new JedisPoolConfig();
if (this.getSentinelConfig() != null) {
return new JedisConnectionFactory(this.getSentinelConfig(), poolConfig);
} else {
return this.getClusterConfiguration() != null ? new JedisConnectionFactory(this.getClusterConfiguration(), poolConfig) : new JedisConnectionFactory(poolConfig);
}
}

private JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
Pool props = this.properties.getPool();
config.setMaxTotal(props.getMaxActive());
config.setMaxIdle(props.getMaxIdle());
config.setMinIdle(props.getMinIdle());
config.setMaxWaitMillis((long)props.getMaxWait());
return config;
}
}
}

2.配置了EnableConfigurationProperties,配置了RedisProperties,而RedisProperties则使用了ConfigurationProperties,加载了前缀为spring.redis的所有配置,故读取了application.properties文件中的内容。

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
* Copyright 2012-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.autoconfigure.data.redis;

import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

/**
* Configuration properties for Redis.
*
* @author Dave Syer
* @author Christoph Strobl
* @author Eddú Meléndez
* @author Marco Aust
*/
@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;

public int getDatabase() {
return this.database;
}

public void setDatabase(int database) {
this.database = database;
}

public String getUrl() {
return this.url;
}

public void setUrl(String url) {
this.url = url;
}

public String getHost() {
return this.host;
}

public void setHost(String host) {
this.host = host;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}

public int getPort() {
return this.port;
}

public void setPort(int port) {
this.port = port;
}

public boolean isSsl() {
return this.ssl;
}

public void setSsl(boolean ssl) {
this.ssl = ssl;
}

public void setTimeout(int timeout) {
this.timeout = timeout;
}

public int getTimeout() {
return this.timeout;
}

public Sentinel getSentinel() {
return this.sentinel;
}

public void setSentinel(Sentinel sentinel) {
this.sentinel = sentinel;
}

public Pool getPool() {
return this.pool;
}

public void setPool(Pool pool) {
this.pool = pool;
}

public Cluster getCluster() {
return this.cluster;
}

public void setCluster(Cluster cluster) {
this.cluster = cluster;
}

/**
* Pool properties.
*/
public static class Pool {

/**
* Max number of "idle" connections in the pool. Use a negative value to indicate
* an unlimited number of idle connections.
*/
private int maxIdle = 8;

/**
* Target for the minimum number of idle connections to maintain in the pool. This
* setting only has an effect if it is positive.
*/
private int minIdle = 0;

/**
* Max number of connections that can be allocated by the pool at a given time.
* Use a negative value for no limit.
*/
private int maxActive = 8;

/**
* Maximum amount of time (in milliseconds) a connection allocation should block
* before throwing an exception when the pool is exhausted. Use a negative value
* to block indefinitely.
*/
private int maxWait = -1;

public int getMaxIdle() {
return this.maxIdle;
}

public void setMaxIdle(int maxIdle) {
this.maxIdle = maxIdle;
}

public int getMinIdle() {
return this.minIdle;
}

public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}

public int getMaxActive() {
return this.maxActive;
}

public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}

public int getMaxWait() {
return this.maxWait;
}

public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}

}

/**
* Cluster properties.
*/
public static class Cluster {

/**
* Comma-separated list of "host:port" pairs to bootstrap from. This represents an
* "initial" list of cluster nodes and is required to have at least one entry.
*/
private List<String> nodes;

/**
* Maximum number of redirects to follow when executing commands across the
* cluster.
*/
private Integer maxRedirects;

public List<String> getNodes() {
return this.nodes;
}

public void setNodes(List<String> nodes) {
this.nodes = nodes;
}

public Integer getMaxRedirects() {
return this.maxRedirects;
}

public void setMaxRedirects(Integer maxRedirects) {
this.maxRedirects = maxRedirects;
}

}

/**
* Redis sentinel properties.
*/
public static class Sentinel {

/**
* Name of Redis server.
*/
private String master;

/**
* Comma-separated list of host:port pairs.
*/
private String nodes;

public String getMaster() {
return this.master;
}

public void setMaster(String master) {
this.master = master;
}

public String getNodes() {
return this.nodes;
}

public void setNodes(String nodes) {
this.nodes = nodes;
}

}

}

四、源码地址

点我查看