先简单写一个thrift文件

本地通过thrift编译之后会生成一个java源文件。------编译口令 :thrift -gen java mytestrequest.thrift

编译后的源代码如下:

 /**
* Autogenerated by Thrift Compiler (0.8.0)
*
* DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
* @generated
*/
package com.thrift; import org.apache.thrift.scheme.IScheme;
import org.apache.thrift.scheme.SchemeFactory;
import org.apache.thrift.scheme.StandardScheme; import org.apache.thrift.scheme.TupleScheme;
import org.apache.thrift.protocol.TTupleProtocol;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.EnumMap;
import java.util.Set;
import java.util.HashSet;
import java.util.EnumSet;
import java.util.Collections;
import java.util.BitSet;
import java.nio.ByteBuffer;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory; /**
* 测试类
*
*/
public class koalasRequest implements org.apache.thrift.TBase<koalasRequest, koalasRequest._Fields>, java.io.Serializable, Cloneable {
private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("koalasRequest"); private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)1);
private static final org.apache.thrift.protocol.TField NAME_FIELD_DESC = new org.apache.thrift.protocol.TField("name", org.apache.thrift.protocol.TType.STRING, (short)2);
private static final org.apache.thrift.protocol.TField ADDRESS_FIELD_DESC = new org.apache.thrift.protocol.TField("address", org.apache.thrift.protocol.TType.STRING, (short)3); private static final Map<Class<? extends IScheme>, SchemeFactory> schemes = new HashMap<Class<? extends IScheme>, SchemeFactory>();
static {
schemes.put(StandardScheme.class, new koalasRequestStandardSchemeFactory());
schemes.put(TupleScheme.class, new koalasRequestTupleSchemeFactory());
} public int age; // required
public String name; // required
public String address; // required /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */
public enum _Fields implements org.apache.thrift.TFieldIdEnum {
AGE((short)1, "age"),
NAME((short)2, "name"),
ADDRESS((short)3, "address"); private static final Map<String, _Fields> byName = new HashMap<String, _Fields>(); static {
for (_Fields field : EnumSet.allOf(_Fields.class)) {
byName.put(field.getFieldName(), field);
}
} /**
* Find the _Fields constant that matches fieldId, or null if its not found.
*/
public static _Fields findByThriftId(int fieldId) {
switch(fieldId) {
case 1: // AGE
return AGE;
case 2: // NAME
return NAME;
case 3: // ADDRESS
return ADDRESS;
default:
return null;
}
} /**
* Find the _Fields constant that matches fieldId, throwing an exception
* if it is not found.
*/
public static _Fields findByThriftIdOrThrow(int fieldId) {
_Fields fields = findByThriftId(fieldId);
if (fields == null) throw new IllegalArgumentException("Field " + fieldId + " doesn't exist!");
return fields;
} /**
* Find the _Fields constant that matches name, or null if its not found.
*/
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;
}
} // isset id assignments
private static final int __AGE_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.AGE, new org.apache.thrift.meta_data.FieldMetaData("age", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.I32)));
tmpMap.put(_Fields.NAME, new org.apache.thrift.meta_data.FieldMetaData("name", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
tmpMap.put(_Fields.ADDRESS, new org.apache.thrift.meta_data.FieldMetaData("address", org.apache.thrift.TFieldRequirementType.DEFAULT,
new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING)));
metaDataMap = Collections.unmodifiableMap(tmpMap);
org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(koalasRequest.class, metaDataMap);
} public koalasRequest() {
} public koalasRequest(
int age,
String name,
String address)
{
this();
this.age = age;
setAgeIsSet(true);
this.name = name;
this.address = address;
} /**
* Performs a deep copy on <i>other</i>.
*/
public koalasRequest(koalasRequest other) {
__isset_bit_vector.clear();
__isset_bit_vector.or(other.__isset_bit_vector);
this.age = other.age;
if (other.isSetName()) {
this.name = other.name;
}
if (other.isSetAddress()) {
this.address = other.address;
}
} public koalasRequest deepCopy() {
return new koalasRequest(this);
} @Override
public void clear() {
setAgeIsSet(false);
this.age = 0;
this.name = null;
this.address = null;
} public int getAge() {
return this.age;
} public koalasRequest setAge(int age) {
this.age = age;
setAgeIsSet(true);
return this;
} public void unsetAge() {
__isset_bit_vector.clear(__AGE_ISSET_ID);
} /** Returns true if field age is set (has been assigned a value) and false otherwise */
public boolean isSetAge() {
return __isset_bit_vector.get(__AGE_ISSET_ID);
} public void setAgeIsSet(boolean value) {
__isset_bit_vector.set(__AGE_ISSET_ID, value);
} public String getName() {
return this.name;
} public koalasRequest setName(String name) {
this.name = name;
return this;
} public void unsetName() {
this.name = null;
} /** Returns true if field name is set (has been assigned a value) and false otherwise */
public boolean isSetName() {
return this.name != null;
} public void setNameIsSet(boolean value) {
if (!value) {
this.name = null;
}
} public String getAddress() {
return this.address;
} public koalasRequest setAddress(String address) {
this.address = address;
return this;
} public void unsetAddress() {
this.address = null;
} /** Returns true if field address is set (has been assigned a value) and false otherwise */
public boolean isSetAddress() {
return this.address != null;
} public void setAddressIsSet(boolean value) {
if (!value) {
this.address = null;
}
} public void setFieldValue(_Fields field, Object value) {
switch (field) {
case AGE:
if (value == null) {
unsetAge();
} else {
setAge((Integer)value);
}
break; case NAME:
if (value == null) {
unsetName();
} else {
setName((String)value);
}
break; case ADDRESS:
if (value == null) {
unsetAddress();
} else {
setAddress((String)value);
}
break; }
} public Object getFieldValue(_Fields field) {
switch (field) {
case AGE:
return Integer.valueOf(getAge()); case NAME:
return getName(); case ADDRESS:
return getAddress(); }
throw new IllegalStateException();
} /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */
public boolean isSet(_Fields field) {
if (field == null) {
throw new IllegalArgumentException();
} switch (field) {
case AGE:
return isSetAge();
case NAME:
return isSetName();
case ADDRESS:
return isSetAddress();
}
throw new IllegalStateException();
} @Override
public boolean equals(Object that) {
if (that == null)
return false;
if (that instanceof koalasRequest)
return this.equals((koalasRequest)that);
return false;
} public boolean equals(koalasRequest that) {
if (that == null)
return false; boolean this_present_age = true;
boolean that_present_age = true;
if (this_present_age || that_present_age) {
if (!(this_present_age && that_present_age))
return false;
if (this.age != that.age)
return false;
} boolean this_present_name = true && this.isSetName();
boolean that_present_name = true && that.isSetName();
if (this_present_name || that_present_name) {
if (!(this_present_name && that_present_name))
return false;
if (!this.name.equals(that.name))
return false;
} boolean this_present_address = true && this.isSetAddress();
boolean that_present_address = true && that.isSetAddress();
if (this_present_address || that_present_address) {
if (!(this_present_address && that_present_address))
return false;
if (!this.address.equals(that.address))
return false;
} return true;
} @Override
public int hashCode() {
return 0;
} public int compareTo(koalasRequest other) {
if (!getClass().equals(other.getClass())) {
return getClass().getName().compareTo(other.getClass().getName());
} int lastComparison = 0;
koalasRequest typedOther = (koalasRequest)other; lastComparison = Boolean.valueOf(isSetAge()).compareTo(typedOther.isSetAge());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetAge()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.age, typedOther.age);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetName()).compareTo(typedOther.isSetName());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetName()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.name, typedOther.name);
if (lastComparison != 0) {
return lastComparison;
}
}
lastComparison = Boolean.valueOf(isSetAddress()).compareTo(typedOther.isSetAddress());
if (lastComparison != 0) {
return lastComparison;
}
if (isSetAddress()) {
lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.address, typedOther.address);
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("koalasRequest(");
boolean first = true; sb.append("age:");
sb.append(this.age);
first = false;
if (!first) sb.append(", ");
sb.append("name:");
if (this.name == null) {
sb.append("null");
} else {
sb.append(this.name);
}
first = false;
if (!first) sb.append(", ");
sb.append("address:");
if (this.address == null) {
sb.append("null");
} else {
sb.append(this.address);
}
first = false;
sb.append(")");
return sb.toString();
} public void validate() throws org.apache.thrift.TException {
// check for required fields
} 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 {
// it doesn't seem like you should have to do this, but java serialization is wacky, and doesn't call the default constructor.
__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 koalasRequestStandardSchemeFactory implements SchemeFactory {
public koalasRequestStandardScheme getScheme() {
return new koalasRequestStandardScheme();
}
} private static class koalasRequestStandardScheme extends StandardScheme<koalasRequest> { public void read(org.apache.thrift.protocol.TProtocol iprot, koalasRequest 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: // AGE
if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
struct.age = iprot.readI32();
struct.setAgeIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 2: // NAME
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
} else {
org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
}
break;
case 3: // ADDRESS
if (schemeField.type == org.apache.thrift.protocol.TType.STRING) {
struct.address = iprot.readString();
struct.setAddressIsSet(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(); // check for required fields of primitive type, which can't be checked in the validate method
struct.validate();
} public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException {
struct.validate(); oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(AGE_FIELD_DESC);
oprot.writeI32(struct.age);
oprot.writeFieldEnd();
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeString(struct.name);
oprot.writeFieldEnd();
}
if (struct.address != null) {
oprot.writeFieldBegin(ADDRESS_FIELD_DESC);
oprot.writeString(struct.address);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
} } private static class koalasRequestTupleSchemeFactory implements SchemeFactory {
public koalasRequestTupleScheme getScheme() {
return new koalasRequestTupleScheme();
}
} private static class koalasRequestTupleScheme extends TupleScheme<koalasRequest> { @Override
public void write(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException {
TTupleProtocol oprot = (TTupleProtocol) prot;
BitSet optionals = new BitSet();
if (struct.isSetAge()) {
optionals.set(0);
}
if (struct.isSetName()) {
optionals.set(1);
}
if (struct.isSetAddress()) {
optionals.set(2);
}
oprot.writeBitSet(optionals, 3);
if (struct.isSetAge()) {
oprot.writeI32(struct.age);
}
if (struct.isSetName()) {
oprot.writeString(struct.name);
}
if (struct.isSetAddress()) {
oprot.writeString(struct.address);
}
} @Override
public void read(org.apache.thrift.protocol.TProtocol prot, koalasRequest struct) throws org.apache.thrift.TException {
TTupleProtocol iprot = (TTupleProtocol) prot;
BitSet incoming = iprot.readBitSet(3);
if (incoming.get(0)) {
struct.age = iprot.readI32();
struct.setAgeIsSet(true);
}
if (incoming.get(1)) {
struct.name = iprot.readString();
struct.setNameIsSet(true);
}
if (incoming.get(2)) {
struct.address = iprot.readString();
struct.setAddressIsSet(true);
}
}
} }

生成了一个500多行的代码,那么我们现在对这个请求对象进行序列化,看看序列化结果。

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TIOStreamTransport; import java.io.ByteArrayOutputStream;
import java.util.Arrays; public class KoalaThriftSerialization { public static void main(String[] args) throws TException { koalasRequest request = new koalasRequest ( );
request.setName ( "小明" );
request.setAge ( 20 );
request.setAddress ( "北京" ); ByteArrayOutputStream out = new ByteArrayOutputStream();
TIOStreamTransport tioStreamTransport = new TIOStreamTransport (out);
TProtocol protocol = new TBinaryProtocol (tioStreamTransport);
request.write ( protocol );
System.out.println (Arrays.toString ( out.toByteArray () ) ); } }

看一看对于这么个简单的request对象,thrift到底序列化成什么了

实际生产中的传输方式一般采用TFramedTransport,序列化采用二进制序列化协议TBinaryProtocol,当然也有json和压缩协议等等,二进制确实最高效的。

我们来看看thrift的序列化方法。

  public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
}
 public void write(org.apache.thrift.protocol.TProtocol oprot, koalasRequest struct) throws org.apache.thrift.TException {
struct.validate(); oprot.writeStructBegin(STRUCT_DESC);
oprot.writeFieldBegin(AGE_FIELD_DESC);
oprot.writeI32(struct.age);
oprot.writeFieldEnd();
if (struct.name != null) {
oprot.writeFieldBegin(NAME_FIELD_DESC);
oprot.writeString(struct.name);
oprot.writeFieldEnd();
}
if (struct.address != null) {
oprot.writeFieldBegin(ADDRESS_FIELD_DESC);
oprot.writeString(struct.address);
oprot.writeFieldEnd();
}
oprot.writeFieldStop();
oprot.writeStructEnd();
}

1:validate 验证一下request中是否合法,主要是验证在thrift文件里属性为required的,但是实际传输中为空的字段。required字段为空则会报错。

2:writeStructBegin 开始序列化request,TBinaryProtocol什么也不做,当然用户可以自己自定义Protocol。

3:  writeFieldBegin 写入age字段开始,下面来看看writeFieldBegin到底写了什么

  private static final org.apache.thrift.protocol.TField AGE_FIELD_DESC = new org.apache.thrift.protocol.TField("age", org.apache.thrift.protocol.TType.I32, (short)1);
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
// package org.apache.thrift.protocol; 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; public TType() {
}
}
public void writeFieldBegin(TField field) throws TException {
this.writeByte(field.type);
this.writeI16(field.id);
}
public void writeByte(byte b) throws TException {
this.bout[0] = b;
this.trans_.write(this.bout, 0, 1);
}
public void writeI16(short i16) throws TException {
this.i16out[0] = (byte)(255 & i16 >> 8);
this.i16out[1] = (byte)(255 & i16);
this.trans_.write(this.i16out, 0, 2);
}

