当前位置: 首页 > 编程日记 > 正文

Thrift源码解析--TBinaryProtocol

本文为原创:http://www.cnblogs.com/leehfly/p/4958206.html,未经许可禁止转载。

关于Tprotocol层都是一些通信协议,个人感觉内容较大,很难分类描述清楚。故打算以TBinaryProtocol为例,分析客户端发请求以及接收服务端返回数据的整个过程。

先将客户端的测试用例贴上。

复制代码
 1 public class DemoClient {2     public static void main(String[] args) throws Exception{3         String param1 = "haha";4         Map<String, String> param3 = new HashMap<String, String>();5         param3.put("1", "2");6         Parameter param2 = new Parameter(10, "kaka");7         8         TSocket socket = new TSocket("127.0.0.1", 7911);9         socket.setTimeout(3000);
10         TTransport transport = socket;
11         transport.open();
12         TProtocol protocol = new TBinaryProtocol(transport);
13         DemoService.Client client = new DemoService.Client.Factory().getClient(protocol);
14         int result = client.demoMethod(param1, param2, param3);
15         System.out.println("result: " + result);
16         transport.close();
17     }
复制代码

首先就是构造transport,这里由于TSocket extens TIOStreamTransport,因此可构造一个TSocket即可,而TSocket包含:host(主机IP),port(端口号),time_out(超时时间)与一个Socket。

复制代码
1  public TSocket(String host, int port, int timeout) {
2     host_ = host;
3     port_ = port;
4     timeout_ = timeout;
5     initSocket();
6   }
复制代码

对于socket.setTimeout(3000);实际操作就是为TSocket中的socket设置timeout

复制代码
1  public void setTimeout(int timeout) {
2     timeout_ = timeout;
3     try {
4       socket_.setSoTimeout(timeout);
5     } catch (SocketException sx) {
6       LOGGER.warn("Could not set socket timeout.", sx);
7     }
8   }
复制代码

下图是构造的transport直观构造:包含了host,inputStream,outputStream,port,socket,timeout.

transport.open所做的事情就是初始化一些输入输出流并且connect the socket to the InetSocketAddress

复制代码
 1 /**2    * Connects the socket, creating a new socket object if necessary.3    */4   public void open() throws TTransportException {5     if (isOpen()) {6       throw new TTransportException(TTransportException.ALREADY_OPEN, "Socket already connected.");7     }8 9     if (host_.length() == 0) {
10       throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open null host.");
11     }
12     if (port_ <= 0) {
13       throw new TTransportException(TTransportException.NOT_OPEN, "Cannot open without port.");
14     }
15 
16     if (socket_ == null) {
17       initSocket();
18     }
19 
20     try {
21       socket_.connect(new InetSocketAddress(host_, port_), timeout_);
22       inputStream_ = new BufferedInputStream(socket_.getInputStream(), 1024);//均采用缓冲模式输入输出流
23       outputStream_ = new BufferedOutputStream(socket_.getOutputStream(), 1024);
24     } catch (IOException iox) {
25       close();
26       throw new TTransportException(TTransportException.NOT_OPEN, iox);
27     }
28   }
复制代码

再看一下open之后的transport:

接下来就是在已有transport也就是TSocket的基础之上,完成Tprotocol的构建,这里选择了TBinaryProtocol。这个工作实际上就是将上一步建好的Ttransport关联到Tprotocol上来。相当于进一步封装。

复制代码
 1 public abstract class TProtocol {2 3   /**4    * Prevent direct instantiation5    */6   @SuppressWarnings("unused")7   private TProtocol() {}8 9   /**
10    * Transport
11    */
12   protected TTransport trans_;
13 
14   /**
15    * Constructor
16    */
17   protected TProtocol(TTransport trans) {
18     trans_ = trans;
19   }
20 
21   /**
22    * Transport accessor
23    */
24   public TTransport getTransport() {
25     return trans_;
26   }
27 /**各种读写方法略去
28 */
29 }
复制代码

从TProtocol的构造方法中可以看出,实际上就是将上一步生成的Transport赋与TProtocol中的trans_变量并将strictRead_与strictWrite_赋值。

复制代码
 1  /**2    * Constructor3    */4   public TBinaryProtocol(TTransport trans) {5     this(trans, false, true);6   }7 8   public TBinaryProtocol(TTransport trans, boolean strictRead, boolean strictWrite) {9     super(trans);
10     strictRead_ = strictRead;
11     strictWrite_ = strictWrite;
12   }
复制代码

其中还有一些字节数组的初始化工作。

复制代码
 1 private byte [] bout = new byte[1];2 3 4  private byte[] i16out = new byte[2];5   6 7  private byte[] i32out = new byte[4];8   9 
10  private byte[] i64out = new byte[8];
11   
复制代码

这时候一切准备就绪。Tprotocol目前状态如下图:

Tprotocol已经准备就绪,接下来的工作就是new 一个client,然后才可以去与服务端进行请求与响应。下面我把一个client的代码全部粘贴出来。

复制代码
 1 public static class Client extends org.apache.thrift.TServiceClient implements Iface {2     public static class Factory implements org.apache.thrift.TServiceClientFactory<Client> {3       public Factory() {}4       public Client getClient(org.apache.thrift.protocol.TProtocol prot) {//通过Tprotocol去构造client5         return new Client(prot);6       }7       public Client getClient(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {8         return new Client(iprot, oprot);9       }
10     }
11 
12     public Client(org.apache.thrift.protocol.TProtocol prot)
13     {
14       super(prot, prot);//使用了相同的Tprotocol进行构造
15     }
16 
17     public Client(org.apache.thrift.protocol.TProtocol iprot, org.apache.thrift.protocol.TProtocol oprot) {
18       super(iprot, oprot);
19     }
20 
21     public int demoMethod(String param1, Parameter param2, Map<String,String> param3) throws org.apache.thrift.TException
22     {
23       send_demoMethod(param1, param2, param3);
24       return recv_demoMethod();
25     }
26 
27     public void send_demoMethod(String param1, Parameter param2, Map<String,String> param3) throws org.apache.thrift.TException
28     {
29       demoMethod_args args = new demoMethod_args();
30       args.setParam1(param1);
31       args.setParam2(param2);
32       args.setParam3(param3);
33       sendBase("demoMethod", args);
34     }
35 
36     public int recv_demoMethod() throws org.apache.thrift.TException
37     {
38       demoMethod_result result = new demoMethod_result();
39       receiveBase(result, "demoMethod");
40       if (result.isSetSuccess()) {
41         return result.success;
42       }
43       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "demoMethod failed: unknown result");
44     }
45 
46   }
复制代码

为了理解客户端构造的具体过程,我把TserviceClient.class的部分源码贴出来:

复制代码
 1  public TServiceClient(TProtocol iprot, TProtocol oprot) {2     iprot_ = iprot;3     oprot_ = oprot;4   }5 6   protected TProtocol iprot_;7   protected TProtocol oprot_;8 9   protected int seqid_;
10 
11   /**
12    * Get the TProtocol being used as the input (read) protocol.
13    * @return the TProtocol being used as the input (read) protocol.
14    */
15   public TProtocol getInputProtocol() {
16     return this.iprot_;
17   }
18 
19   /**
20    * Get the TProtocol being used as the output (write) protocol.
21    * @return the TProtocol being used as the output (write) protocol.
22    */
23   public TProtocol getOutputProtocol() {
24     return this.oprot_;
25   }
复制代码

明显的可以看到,client有三个变量,TProtocol类型的iprot_和oprot_,还有一个顺序号seqid_.由于在构造client的过程中使用了相同的Tprotocol,在这里也就是使用了相同的TBinaryProtocol,因此iprot_与oprot_是相同的,都指向上一步生成的TProtocol,也就是TBinaryProtocol.当DemoService.Client client = new DemoService.Client.Factory().getClient(protocol);执行完毕后,client的状态如下图:

client已经准备完毕,我们调用client的方法就可以向服务端发送请求了。而这个过程的总体代码也就那么一点点,先直接贴出来:

复制代码
 1   public int demoMethod(String param1, Parameter param2, Map<String,String> param3) throws org.apache.thrift.TException2     {3       send_demoMethod(param1, param2, param3);//发送请求4       return recv_demoMethod();//接收响应5     }6 7     public void send_demoMethod(String param1, Parameter param2, Map<String,String> param3) throws org.apache.thrift.TException8     {9       demoMethod_args args = new demoMethod_args();//封装请求参数demoMethod_args
10       args.setParam1(param1);
11       args.setParam2(param2);
12       args.setParam3(param3);
13       sendBase("demoMethod", args);//发请求
14     }
15 
16     public int recv_demoMethod() throws org.apache.thrift.TException
17     {
18       demoMethod_result result = new demoMethod_result();//封装接收响应数据demoMethod_result,貌似与demoMethod_args还不一样
19       receiveBase(result, "demoMethod");//接收返回数据
20       if (result.isSetSuccess()) {
21         return result.success;
22       }
23       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "demoMethod failed: unknown result");
24     }
复制代码

