Convert 组件介绍

       Convert 是一个比较独立的组件,仅依赖于util包。提供Java对象的序列化与反解析功能。支持JSON(JavaScript Object Notation)、BSON(Binary Stream Object Notation)两种格式化。 两种格式使用方式完全一样,其性能都大幅度超过其他JSON框架。同时JSON内置于HTTP协议中,BSON也是SNCP协议数据序列化的基础。

Convert 快速上手

      本介绍仅以JSON为例(BSON与JSON使用方式雷同)。其操作类主要是JsonConvert,配置类主要是JsonFactory、ConvertColumn。

      JsonConvert 序列化encode方法:

    public String convertTo(Object value);

    public String convertTo(final Type type, Object value);

    public void convertTo(final JsonWriter out, Object value);

    public void convertTo(final JsonWriter out, final Type type, Object value);

    public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, final Type type, Object value);

    public ByteBuffer[] convertTo(final Charset charset, final Supplier<ByteBuffer> supplier, final Type type, Object value);

    public ByteBuffer[] convertTo(final Supplier<ByteBuffer> supplier, Object value);

    public ByteBuffer[] convertTo(final Charset charset, final Supplier<ByteBuffer> supplier, Object value);

      JsonConvert 反解析decode方法:

    public <T> T convertFrom(final Type type, final String text);

    public <T> T convertFrom(final Type type, final char[] text);

    public <T> T convertFrom(final Type type, final char[] text, int start, int len);

    public <T> T convertFrom(final Type type, final ByteBuffer... buffers);

      Convert 与 ByteBuffer 的结合

      从以上的方法可以看出,与其他JSON框架相比Convert多了与ByteBuffer结合的方法。特别是convertTo方法加了Supplier<ByteBuffer>方法,这么做是为了提高数据传输的性能。在大部分情况下JSON序列化得到的数据流是为了传输出去,常见的场景就是HTTP+JSON接口。Convert提供ByteBuffer接口会大量减少中间临时数据的产生。通常大家输出JSON数据的方法如下:

    public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String json = new Gson().toJson(record);
        resp.setContentType("text/json; charset=UTF-8");
        resp.getOutputStream().write(json.getBytes("UTF-8"));
    }

      几乎所有的JSON框架提供的接口以String作为返回结果为主,其内在都是以char[]作为JsonWriter的载体。以Gson为例,Gson拼接JSON默认使用的是StringWriter,StringWriter的扩容策略是翻倍。为了方便计算,假设一个对象转换成JSON字符串大小为了10K。Gson在转换过程中产生的临时的char[]的大小: 16 + 32 + 64 + 128 + 256 + 512 + 1K + 2K + 4K + 8K + 16K = 32K, char[]转换成最终的String结果又会产生10K的char[], 最后在response输出时又回产生10K的byte[](方便计算不考虑双字节),也就是说整个对象输出过程中会产生52K的临时数据。而且常见的HTTP服务器(如实现java-servlet规范的服务器)不会把底层的ByteBuffer对象池暴露给上层。所以目前所有使用其他JSON框架输出数据都会产生至少5倍于数据体积大小(其他低于1倍扩容策略的框架会产生更多)的垃圾数据。
      RedKale框架的HTTP服务内置了Convert的JSON接口,避免了这么大量的垃圾数据产生。finishJson方法将HTTP服务的ByteBuffer对象池传给Convert, 使Convert在序列化过程中直接以UTF-8编码方式输出到ByteBuffer里。

    protected void execute(HttpRequest req, HttpResponse resp) throws IOException {
        resp.finishJson(record);
    }

      JSON基本用法:

    public class UserRecord {

        private int userid;

        private String username = "";

        private String password = "";

        public UserRecord() {
        }

        /** 以下省略getter setter方法 */
    }

    public static void main(String[] args) throws Exception {
        UserRecord user = new UserRecord();
        user.setUserid(100);
        user.setUsername("redkalename");
        user.setPassword("123456");
        final JsonConvert convert = JsonFactory.root().getConvert();
        String json = convert.convertTo(user);
        System.out.println(json);  //应该是 {"password":"123456","userid":100,"username":"redkalename"}
        UserRecord user2 = convert.convertFrom(UserRecord.class, json);
        System.out.println(convert.convertTo(user2)); //应该也是 {"password":"123456","userid":100,"username":"redkalename"}
        
        /**
         * 以下功能是为了屏蔽password字段。
         * 等价于 public String getPassword() 加上 @ConvertColumn :
         *
         *      @ConvertColumn(ignore = true, type = ConvertType.JSON)
         *      public String getPassword() {
         *          return password;
         *      }
         **/
        final JsonFactory childFactory = JsonFactory.root().createChild();
        childFactory.register(UserRecord.class, true, "password");
        childFactory.reloadCoder(UserRecord.class);
        final JsonConvert childConvert = childFactory.getConvert();
        json = childConvert.convertTo(user);
        System.out.println(json);  //应该是 {"userid":100,"username":"redkalename"}
        user2 = childConvert.convertFrom(UserRecord.class, json);
        System.out.println(childConvert.convertTo(user2)); //应该也是 {"userid":100,"username":"redkalename"}
    }

      Convert 支持非空构造函数, 必须在其构造函数加上 @ConstructorProperties 注释,且构造函数必须是public修饰。

public class UserRecord {

    private int userid;

    private String username = "";

    private String password = "";

    @java.beans.ConstructorProperties({"userid", "username", "password"})
    public UserRecord(int userid, String username, String password) {
        this.userid = userid;
        this.username = username;
        this.password = password;
    }

    public int getUserid() {
        return userid;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

      Convert 支持自定义Decode、Encode。

public class FileSimpleCoder<R extends Reader, W extends Writer> extends SimpledCoder<R, W, File> {

    public static final FileSimpleCoder instance = new FileSimpleCoder();

    private static final org.redkale.convert.ext.StringSimpledCoder stringCoder = org.redkale.convert.ext.StringSimpledCoder.instance;

    @Override
    public void convertTo(W out, File value) {
        stringCoder.convertTo(out, value == null ? null : value.getPath());
    }

    @Override
    public File convertFrom(R in) {
        String path = stringCoder.convertFrom(in);
        return path == null ? null : new File(path);
    }

}

JsonFactory.root().register(java.io.File.class, FileSimpleCoder.instance);

BsonFactory.root().register(java.io.File.class, FileSimpleCoder.instance);
                

BSON的协议格式

      BSON类似Java自带的Serializable, 其格式如下:
            1). 基本数据类型: 直接转换成byte[]
            2). SmallString(无特殊字符且长度小于256的字符串): length(1 byte) + byte[](utf8); 通常用于类名、字段名、枚举。
            3). String: length(4 bytes) + byte[](utf8);
            4). 数组: length(4 bytes) + byte[]...
            5). Object:
                  1. realclass (SmallString) (如果指定格式化的class与实体对象的class不一致才会有该值, 该值可以使用@ConvertEntity给其取个别名)
                  2. 空字符串(SmallString)
                  3. SIGN_OBJECTB 标记位,值固定为0xBB (short)
                  4. 循环字段值:
                        4.1 SIGN_HASNEXT 标记位,值固定为1 (byte)
                        4.2 字段类型; 1-9为基本类型&字符串; 101-109为基本类型&字符串的数组; 127为Object
                        4.3 字段名 (SmallString)
                        4.4 字段的值Object
                  5. SIGN_NONEXT 标记位,值固定为0 (byte)
                  6. SIGN_OBJECTE 标记位,值固定为0xEE (short)