# Spring-Redis **Repository Path**: bosszyy/Spring-Redis ## Basic Information - **Project Name**: Spring-Redis - **Description**: 使用Redis作为数据库 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-02-06 - **Last Updated**: 2020-12-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Spring-Redis [TOC] ### 1. 介绍 使用Redis作为数据库 ### 2.使用Jedis框架 #### 2.1 pom.xml ```xml redis.clients jedis 2.9.0 ``` #### 2.2 测试代码 ``` @Test public void test(){ Jedis jedis = new Jedis(); String s = jedis.get("s11"); System.out.println(s); jedis.close(); } ``` #### 2.3 总结 > 分析源码发现执行Jedis jedis = new Jedis()时并不连接数据库,而只是创建了一个Client对象,当然还有其他的,这里将关注点放在Client上,如果不指定URL和端口号将会使用默认的localhost和6379。 > 但只有当执行了get set hset等等的语句时才会真正连接数据库,每次都会执行Connection的下面 方法 ``` redis.clients.jedis.Connection.sendCommand(redis.clients.jedis.Protocol.Command, byte[]...){ this.connect(); } public void connect() { if (!this.isConnected()) { try { this.socket = new Socket(); this.socket.setReuseAddress(true); this.socket.setKeepAlive(true); this.socket.setTcpNoDelay(true); ... } ``` ### 3. Jedis和Spring整合 #### 3.1 pom.xml ```xml org.springframework.data spring-data-redis 2.1.11.RELEASE com.fasterxml.jackson.core jackson-databind 2.9.8 ``` #### 3.2StringRedisTemplate ##### 3.2.1 RedisConfig.java ```java @Configuration public class RedisConfig { @Value("${redis.database}") private int database; @Value("${redis.hostName}") private String hostName; @Value("${redis.port}") private int port; @Value("${redis.password}") private String password; /* * redis配置类 * */ @Bean public RedisStandaloneConfiguration standaloneConfiguration() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(hostName, port); configuration.setDatabase(database); configuration.setPassword(RedisPassword.of(password)); return configuration; } /* * redis的连接工厂 * */ @Bean public RedisConnectionFactory connectionFactory() { return new JedisConnectionFactory(standaloneConfiguration()); } /* * redis的键值对映射为字符串的模板类 * */ @Bean public StringRedisTemplate stringRedisTemplate() { return new StringRedisTemplate(connectionFactory()); } } ``` ##### 3.2.2 测试代码 ```java public class RedisStringTemplateTest extends BaseTest { @Autowired private StringRedisTemplate redisTemplate; @Test public void test() { String value = redisTemplate.opsForValue().get("s1"); System.out.println(value); } @Test public void test1() { redisTemplate.opsForValue().set("k1","2020-2-5"); } } ``` ##### 3.2.3 总结 > StringRedisTemplate只支持将序列化为字符串形式往数据库存储,因为StringRedisTemplate只提供了两个构造方法,一个是设置数据库连接的,另一个就设置了序列化的类型为string,如果想往数据库里放入更加丰富的数据类型,需要用到RedisTemplate,也是StringRedisTemplate的父类 > > > > ```java > public StringRedisTemplate() { > this.setKeySerializer(RedisSerializer.string()); > this.setValueSerializer(RedisSerializer.string()); > this.setHashKeySerializer(RedisSerializer.string()); > this.setHashValueSerializer(RedisSerializer.string()); > } > public StringRedisTemplate(RedisConnectionFactory connectionFactory) { > this(); > this.setConnectionFactory(connectionFactory); > this.afterPropertiesSet(); > } > ``` #### 3.3 RedisTemplate ##### 3.3.1 RedisConfig.java ```java @Bean public RedisTemplate redisTemplate(){ RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory()); template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(RedisSerializer.json()); return template; } ``` ##### 3.3.2 测试代码 ```java public class RedisTemplateTest extends BaseTest{ @Autowired private RedisTemplate redisTemplate; @Test public void test(){ User user = new User(); user.setUsername("zyy"); user.setAge("20"); redisTemplate.opsForValue().set("k1",user); user = (User) redisTemplate.opsForValue().get("k1"); System.out.println(user); } } ``` ##### 3.3.3 总结 > 可以方便的将复杂的Java对象序列化为Json字符串存储到数据库中 #### 3.4 使用模板总结 spring-data-redis为Java编程操作Redis数据提供了模板方式:RedisTemplate类,而且还提供了常用的模板RedisTemplate的子类StringRedisTemplate来处理简单的字符串类型的数据,如果满足不了业务需求,可以使用RedisTemplate,但必须指定序列化方式,否则 ```java if (this.defaultSerializer == null) { this.defaultSerializer = new JdkSerializationRedisSerializer(this.classLoader != null ? this.classLoader : this.getClass().getClassLoader()); } ``` ```java @Bean public RedisTemplate redisTemplate(){ RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory()); template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(RedisSerializer.json()); return template; } ``` org.springframework.data.redis.serializer.RedisSerializer接口里提供了三个静态方法 ```java //默认的jdk序列化方式:将传入数据库的数据序列化为二进制,但是要注意序列化的对象必须是可序列化的,即必须继承序列化接口 static RedisSerializer java(@Nullable ClassLoader classLoader) { return new JdkSerializationRedisSerializer(classLoader); } //默认的json序列化方式:将传入数据库的数据序列化为Json格式 static RedisSerializer json() { return new GenericJackson2JsonRedisSerializer(); } //默认的字符串序列化方式:将传入数据库的数据序列化为字符串格式 static RedisSerializer string() { return StringRedisSerializer.UTF_8; } ``` 可以根据情况选择对应的序列化策略。 #### 3.5 不直接使用模板 ```xml ``` ```java public class ListOperationClassTest extends BaseTest{ @Autowired private ListOperations listOperations; @Test public void test(){ listOperations.rightPushAll("list1","1","2","3"); } } ``` #### 3.6 Redis的事务 ```java @Configuration @EnableTransactionManagement 1 public class RedisTxContextConfiguration { @Bean public StringRedisTemplate redisTemplate() { StringRedisTemplate template = new StringRedisTemplate(redisConnectionFactory()); // explicitly enable transaction support template.setEnableTransactionSupport(true); 2 return template; } @Bean public RedisConnectionFactory redisConnectionFactory() { // jedis || Lettuce } @Bean 3 public PlatformTransactionManager transactionManager() throws SQLException { return new DataSourceTransactionManager(dataSource()); } @Bean public DataSource dataSource() throws SQLException { // ... } } public RedisTemplate redisTemplate(){ RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(connectionFactory()); // template.setDefaultSerializer(RedisSerializer.string()); template.setKeySerializer(RedisSerializer.string()); template.setValueSerializer(RedisSerializer.json()); //模板必须先开启事务 template.setEnableTransactionSupport(true); return template; } ``` ```java public void testTx(){ redisTemplate.multi(); //标志着一个事物的开始 原子性、一致性、隔离性、持久性 try{ redisTemplate.opsForValue().set("k3",300); int i = 1 / 0; redisTemplate.opsForValue().set("k4",400); redisTemplate.exec(); }catch (Exception e){ redisTemplate.discard(); //取消这次事务的所有操作 } } ``` #### 3.7 Redis的Sub/Pub ##### 3.7.1 Receiver类 ```java public class Receiver { private static final Logger logger = LogManager.getLogger(Receiver.class); /* 用处:假如有10个订阅者,就设置为10,这样每当一个订阅者接收之后调用latch.cutDown() 发布者发布方法最后调用latch.await(),即所有订阅者都接受到消息方法结束 */ private CountDownLatch latch; public void setLatch(CountDownLatch latch) { this.latch = latch; } public void receiveMessage(String message){ // 可以在这里执行一些操作,比如向某些用户发送信息 logger.info("receive"+message); latch.countDown(); } } ``` ##### 3.7.2 RedisConfig.java ```java //**************pub/sub************* /* * 信息的接受者 * */ @Bean public RedisMessageListenerContainer listenerContainer(){ RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory(connectionFactory()); // addMessageListener(监听器,监听的频道),可以有多个Listener container.addMessageListener(messageListenerAdapter(),new PatternTopic("zyy")); return container; } @Bean public MessageListenerAdapter messageListenerAdapter(){ // MessageListenerAdapter(监听实例,监听的方法) return new MessageListenerAdapter(receiver(),"receiveMessage"); } @Bean public Receiver receiver(){ Receiver receiver = new Receiver(); receiver.setLatch(latch()); return receiver; } @Bean CountDownLatch latch(){ return new CountDownLatch(1); //灵活的设置值 } ``` ##### 3.7.3 测试类 ```java public class SubPubTest extends BaseTest { @Autowired private StringRedisTemplate template; @Autowired CountDownLatch latch; @Test public void test() throws InterruptedException { // 模拟服务器向某个监听器发送信息,这里会执行Receiver类的receiveMessage方法 template.convertAndSend("zyy","hello SubPub!"); latch.await(); //直到CountDownLatch的值为0才停止 } } ``` ##### 3.7.4 总结 > 1. 先建立接受者,可以认为是一个有若干个方法(receiveMessage)的普通类(Receiver),将类装载到Spring容器中 > 2. 需要配置一个监听器(MessageListenerAdapter),并告诉监听器哪个类(Receiver)接受信息,以及处理信息的方法名(receiveMessage) > 3. 配置监听器容器(RedisMessageListenerContainer),将监听器加入到容器中,还要告诉该监听器负责监听的关键话题(new PatternTopic("zyy")),可以是Topic的任一个实现类,但关键字只能为字符串 ##### 3.7.5 CountDownLatch类 > 作用->辅助监听器:每次发送给一个订阅者就执行cutDown(),到0就意味着全部发送完毕