(1)首先先写入age的类型和序号,age为int类型,int类型在thrift里面设置是byte类型的【8】,在写入age的位置,因为thrift的定义的age的位置序号是1,thrift会写入一下short类型的1,也就说thrift的请求体的成员最大数量不能超过short的最大值32767,一般来说够了,哪有变态的请求体需要几万个成员变量啊,不得把程序员搞死!写入short占用两个字节那么继续写入【0 1】

4: writeI32

public void writeI32(int i32) throws TException {
this.i32out[0] = (byte)(255 & i32 >> 24);
this.i32out[1] = (byte)(255 & i32 >> 16);
this.i32out[2] = (byte)(255 & i32 >> 8);
this.i32out[3] = (byte)(255 & i32);
this.trans_.write(this.i32out, 0, 4);
}

直接将age的值写入,也就是 0 0 0 20 占4个字节

5:writeFieldEnd 写入age字段结束,什么也不做。

以上写入一个int类型的字段就写完了,接下来写入String类型的name和String类型的address同理,需要注意的是

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的时候需要先write字符串的长度,int类型占4个字节,因为string类型占用的字节不固定,反序列化时需要知道字符串类型到底是几个字节。

重复这个过程一直到写完所有的字段,这个过程都是由thrift给java语言自动生成的,很方便吧。