当执行完demoMethod_args args = new demoMethod_args();之后,其实就是对demoMethod_args中的静态变量进行了初始化,STRUCT_DESC,PARAM1_FIELD_DESC,PARAM2_FIELD_DESC,schemes,PARAM3_FIELD_DESC,metaDataMap等都有了初始值。args.setParam之后,demoMethod_args的状态:

接下来就是:

复制代码
1 protected void sendBase(String methodName, TBase args) throws TException {
2     oprot_.writeMessageBegin(new TMessage(methodName, TMessageType.CALL, ++seqid_));//注意这里的++seqid,就是发送请求的序号,递增
3     args.write(oprot_);
4     oprot_.writeMessageEnd();
5     oprot_.getTransport().flush();//这里最终其实就是outputStream进行flush
6   }
复制代码

将methodName: demoMethod, args: demoMethod_args(param1:haha, param2:Parameter(id:10, name:kaka), param3:{1=2})写入Tprotocol,在这里是oprot_。

复制代码
 1  public void writeMessageBegin(TMessage message) throws TException {2     if (strictWrite_) {3       int version = VERSION_1 | message.type;//异或形成版本号4       writeI32(version);//写入版本号5       writeString(message.name);//写方法名6       writeI32(message.seqid);//方法序号7     } else {8       writeString(message.name);9       writeByte(message.type);
10       writeI32(message.seqid);
11     }
12   }
复制代码
复制代码
1  public void writeString(String str) throws TException {
2     try {
3       byte[] dat = str.getBytes("UTF-8");
4       writeI32(dat.length);
5       trans_.write(dat, 0, dat.length);
6     } catch (UnsupportedEncodingException uex) {
7       throw new TException("JVM DOES NOT SUPPORT UTF-8");
8     }
9   }
复制代码
复制代码
1  public void writeI32(int i32) throws TException {
2     i32out[0] = (byte)(0xff & (i32 >> 24));
3     i32out[1] = (byte)(0xff & (i32 >> 16));
4     i32out[2] = (byte)(0xff & (i32 >> 8));
5     i32out[3] = (byte)(0xff & (i32));
6     trans_.write(i32out, 0, 4);
7   }
复制代码
复制代码
 1  /**2    * Writes to the underlying output stream if not null.3    */4   public void write(byte[] buf, int off, int len) throws TTransportException {5     if (outputStream_ == null) {6       throw new TTransportException(TTransportException.NOT_OPEN, "Cannot write to null outputStream");7     }8     try {9       outputStream_.write(buf, off, len);
10     } catch (IOException iox) {
11       throw new TTransportException(TTransportException.UNKNOWN, iox);
12     }
13   }
复制代码

