SpringBoot对Redis进行了封装,使用起来十分方便。这里使用的SpringBoot版本为1.5.9.RELEASE。

【1】安装redis并进行配置

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。

Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(highavailability)。

Redis中文官网:http://www.redis.cn/

Centos7下安装Redis:。

【2】SpringBoot中引入Redis并配置

① 在pom文件中引入redis的starter

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

② 在application.properties中配置redis

spring.redis.host=192.168.2.110
spring.redis.port=6379
spring.redis.pool.max-active=50
//根据情况自定义配置

引入Redis的starter,容器中默认保存的是RedisCacheManager。RedisCacheManager帮我们创建RedisCache,RedisCache通过操作Redis缓存数据。默认保存数据k/v 都是Object,使用默认的JDK序列化器。

【3】RedisTemplate和StringRedisTemplate

RedisTemplate是SpringBoot为我们提供的redis操作Helper,K:V均为Object。StringRedisTemplate继承自RedisTemplate,是专为String:String提供服务。

RedisAutoConfiguration 中SpringBoot自动注册了RedisTemplateStringRedisTemplate。源码示例如下:

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

	/**
	 * Redis connection configuration.
	 */
	@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 = sentinelConfiguration.getIfAvailable();
			this.clusterConfiguration = clusterConfiguration.getIfAvailable();
		}
//注册 JedisConnectionFactory 
		@Bean
		@ConditionalOnMissingBean(RedisConnectionFactory.class)
		public JedisConnectionFactory redisConnectionFactory()
				throws UnknownHostException {
			return applyProperties(createJedisConnectionFactory());
		}

		protected final JedisConnectionFactory applyProperties(
				JedisConnectionFactory factory) {
			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())) {
				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.indexOf(":");
					if (index >= 0) {
						password = password.substring(index + 1);
					}
					factory.setPassword(password);
				}
			}
			catch (URISyntaxException ex) {
				throw new IllegalArgumentException("Malformed 'spring.redis.url' " + url,
						ex);
			}
		}

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

		/**
		 * Create a {@link RedisClusterConfiguration} if necessary.
		 * @return {@literal null} if no cluster settings are set.
		 */
		protected final RedisClusterConfiguration getClusterConfiguration() {
			if (this.clusterConfiguration != null) {
				return this.clusterConfiguration;
			}
			if (this.properties.getCluster() == null) {
				return null;
			}
			Cluster clusterProperties = this.properties.getCluster();
			RedisClusterConfiguration config = new RedisClusterConfiguration(
					clusterProperties.getNodes());

			if (clusterProperties.getMaxRedirects() != null) {
				config.setMaxRedirects(clusterProperties.getMaxRedirects());
			}
			return config;
		}

		private List<RedisNode> createSentinels(Sentinel sentinel) {
			List<RedisNode> nodes = new ArrayList<RedisNode>();
			for (String node : StringUtils
					.commaDelimitedListToStringArray(sentinel.getNodes())) {
				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])));
				}
				catch (RuntimeException ex) {
					throw new IllegalStateException(
							"Invalid redis sentinel " + "property '" + node + "'", ex);
				}
			}
			return nodes;
		}

		private JedisConnectionFactory createJedisConnectionFactory() {
			JedisPoolConfig poolConfig = this.properties.getPool() != null
					? jedisPoolConfig() : new JedisPoolConfig();

			if (getSentinelConfig() != null) {
				return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
			}
			if (getClusterConfiguration() != null) {
				return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
			}
			return new JedisConnectionFactory(poolConfig);
		}

		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;
		}

	}

	/**
	 * Standard Redis configuration.
	 */
	@Configuration
	protected static class RedisConfiguration {
//如果容器中没有id为注册redisTemplate的RedisTemplate<Object, Object>,
//就会往容器中注册 RedisTemplate<Object, Object>,id为redisTemplate;
//可以根据这个规则自定义RedisTemplate<Object, Object>,默认使用JDK序列化器
		@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;
		}
// 注册 StringRedisTemplate 
		@Bean
		@ConditionalOnMissingBean(StringRedisTemplate.class)
		public StringRedisTemplate stringRedisTemplate(
				RedisConnectionFactory redisConnectionFactory)
				throws UnknownHostException {
			StringRedisTemplate template = new StringRedisTemplate();
			template.setConnectionFactory(redisConnectionFactory);
			return template;
		}

	}

}

【4】对五大类数据进行操作

Redis中常见的五大类数据类型:String,List,Set,Hash和ZSet。

RedisTemplate封装了方法对五大类数据进行操作,示例如下:

//针对String
RedisTemplate.opsForValue();

//针对List
RedisTemplate.opsForList();

//针对Set
RedisTemplate.opsForSet();

//针对Hash
RedisTemplate.opsForHash();

