设置序列化方式
为key和value配置特殊的序列化工具
下面的内容,使得key使用了StringRedisSerializer序列化工具。
使用value 使用了jackson2JsonRedisSerializer的序列化工具
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// json序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setConnectionFactory(factory);
// key序列化方式
template.setKeySerializer(new StringRedisSerializer());
// value序列化
template.setValueSerializer(jackson2JsonRedisSerializer);
// value hasHmap序列化
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
new Jackson2JsonRedisSerializer(Object.class);
// 解决查询缓存转换异常的问题
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// 配置序列化(解决乱码的问题),过期时间30秒
RedisCacheConfiguration config =
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
.disableCachingNullValues();
RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
return cacheManager;
}
}
要想检测配置是否成功,可以使用下面的测试内容
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = RedisApplication.class)
@Slf4j
public class TestSerializer {
@Autowired
private RedisTemplate redisTemplate;
/**
* 启动RedisConfig配置的时候可以此测试可以通过,否则无法通过
*/
@Test
public void setUserByJackson2JsonRedisSerializer() {
User user = new User();
user.setAge(10);
user.setName("user");
user.setId(1L);
redisTemplate.opsForValue().set("test",user);
Object o = redisTemplate.opsForValue().get("test");
Assert.assertTrue(o instanceof User);
Assert.assertEquals("user",((User)o).getName());
}
/**
* 启动RedisConfig配置的时候可以此测试可以无法通过,否则通过
*/
@Test
public void setIntKey() {
redisTemplate.opsForValue().set(10L,"10");
Object o = redisTemplate.opsForValue().get(10L);
Assert.assertEquals("10",o);
}
}
因为我们设置key使用了字符串序列化方式所以setIntKey
使用long类型作为key会抛出异常,而setUserByJackson2JsonRedisSerializer
则会通过。
setIntKey
抛出的异常
java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
at org.springframework.data.redis.serializer.StringRedisSerializer.serialize(StringRedisSerializer.java:35)
at org.springframework.data.redis.core.AbstractOperations.rawKey(AbstractOperations.java:111)
at org.springframework.data.redis.core.AbstractOperations$ValueDeserializingRedisCallback.doInRedis(AbstractOperations.java:59)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.AbstractOperations.execute(AbstractOperations.java:95)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:198)
......
假如我们取消掉配置的内容,因为setUserByJackson2JsonRedisSerializer
尝试保存对象而不是字符串,则会抛出下面的异常。
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [dai.samples.core.entitiy.User]
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:96)
at org.springframework.data.redis.core.AbstractOperations.rawValue(AbstractOperations.java:126)
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:197)
at dai.samples.redis.base.service.impl.TestSerializer.setUserByJackson2JsonRedisSerializer(TestSerializer.java:35)
......
使用redis实现消息队列
根据之前的API文档我们可以发现redisTemplate的convertAndSend方法,这个方法的作用
就是向指定通道发送消息,使用这个方法我们可以实现消息队列的操作
消息发送者
首先我们设置一个通道的名称
/**
* 通道名称
*/
public static String CHANNEL = "dai.channel";
然后我们创建一个消息发送者
@Component
public class PubSend {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 发布消息到通道
*
* @param message
*/
public void sendTopicMessage(String message) {
redisTemplate.convertAndSend(RedisListenerConfig.CHANNEL,
message);
}
}
这样我们可以使用sendTopicMessage
将消息发送至dai.channel
通道中
消息监听者
要监听通道的消息,我们需要一个实现了org.springframework.data.redis.connection.MessageListener
的方法。然后我们可以在onMessage中处理接收到的消息。
@Slf4j
@Component
public class MyRedisMessageListener implements MessageListener {
@Autowired
private RedisTemplate <String, String> redisTemplate;
/**
* 接收消息的处理
* Callback for processing received objects through Redis.
* @param message message must not be {@literal null}.
* @param pattern pattern matching the channel (if specified) - can be {@literal null}.
*/
@Override
public void onMessage(Message message, byte[] pattern) {
byte[] body = message.getBody();
byte[] channel = message.getChannel();
String msgContent =
(String) redisTemplate
.getValueSerializer()
.deserialize(body);
String topic =
redisTemplate
.getStringSerializer()
.deserialize(channel);
// 设置输出内容
log.info("redis--topic:" + topic + " body:" + msgContent);
}
}
配置监听
现在我们有了消息监听的类,现在只要我们将此监听器和通道进行绑定即可。这个时候需要使用
org.springframework.data.redis.listener.RedisMessageListenerContainer
监听容器
@Configuration
public class RedisListenerConfig {
/**
* 通道名称
*/
public static String CHANNEL = "dai.channel";
@Bean
RedisMessageListenerContainer container(
RedisConnectionFactory connectionFactory,
MyRedisMessageListener listener) {
// 新建监听对象
RedisMessageListenerContainer container =
new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
// 订阅了一个通道
container.addMessageListener(listener, new PatternTopic(CHANNEL));
// 这个container 可以添加多个 messageListener
return container;
}
}
测试接口
现在我们配置一个测试的接口,进行消息请求的测试
@RestController
@RequestMapping("pub")
public class PubController {
@Autowired
private PubSend send;
/**
* 测试消息发送
* @param message
* @return
*/
@RequestMapping(value = "send/{message}",method = RequestMethod.GET)
public String send(@PathVariable("message") String message) {
send.sendTopicMessage(message);
return message;
}
}
现在我们访问http://localhost:8000/pub/send//testMessage地址
现在我们查看控制台可以看到,通道的名称和消息内容已经成功输出
2019-08-07 21:24:19.060 INFO 19468 --- [ container-4] d.s.r.p.listener.MyRedisMessageListener : redis--topic:dai.channel body:testMessage
本篇文章涉及的源码下载地址:https://gitee.com/daifyutils/springboot-samples