Spring Boot中使用Redis小结
Spring Boot 中除了对常用的关系型数据库提供了优秀的自动化支持之外,对于很多 NoSQL 数据库一样提供了自动化配置的支持,包括:Redis, MongoDB, 等。
Redis 简单介绍
Redis 是 Redis 是 Remote DIctionary Server 的缩写,是目前业界使用最广泛的内存数据存储。相比 memcached,Redis 支持更丰富的数据结构 (Memcached 完全基于内存,而 Redis 具有持久化保存特性,Redis 可以将数据写入到磁盘中 ( 以字节(0101 这样的二进制数据)的形式写入的),例如 hashes, lists, sets 等,同时支持数据持久化。除此之外,Redis 还提供一些类数据库的特性,比如事务,HA,主从库。可以说 Redis 兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。
Spring boot 集成 Redis
添加依赖
Spring Boot 提供的数据访问框架 Spring Data Redis 基于 Jedis。可以通过引入 spring-boot-starter-redis 来配置依赖关系。
<!-- 添加 Spring-boot-starter-redis 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
对 Redis 进行配置,修改配置文件 application.properties
# REDIS (RedisProperties) # Redis 数据库索引(默认为 0) spring.redis.database=0 # Redis 服务器地址 spring.redis.host=localhost # Redis 服务器连接端口 spring.redis.port=6379 # Redis 服务器连接密码(默认为空) spring.redis.password=qpc_redis # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=0
其中 spring.redis.database 的配置通常使用 0 即可,Redis 在配置的时候可以设置数据库数量,默认为 16,可以理解为数据库的 schema.
使用 Redis
使用自动配置的 StringRedisTemplate 对象进行 Redis 读写操作。
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest {@Test public void testStringWithRedis(){stringRedisTemplate.opsForValue().set("name", "guanguan"); String val = stringRedisTemplate.opsForValue().get("name"); Assert.assertEquals("guanguan", val);} }</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger LOG = Logger.getLogger(RedisApplicationTest.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); @Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisTemplate stringRedisTemplate; </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">@Autowired </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">private RedisTemplate<Serializable, Object> redisTemplate; </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">@Autowired </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">private RedisService redisService;</span>
当然,根据StringRedisTemplate 对象命名我们可以知道该对象支持 String 类型,但是在实际的应用中,我们可能需要存入 Object 对象。那该怎么存储呢。聪明的你,肯定立刻想到了,直接把对象转成 json 格式字符串,不就可以存储了嘛。这里我使用 jackson 依赖转换成 json 数据。
首先添加 jackson 依赖
<!-- java json 解析依赖 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.3</version> </dependency>
实现 json 转换工具类
public class JsonUtil {</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> ObjectMapper objectMapper = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectMapper(); </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String convertObj2String(Object object) { String s </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { s </span>=<span style="color: rgba(0, 0, 0, 1)"> objectMapper.writeValueAsString(object); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (JsonProcessingException e) { e.printStackTrace(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> s; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <T> T convertString2Obj(String s, Class<T><span style="color: rgba(0, 0, 0, 1)"> clazz) { T t </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { t </span>=<span style="color: rgba(0, 0, 0, 1)"> objectMapper.readValue(s, clazz); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (IOException e) { e.printStackTrace(); } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> t; }
}
我们知道,RedisTemplate 是 redis 模块的核心类,是对 redis 操作的较高抽象具有丰富的特性。他关注的是序列化和连接管理,线程安全,提供了如下操作接口:
HashOperations
HyperLogLogOperations
ListOperations
SetOperations
ValueOperations
ZSetOperations
那我们就实现一个通用的 RedisService 类完成 Redis 的读写操作
@Service public class RedisService {@Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisTemplate redisTemplate; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 一周有多少秒 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">long</span> WEEK_SECONDS = 7 * 24 * 60 * 60<span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 将 key,value 存放到redis数据库中,默认设置过期时间为一周 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> value </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> set(String key, Object value) { redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), WEEK_SECONDS, TimeUnit.SECONDS); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 将 key,value 存放到redis数据库中,设置过期时间单位是秒 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> value * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> expireTime </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> set(String key, Object value, <span style="color: rgba(0, 0, 255, 1)">long</span><span style="color: rgba(0, 0, 0, 1)"> expireTime) { redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), expireTime, TimeUnit.SECONDS); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 判断 key 是否在 redis 数据库中 * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> exists(<span style="color: rgba(0, 0, 255, 1)">final</span><span style="color: rgba(0, 0, 0, 1)"> String key) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> redisTemplate.hasKey(key); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 获取与 key 对应的对象 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> clazz 目标对象类型 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> <T> * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <T> T get(String key, Class<T><span style="color: rgba(0, 0, 0, 1)"> clazz) { String s </span>=<span style="color: rgba(0, 0, 0, 1)"> get(key); </span><span style="color: rgba(0, 0, 255, 1)">if</span> (s == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> JsonUtil.convertString2Obj(s, clazz); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 获取 key 对应的字符串 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String get(String key) { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> redisTemplate.opsForValue().get(key); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 删除 key 对应的 value * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> key </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> delete(String key) { redisTemplate.delete(key); }
}
新建一个 User 对象
public class User implements Serializable{</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">long</span> serialVersionUID = 3456232569272497427L<span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> id; </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> String name; </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> age; </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> User() { } </span><span style="color: rgba(0, 0, 255, 1)">public</span> User(<span style="color: rgba(0, 0, 255, 1)">int</span> id, String name, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> age) { </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">(); </span><span style="color: rgba(0, 0, 255, 1)">this</span>.id =<span style="color: rgba(0, 0, 0, 1)"> id; </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name; </span><span style="color: rgba(0, 0, 255, 1)">this</span>.age =<span style="color: rgba(0, 0, 0, 1)"> age; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getId() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> id; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setId(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> id) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.id =<span style="color: rgba(0, 0, 0, 1)"> id; } </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String getName() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> setName(String name) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.name =<span style="color: rgba(0, 0, 0, 1)"> name; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> getAge() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> age; } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> setAge(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> age) { </span><span style="color: rgba(0, 0, 255, 1)">this</span>.age =<span style="color: rgba(0, 0, 0, 1)"> age; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> String toString() { </span><span style="color: rgba(0, 0, 255, 1)">return</span> "User [id=" + id + ", name=" + name + ", age=" + age + "]"<span style="color: rgba(0, 0, 0, 1)">; }
}
新建测试类
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest {</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger LOG = Logger.getLogger(ApplicationTest.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); @Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> RedisService redisService; @Test </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> testRedisService(){ User user3 </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> User(2,"xiaoxiaoping",16<span style="color: rgba(0, 0, 0, 1)">); redisService.set(</span>"user3", user3, 1000*60l<span style="color: rgba(0, 0, 0, 1)">); User userV3 </span>= redisService.get("user3",User.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); LOG.info(</span>"userV3====="+<span style="color: rgba(0, 0, 0, 1)">userV3.toString()); }
}
测试结果
通过使用 StringRedisTemplate 对象完全实现了对 Object 对象的存储. 通过 redis-cli.exe 可以查看到我们存储的 Object 对象是 json 格式字符串,但是当某个对象很大时, 这个 json 字符串会很冗长,那我们有没有其他方式实现呢。如果有使用过 spring-data-redis 的开发者一定熟悉 RedisTemplate<K, V> 接口,StringRedisTemplate 就相当于 RedisTemplate<String, String> 的实现。没有使用过,可以先看下 StringRedisTemplate 类源码。
public class StringRedisTemplate extends RedisTemplate<String, String> {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * Constructs a new <code>StringRedisTemplate</code> instance. {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> #setConnectionFactory(RedisConnectionFactory)} * and {</span><span style="color: rgba(128, 128, 128, 1)">@link</span><span style="color: rgba(0, 128, 0, 1)"> #afterPropertiesSet()} still need to be called. </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisTemplate() { <span style="background-color: rgba(255, 0, 0, 1)"><strong>RedisSerializer</strong></span></span><span style="background-color: rgba(255, 0, 0, 1)"><strong><String> stringSerializer = <span style="color: rgba(0, 0, 255, 1)">new</span></strong></span><span style="color: rgba(0, 0, 0, 1)"><span style="background-color: rgba(255, 0, 0, 1)"><strong> StringRedisSerializer();</strong></span> setKeySerializer(stringSerializer); setValueSerializer(stringSerializer); setHashKeySerializer(stringSerializer); setHashValueSerializer(stringSerializer); } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * Constructs a new <code>StringRedisTemplate</code> instance ready to be used. * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> connectionFactory connection factory for creating new connections </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> StringRedisTemplate(RedisConnectionFactory connectionFactory) { </span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">(); setConnectionFactory(connectionFactory); afterPropertiesSet(); } </span><span style="color: rgba(0, 0, 255, 1)">protected</span> RedisConnection preProcessConnection(RedisConnection connection, <span style="color: rgba(0, 0, 255, 1)">boolean</span><span style="color: rgba(0, 0, 0, 1)"> existingConnection) { </span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DefaultStringRedisConnection(connection); }
}
从源码分析,我们可以看出 StringRedisTemplate 实现 RedisTemplate<K, V> 接口,那我们完全可以模仿写一个 RedisTemplate<Serializable, Object> 模板类。但是 Spring boot 不支直接使用,所以根据源码,我们需要实现一个RedisSerializer<T>将来对传入对象进行序列化和反序列化。这个实现类 ObjectRedisSerializer 可以参考 StringRedisSerializer 类。另外,根据源码,可以发现,Redis 默认的序列化方式为 JdkSerializationRedisSerializer ,利用 JDK 的序列化和反序列化,持久化就是以字节(0101 这样的二进制数据)的形式写入的。
Redis 存储对象实现如下
添加 ObjectRedisSerializer 实现类,需要实现 RedisSerializer<T> 接口。
/** * 实现 Redis 对象的序列化接口 * 参考:JdkSerializationRedisSerializer 源码 * */ public class ObjectRedisSerializer implements RedisSerializer<Object>{</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger LOG = Logger.getLogger(ObjectRedisSerializer.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 定义序列化和发序列化转化类 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> Converter<Object, <span style="color: rgba(0, 0, 255, 1)">byte</span>[]> serializer = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SerializingConverter(); </span><span style="color: rgba(0, 0, 255, 1)">private</span> Converter<<span style="color: rgba(0, 0, 255, 1)">byte</span>[], Object> deserializer = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> DeserializingConverter(); </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 定义转换空字节数组 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[] EMPTY_ARRAY = <span style="color: rgba(0, 0, 255, 1)">new</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[0<span style="color: rgba(0, 0, 0, 1)">]; @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">byte</span>[] serialize(Object obj) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SerializationException { </span><span style="color: rgba(0, 0, 255, 1)">byte</span>[] byteArray = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">null</span> ==<span style="color: rgba(0, 0, 0, 1)"> obj) { LOG.warn(</span>"Redis待序列化的对象为空."<span style="color: rgba(0, 0, 0, 1)">); byteArray </span>=<span style="color: rgba(0, 0, 0, 1)"> EMPTY_ARRAY; } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { byteArray </span>=<span style="color: rgba(0, 0, 0, 1)"> serializer.convert(obj); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) { LOG.error(</span>"Redis序列化对象失败,异常:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage()); byteArray </span>=<span style="color: rgba(0, 0, 0, 1)"> EMPTY_ARRAY; } } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> byteArray; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> Object deserialize(<span style="color: rgba(0, 0, 255, 1)">byte</span>[] datas) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SerializationException { Object obj </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">; </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)">(isNullOrEmpty(datas)){ LOG.warn(</span>"Redis待反序列化的对象为空."<span style="color: rgba(0, 0, 0, 1)">); }</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { obj </span>=<span style="color: rgba(0, 0, 0, 1)"> deserializer.convert(datas); } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) { LOG.error(</span>"Redis反序列化对象失败,异常:"+<span style="color: rgba(0, 0, 0, 1)">e.getMessage()); } } </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> obj; } </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">boolean</span> isNullOrEmpty(<span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">[] datas){ </span><span style="color: rgba(0, 0, 255, 1)">return</span> (<span style="color: rgba(0, 0, 255, 1)">null</span> == datas)|| (datas.length == 0<span style="color: rgba(0, 0, 0, 1)">); }
}
创建 RedisConfig 配置类,将 RedisTemplate 的 setValueSerializer 设置成 ObjectRedisSerializer 转换类。
@Configuration public class RedisConfig {// /**
// * 连接 redis 需要 RedisConnection 和 RedisConnectionFactory,
// * RedisConnection 是通过 RedisConnectionFactory 进行创建
// * RedisConnection 提供较低级的数据操作 (byte arrays)
// */
// @Bean
// RedisConnectionFactory initJedisConnectionFactory(){
// //在这里设置 redis 连接对象配置
// return new JedisConnectionFactory();
// }<span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 配置RedisTemplate实例 * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> factory * </span><span style="color: rgba(128, 128, 128, 1)">@return</span> <span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)"> @Bean </span><span style="color: rgba(0, 0, 255, 1)">public</span> RedisTemplate<Serializable, Object><span style="color: rgba(0, 0, 0, 1)"> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate</span><Serializable, Object> template = <span style="color: rgba(0, 0, 255, 1)">new</span> RedisTemplate<Serializable, Object><span style="color: rgba(0, 0, 0, 1)">();
template.setConnectionFactory(connectionFactory);
template.afterPropertiesSet();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new ObjectRedisSerializer());
return template;
}
}
需要注意几点:
在添加 RedisConfig 配置时,因为连接 redis 需要 RedisConnection 和 RedisConnectionFactory,RedisConnection 是通过 RedisConnectionFactory 进行创建若注入 JedisConnnectionFactory,如果我们 Redis 设置了密码,在重新注入 RedisConnectionFactory(如上注释代码),就会报错如下:
org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required. at redis.clients.jedis.Protocol.processError(Protocol.java:117) at redis.clients.jedis.Protocol.process(Protocol.java:151) at redis.clients.jedis.Protocol.read(Protocol.java:205) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:297) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:196) at redis.clients.jedis.BinaryJedis.set(BinaryJedis.java:126) at org.springframework.data.redis.connection.jedis.JedisConnection.set(JedisConnection.java:1136) ... 36 more
根据 StringRedisTemplate 源码,在注入 RedisTemplate<Serializable, Object> 直接使用默认的连接对象即可。设置如下代码:
template.setConnectionFactory(connectionFactory);
template.afterPropertiesSet();
或者我们注入 RedisConnectionFactory 设置连接属性应该也是可以的,有兴趣可以尝试下。
创建测试类
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest {</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">final</span> Logger LOG = Logger.getLogger(ApplicationTest.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">); @Autowired </span><span style="color: rgba(0, 0, 255, 1)">private</span> RedisTemplate<Serializable, Object><span style="color: rgba(0, 0, 0, 1)"> redisTemplate; @Test </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> testObjectWithRedis(){ User user1 </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> User(1,"guanguan",18<span style="color: rgba(0, 0, 0, 1)">); redisTemplate.opsForValue().set(</span>"user1"<span style="color: rgba(0, 0, 0, 1)">, user1); User userV1 </span>= (User)redisTemplate.opsForValue().get("user1"<span style="color: rgba(0, 0, 0, 1)">); LOG.info(</span>"userV1====="+<span style="color: rgba(0, 0, 0, 1)">userV1.toString()); User user2 </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> User(2,"xiaoyan",16<span style="color: rgba(0, 0, 0, 1)">); redisTemplate.opsForValue().set(</span>"user2"<span style="color: rgba(0, 0, 0, 1)">, user2); User userV2 </span>= (User)redisTemplate.opsForValue().get("user2"<span style="color: rgba(0, 0, 0, 1)">); LOG.info(</span>"user2====="+<span style="color: rgba(0, 0, 0, 1)">userV2.toString()); User user3 </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> User(3,"xiaoxiaoping",18<span style="color: rgba(0, 0, 0, 1)">); redisTemplate.opsForValue().set(</span>"user3"<span style="color: rgba(0, 0, 0, 1)">, user3); User userV3 </span>= (User)redisTemplate.opsForValue().get("user3"<span style="color: rgba(0, 0, 0, 1)">); LOG.info(</span>"userV3====="+<span style="color: rgba(0, 0, 0, 1)">userV3.toString()); }
}
测试结果:
可以看出,是以字节方式存储的。