//针对ZSet
RedisTemplate.opsForZSet();

其他还有,如下图:

springboot redis 缓存时间 springboot整合redis缓存_Redis

每一个方法都会返回一个Operations对象,该对象封装了该类型的一系列操作。
以opsForValue为例:

public ValueOperations<K, V> opsForValue() {
	if (valueOps == null) {
		valueOps = new DefaultValueOperations<K, V>(this);
	}
	return valueOps;
}

ValueOperations源码如下:

public interface ValueOperations<K, V> {

	/**
	 * Set {@code value} for {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @param value
	 * @see <a href="http://redis.io/commands/set">Redis Documentation: SET</a>
	 */
	void set(K key, V value);

	/**
	 * Set the {@code value} and expiration {@code timeout} for {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @param value
	 * @param timeout
	 * @param unit must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/setex">Redis Documentation: SETEX</a>
	 */
	void set(K key, V value, long timeout, TimeUnit unit);

	/**
	 * Set {@code key} to hold the string {@code value} if {@code key} is absent.
	 *
	 * @param key must not be {@literal null}.
	 * @param value
	 * @see <a href="http://redis.io/commands/setnx">Redis Documentation: SETNX</a>
	 */
	Boolean setIfAbsent(K key, V value);

	/**
	 * Set multiple keys to multiple values using key-value pairs provided in {@code tuple}.
	 *
	 * @param map must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/mset">Redis Documentation: MSET</a>
	 */
	void multiSet(Map<? extends K, ? extends V> map);

	/**
	 * Set multiple keys to multiple values using key-value pairs provided in {@code tuple} only if the provided key does
	 * not exist.
	 *
	 * @param map must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/mset">Redis Documentation: MSET</a>
	 */
	Boolean multiSetIfAbsent(Map<? extends K, ? extends V> map);

	/**
	 * Get the value of {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/get">Redis Documentation: GET</a>
	 */
	V get(Object key);

	/**
	 * Set {@code value} of {@code key} and return its old value.
	 *
	 * @param key must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/getset">Redis Documentation: GETSET</a>
	 */
	V getAndSet(K key, V value);

	/**
	 * Get multiple {@code keys}. Values are returned in the order of the requested keys.
	 *
	 * @param keys must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/mget">Redis Documentation: MGET</a>
	 */
	List<V> multiGet(Collection<K> keys);

	/**
	 * Increment an integer value stored as string value under {@code key} by {@code delta}.
	 *
	 * @param key must not be {@literal null}.
	 * @param delta
	 * @see <a href="http://redis.io/commands/incr">Redis Documentation: INCR</a>
	 */
	Long increment(K key, long delta);

	/**
	 * Increment a floating point number value stored as string value under {@code key} by {@code delta}.
	 *
	 * @param key must not be {@literal null}.
	 * @param delta
	 * @see <a href="http://redis.io/commands/incrbyfloat">Redis Documentation: INCRBYFLOAT</a>
	 */
	Double increment(K key, double delta);

	/**
	 * Append a {@code value} to {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @param value
	 * @see <a href="http://redis.io/commands/append">Redis Documentation: APPEND</a>
	 */
	Integer append(K key, String value);

	/**
	 * Get a substring of value of {@code key} between {@code begin} and {@code end}.
	 *
	 * @param key must not be {@literal null}.
	 * @param start
	 * @param end
	 * @see <a href="http://redis.io/commands/getrange">Redis Documentation: GETRANGE</a>
	 */
	String get(K key, long start, long end);

	/**
	 * Overwrite parts of {@code key} starting at the specified {@code offset} with given {@code value}.
	 *
	 * @param key must not be {@literal null}.
	 * @param value
	 * @param offset
	 * @see <a href="http://redis.io/commands/setrange">Redis Documentation: SETRANGE</a>
	 */
	void set(K key, V value, long offset);

	/**
	 * Get the length of the value stored at {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @see <a href="http://redis.io/commands/strlen">Redis Documentation: STRLEN</a>
	 */
	Long size(K key);

	/**
	 * Sets the bit at {@code offset} in value stored at {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @param offset
	 * @param value
	 * @since 1.5
	 * @see <a href="http://redis.io/commands/setbit">Redis Documentation: SETBIT</a>
	 */
	Boolean setBit(K key, long offset, boolean value);

	/**
	 * Get the bit value at {@code offset} of value at {@code key}.
	 *
	 * @param key must not be {@literal null}.
	 * @param offset
	 * @since 1.5
	 * @see <a href="http://redis.io/commands/setbit">Redis Documentation: GETBIT</a>
	 */
	Boolean getBit(K key, long offset);

	RedisOperations<K, V> getOperations();

}

其他数据类型类似。

【5】Redis命令接口

