概诉
上一节,我们已经详解了IDL详细的语法,Thrift 就是根据这个语法,替我们生成目标代码的。下面我们来具体分析一下,生成的代码究竟是什么作用。
准备
目录结构
我们来看一下代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ├── java │ └── learn │ └── thrift │ ├── bean │ ├── client │ │ └── Client.java │ ├── constant │ │ └── ServerConfig.java │ ├── gen_code.sh │ ├── idl │ │ ├── bean.thrift │ │ └── hello.thrift │ ├── idlcode │ │ ├── HelloWorldService.java │ │ └── UserService.java │ └── server │ ├── Server.java │ └── handler │ ├── HelloWorldServiceImpl.java │ └── UserServiceImpl.java
代码生成
我们来看一下 gen_code.sh 的代码
1 2 3 4 5 6 7 8 9 #!/bin/zsh thrift_name=hello.thrift thrift_bean=bean.thrift pathroot=../.. echo "gen code " thrift --gen java -out $pathroot ./idl/$thrift_name thrift --gen java -out $pathroot ./idl/$thrift_bean echo "finsh"
当我们执行这个脚本的时候 会将 idl 的两个文件生成对应的代码。
来看一下 bean.thrift 文件,这里主要是定义的 thrift struct 文件。
1 2 3 4 5 6 7 namespace java learn.thrift.bean struct Friends { 1 : required i16 No }
我们执行后发现在bean 下多了一个文件叫Friends
的类。
1 2 3 4 5 6 ├── java │ └── learn │ └── thrift │ ├── bean |-- Friends.java
生成代码样例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 package learn.thrift.bean;public class Friends implements org .apache .thrift .TBase <Friends , Friends ._Fields >, java .io .Serializable , Cloneable { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Friends" ); private static final org.apache.thrift.protocol.TField NO_FIELD_DESC = new org.apache.thrift.protocol.TField("No" , org.apache.thrift.protocol.TType.I16, (short )1 ); private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); static { schemes.put(StandardScheme.class , new FriendsStandardSchemeFactory ()) ; schemes.put(TupleScheme.class , new FriendsTupleSchemeFactory ()) ; } public short No; public enum _Fields implements org.apache.thrift.TFieldIdEnum { NO((short )1 , "No" ); private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); static { for (_Fields field : EnumSet.allOf(_Fields.class )) { byName.put(field.getFieldName(), field); } } public static _Fields findByThriftId (int fieldId) { switch (fieldId) { case 1 : return NO; default : return null ; } } public static _Fields findByThriftIdOrThrow (int fieldId) { _Fields fields = findByThriftId(fieldId); if (fields == null ) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!" ); return fields; } public static _Fields findByName (String name) { return byName.get(name); } private final short _thriftId; private final String _fieldName; _Fields(short thriftId, String fieldName) { _thriftId = thriftId; _fieldName = fieldName; } public short getThriftFieldId () { return _thriftId; } public String getFieldName () { return _fieldName; } } private static final int __NO_ISSET_ID = 0 ; private BitSet __isset_bit_vector = new BitSet(1 ); public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class ) ; tmpMap.put(_Fields.NO, new org.apache.thrift.meta_data.FieldMetaData("No" , org.apache.thrift.TFieldRequirementType.REQUIRED, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I16))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Friends.class , metaDataMap ) ; } public Friends () { } public Friends ( short No) { this (); this .No = No; setNoIsSet(true ); } public Friends (Friends other) { __isset_bit_vector.clear(); __isset_bit_vector.or(other.__isset_bit_vector); this .No = other.No; } public Friends deepCopy () { return new Friends(this ); } @Override public void clear () { setNoIsSet(false ); this .No = 0 ; } public short getNo () { return this .No; } public Friends setNo (short No) { this .No = No; setNoIsSet(true ); return this ; } public void unsetNo () { __isset_bit_vector.clear(__NO_ISSET_ID); } public boolean isSetNo () { return __isset_bit_vector.get(__NO_ISSET_ID); } public void setNoIsSet (boolean value) { __isset_bit_vector.set(__NO_ISSET_ID, value); } public void setFieldValue (_Fields field, Object value) { switch (field) { case NO: if (value == null ) { unsetNo(); } else { setNo((Short)value); } break ; } } public Object getFieldValue (_Fields field) { switch (field) { case NO: return Short.valueOf(getNo()); } throw new IllegalStateException(); } public boolean isSet (_Fields field) { if (field == null ) { throw new IllegalArgumentException(); } switch (field) { case NO: return isSetNo(); } throw new IllegalStateException(); } @Override public boolean equals (Object that) { if (that == null ) return false ; if (that instanceof Friends) return this .equals((Friends)that); return false ; } public boolean equals (Friends that) { if (that == null ) return false ; boolean this_present_No = true ; boolean that_present_No = true ; if (this_present_No || that_present_No) { if (!(this_present_No && that_present_No)) return false ; if (this .No != that.No) return false ; } return true ; } @Override public int hashCode () { return 0 ; } public int compareTo (Friends other) { if (!getClass().equals(other.getClass())) { return getClass().getName().compareTo(other.getClass().getName()); } int lastComparison = 0 ; Friends typedOther = (Friends)other; lastComparison = Boolean.valueOf(isSetNo()).compareTo(typedOther.isSetNo()); if (lastComparison != 0 ) { return lastComparison; } if (isSetNo()) { lastComparison = org.apache.thrift.TBaseHelper.compareTo(this .No, typedOther.No); if (lastComparison != 0 ) { return lastComparison; } } return 0 ; } public _Fields fieldForId (int fieldId) { return _Fields.findByThriftId(fieldId); } public void read (org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { schemes.get(iprot.getScheme()).getScheme().read(iprot, this ); } public void write (org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { schemes.get(oprot.getScheme()).getScheme().write(oprot, this ); } @Override public String toString () { StringBuilder sb = new StringBuilder("Friends(" ); boolean first = true ; sb.append("No:" ); sb.append(this .No); first = false ; sb.append(")" ); return sb.toString(); } public void validate () throws org.apache.thrift.TException { } private void writeObject (java.io.ObjectOutputStream out) throws java.io.IOException { try { write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); } catch (org.apache.thrift.TException te) { throw new java.io.IOException(te); } } private void readObject (java.io.ObjectInputStream in) throws java.io.IOException, ClassNotFoundException { try { __isset_bit_vector = new BitSet(1 ); read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); } catch (org.apache.thrift.TException te) { throw new java.io.IOException(te); } } private static class FriendsStandardSchemeFactory implements SchemeFactory { public FriendsStandardScheme getScheme () { return new FriendsStandardScheme(); } } private static class FriendsStandardScheme extends StandardScheme <Friends > { public void read (org.apache.thrift.protocol.TProtocol iprot, Friends struct) throws org.apache.thrift.TException { org.apache.thrift.protocol.TField schemeField; iprot.readStructBegin(); while (true ) { schemeField = iprot.readFieldBegin(); if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { break ; } switch (schemeField.id) { case 1 : if (schemeField.type == org.apache.thrift.protocol.TType.I16) { struct.No = iprot.readI16(); struct.setNoIsSet(true ); } else { org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break ; default : org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } iprot.readFieldEnd(); } iprot.readStructEnd(); if (!struct.isSetNo()) { throw new org.apache.thrift.protocol.TProtocolException("Required field 'No' was not found in serialized data! Struct: " + toString()); } struct.validate(); } public void write (org.apache.thrift.protocol.TProtocol oprot, Friends struct) throws org.apache.thrift.TException { struct.validate(); oprot.writeStructBegin(STRUCT_DESC); oprot.writeFieldBegin(NO_FIELD_DESC); oprot.writeI16(struct.No); oprot.writeFieldEnd(); oprot.writeFieldStop(); oprot.writeStructEnd(); } } private static class FriendsTupleSchemeFactory implements SchemeFactory { public FriendsTupleScheme getScheme () { return new FriendsTupleScheme(); } } private static class FriendsTupleScheme extends TupleScheme <Friends > { @Override public void write (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol oprot = (TTupleProtocol) prot; oprot.writeI16(struct.No); } @Override public void read (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol iprot = (TTupleProtocol) prot; struct.No = iprot.readI16(); struct.setNoIsSet(true ); } } }
代码详解
类图
下面我们会对上文的代码做详细分析。首先我们来看一下类图。
我们可以看到 Friends
是继承 TBase
, TBase
又实现 TFieldIdEnum
。
我们来看TBase 代码
基础数据结构
TBase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public interface TBase <T extends TBase <?, ?>, F extends TFieldIdEnum > extends Comparable <T >, Serializable { void read (TProtocol var1) throws TException ; void write (TProtocol var1) throws TException ; F fieldForId (int var1) ; boolean isSet (F var1) ; Object getFieldValue (F var1) ; void setFieldValue (F var1, Object var2) ; TBase<T, F> deepCopy () ; void clear () ; } public interface TFieldIdEnum { short getThriftFieldId () ; String getFieldName () ; }
TBase
是 Thrift
的所有Type 类型的基类接口,其中定了几个比较重要的方法,比如说读写,克隆和清空。 而TFieldIdEnum
只是一个id 和名字的枚举。 从这里可以看到,实现 TBase
的类具有可序列化的功能。
TStruct
接下来是 本 Struct
的一个描述, TStruct
只是对 struct
名字的一个描述的封装类,并没有什么特别。
1 2 3 4 5 6 7 8 9 10 11 private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("Friends" );public final class TStruct { public final String name; public TStruct () { this ("" ); } public TStruct (String n) { this .name = n; } }
TStruct 类似的文件是 Thrift 协议层的一个定义,另外协议层还有一下方法
协议层
这一层主要是用来定义如何序列化数据,属于协议层的一部分,将会在其他章节详细讨论,本节不在详细讲述。
TProtocol
TBinaryProtocol
TCompactProtocol
TJSONProtocol
TProtocolFactory
TProtocolUtil
TSimpleJSONProtocol
TTupleProtocol
TProtocolException
元数据
这里定义的各种其他元数据的类声明
TBase64Utils : Base64 数据的编解码工具
TField : Field 字段的声明
TList : List 结构的声明
TMap : Map 结构的声明
TMessage : Message 结构的声明
TMessageType : Message类型
TSet : set类型的声明
TStruct : struct 类型的声明
TType : thrift 所有支持类型的枚举
TType
Thrift 所有支持的类型 都在 TType
里定义的,我们看一下TType的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public final class TType { public static final byte STOP = 0 ; public static final byte VOID = 1 ; public static final byte BOOL = 2 ; public static final byte BYTE = 3 ; public static final byte DOUBLE = 4 ; public static final byte I16 = 6 ; public static final byte I32 = 8 ; public static final byte I64 = 10 ; public static final byte STRING = 11 ; public static final byte STRUCT = 12 ; public static final byte MAP = 13 ; public static final byte SET = 14 ; public static final byte LIST = 15 ; public static final byte ENUM = 16 ; }
TMessage
这个是 封装结构元数据的上层类, 当我们调用RPC 的时候, Thrift 会将我们要求我们按照他的格式封装元数据,然后在通过TMessage 封装元数据,然后通过网络发送出去。
1 2 3 4 5 6 public final class TMessageType { public static final byte CALL = 1 ; public static final byte REPLY = 2 ; public static final byte EXCEPTION = 3 ; public static final byte ONEWAY = 4 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public final class TMessage { public final String name; public final byte type; public final int seqid; public TMessage () { this ("" , (byte )0 , 0 ); } public TMessage (String n, byte t, int s) { this .name = n; this .type = t; this .seqid = s; } public String toString () { return "<TMessage name:'" + this .name + "' type: " + this .type + " seqid:" + this .seqid + ">" ; } public boolean equals (Object other) { return other instanceof TMessage ? this .equals((TMessage)other) : false ; } public boolean equals (TMessage other) { return this .name.equals(other.name) && this .type == other.type && this .seqid == other.seqid; } }
TMap 结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public final class TMap { public final byte keyType; public final byte valueType; public final int size; public TMap () { this ((byte )0 , (byte )0 , 0 ); } public TMap (byte k, byte v, int s) { this .keyType = k; this .valueType = v; this .size = s; } }
TSet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public final class TSet { public final byte elemType; public final int size; public TSet () { this ((byte )0 , 0 ); } public TSet (byte t, int s) { this .elemType = t; this .size = s; } public TSet (TList list) { this (list.elemType, list.size); } }
TList
1 2 3 4 5 6 7 8 9 10 11 12 13 public final class TList { public TList () { this (TType.STOP, 0 ); } public TList (byte t, int s) { elemType = t; size = s; } public final byte elemType; public final int size; }
TField
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class TField { public final String name; public final byte type; public final short id; public TField () { this ("" , TType.STOP, (short )0 ); } public TField (String n, byte t, short i) { name = n; type = t; id = i; } public String toString () { return "<TField name:'" + name + "' type:" + type + " field-id:" + id + ">" ; } public boolean equals (TField otherField) { return type == otherField.type && id == otherField.id; } }
Friends
中每个变量都会用 TField 来封装,如:
1 2 private static final TField NO_FIELD_DESC = new TField("No" , TType.I16, (short )1 );
里面记录了 Field 的名字,类型,和FieldId , thrift 会给每一个Struct的成员变量增加一个Field封装类。
IScheme
IScheme 的作用
除了变量的定义之外 Friends 类当中还有一个概念 叫 Scheme
1 2 3 4 5 private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>(); static { schemes.put(StandardScheme.class , new FriendsStandardSchemeFactory ()) ; schemes.put(TupleScheme.class , new FriendsTupleSchemeFactory ()) ; }
看一下继承关系
FriendsStandardSchemeFactory
就是 Scheme
的工厂,意思就是新建一个 FriendsStandardScheme
。 而 FriendsStandardScheme
又去实现了 IScheme
这个接口。 从而实现了 read
和 write
方法。
这个 Scheme
是干什么呢, 和 FriendsStandardScheme
, Friends
又是什么关系呢?
我们知道,Friends
是继承 TBase
的 TBase
中有两个重要的方法
1 2 void read (TProtocol var1) throws TException ;void write (TProtocol var1) throws TException ;
而 Friends
就是要实现这两个方法,
1 2 3 4 5 6 7 public void read (org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { schemes.get(iprot.getScheme()).getScheme().read(iprot, this ); } public void write (org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { schemes.get(oprot.getScheme()).getScheme().write(oprot, this ); }
Scheme
其实就是一种序列化方式,标识该类的具体读写策略、
Friends
序列化的时候,需要传递协议,也就是 TProtocol
的实现类,TProtocol
会根据自身支持的 序列化方式选择来选择 Friends
所支持的序列化方式,也就是的对应的具体 Scheme
工厂。从而创建出具体的 Scheme
实现类。
上述代码中 schemes.get(oprot.getScheme()).getScheme()
,第一个oprot.getScheme()
是选择对应的Scheme
方式,也就是 StandardScheme
还是 TupleScheme
。 从 Friends
静态对象 schemes 中找到Scheme
工厂然后实例化具体的 Scheme
的实现类。
本例中,Friends
一共实现类两种实现类 一种是 FriendsStandardScheme
, 一种是·FriendsTupleScheme
来看代码
1 2 3 4 5 6 7 8 9 10 11 private static class FriendsTupleSchemeFactory implements SchemeFactory { public FriendsTupleScheme getScheme () { return new FriendsTupleScheme(); } } private static class FriendsStandardSchemeFactory implements SchemeFactory { public FriendsStandardScheme getScheme () { return new FriendsStandardScheme(); } }
IScheme 具体实现方式。
下面我们具体来分析一下 FriendsStandardScheme
和 FriendsTupleSchemeFactory
具体的读写策略。
FriendsStandardScheme
我们来具体分析一下 FriendsStandardScheme
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 private static class FriendsStandardScheme extends StandardScheme <Friends > { public void read (org.apache.thrift.protocol.TProtocol iprot, Friends struct) throws org.apache.thrift.TException { org.apache.thrift.protocol.TField schemeField; iprot.readStructBegin(); while (true ) { schemeField = iprot.readFieldBegin(); if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { break ; } switch (schemeField.id) { case 1 : if (schemeField.type == org.apache.thrift.protocol.TType.I16) { struct.No = iprot.readI16(); struct.setNoIsSet(true ); } else { org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break ; default : org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } iprot.readFieldEnd(); } iprot.readStructEnd(); if (!struct.isSetNo()) { throw new org.apache.thrift.protocol.TProtocolException("Required field 'No' was not found in serialized data! Struct: " + toString()); } struct.validate(); } public void write (org.apache.thrift.protocol.TProtocol oprot, Friends struct) throws org.apache.thrift.TException { struct.validate(); oprot.writeStructBegin(STRUCT_DESC); oprot.writeFieldBegin(NO_FIELD_DESC); oprot.writeI16(struct.No); oprot.writeFieldEnd(); oprot.writeFieldStop(); oprot.writeStructEnd(); } }
Write 操作
从代码来分析 write 方法是写入规则,首先写入了 STRUCT_DESC
描述, 然后写入了 NO_FIELD_DESC
就是 No 这个字段,然后根据No 的大小吸入了No的具体的值,因为 No 是i16的类型,所以调用了 writeI16
的方法,然后 writeFieldEnd
标识该字段结束。
读操作
读取操作其实很写入操作相反, 需要注意一点是 读操作有个标记为 为 schemeField.type == org.apache.thrift.protocol.TType.STOP
遇到这个标记为,则读取直接停止。那么这个 STOP
是什么作用呢?
STOP 是 writeFieldStop
结束的标记为,标记该 Struct
的所有 Field
都已经读取完毕
从图中我们可以看到, 当Field 写入完毕后 wirte
会写入 TType.STOP
标记,代表所有的Field
都已经图区完毕,这个时候 read
就可以退出了。
而在每个Field
,Struct
协议的最后,又会写入结束的标记。
这个标记是由协议来实现的,目前不同的协议有不同的写入方法
例如
1 2 3 4 5 6 7 public void writeFieldEnd () {}public void writeFieldEnd () throws TException { this .writeJSONObjectEnd(); }
这里有个疑问,为什么 TBinaryProtocol
什么也不写呢?
问知道 TField
中有个 type
的字段, type
字段标明了 value
类型所占的长度,
所有我们用 往后读 type
所占的长度就可以获取 value
的值,所以 Field
的结束标记位可以什么都不用写。
那么又有读者会问,那么 String
类型呢,他没有固定长度。
二级制协议 TBinaryProtocol
中关于 写 String
写入的代码如下:
1 2 3 4 5 6 7 8 9 public void writeString (String str) throws TException { try { byte [] dat = str.getBytes("UTF-8" ); this .writeI32(dat.length); this .trans_.write(dat, 0 , dat.length); } catch (UnsupportedEncodingException var3) { throw new TException("JVM DOES NOT SUPPORT UTF-8" ); } }
从这里我们可以看出, 写 String
string 转成UTF8的编码,然后在value的地方前两个字节写入字符串的长度,然后在写入字符创的 byte 数据。
所以说,对于变长类型,写入的规则为 长度 + 数据 的方式。
FriendsTupleScheme
FriendsTupleScheme
重新定义了一种名为 TTupleProtocol
的协议,这个协议是一个
TCompactProtocol
的子类, TCompactProtocol
我会在协议层分析。
简单说 FriendsTupleScheme
一种压缩的编码方式,将所有的指以固定长度压缩在一起。结构如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private static class FriendsTupleScheme extends TupleScheme <Friends > { @Override public void write (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol oprot = (TTupleProtocol) prot; oprot.writeI16(struct.No); } @Override public void read (org.apache.thrift.protocol.TProtocol prot, Friends struct) throws org.apache.thrift.TException { TTupleProtocol iprot = (TTupleProtocol) prot; struct.No = iprot.readI16(); struct.setNoIsSet(true ); } }
GET SET 方法
SET 方法
Friends
为每一个Field 都生成了一个 set 方法 和一个 isSetXXX 的方法,标识该值是否被set。之所以有这个方式是因为 当我们在给 一个 i31的数 定义为 optional 的时候,i32 对应的是Java 语言的int 型 那么 这个值被初始化为 0,那么当rpc 过来的 这个值 就是0的时候,程序无法区分 这个0 是没有set 还是值就是0
注意,在设计RPC 协议或者其他协议的时候,避免 0 这个值。
Thrift 会给String 每个字段顶一个id, 名字为:__{filed name}_ISSET_ID
,又会定义个bitSet 结构 bitSet 的大小为字段的个数。
当我们执行 setXX 这个方法的时候会调用 setXXIsSet(true) 方法。 来标明这个方法已经赋过值了。
setXXIsSet(true)
本意就是讲 这个字段对应的BitSet 的位置控制成1。
1 2 3 4 5 6 7 8 private static final int __NO_ISSET_ID = 0 ;private BitSet __isset_bit_vector = new BitSet(1 );public Friends (short No) { this (); this .No = No; setNoIsSet(true ); }
同样我们还可以判断这个指是否
1 2 3 public boolean isSetNo () { return __isset_bit_vector.get(__NO_ISSET_ID); }
FieldMetaData 结构
Thrift
对于每个生成的类会有一个 FieldMetaData
的结构
这个类会和 Field 一个名称的枚举类型来对应成一个map结构。
FieldMetaData
主要是封装了 Field 值的一些元数据。
1 2 3 4 5 6 7 8 public static final Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; static { Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class ) ; tmpMap.put(_Fields.NO, new org.apache.thrift.meta_data.FieldMetaData("No" , org.apache.thrift.TFieldRequirementType.REQUIRED, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I16))); metaDataMap = Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(Friends.class , metaDataMap ) ; }
首先我们来看一下meta包下都有哪些字段
EnumMetaData
FieldMetaData
FieldValueMetaData
ListMetaData
MapMetaData
SetMetaData
StructMetaData
继承关系类图
FieldMetaData
这个数据结构是Friends
用来说明其中涉及Field
信息的元数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class FieldMetaData implements java .io .Serializable { public final String fieldName; public final byte requirementType; public final FieldValueMetaData valueMetaData; private static Map<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>> structMap; static { structMap = new HashMap<Class<? extends TBase>, Map<? extends TFieldIdEnum, FieldMetaData>>(); } public FieldMetaData (String name, byte req, FieldValueMetaData vMetaData) { this .fieldName = name; this .requirementType = req; this .valueMetaData = vMetaData; } }
Friends
或 new
一个这个出来,并传入了相应的信息,我们struct
的中的required
optional
的关键字 就是通过 这个常量类来定义的。
1 2 3 4 5 6 7 8 public final class TFieldRequirementType { public static final byte REQUIRED = 1 ; public static final byte OPTIONAL = 2 ; public static final byte DEFAULT = 3 ; public TFieldRequirementType () { } }
FieldValueMetaData
既然大部分是继承 FieldValueMetaData
那么我们首先来看这个数据结构,
1 2 3 4 5 6 7 public class FieldValueMetaData implements java .io .Serializable { public final byte type; private final boolean isTypedefType; private final String typedefName; private final boolean isBinary; }
获取FiledMetaData
全局获取
当我们新建好了 FiledMetaData之后可以通过 FiledMetaData 静态类来获取,
FiledMetaData.getStructMetaDataMap(Friends.class)
1 2 Friends friends = new Friends(); friends.getFieldValue(Friends._Fields.NO);
复杂符合类型
我们的 Friends 这是简单定义了 Friends 一个i16的字段No
如果我们在Friends 里增加一些复杂类型,Java 文件又是什么情况呢?
这里还要还有一个问题,我们只是分析了Struct 的生成类,那么Service的生成类是怎样的呢?
由于篇幅问题,我们这个话题留在下期去讨论。
总结
本节主要是总结了通过struct 文件生成的代码,主要谈论了框架生成的一些特性。更有一些协议层的相关的方法,thrift 的Java 代码非常轻量级,分层也非常明确,那么这些东西将会如何组织,整个的分层又是什么呢?我们下一节在详细论述。