从以上代码可以看出来,无论怎么写,都是一层层深入的,TProtocol oprot_ ----->Ttransport trans_ ----->OutputStream outputStream(TODO:这里的outputStream其实也是bufferedOutputStream,也就是刚刚初始化transport的时候那个outputstream.其中比较奇葩的是args_.write,其代码如下,最后还是绕到了oprot.write,只不过这里有Struct,Field.目测这里用  schemes.get(oprot.getScheme()).getScheme().write(oprot, this);就是因为args的一些参数在静态初始化的时候已经放入了schemes

1   public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException {
2       schemes.get(oprot.getScheme()).getScheme().write(oprot, this);
3     }
复制代码
 1   public void write(org.apache.thrift.protocol.TProtocol oprot, demoMethod_args struct) throws org.apache.thrift.TException {2         struct.validate();3 4         oprot.writeStructBegin(STRUCT_DESC);5         if (struct.param1 != null) {6           oprot.writeFieldBegin(PARAM1_FIELD_DESC);7           oprot.writeString(struct.param1);8           oprot.writeFieldEnd();9         }
10         if (struct.param2 != null) {
11           oprot.writeFieldBegin(PARAM2_FIELD_DESC);
12           struct.param2.write(oprot);
13           oprot.writeFieldEnd();
14         }
15         if (struct.param3 != null) {
16           oprot.writeFieldBegin(PARAM3_FIELD_DESC);
17           {
18             oprot.writeMapBegin(new org.apache.thrift.protocol.TMap(org.apache.thrift.protocol.TType.STRING, org.apache.thrift.protocol.TType.STRING, struct.param3.size()));
19             for (Map.Entry<String, String> _iter4 : struct.param3.entrySet())
20             {
21               oprot.writeString(_iter4.getKey());
22               oprot.writeString(_iter4.getValue());
23             }
24             oprot.writeMapEnd();
25           }
26           oprot.writeFieldEnd();
27         }
28         oprot.writeFieldStop();
29         oprot.writeStructEnd();
30       }
31 
32     }
复制代码