如下图所示,Redis提供了许多命令供我们使用,同样在SpringBoot中也封装了对应类型的命令接口。

springboot redis 缓存时间 springboot整合redis缓存_SpringBoot_02

SpringBoot中RedisCommands 源码示例如下:

/**
 * Interface for the commands supported by Redis.
 * 
 * @author Costin Leau
 * @author Christoph Strobl
 */
public interface RedisCommands extends RedisKeyCommands, RedisStringCommands, RedisListCommands, RedisSetCommands,
		RedisZSetCommands, RedisHashCommands, RedisTxCommands, RedisPubSubCommands, RedisConnectionCommands,
		RedisServerCommands, RedisScriptingCommands, RedisGeoCommands, HyperLogLogCommands {

	/**
	 * 'Native' or 'raw' execution of the given command along-side the given arguments. The command is executed as is,
	 * with as little 'interpretation' as possible - it is up to the caller to take care of any processing of arguments or
	 * the result.
	 * 
	 * @param command Command to execute
	 * @param args Possible command arguments (may be null)
	 * @return execution result.
	 */
	Object execute(String command, byte[]... args);
}

基本上,在Linux Redis中能够使用的命令,在接口中都能找到!如RedisTemplate.hasKey()判断Redis是否存在该key。

【6】测试示例

测试源码如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootCacheApplicationTests {

	@Autowired
	EmployeeMapper employeeMapper;

	@Autowired
	StringRedisTemplate stringRedisTemplate;

	@Autowired
	RedisTemplate redisTemplate;

	@Test
	public void test01() {
		Boolean aBoolean = stringRedisTemplate.hasKey("msg");
		if (aBoolean){
			String msg = stringRedisTemplate.opsForValue().get("msg");
			System.out.println(msg);
		}
	}
	@Test
	public void test02() {
		Employee empById = employeeMapper.getEmpById(1);
		redisTemplate.opsForValue().set("emp01",empById);
		Object emp01 = redisTemplate.opsForValue().get("emp01");
		System.out.println(emp01);
	}

}

其中test01主要对String进行测试,test02则向redis中保存了一个对象!

使用Redis桌面管理工具查看如下:

springboot redis 缓存时间 springboot整合redis缓存_Redis_03

注意,Redis中对象的保存是需要进行序列化的,默认使用JdkSerializationRedisSerializer序列化器,所以图片中看到的是序列化后的二进制。

正常使用是没有问题的,查询出来的时候会自动反序列化,如下所示:

Employee{id=1, lastName='张三', gender=1, email='aa@qq.com', dId=1}

如果习惯于使用String,那么可以将其JSON化,两种方式:使用第三方JSON或者向容器中添加自定义RedisTemplate,改变其默认序列化器。

添加自定义RedisTemplate源码如下:

@Configuration
public class MyRedisConfig {

    @Bean
    public RedisTemplate<Object,Employee> empRedisTemplate(RedisConnectionFactory connectionFactory){
        RedisTemplate<Object,Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(connectionFactory);
        Jackson2JsonRedisSerializer<Employee> serializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(serializer);
        return template;
    }
}

测试如下:

@Autowired
RedisTemplate redisTemplate;

@Autowired
RedisTemplate<Object,Employee> empRedisTemplate;

@Test
public void test02() {
	Employee empById = employeeMapper.getEmpById(1);
	empRedisTemplate.opsForValue().set("emp01",empById);
	Object emp01 = empRedisTemplate.opsForValue().get("emp01");
	System.out.println(emp01);
}

此时桌面管理工具显示如下:

springboot redis 缓存时间 springboot整合redis缓存_redis_04


有趣的测试

redisTemplate.opsForValue().set("emp01","123456");
Object emp01 = redisTemplate.opsForValue().get("emp01");
System.out.println(emp01);

empRedisTemplate.opsForValue().set("emp01",empById);
Object emp02 = empRedisTemplate.opsForValue().get("emp01");
System.out.println(emp02);

输出结果如下:

123456
Employee{id=1, lastName='张三', gender=1, email='aa@qq.com', dId=1}

使用不同模板向Redis中存储在我们看来一样的key,不同的value时,Redis存储机制不同,且使用对应的Template从Redis获取同一个key得到对应值!

进入Linux环境查看Redis中的key :

springboot redis 缓存时间 springboot整合redis缓存_redis_05


分别使用三种模板存储key"emp01",如下:

stringRedisTemplate.opsForValue().set("emp01","hello world");
redisTemplate.opsForValue().set("emp01","123456");
empRedisTemplate.opsForValue().set("emp01",empById);

其在Redis中key的对应存储形式如下:

1) "emp01"
2) "\xac\xed\x00\x05t\x00\x05emp01"
3) "\"emp01\""

参考博文:SpringBoot2.0整合Redis