深入浅出Redis(十三):SpringBoot整合Redis客户端
引言
Redis是一款性能高效的键值对数据库,之前的多篇文章深入浅出的分析Redis的原理,这篇文章主要来说明从代码层面来使用Redis
本篇文章将使用Java语言并整合SpringBoot分别来使用Jedis、以及SpringBoot封装的Lettuc两种方式作为Redis客户端来使用Redis
Jedis
Jedis是Redis官方推荐Java操作Redis服务端的客户端,封装一系列与redis客户端命令同名的API提供调用
使用 Jedis
-
将idea(开发工具)的JDK调为8
-
导入Maven依赖
<dependencies> <!-- json解析工具 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.49</version> </dependency> <!--Jedis--> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> </dependencies>
-
测试环境是否成功
//Jedis源码: public Jedis(final String host, final int port) { super(host, port); }
public class TestJedis { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1",6379); //输出pong 成功! System.out.println(jedis.ping()); } }
-
练习事务
package com.liang; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TestJedis { public static void main(String[] args) { //使用fastjson将实体类转换为json格式 JSONObject jsonObject = new JSONObject(); jsonObject.put("name","Tc.l"); jsonObject.put("age",99); //{"name":"Tc.l","age":99} String jsonString = jsonObject.toJSONString(); Jedis jedis = new Jedis("127.0.0.1",6379); //清空数据库 jedis.flushDB(); //watch命令 监听key: {"name":"Tc.l","age":99} jedis.watch(jsonString); //开启事务 Transaction multi = jedis.multi(); try { //key=user1,value={"name":"Tc.l","age":99} multi.set("user1",jsonString); //key=user2,value={"name":"Tc.l","age":99} multi.set("user2",jsonString); //执行事务 multi.exec(); } catch (Exception e) { e.printStackTrace(); //如果异常,放弃事务 multi.discard(); }finally { //{"name":"Tc.l","age":99} System.out.println(jedis.get("user1")); //{"name":"Tc.l","age":99} System.out.println(jedis.get("user2")); //关闭连接 jedis.close(); } } }
SpringBoot操作Redis
Jedis:采用直连,多个线程操作不安全,如果要避免不安全就要使用jedis pool连接池,类似BIO模式
Lettuce:采用netty,实例可以在多个线程中共享,不存在线程不安全,可以减少线程数据,类似NIO模式
SpringBoot 2之后,原来使用的Jedis替换为Lettuce
导入maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
源码分析
RedisAutoConfiguration源码:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
//可以自定义RedisTemplate来替换这个默认的"redisTemplate"
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
//默认的redisTemplate没有过多的设置,redis对象都需要序列化
//俩个泛型都是object ,我们常用的是 <String,Object> 还需要强制转换,用默认的非常麻烦,所以还是自定义的好
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
//String比较常用所以说单独提出来一个bean
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory)
throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
RedisTemplate源码:
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
//...
//序列化配置
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer keySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer valueSerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashKeySerializer = null;
@SuppressWarnings("rawtypes") private @Nullable RedisSerializer hashValueSerializer = null;
//...
@Override
public void afterPropertiesSet() {
//...
if (defaultSerializer == null) {
//默认序列化方式,使用的JDK的序列化,我们中文会乱码,可以自定义json的序列化
defaultSerializer = new JdkSerializationRedisSerializer(
classLoader != null ? classLoader : this.getClass().getClassLoader());
}
//...
}
}
RedisAutoConfiguration自动配置类绑定的配置文件RedisProperties
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;//默认使用第一个数据库
private String url;
private String host = "localhost"; //默认本机
//...
}
点击查看JedisConnectionConfiguration.class发现@ConditionalOnClass报红 说明条件未满足不能使用
点击查看LettuceConnectionConfiguration.class发现@ConditionalOnClass没报红 说明条件满足可以使用
这也就是前面说的spring boot 2后使用lettuce替换了jedis
测试
设置字符串:
@SpringBootTest
class SpringbootRedis01ApplicationTests {
@Autowired
RedisTemplate redisTemplate;
@Test
void contextLoads() {
/*
*redisTempalte 操作不同的数据类型 操作API与指令几乎一样
*opsForValue() 操作String
*opsForHash()
*opsForList()
*opsForSet()
*opsForZSet()
*opsForGeo()
*opsForHyperLogLog()
*multi()
* */
//获得redis连接对象 操作数据库
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
redisTemplate.opsForValue().set("name","chenliang");
System.out.println(redisTemplate.opsForValue().get("name"));
}
}
结果:chenliang
打开redis客户端(Windows)
keys * 查看: "\xac\xed\x00\x05t\x00\x04name" 发现name前面有一堆中文乱码(因为它是jdk序列化的)
设置对象(如果不序列化对象会报异常SerializationException
):
@AllArgsConstructor
@NoArgsConstructor
@Data
public class User implements Serializable {
private String name;
private int age;
}
测试
User user = new User("chenliang", 18);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
结果:
User(name=chenliang, age=18)
自定义RedisTemplate
package com.liang.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.JsonSerializable;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
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;
import java.net.UnknownHostException;
@Configuration
public class RedisConfig {
// 自己定义了一个 RedisTemplate
@Bean
@SuppressWarnings("all")
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
// 我们为了自己开发方便,一般直接使用 <String, Object>
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// Json序列化配置
//json解析任意对象变成一个json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//使用ObjectMapper转义
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// String 的序列化
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;
}
}
@SuppressWarnings("all")抑制所有警告(类中有的方法过期警告)
setKeySerializer(RedisSerializer<?>)
setHashKeySerializer(RedisSerializer<?>)
setValueSerializer(RedisSerializer<?>)
setHashValueSerializer(RedisSerializer<?>)
可以选择的RedisSerializer<?>:
测试
@SpringBootTest
class SpringbootRedis01ApplicationTests {
@Autowired
@Qualifier("myRedisTemplate")
RedisTemplate redisTemplate;
@Test
void contextLoads() {
RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
connection.flushAll();
User user = new User("chenliang", 18);
redisTemplate.opsForValue().set("user",user);
System.out.println(redisTemplate.opsForValue().get("user"));
}
}
客户端中不再乱码(换成json序列化之后)
127.0.0.1:6379> keys *
1) "user"
127.0.0.1:6379> get user
"["com.liang.entity.User",{"name":"chenliang","age":18}]"
总结
本篇文章在SpringBoot中简单的使用Jedis和Spring Boot封装的 Lettuce客户端,Spring Boot封装的 Lettuce客户端默认情况下是使用JDK序列化,通常是使用JSON格式进行序列化,需要自定义序列化方式将key、value都设置为使用json格式序列化
转载自:https://juejin.cn/post/7175798751372509245