我们以spring-boot-starter-data-redis-2.1.7为例,starter本身没有包含任何代码,只是引入了spring-data-redis的依赖,因此肯定是在spring-boot-autoconfigure中加了自动配置:

我们就看下这几个配置类:
其中RedisAutoConfiguration里面就配置了我们常用的RedisTemplate,RedisRepositoriesAutoConfiguration这里面是实现了spring-data规范的一些配置,RedisReactiveAutoConfiguration是当需要用Reactive方式编程的时候用的,本文忽略。
RedisAutoConfiguration
@Configuration@ConditionalOnClass(RedisOperations.class)@EnableConfigurationProperties(RedisProperties.class)//内置了对lettuce和jedis的支持//但是默认只添加了lettuce的依赖,因此默认是使用的lettuce@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })public class RedisAutoConfiguration {@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@ConditionalOnMissingBeanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)throws UnknownHostException {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(redisConnectionFactory);return template;}}
这里面内置了对lettuce和jedis的支持,因为默认是引入的lettuce的jar,因此默认会使用lettuce去访问redis,同时这里面创建了一个RedisTemplate和一个StringRedisTemplate,一般我们经常会直接注入StringRedisTemplate来访问redis。
首先看下StringRedisTemplate:
public StringRedisTemplate() {setKeySerializer(RedisSerializer.string());setValueSerializer(RedisSerializer.string());setHashKeySerializer(RedisSerializer.string());setHashValueSerializer(RedisSerializer.string());}
从名字也可以看出来,它在序列化key、value、hash key和hash value的时候都是转化成String存入redis的,RedisSerializer#string:
static RedisSerializer<String> string() {return StringRedisSerializer.UTF_8;}public static final StringRedisSerializer UTF_8 =new StringRedisSerializer(StandardCharsets.UTF_8);
看下具体的序列化和反序列化的方法:
@Overridepublic String deserialize(@Nullable byte[] bytes) {return (bytes == null ? null : new String(bytes, charset));}@Overridepublic byte[] serialize(@Nullable String string) {return (string == null ? null : string.getBytes(charset));}
一眼就能明白,没什么好说的。但是很显然,它只能处理String类型,如果是对象得需要自己手动转化成String才可以,一般我们会把对象转化成json字符串存储到redis里面。
我们回头再来看下RedisTemplate,RedisTemplate#afterPropertiesSet:
@Overridepublic void afterPropertiesSet() {boolean defaultUsed = false;//判断是否幽默ren的序列化器if (defaultSerializer == null) {//默认是采用jdk自带的序列化,//也就是说必须得实现Serializable接口才行defaultSerializer = new JdkSerializationRedisSerializer(classLoader != null ? classLoader : this.getClass().getClassLoader());}//默认j就是启用的if (enableDefaultSerializer) {//key、value、hash key、hash value都使用默认的那个序列化器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;}}。。。}
从名字也可以看出来,RedisTemplate默认是采用JDK自带的序列化方式来做序列化器,看一下:
public JdkSerializationRedisSerializer(@Nullable ClassLoader classLoader) {this(new SerializingConverter(), new DeserializingConverter(classLoader));}public SerializingConverter() {this.serializer = new DefaultSerializer();}public class DefaultSerializer implements Serializer<Object> {@Overridepublic void serialize(Object object, OutputStream outputStream) throws IOException {if (!(object instanceof Serializable)) {throw new IllegalArgumentException(getClass().getSimpleName() + " requires a Serializable payload " +"but received an object of type [" + object.getClass().getName() + "]");}ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);objectOutputStream.writeObject(object);objectOutputStream.flush();}}
很清楚了,最终是使用ObjectOutputStream和ObjectInputStream来读写对象,因此对象必须得实现Serializable接口才可以,最终是以二进制的格式存储到redis里面,比如:

很显然,这种序列化方式的可读性太不友好了。
RedisRepositoriesAutoConfiguration
它实现了spring-data规范,其实就是把redis以数据库dao的形式来访问,这个东西相对就比较复杂了,但是在实际中使用的并不是很多,我们只说一下如何使用。
首先定义一个dao,需要继承CrudRepository:
@Repositorypublic interface UserDao extends CrudRepository<UserEntity,Long> {}
定义一个数据库实体:
@RedisHash("user")public class UserEntity {@Idprivate Long id;private String username;private String password;}
@RedisHash("user")会把user对象存储到一个key是user:id的hash中,比如:

定义service和controller:
@Servicepublic class UserService {@Autowiredprivate UserDao userDao;public UserEntity save(UserEntity userEntity){userDao.save(userEntity);return userEntity;}public UserEntity findById(Long id){Optional<UserEntity> optional = userDao.findById(id);return optional.isPresent()?optional.get():null;}}@RestControllerpublic class UserController {@Autowiredprivate UserService userService;private static AtomicLong id = new AtomicLong(0);@GetMapping("/add_user")public String addUser(String username, String password){long idLong = id.incrementAndGet();userService.save(new UserEntity(idLong, username, password));UserEntity entity = userService.findById(idLong);return entity.toString();}}
总结一下
- 1.spring-boot-starter-data-redis默认是使用lettuce去访问redis
- 2.内置了StringRedisTemplate和RedisTemplate,应用可以直接使用。当存取对象的时候,StringRedisTemplate需要手动把对象转化成String,RedisTemplate虽然可以直接存取对象,但是需要对象实现Serializable接口,同时在redis库中的可读性比较差。
- 3.由于存在以上的缺点,因此可以把这两个template的优点给融合到一起,既可以直接存取对象,还可以方便人类阅读,当然也可以存取List和基本类型,更重要的是还可以支持给key统一添加前缀,赶快来使用吧:github地址:https://github.com/xjs1919/redis-client。




