接码字。。。
5.6. 方便操作String类型的类
一般情况下对于存于Redis的key和value都是String类型的数据。为此Redis模块提供两个扩展RedisConnection和RedisTemplate的实现类,它们分别为StringRedisConnection(和他默认实现类DefaultStringRedisConnection)和StringRedisTemplate,提供对String类型一站式操作。除了绑定String的key,template和connection都要采用StringRedisSerializer进行序列化,这就意味找存储的键值具有可读性(假设你和Redis使用相同的编码),例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:use-pool="true"/>
<bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate" p:connection-factory-ref="jedisConnectionFactory"/>
...
</beans>
public class Example {
@Autowired
private StringRedisTemplate redisTemplate;
public void addLink(String userId, URL url) {
redisTemplate.opsForList().leftPush(userId, url.toExternalForm());
}
}
类似其他Spring的template,RedisTemplate和StringRedisTemplate允许开发者通过RedisCallback回调接口直接与Redis进行通讯,开发者就可以通过RedisConnection进行完全的控制Redis。注意当一个StringRedisTemplate被使用这个回调接口将会收到一个StringRedisConnection实例。
public void useCallback() {
redisTemplate.execute(new RedisCallback<Object>() {
public Object doInRedis(RedisConnection connection) throws DataAccessException {
Long size = connection.dbSize();
// Can cast to StringRedisConnection if using a StringRedisTemplate
((StringRedisConnection)connection).set("key", "value");
}
});
}
5.7.(Serializers)序列化
从框架的视图来看,存储在Redis的数据就是字节。Redis它自己支持多种数据类型。对于大多数存储的是数据的引用而不是它所代表的数据。这要取决于这些信息被转换成字符串还是任何其他对象。这种转换出现在用户自定义类型和原生数据类型(反之亦然)。这种转换可以通过实现RedisSerializer接口具体的类决定。(org.springframework.data.redis.serializer),多数都是开箱即用。有两个已经在文档中提及了:StringRedisSerializer和JdkSerializationRedisSerializer,然而一个可以使用OxmSerializer 对于Object/xml进行序列化。不过需要Spring 3 OXM 进行支持。JacksonJsonRedisSerializer、Jackson2JsonRedisSerializer或者GenericJackson2JsonRedisSerializer对于JSON格式的数据进行序列化。注意对于格式化不限于value(值),它可以用到keys(键)、hashes等等等。
5.8.Hash Mapping
Redis可以存储多种数据类型。你已经学习到Jackson2JsonRedisSerializer可以转化JSON格式数据。JSON可以值,而键可以简单字符串。多数复杂mapping结构对象也是可以使用Redis Hashes进行存储。Spring Data Redis 提供多种策略去解决mapping数据的Hash值问题。这主要取决于用户使用场景。
1、直接使用HashOperations和序列化去操作mapping
2、使用RedisRepositories
3、使用HashMapper和HashOperations
5.8.1. Hash mappers
Hash mappers是转换器--将map对象转换成Map<K, V>。HashMapper与Redis Hashes进行搭配使用。
多个开箱即用的实现类
1. BeanUtilsHashMapper 使用的是Spring的BeanUtils
2. ObjectHashMapper 使用的Object to Hash Mapping
3.Jackson2HashMapper 使用的FasterXML Jackson.
public class Person {
String firstname;
String lastname;
// …
}
public class HashMapping {
@Autowired
HashOperations<String, byte[], byte[]> hashOperations;
HashMapper<Object, byte[], byte[]> mapper = new ObjectHashMapper();
public void writeHash(String key, Person person) {
Map<byte[], byte[]> mappedHash = mapper.toHash(person);
hashOperations.putAll(key, mappedHash);
}
public Person loadHash(String key) {
Map<byte[], byte[]> loadedHash = hashOperations.entries("key");
return (Person) mapper.fromHash(loadedHash);
}
}
5.8.2. Jackson2HashMapper
Jackson2HashMapper 使用FasterXML Jackson为域对象提供Redis Hash映射.Jackson2HashMapper可以映射最外层的属性作为Hash字段名,也可以将json进行扁平化。简单的map对简单的值,复制类型(内部类,集合,map)代表内部json。
可以为所有属性进行扁平化。复杂类型尽可能分解成简单类型。
public class Person {
String firstname;
String lastname;
Address address;
}
public class Address {
String city;
String country;
}
表格2.正常映射
HashField | Value |
firstname | Jon |
lastname | Snon |
address | {"city":"Castle Black","country":"The North"} |
笔者:看来Spring Data Redis 也是很喜欢《权利的游戏》,O(∩_∩)O~
表格3.扁平映射
HashField | Value |
firstname | Jon |
lastname | Snow |
address.city | Castle Black |
address.country | The North |
提示:
扁平化所有的属性名都不要以路径命名,(也就是不能出现 address.city),使用点或括弧的键组成json不能扁平化。它结果Hash也不能映射成一个对象。
5.9.Redis Messaging/PubSub (Redis的订阅与发布)
Spring Data 提供专门的方法去整合Redis的信息相关。它在命名和功能上非常类似Spring框架整合JMS,事实上,如果读者非常熟悉Spring对于JMS操作,那么对于Redis的信息操作就是轻车熟路。
Redis 信息粗略从功能和命名上分为两个大部分,即生产或发布信息和消费或订阅信息。它们简写就是pubsub(Publish/Subscribe).RedisTemplate 类用作信息生产。对于异步接受类似与Java EE 的消息驱动 bean的风格,Spring Data提供一个专门消息监听容器,它被用来创建消息驱动(Message-Driven POJOS),对于同步 请求需要尊徐RedisConnection的协议。
在包org.springframework.data.redis.conneciton和org.springframework.data.redis.listener为Redis 信息提供核心的功能
5.9.1.Sending/Publishing messages(发送消息)
对于发送一个消息,不管是对于低级RedisConnection还是高级RedisTemplate都是可以完成的。两个实体都提供带有一个消息参数的发送方法。并且需要目标的地址。唯一不同的是RedisConnection需要的原生数据(字节数组),而RedisTemplate需要任意对象作为发送消息体。
// send message through connection RedisConnection con = ...
byte[] msg = ...
byte[] channel = ...
con.publish(msg, channel); // send message through RedisTemplate
RedisTemplate template = ...
template.convertAndSend("hello!", "world");
5.9.2. Receiving/Subscribing for messages(接收消息)
在接收一方,它可以订阅一个或者多个(Channel)频道,它是通过名称或者匹配规则进行订阅。当使用一条命令订阅多个频道的时候,后者(匹配规则)方式相当有用,同时在监听的时候不需要花费创建订阅的时间(只有匹配上才会创建)。
在低级抽象化操作类,RedisConnection提供subscribe和pSubscribe方法,这个方法映射到Redis命令去订阅匹配的渠道。注意多个渠道或者模式都可以被用作参数。为了改变订阅一个连接或简单查询是否被监听,RedisConnection提供了getSubscription 和 isSubscribed方法。
提示:订阅命令在SpringData是阻塞式的,也就是,当在一个连接上调用订阅命令,那么当前线程就处于阻塞状态,它将开始等待消息到来,只有订阅被取消这个线程才会被释放,也就是额外线程在相同连接上调用了unsubscribe或pUnsubscribe方法,详情请参考message listener container去解决这个问题。
如上所述。一旦连接上订阅之后就会等待消息。没有其他命令可以被调用,除非增加新的订阅或者修改取消现有的这个。也就是说,调用其他subscribe、pSubscribe,unsubscribe,或者pUnsubscribe都是非法的,它也将会抛出异常。
为了实现订阅消息就需要实现MessageListener回调函数:每一次信息消息到达的时候,这个回调函数将会被调用,用户写在onMessage 方法内代码将会被执行。这个接口可以获取不仅仅是实际的消息,对于采用匹配规则匹配的渠道也是可以接收消息的。这些消息允许callee(执行中对象)不相同的。它不仅仅是内容,可以是数据。
Message Listener Containers(消息监听容器)
由于订阅的阻塞特性,低级的订阅不那么吸引人了,他要求连接和线程管理每一次监听。为解决这问题,SpringData提供RedisMessageLIstenerContainer,它将会为用户做很多繁杂的事情。如果用户熟悉EJB和JMS这个概念熟悉,它被设计尽可能支持Spring 框架,它就是message-driven POJOS(MDPs消息驱动)。
RedisMessageListenerContainer作为消息监听容器角色。它被用来接收来自Redis 频道的信息。然后驱动MessageListener将会信息注入其中。这个监听容器的职责就是接收所有线程的消息,然后分发给监听器的过程。消息监听容器就是MDP和消息提供者的中间人。它关心注册接收消息,资源获取和释放,以及异常转换。它让你专注于业务逻辑代码,那些样板的代码交给框架去解决。
尽可能使用应用不用关心底层代码
RedisMessageListenerContainer 允许一个连接和一个线程被多个监听器共享,甚至它们不需要分享同一个订阅,所以一个应用无论有多少监听器或者频道,在生命周期中运行消耗将会保持相同。更进一步,容器允许运行时改变配置。比如移除或添加一个监听器,它是可以不用重启的。除此之外,容器使用的延迟订阅模式,只有需要的时候RedisConnection才会被创建,如果所有监听器被取消订阅,清除将会自动完成,监听所有使用的线程将会被释放。
为帮助异步消息处理,容器需要一个java.util.concurrent.Executor(或 Spring's TaskExecutor)去分发消息。对于监听器数量或者运行环境都要取决于加载,它应该进行稍微的调整和改变executor使得服务器达到更好的状态.特别是在管理环境(例如app 服务器),强烈推荐选择合适的TaskExecutor提供性能。
The MessageListenerAdapter (消息适配器)
MessageListenerAdapter 是Spring异步消息支持的最终类:在 nutshell中,它允许你暴露几乎任何类作为MDP(这里当然有些限制)。
考虑到接口定义。注意虽然这个接口并没有继承MessageListener接口。它仍然可以通过使用MessageListenerAdapter把它被作为MDP,注意这些消息处理方法的类型是强类型,它根据是各种消息类型,它都可以被接收和处理,除此之外,频道或模式将会作为第二个参数传入方法当中。
public interface MessageDelegate {
void handleMessage(String message);
void handleMessage(Map message); void handleMessage(byte[] message);
void handleMessage(Serializable message);
// pass the channel/pattern as well
void handleMessage(Serializable message, String channel);
}
public class DefaultMessageDelegate implements MessageDelegate {
// implementation elided for clarity...
}
特别是,注意以上实现MessageDelegate接口的类(DefaultMessageDelegate 类)并没有Redis依赖。它是真正的POJO类,我们可以通过如下配置,使他作为一个MDP。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:redis="http://www.springframework.org/schema/redis"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/redis http://www.springframework.org/schema/redis/spring-redis.xsd">
<!-- the default ConnectionFactory -->
<redis:listener-container>
<!-- the method attribute can be skipped as the default method name is "handleMessage" -->
<redis:listener ref="listener" method="handleMessage" topic="chatroom" />
</redis:listener-container>
<bean id="listener" class="redisexample.DefaultMessageDelegate"/>
...
<beans>
提示:这里话题要么是频道(例如, topic="chatroom")或模式(例如:topic="*room")
以上例子使用Redis的命名空间去声明一个消息监听容器,它将自动将会POJO类注册成监听器。全文如下,beans定义如下
<bean id="messageListener" class="org.springframework.data.redis.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="redisexample.DefaultMessageDelegate"/>
</constructor-arg>
</bean>
<bean id="redisContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="messageListeners">
<map>
<entry key-ref="messageListener">
<bean class="org.springframework.data.redis.listener.ChannelTopic">
<constructor-arg value="chatroom">
</bean>
</entry>
</map>
</property>
</bean>
每一次消息被接收的时候,这个适配器将会自动(使用配置RedisSerializer)在低级格式和要求对象类型透明的转换,任何方法调用产生的异常将会被容器捕获和处理(默认情况记录在日志中)。
待续。。。。。。。。。。。。。