到此为止,send_domoMethod完毕,接下来就是recv_demoMethod()也就是接受服务端返回的数据。

复制代码
1  public int recv_demoMethod() throws org.apache.thrift.TException
2     {
3       demoMethod_result result = new demoMethod_result();//与封装请求参数类似,加入一些内容到schema中
4       receiveBase(result, "demoMethod");//读取数据进行一些组装工作
5       if (result.isSetSuccess()) {
6         return result.success;//返回result中的success值
7       }
8       throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "demoMethod failed: unknown result");
9     }
复制代码
复制代码
 1  protected void receiveBase(TBase result, String methodName) throws TException {//读取返回结果,并将返回结果组装好放到result中2     TMessage msg = iprot_.readMessageBegin();3     if (msg.type == TMessageType.EXCEPTION) {4       TApplicationException x = TApplicationException.read(iprot_);5       iprot_.readMessageEnd();6       throw x;7     }8     if (msg.seqid != seqid_) {9       throw new TApplicationException(TApplicationException.BAD_SEQUENCE_ID, methodName + " failed: out of sequence response");
10     }
11     result.read(iprot_);//将所读取的数据封装成需要类型返回
12     iprot_.readMessageEnd();//这一步其实什么也没做,到此为止result其实已经形成
13   }
复制代码

由于写入的时候有写入信息的类型,序号之类的东西,故这里读取和写入保持一致,也要readMessageBegin,只不过这里使用的是iprot_,其实还是Tprotocol。Tprotocol iprot_ ----->Ttransport trans_ ----->InputStream inputstream

