Simple-Spring-Memcached使用Protobuf序列化Java对象

来源:互联网 发布:域名快速备案多少钱 编辑:程序博客网 时间:2024/05/16 09:31

本文提供一段代码示例,演示如何在Simple-Spring-Memcached中使用Protobuf序列化Java对象。主要解决的问题有:

  1. Protobuf序列化Map对象
  2. 实现SSM中CacheTranscoder接口的Protobuf实现
  3. 在SSM中为需要序列化的对象配置相应的Transcoder

Protobuf序列化Java对象

在项目中使用Protobuf序列化时,需要编写.proto消息定义文件,然后使用protoc编译出相应的java对象。其中的Java对象可以作为一个Bean在系统中使用,但是并不推荐。我们目前采用的策略如下:

  • 项目中使用独立的Domain对象,在类中提供一个通过Probobuf生成类为参数的构造函数
  • Protobuf生成的Java对象只用于序列化,不允许出现在其它场合
  • 每个项目模块维护一份.proto消息定义文件,放在domain/protobuf目录下,protoc编译生成的Java对象,也放在该目录下。结构如下:
  • 首先编辑.proto文件,按ProtoBuf的规则,添加需要的字段。如下:
    ssm.proto
    package org.colorfuldays.ssm.domain.protobuf; option java_package = "org.colorfuldays.ssm.domain.protobuf"; message MapEntity{    optional string key = 1;    optional string value = 2;} message Session{    optional int64 sessionId = 1;    repeated MapEntity attributes = 2;}

    编辑完成后,使用protoc编译出Java对象的源码

    执行下面命令即可编译中上图中的Ssm类源码,上面定义的Session,MapEntity生成的源码以Ssm的内部类形式放在Ssm.java文件中。

    protoc --java_out=/Users/star/Workspace/github/ssm-demo/src/main/java ssm.proto

    注:–java_out必须是绝对路径,在maven的项目中,需要定位在java目录。

    Protobuf序列化Session对象

    在生成的Ssm.java对象中包含了为Session,MapEntity做序列化操作的对象。

    • 序列化
      通过生成代码中各对象对应Builder类来构建对象,代码如下:
    Ssm.Session.Builder builder = Ssm.Session.newBuilder();Iterator<String> iterator = session.getAttributeKeySet().iterator();int index = 0;while (iterator.hasNext()) {    Ssm.MapEntity.Builder entityBuilder = Ssm.MapEntity.newBuilder();    entityBuilder.setKey(iterator.next()).setValue(session.getAttribute(iterator.next()));    builder.setAttributes(index, entityBuilder.build());}Ssm.Session = builder.build();

    反序列化
    反序列化比较简单,通过parseFrom方法即可,如下:
  • try {    Ssm.Session session = Ssm.Session.parseFrom(data.getData());    return new Session(session);} catch (InvalidProtocolBufferException e) {    LOG.error("parse session from protobuf error", e);}

    其中new Session(session) 使用的是Ssm.Session对象为参数的构造方法。

    Map在ProtoBuf中的序列化

    Protobuf并不提供原生Map类型,因此在序列化Java的Map对象时,需要自己想办法。在示例中,先定义了一个MapEntity,然后将其作为一个repeated域定义在Session中,在生在的代码中Session中存在一个包含MapEntity的List,类似于Java Api中Map的内部实现。通过这种变通的方式实现Map的序列化时,在序列化与反序列化时,需要手动作数据转换。

    Protobuf序列化与SSM框架结合

    SSM框架中提供了用户定义序列化方法的扩展点。要定制序列化方式首先需要实现CacheTranscoder接口,之后在配置文件中添加上相应的配置。
    SSM配置的自定义Transcoder粒度非常细,可以针对每一个需要序列化的对象配置相应的Transcoder实现类。

    实现CacheTranscoder接口

    CacheTranscoder接口只提供两个方法:

    public CachedObject encode(T t);public T decode(CachedObject data);

    其中encode为序列化就走,decode为反序列化方法。在这个demo示例的实现代码如下:

    SessionPBTranscoder
    public class SessionPBTranscoder implements CacheTranscoder<Session> {    private static final Logger LOG = LoggerFactory.getLogger(SessionPBTranscoder.class);    @Override    public Session decode(CachedObject data) {        try {            Ssm.Session session = Ssm.Session.parseFrom(data.getData());            return new Session(session);        } catch (InvalidProtocolBufferException e) {            LOG.error("parse session from protobuf error", e);        }        return null;    }     @Override    public CachedObject encode(Session session) {        Ssm.Session.Builder builder = Ssm.Session.newBuilder();        Iterator<String> iterator = session.getAttributeKeySet().iterator();        int index = 0;        while (iterator.hasNext()) {            Ssm.MapEntity.Builder entityBuilder = Ssm.MapEntity.newBuilder();            entityBuilder.setKey(iterator.next()).setValue(session.getAttribute(iterator.next()));            builder.setAttributes(index, entityBuilder.build());        }         return new CachedObjectImpl(PROTOBUF_SERIALIZED,builder.build().toByteArray());    }     private static final int PROTOBUF_SERIALIZED = 9;}

    CacheTranscoder配置

    CacheTranscoder的配置是在SSM选用的memcached client中配置的。在这里使用的是XMemcached客户端,配置如下:

    <bean name="defaultMemcachedClient" class="com.google.code.ssm.CacheFactory">        <property name="cacheClientFactory">            <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>        </property>        <property name="addressProvider">            <bean class="com.google.code.ssm.config.DefaultAddressProvider">                <property name="address" value="127.0.0.1:11211"/>            </bean>        </property>        <property name="configuration">            <bean class="com.google.code.ssm.providers.CacheConfiguration">                <property name="consistentHashing" value="true"/>            </bean>        </property>        <property name="cacheTranscoders">            <map>                <entry key="org.colorfuldays.ssm.domain.BookDO" value-ref="jsonTranscoder"/>                <entry key="org.colorfuldays.ssm.domain.UserDO" value-ref="userProtobufTranscoder"/>                <entry key="org.colorfuldays.ssm.domain.Session" value-ref="sessionPBTranscoder"/>            </map>        </property>    </bean>     <bean name="jsonTranscoder" class="com.google.code.ssm.transcoders.JsonTranscoder">        <constructor-arg index="0"  value="org.colorfuldays.ssm.domain.BookDO"/>        <constructor-arg index="1">            <ref bean="JsonObjectMapper"/>        </constructor-arg>        <constructor-arg index="2">            <ref bean="longToStringTranscoder"/>        </constructor-arg>    </bean>     <bean name="userProtobufTranscoder" class="org.colorfuldays.ssm.transcoders.ProtobufTranscoder"/>    <bean name="sessionPBTranscoder" class="org.colorfuldays.ssm.transcoders.SessionPBTranscoder"/>    <bean name="longToStringTranscoder" class="com.google.code.ssm.transcoders.LongToStringTranscoder"/>    <bean name="JsonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>

    注:上述cacheTranscoders是一个Map<Class,CacheTranscoder>对象,其中Class作为Map的key,在Spring配置中只需要配置Class的全名即可。

    完成上述配置后,即整个过程就全部完成了。具体使用的例子请参考github里面的源码 https://github.com/iamxhu/ssm-demo

    [update]:测试发现,在使用自定义的CacheTranscoder时必须给AOP拦截的方法加上 @UseJson注解。


原创粉丝点击