引言
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格式序列化