所以对于thrift来说,他的序列化就是对各个字段进行写入 1 字段类型 (1 byte)、2字段位置(2 byte)、3字段内容 ,TBinaryProtocol中对java所有的类型的写入都有封装,小伙伴们可以自行下载代码查看!

高级java交流群:825199617

欢迎热爱源码志同道合的朋友加入。

koalas rpc源码地址https://gitee.com/a1234567891/koalas-rpc

JAVA RPC (四) 之thrift序列化普通对象的更多相关文章

  1. JAVA RPC (五) 之thrift序列化RPC消息体

    让大家久等了.继续更新thrift序列化的消息体,下面我们一步一步的看一看thrift的rpc是怎么实例化消息体的. 首先我们先准备一个request文件 namespace java bky str ...

  2. JAVA RPC (三) 之thrift序列化协议入门杂谈

    首先抱歉让大家久等了,最近工作的原因,再加上自己维护koalas rpc利用的大部分时间,一直没腾出空来写这篇文章. 先放出来自研的企业级RPC框架源代码地址,上面有使用方式和部署环境说明,说环境部署 ...

  3. Thrift RPC实战(三) thrift序列化揭秘

    本文主要讲解Thrift的序列化机制, 看看thrift作为数据交换格式是如何工作的? 1.构造应用场景: 1). 首先我们先来定义下thrift的简单结构. 1 2 3 4 5 namespace ...

  4. JAVA RPC(二)序列化协议杂谈

    序列化和反序列化作为Java里一个较为基础的知识点,大家心里也有那么几句要说的,但我相信很多小伙伴掌握的也就是那么几句而已,如果再深究问一下Java如何实现序列化和反序列化的,就可能不知所措了!遥记当 ...

  5. java提高篇(六)-----使用序列化实现对象的拷贝

    我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性能的提升非常 ...

  6. JAVA RPC (六) 之thrift反序列化RPC消息体

    我们来看一下服务端的简单实现,直接上thrift代码,很直观的来看一看thrift的server到底干了些什么 public boolean process(TProtocol in, TProtoc ...

  7. 菜鸟学Java(四)——JSP内置对象

    学习JavaWeb就离不开JSP,而学习JSP又不得不了解它常用的九个内置对象.今天来做一个简单介绍. request Request封装了用户提交的信息,通过调用Request相应的方法可以获取封装 ...

  8. 一起学 Java(四) File、Try 、序列化、MySQL、Socket

    一.Java 流(Stream).文件(File)和IO Java.io 包几乎包含了所有操作输入.输出需要的类.所有这些流类代表了输入源和输出目标. Java.io 包中的流支持很多种格式,比如:基 ...

  9. java提高篇(五)-----使用序列化实现对象的拷贝

          我们知道在Java中存在这个接口Cloneable,实现该接口的类都会具备被拷贝的能力,同时拷贝是在内存中进行,在性能方面比我们直接通过new生成对象来的快,特别是在大对象的生成上,使得性 ...

随机推荐

  1. 解决只读时ios下input光标问题

    应用场景:在ios手机下对只读的input设置readonly=readonly属性还是会出现光标 解决方法: //解决ios日期光标问题 $("#Stime ,#provinceCity& ...

  2. Diango 框架起步

    一.命令行搭建Django项目 安装django # 在指定解释器环境下安装django 1.11.9# 在真实python3环境下: pip3 install django==1.11.9# 在虚拟 ...

  3. Python-Django 路由控制器

    1 路由的基本使用: # url是个函数,有四个参数,第一个参数要传正则表达式,第二参数传函数内存地址,第三个参数传默认参数,第四个是路由的别名 url(r'^liuqingzheng/article ...

  4. VMware Workstation 15 Pro 永久激活密钥 下载

    注:本文来源于:Felix__H 的<VMware Workstation 15 Pro 永久激活密钥 下载 > 一. 激活密钥 YG5H2-ANZ0H-M8ERY-TXZZZ-YKRV8 ...

  5. 使用Java API操作HDFS文件系统

    使用Junit封装HFDS import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org ...

  6. 用什么方法给PDF添加页眉页脚

    我们所看到的书本中都会设置好有页眉页脚,那么电子书想要添加页眉页脚要怎么操作呢,用什么方法可以在PDF中添加页眉页脚呢,今天就为大家分享一下,如何在电子文件中添加页眉页脚,想知道的小伙伴们就一起来看看 ...

  7. javascript数组去重 String字符串去掉两端空格 javascript Array二分法排序 比较 javascript 求和

    通过原形添加方法: ==================数组去重(对象去重法)======================= Array.prototype.unique=function(){ va ...

  8. mysql触发器Before和After的区别

    Before与After区别:before:(insert.update)可以对new进行修改.                    after不能对new进行修改.                 ...

  9. phonegap走起

    最近phonegap已发布4.0的了..速度提升了不少,很给力.小白们可以看下如何构建phonegap开发平台. 此文将说明如何建立一个可以被vs2015打开的phonegap的项目.我还会加上ion ...

  10. yii2的csrf验证原理分析及token缓存解决方案

    本文主要分三个部分,首先简单介绍csrf,接着对照源码重点分析一下yii框架的验证原理,最后针对页面缓存导致的token被缓存提出一种可行的方案.涉及的知识点会作为附录附于文末. 1.CSRF描述 C ...