复制代码
 1 public TMessage readMessageBegin() throws TException {2     int size = readI32();3     if (size < 0) {4       int version = size & VERSION_MASK;5       if (version != VERSION_1) {6         throw new TProtocolException(TProtocolException.BAD_VERSION, "Bad version in readMessageBegin");7       }8       return new TMessage(readString(), (byte)(size & 0x000000ff), readI32());9     } else {
10       if (strictRead_) {
11         throw new TProtocolException(TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?");
12       }
13       return new TMessage(readStringBody(size), readByte(), readI32());
14     }
15   }
复制代码

其中result.read(iprot_)还是对应着写入时候的args.write,代码贴出来:

复制代码
 1 private static class demoMethod_resultStandardScheme extends StandardScheme<demoMethod_result> {2 3       public void read(org.apache.thrift.protocol.TProtocol iprot, demoMethod_result struct) throws org.apache.thrift.TException {4         org.apache.thrift.protocol.TField schemeField;5         iprot.readStructBegin();6         while (true)7         {8           schemeField = iprot.readFieldBegin();9           if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { 
10             break;
11           }
12           switch (schemeField.id) {
13             case 0: // SUCCESS
14               if (schemeField.type == org.apache.thrift.protocol.TType.I32) {
15                 struct.success = iprot.readI32();//在这里读取返回结果,这些结果的结构都是早已经定义好的,因为我们这里的例子是int类型,故这里只需要读取readI32即可
16                 struct.setSuccessIsSet(true);
17               } else { 
18                 org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
19               }
20               break;
21             default:
22               org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type);
23           }
24           iprot.readFieldEnd();
25         }
26         iprot.readStructEnd();
27 
28         // check for required fields of primitive type, which can't be checked in the validate method
29         struct.validate();
30       }
31 
32       public void write(org.apache.thrift.protocol.TProtocol oprot, demoMethod_result struct) throws org.apache.thrift.TException {
33         struct.validate();
34 
35         oprot.writeStructBegin(STRUCT_DESC);
36         oprot.writeFieldBegin(SUCCESS_FIELD_DESC);
37         oprot.writeI32(struct.success);
38         oprot.writeFieldEnd();
39         oprot.writeFieldStop();
40         oprot.writeStructEnd();
41       }
42 
43     }
复制代码

综上,整个客户端发请求以及接受返回数据也就是先写后读的一个完整过程也就完毕。整体流程图我就用从网上找到的一个例子来看就好了,除了方法不一样,其他都是一样的道理。

本文为博主原创,未经许可禁止转载。谢谢。

做人第一,做学问第二。

转载于:https://www.cnblogs.com/xumaojun/p/8526522.html

相关文章:

一篇价值百万的文章:我为什么在22岁辞去年薪150万的工作?

本文转载自http://www.cricode.com/4208.html 作者&#xff1a;奶牛Denny 今天是22岁的最后一天。几个月前&#xff0c;我从沃顿商学院毕业&#xff0c;用文凭上“最高荣誉毕业”的标签安抚了已经年过半百的老妈&#xff0c;然后转头辞去了毕业后的第一份工作&#xff0c;跟一…

电脑温度检测软件哪个好_一般电脑录音软件哪个好?

随着科技的快速发展&#xff0c;为了满足各行各业的音频录制需求&#xff0c;电脑版录音软件也在陆续不断的出现在各大应用市场&#xff0c;与传统的自带音频录制工具相比&#xff0c;电脑版录音软件更加专注的是电脑上的音频录制&#xff0c;它能够将发生在电脑上的一切声音源…

区块链的隐私性

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 我最近看区块链简单介绍的时候会发现有的人就说其有良好的隐私性&#xff0c;但也有人说保护隐私性上有缺陷&#xff0c;今天就简单的对区块链的隐私…

ssh免密连接远程服务器

ssh免密连接远程服务器 借助ssky-keygen和ssh-copy-id工具&#xff0c;通过4个简单的步骤实现无需输入密码登录远程Linux主机 1 生成密钥 通过内置的工具生成RSA算法加密的密钥 ssh-keygen -t rsa 回车默认选项即可&#xff0c;会在当前用户的home文件下生成密钥文件 2 拷贝密钥…

什么是互联网大厂_仅限今天!大厂最热数据分析经典实战项目大公开!

原标题&#xff1a;仅限今天&#xff01;大厂最热数据分析经典实战项目大公开&#xff01;如今&#xff0c;全球早已步入数据时代&#xff0c;随着行业的高速发展&#xff0c;相关岗位缺口已超150万&#xff0c;且薪资超同行业50%。未来十年&#xff0c;数据细分岗位将扩张5倍&…

php教学视频

http://www.52pojie.cn/forum.php?modviewthread&tid283589转载于:https://www.cnblogs.com/dawanxiaobao/p/5703157.html

Go基础编程:作用域

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 Go基础编程&#xff1a;作用域11 小弟初学go语言&#xff0c;感觉作用域的运用还是很广泛的&#xff0c;所以就写上一点&#xff0c;也许有的都是很…

数据分析之CE找数据大法

一&#xff0e;基本介绍 CE的全称为Cheat Engine&#xff0c;是一款内存修改编辑工具&#xff0c;其官网是http://www.cheatengine.org&#xff0c;可以在其官网下载到最新的CE工具&#xff0c;目前最新版本是Cheat Engine6.5。CE同时也是一款开源的工具&#xff0c;使用者可以…

a++浏览器_“公司发给我的职业装太小了,我该怎么和领导说?”哈哈哈哈哈|职业装|uc|浏览器...

GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片GIF打开UC浏览器 查看更多精彩图片打开UC浏览器 查看更多精彩图片GIF…

Go基础编程:延迟调用defer

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 本篇文章所讲的就是go编程中的延迟调用defer&#xff0c;希望对社区的成员有较多的帮助。 1 defer作用 关键字defer ⽤于延迟一个函数、方法或者当…

maven工程插件配置

1 <build>2 <!-- 该级工程会加载插件&#xff0c;放在父工程里 -->3 <plugins>4 <!-- 资源文件拷贝插件 -->5 <plugin>6 <groupId>org.apache.maven.plugins</groupId>7 …

卡巴斯基安全浏览器_卡巴斯基 for windows 全方位安全软件2021注册表清除版

软件介绍最近从来不相信木马的Run同学中招了&#xff0c;wup.exe(俗称矿机木马)&#xff0c;此款木马真可谓厉害&#xff0c;无敌&#xff0c;用过所有方法都无法清理&#xff0c;百度了所有的道道几乎无用&#xff0c;中了此毒&#xff0c;电脑不会有什么特别异常&#xff0c;…

Android通过ksoap2调用.net(c#)的webservice

转载于:https://www.cnblogs.com/Nagisa-Saku/p/5706183.html

●洛谷P3688 [ZJOI2017]树状数组

题链&#xff1a; https://www.luogu.org/problemnew/show/P3688题解&#xff1a; 二维线段树。 先不看询问时l1的特殊情况。 对于一个询问(l,r)&#xff0c;如果要让错误的程序得到正确答案&#xff0c; 显然应该满足l-1位置的值r位置的值&#xff08;或者说两个位置的异或值…

Activity-生命周期

Activity不是什么陌生的东西&#xff0c;作为Android程序媛对Activity再熟悉不过。每当说起Activity总最关注的还是它的生命周期。 1、一张来自谷歌官方文档的Activity的生命周期图&#xff1a; 直接来个MainActivity覆盖上面所有的方法通过log打印方式给大家展现&#xff0c;通…

arial unicode ms字体_5个检测商用字体和免费字体合集的网站

对于做新媒体和设计的小伙伴来说&#xff0c;最恐慌的就是加班、改稿、脱发、没钱...侵权问题了。一个不注意就是律师函警告。正所谓律师函不是不到&#xff0c;只是晚到。所以&#xff0c;皮皮特意为小伙伴们搜集了这5个远离字体侵权的网站&#xff0c;有检测字体版权的&#…

DAPP是什么

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 1 DAPP是什么1 当满足下所有条件的时候&#xff0c;一个应用才可以称为DAPP [if !supportLists]1. [endif]必须是开源、自治并没有一个实体控制着…

JVM学习--(一)基本原理

前言 JVM一直是java知识里面进阶阶段的重要部分&#xff0c;如果希望在java领域研究的更深入&#xff0c;则JVM则是如论如何也避开不了的话题&#xff0c;本系列试图通过简洁易读的方式&#xff0c;讲解JVM必要的知识点。 运行流程 我们都知道java一直宣传的口号是&#xff1a;…

phpexcel导出超过26列解决方案

phpexcel导出超过26列解决方案 原文:phpexcel导出超过26列解决方案 将列的数字序号转成字母使用,代码如下: PHPExcel_Cell::stringFromColumnIndex($i); // 从o,1,2,3,..开始,相应返回返回 A,B,C,...Z,AA,AB,...将列的字母转成数字序号使用,代码如下: PHPExcel_Cell::columnIn…

h5大转盘抽奖源码后台_微信H5互动营销应该要如何做?

现在微信营销的队伍有越来越多的人群&#xff0c;许多的企业品牌都会选择用微信营销&#xff0c;而微信营销这么受欢迎是因为微信拉近了用户与企业品牌的关系。其中H5互动营销是最受欢迎的一种方式&#xff0c;那么微信H5互动营销要如何做呢&#xff1f;企业想要在微信H5营销中…

Solidity语言

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 Solidity语言11 Solidity是以太坊智能合约的编程语言&#xff0c;我自己也是学习了很久&#xff0c;感觉是有些难度&#xff0c;所以需要去认真的去…

同一个类 cannot be cast to_留学热门assignment之 税收筹划类essay

税法和税务筹划一直以来都是热门的行业&#xff0c;由于近些年对于税务人才的需求越来越大&#xff0c;税法专业成为了当下最火爆的留学专业之一。发达国家由于税收和法律体系相对完善&#xff0c;法律的条文相较于其他国家而言也更加的细致和有操作性&#xff0c;因此&#xf…

LeetCode 7. Reverse Integer

问题链接 LeetCode 7 题目解析 给定一个32位有符号整数&#xff0c;求其反转数字。 解题思路 如果是简单反转的话&#xff0c;那这道题就太简单了。题目要求判断溢出问题&#xff0c;32位int类型的范围是-2147483648&#xff5e;2147483647。数字反转过后是有可能超出范围的&am…

Ultra-QuickSort POJ 2299(归并排序)

http://acm.hust.edu.cn/vjudge/contest/124435#problem/D 题意&#xff1a;给出一个长度为n的数列&#xff0c;你每一次可以随意交换其中两个数字的位置。问你至少交换几次&#xff0c;才能使得这个数列是个单调递增数列。 比赛时没做出来&#xff0c;&#xff08;自然&#x…

Geth 控制台使用及 Web3.js 使用实战

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 突然发现没有太多写实战的&#xff0c;所以就写一点自己的拙见&#xff0c;提供给成员一些参考。Geth 控制台&#xff08;REPL&#xff09;实现了所…

Java多线程的同步机制(synchronized)

一段synchronized的代码被一个线程执行之前&#xff0c;他要先拿到执行这段代码的权限&#xff0c;在 java里边就是拿到某个同步对象的锁&#xff08;一个对象只有一把锁&#xff09;&#xff1b; 如果这个时候同步对象的锁被其他线程拿走了&#xff0c;他&#xff08;这个线程…

mouseenter 延迟_桃园台服加速器 电狐加速器带你低延迟玩游戏

桃园是由冰动娱乐自主研发的全球首款运用世界顶级开发引擎Unreal Engine 3的次世代回合制网络游戏。Unreal 3引擎在骨骼动画树、特效渲染、游戏性完善等方面表现杰出&#xff0c;而且游戏中还可以呈现广角纵身大场景&#xff0c;1080P的高清画质将会带给玩家前所未有的视觉震撼…

私有链的特点简单介绍

链客&#xff0c;专为开发者而生&#xff0c;有问必答&#xff01; 此文章来自区块链技术社区&#xff0c;未经允许拒绝转载。 私有链是区块链的一种&#xff0c;它指的是某个区块链的写入权限仅掌握在某个人或某个组织手中&#xff0c;数据的访问以及编写等有着十分严格的权限…

typescript调用javascript URI.js

URI.js URI.js是一个用于处理URL的JavaScript库它提供了一个“jQuery风格”的API&#xff08;Fluent接口&#xff0c;方法链接&#xff09;来读写所有常规组件和许多便利方法&#xff0c;如.directory&#xff08;&#xff09;和.authority&#xff08;&#xff09;本文以URI.j…

richeditctrl 选中ole图片 拖拽 空白_高质量的图片素材,碾压度娘几条街......

答应我不要错过​哈喽大家周末好啊&#xff0c;总有小伙伴来问公子说每周的素材分享我到底都是从哪里找的呢&#xff0c;其实公子之前也有告诉过大家&#xff0c;可能是隔的时间太久了。所以今天呢我又给你们整理了一些经常会用到的几个图片网站&#xff0c;都是非常知名而且基…