-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
1209 lines (1002 loc) · 410 KB
/
atom.xml
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
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>鱼儿塘</title>
<link href="/atom.xml" rel="self"/>
<link href="https://tunsuy.github.io/"/>
<updated>2017-04-19T07:20:07.497Z</updated>
<id>https://tunsuy.github.io/</id>
<author>
<name>tunsuy</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>深入详解Java中的Gson反序列化</title>
<link href="https://tunsuy.github.io/2017/03/25/%E8%AF%A6%E8%A7%A3Java%E4%B8%AD%E7%9A%84Gson%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/"/>
<id>https://tunsuy.github.io/2017/03/25/详解Java中的Gson反序列化/</id>
<published>2017-03-25T05:13:12.000Z</published>
<updated>2017-04-19T07:20:07.497Z</updated>
<content type="html"><![CDATA[<p>译自 <a href="http://www.javacreed.com/gson-deserialiser-example/" target="_blank" rel="external">GSON DESERIALISER EXAMPLE</a></p>
<p>在这篇文章中,我们将看到如何将复杂的JSON实体反序列化为已有的Java对象。我们将看到怎么使用GSON的<code>deserialiser</code>,以控制JSON实体映射到Java对象。</p>
<h1 id="一、-前言"><a href="#一、-前言" class="headerlink" title="一、 前言"></a>一、 前言</h1><p>请注意,在这篇文章中我们将使用术语解析或反序列化。</p>
<p>下面列出的所有代码均放在:<a href="https://java-creed-examples.googlecode.com/svn/gson/Gson Deserialiser Example" target="_blank" rel="external"> https://java-creed-examples.googlecode.com/svn/gson/Gson Deserialiser Example</a>。</p>
<a id="more"></a>
<h1 id="二、-简单例子"><a href="#二、-简单例子" class="headerlink" title="二、 简单例子"></a>二、 简单例子</h1><p>比方说,我们有以下的JSON实体<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> 'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',</div><div class="line"> 'isbn-10': '032133678X',</div><div class="line"> 'isbn-13': '978-0321336781',</div><div class="line"> 'authors': ['Joshua Bloch', 'Neal Gafter']</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>上述JSON包括四个字段,其中之一是一个数组。这些字段代表我们的书。默认情况下,GSON希望在Java中的变量名跟JSON中是一样的。因此,我们应该创建具有如下字段名的类:title,isbn-10,isbn-13和authors。但在Java中,变量名不能包含减号。</p>
<p>那怎么解决上面的问题呢? </p>
<p>一种方法就是使用注解:注释提供了较少的控制,但更简单的使用和理解。也就是说,注释其局限性太大,不能满足这里描述的所有问题。</p>
<p>另一种方法就是使用 <code>JsonDeserializer</code>,使用它我们可以完全控制Json实体怎么被解析。</p>
<p>看如下的Java类<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String[] authors;</div><div class="line"> <span class="keyword">private</span> String isbn10;</div><div class="line"> <span class="keyword">private</span> String isbn13;</div><div class="line"> <span class="keyword">private</span> String title;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>此Java对象将被用来保存在前面显示的JSON实体列出的书。这两个对象(Java和JSON)的结构是在本例中相同,但是这不是必需的。</p>
<p>为了能够解析JSON到Java,我们需要创建实现 <code>JsonDeserializer</code> 接口的类。下面的例子显示了我们实现的 <code>JsonDeserializer</code> 类。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonArray;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializer;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonParseException;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookDeserializer</span> <span class="keyword">implements</span> <span class="title">JsonDeserializer</span><<span class="title">Book</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Book <span class="title">deserialize</span><span class="params">(<span class="keyword">final</span> JsonElement json, <span class="keyword">final</span> Type typeOfT, <span class="keyword">final</span> JsonDeserializationContext context)</span></span></div><div class="line"> <span class="keyword">throws</span> JsonParseException {</div><div class="line"></div><div class="line"> <span class="comment">//The deserialisation code is missing</span></div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book book = <span class="keyword">new</span> Book();</div><div class="line"> book.setTitle(title);</div><div class="line"> book.setIsbn10(isbn10);</div><div class="line"> book.setIsbn13(isbn13);</div><div class="line"> book.setAuthors(authors);</div><div class="line"> <span class="keyword">return</span> book;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>上面的代码是不完整的,我们还需要增加其他的处理,先让我们来解释下这些代码。</p>
<p>接口 <code>JsonDeserializer</code> 是一个泛型,接收一个类型,就是我们的目的解析对象的类型。在这个例子中就是Book。该接口的方法 <code>deserialize()</code> 方法也必须返回同样类型的对象。</p>
<p>GSON将JSON实体解析为 <code>JsonElement</code> 类型的Java对象。<code>JsonElement</code> 实例可以是下列之一: </p>
<ul>
<li>JsonPrimitive - 如字符串或整数</li>
<li>JsonObject - <code>JsonElement</code> 对象的集合,索引就是它们的名称(类型String)。类似于 <code>Map<String, JsonElement></code></li>
<li>JsonArray - <code>JsonElement</code> 对象的集合。该集合中的元素可以是任意的 <code>JsonElement</code> 类型对象。</li>
<li>JsonNull - 一个null值</li>
</ul>
<img src="/2017/03/25/详解Java中的Gson反序列化/Types-of-JsonElement.png" alt="Types-of-JsonElement.png" title="">
<p>上图为 <code>JsonElement</code> 的类型图</p>
<img src="/2017/03/25/详解Java中的Gson反序列化/Json-Object-Hierarchy.png" alt="Json-Object-Hierarchy.png" title="">
<p>上图为 <code>JsonObject</code> 的嵌套结构图</p>
<img src="/2017/03/25/详解Java中的Gson反序列化/Book-Json-Object-Hierarchy.png" alt="Book-Json-Object-Hierarchy.png" title="">
<p>上图为 Book例子中的 <code>JsonObject</code> 结构图</p>
<p>如果我们要反序列化此JSON实体,我们首先需要将JSON实体转换成 <code>JsonObject</code> ,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The variable 'json' is passed as a parameter to the deserialize() method</span></div><div class="line"><span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div></pre></td></tr></table></figure></p>
<p>一个JSON实体能够转换为上述中的任何一种类型。</p>
<p>可以通过JSON实体中的字段名称检索出该字段值<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The variable 'json' is passed as a parameter to the deserialize() method</span></div><div class="line"><span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line">JsonElement titleElement = jsonObject.get(<span class="string">"title"</span>)</div></pre></td></tr></table></figure></p>
<p>该方法返回的是 <code>JsonElement</code> 类型对象,可以通过如下方法将其转换为 <code>String</code> 对象<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The variable 'json' is passed as a parameter to the deserialize() method</span></div><div class="line"><span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line">JsonElement titleElement = jsonObject.get(<span class="string">"title"</span>)</div><div class="line"><span class="keyword">final</span> String title = jsonTitle.getAsString();</div></pre></td></tr></table></figure></p>
<p>下面的代码完整的演示了自定义反序列化类 <code>BookDeserializer</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonArray;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializer;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonParseException;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookDeserializer</span> <span class="keyword">implements</span> <span class="title">JsonDeserializer</span><<span class="title">Book</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Book <span class="title">deserialize</span><span class="params">(<span class="keyword">final</span> JsonElement json, <span class="keyword">final</span> Type typeOfT, <span class="keyword">final</span> JsonDeserializationContext context)</span></span></div><div class="line"> <span class="keyword">throws</span> JsonParseException {</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonElement jsonTitle = jsonObject.get(<span class="string">"title"</span>);</div><div class="line"> <span class="keyword">final</span> String title = jsonTitle.getAsString();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String isbn10 = jsonObject.get(<span class="string">"isbn-10"</span>).getAsString();</div><div class="line"> <span class="keyword">final</span> String isbn13 = jsonObject.get(<span class="string">"isbn-13"</span>).getAsString();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonArray jsonAuthorsArray = jsonObject.get(<span class="string">"authors"</span>).getAsJsonArray();</div><div class="line"> <span class="keyword">final</span> String[] authors = <span class="keyword">new</span> String[jsonAuthorsArray.size()];</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < authors.length; i++) {</div><div class="line"> <span class="keyword">final</span> JsonElement jsonAuthor = jsonAuthorsArray.get(i);</div><div class="line"> authors[i] = jsonAuthor.getAsString();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book book = <span class="keyword">new</span> Book();</div><div class="line"> book.setTitle(title);</div><div class="line"> book.setIsbn10(isbn10);</div><div class="line"> book.setIsbn13(isbn13);</div><div class="line"> book.setAuthors(authors);</div><div class="line"> <span class="keyword">return</span> book;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>为了自定义JSON实体的解析,还需要先指定(注册)目的解析对象的自定义反序列化类,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.InputStreamReader;</div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="comment">// Configure Gson</span></div><div class="line"> GsonBuilder gsonBuilder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> gsonBuilder.registerTypeAdapter(Book.class, <span class="keyword">new</span> BookDeserializer());</div><div class="line"> Gson gson = gsonBuilder.create();</div><div class="line"></div><div class="line"> <span class="comment">// The JSON data</span></div><div class="line"> <span class="keyword">try</span>(Reader reader = <span class="keyword">new</span> InputStreamReader(Main.class.getResourceAsStream(<span class="string">"/part1/sample.json"</span>), <span class="string">"UTF-8"</span>)){</div><div class="line"></div><div class="line"> <span class="comment">// Parse JSON to Java</span></div><div class="line"> Book book = gson.fromJson(reader, Book.class);</div><div class="line"> System.out.println(book);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>自定义解析JSON实体的内部原理一般为如下:<br>1、将输入的JSON实体解析为 <code>JsonElement</code> 类型的Java对象<br>2、找到目的解析对象的反序列化类,这个例子中就是 <code>BookDeserializer</code><br>3、调用 <code>deserialize()</code> 方法,并返回目的解析对象<br>4、将<code>deserialize()</code> 方法的返回结果作为 <code>fromJson()</code> 方法的返回结果</p>
<p>上述例子的结果输出为:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Java Puzzlers: Traps, Pitfalls, and Corner Cases</div><div class="line"> [ISBN-<span class="number">10</span>: <span class="number">032133678</span>X] [ISBN-<span class="number">13</span>: <span class="number">978</span>-<span class="number">0321336781</span>]</div><div class="line">Written by:</div><div class="line"> >> Joshua Bloch</div><div class="line"> >> Neal Gafter</div></pre></td></tr></table></figure></p>
<h1 id="三、-嵌套对象"><a href="#三、-嵌套对象" class="headerlink" title="三、 嵌套对象"></a>三、 嵌套对象</h1><p>现在让我们扩展上面的例子,将JSON实体嵌套如下:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> 'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',</div><div class="line"> 'isbn': '032133678X',</div><div class="line"> 'authors':[</div><div class="line"> {</div><div class="line"> 'id': 1,</div><div class="line"> 'name': 'Joshua Bloch'</div><div class="line"> },</div><div class="line"> {</div><div class="line"> 'id': 2,</div><div class="line"> 'name': 'Neal Gafter'</div><div class="line"> }</div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>该JSON实体对应的 <code>JsonObject</code> 结构图如下:</p>
<img src="/2017/03/25/详解Java中的Gson反序列化/Book-and-Authors-Json-Objects-Hierarchy.png" alt="Book-and-Authors-Json-Objects-Hierarchy.png" title="">
<p>因为Book嵌套author这样一个JSON实体,所以我们需要新增一个author类来表示该author实体,并且Book类需要关联该author类。 </p>
<p>问题就来了:我们应该怎么反序列化出author类。<br>有如下几个解决方案:<br>1、直接在 <code>BookDeserializer</code> 增加author的反序列化代码。但是这对于程序的扩展很不好,因此不推荐。<br>2、我们可以使用默认的GSON实现,因为该例中author类的实例变量和author实体字段具有相同的名称,因此可以使用默认的GSON解析。<br>3、我们可以再写一个 <code>AuthorDeserializer</code> 类,将author的反序列化独立出来。</p>
<p>第二种方案的实现</p>
<p>先说下GSON中 <code>JsonDeserializer</code> 上下文<br>在 <code>JsonDeserializer</code> 接口的 <code>deserialize()</code> 方法中,提供了参数 <code>JsonDeserializationContext</code>。我们可以将对象的反序列化委托给 <code>JsonDeserializationContext</code> 实例,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Author author = context.deserialize(jsonElement, Author.class);</div></pre></td></tr></table></figure></p>
<p>完整的代码如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonArray;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializer;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonParseException;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookDeserializer</span> <span class="keyword">implements</span> <span class="title">JsonDeserializer</span><<span class="title">Book</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Book <span class="title">deserialize</span><span class="params">(<span class="keyword">final</span> JsonElement json, <span class="keyword">final</span> Type typeOfT, <span class="keyword">final</span> JsonDeserializationContext context)</span></span></div><div class="line"> <span class="keyword">throws</span> JsonParseException {</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String title = jsonObject.get(<span class="string">"title"</span>).getAsString();</div><div class="line"> <span class="keyword">final</span> String isbn10 = jsonObject.get(<span class="string">"isbn-10"</span>).getAsString();</div><div class="line"> <span class="keyword">final</span> String isbn13 = jsonObject.get(<span class="string">"isbn-13"</span>).getAsString();</div><div class="line"></div><div class="line"> <span class="comment">// Delegate the deserialization to the context</span></div><div class="line"> Author[] authors = context.deserialize(jsonObject.get(<span class="string">"authors"</span>), Author[].class);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book book = <span class="keyword">new</span> Book();</div><div class="line"> book.setTitle(title);</div><div class="line"> book.setIsbn10(isbn10);</div><div class="line"> book.setIsbn13(isbn13);</div><div class="line"> book.setAuthors(authors);</div><div class="line"> <span class="keyword">return</span> book;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>第三种方案的实现</p>
<p>自定义author实体的反序列化类 <code>AuthorDeserializer</code> 如下所示:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializer;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonParseException;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AuthorDeserializer</span> <span class="keyword">implements</span> <span class="title">JsonDeserializer</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Author <span class="title">deserialize</span><span class="params">(<span class="keyword">final</span> JsonElement json, <span class="keyword">final</span> Type typeOfT, <span class="keyword">final</span> JsonDeserializationContext context)</span></span></div><div class="line"> <span class="keyword">throws</span> JsonParseException {</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author author = <span class="keyword">new</span> Author();</div><div class="line"> author.setId(jsonObject.get(<span class="string">"id"</span>).getAsInt());</div><div class="line"> author.setName(jsonObject.get(<span class="string">"name"</span>).getAsString());</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>为了使用 <code>ArthurDeserialiser</code>,我们也需要像之前Book一样进行注册,如下所示<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.IOException;</div><div class="line"><span class="keyword">import</span> java.io.InputStreamReader;</div><div class="line"><span class="keyword">import</span> java.io.Reader;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> <span class="comment">// Configure GSON</span></div><div class="line"> <span class="keyword">final</span> GsonBuilder gsonBuilder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> gsonBuilder.registerTypeAdapter(Book.class, <span class="keyword">new</span> BookDeserializer());</div><div class="line"> gsonBuilder.registerTypeAdapter(Author.class, <span class="keyword">new</span> AuthorDeserializer());</div><div class="line"> <span class="keyword">final</span> Gson gson = gsonBuilder.create();</div><div class="line"></div><div class="line"> <span class="comment">// Read the JSON data</span></div><div class="line"> <span class="keyword">try</span> (Reader reader = <span class="keyword">new</span> InputStreamReader(Main.class.getResourceAsStream(<span class="string">"/part2/sample.json"</span>), <span class="string">"UTF-8"</span>)) {</div><div class="line"></div><div class="line"> <span class="comment">// Parse JSON to Java</span></div><div class="line"> <span class="keyword">final</span> Book book = gson.fromJson(reader, Book.class);</div><div class="line"> System.out.println(book);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>该例子输出如下所示:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">Java Puzzlers: Traps, Pitfalls, and Corner Cases [<span class="number">032133678</span>X]</div><div class="line">Written by:</div><div class="line"> >> [<span class="number">1</span>] Joshua Bloch</div><div class="line"> >> [<span class="number">2</span>] Neal Gafter</div></pre></td></tr></table></figure></p>
<h1 id="四、-对象引用"><a href="#四、-对象引用" class="headerlink" title="四、 对象引用"></a>四、 对象引用</h1><p>请看下面的JSON。<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> 'authors': [</div><div class="line"> {</div><div class="line"> 'id': 1,</div><div class="line"> 'name': 'Joshua Bloch'</div><div class="line"> },</div><div class="line"> {</div><div class="line"> 'id': 2,</div><div class="line"> 'name': 'Neal Gafter'</div><div class="line"> }</div><div class="line"> ],</div><div class="line"> 'books': [</div><div class="line"> {</div><div class="line"> 'title': 'Java Puzzlers: Traps, Pitfalls, and Corner Cases',</div><div class="line"> 'isbn': '032133678X',</div><div class="line"> 'authors':[1, 2]</div><div class="line"> },</div><div class="line"> {</div><div class="line"> 'title': 'Effective Java (2nd Edition)',</div><div class="line"> 'isbn': '0321356683',</div><div class="line"> 'authors':[1]</div><div class="line"> }</div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>上面所示的JSON实体包括两个author和两本book。其中book通过author的id对author进行引用。这是一个相当常见的情况,因为这种方法会降低JSON实体的大小。下图为该JSON实体的 <code>JsonObject</code> 结构图</p>
<img src="/2017/03/25/详解Java中的Gson反序列化/New-JSON-Object-Hierarchy.png" alt="New-JSON-Object-Hierarchy.png" title="">
<p>这种新的JSON实体结构中引入了新的挑战:当反序列化book时,我们需要持有从JSON实体层次的其他分支反序列化出的author。</p>
<p>有很多种方案可以解决这个问题,下面只介绍一种 </p>
<p>具体方案为:<code>AuthorDeserialiser</code> 作为反序列出的作者的缓存,返回下一次过来的id请求。这种方法的优势在于它利用了 <code>JsonDeserializationContext</code>,并使关系变得透明。不幸的是,它因为需要处理缓存而增加了复杂性。</p>
<p>下面介绍下具体实现</p>
<p>由于该JSON实体包含两个数组,故我们需要定义一个新的类来映射该JSON实体<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Data</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> Author[] authors;</div><div class="line"> <span class="keyword">private</span> Book[] books;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>字段顺序决定了谁先被反序列化,但是这在我们这个例子中是没有关系的。</p>
<p><code>AuthorDeserialiser</code> 需要被修改,使得它缓存反序列化出来的author。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"><span class="keyword">import</span> java.util.HashMap;</div><div class="line"><span class="keyword">import</span> java.util.Map;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonDeserializer;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonParseException;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonPrimitive;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AuthorDeserializer</span> <span class="keyword">implements</span> <span class="title">JsonDeserializer</span><<span class="title">Author</span>> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> ThreadLocal<Map<Integer, Author>> cache = <span class="keyword">new</span> ThreadLocal<Map<Integer, Author>>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> Map<Integer, Author> <span class="title">initialValue</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> HashMap<>();</div><div class="line"> }</div><div class="line"> };</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> Author <span class="title">deserialize</span><span class="params">(<span class="keyword">final</span> JsonElement json, <span class="keyword">final</span> Type typeOfT, <span class="keyword">final</span> JsonDeserializationContext context)</span></span></div><div class="line"> <span class="keyword">throws</span> JsonParseException {</div><div class="line"></div><div class="line"> <span class="comment">// Only the ID is available</span></div><div class="line"> <span class="keyword">if</span> (json.isJsonPrimitive()) {</div><div class="line"> <span class="keyword">final</span> JsonPrimitive primitive = json.getAsJsonPrimitive();</div><div class="line"> <span class="keyword">return</span> getOrCreate(primitive.getAsInt());</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// The whole object is available</span></div><div class="line"> <span class="keyword">if</span> (json.isJsonObject()) {</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author author = getOrCreate(jsonObject.get(<span class="string">"id"</span>).getAsInt());</div><div class="line"> author.setName(jsonObject.get(<span class="string">"name"</span>).getAsString());</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> JsonParseException(<span class="string">"Unexpected JSON type: "</span> + json.getClass().getSimpleName());</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">private</span> Author <span class="title">getOrCreate</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> id)</span> </span>{</div><div class="line"> Author author = cache.get().get(id);</div><div class="line"> <span class="keyword">if</span> (author == <span class="keyword">null</span>) {</div><div class="line"> author = <span class="keyword">new</span> Author();</div><div class="line"> author.setId(id);</div><div class="line"> cache.get().put(id, author);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>下面让我们来一个个讲解这个类的变更<br>1、author被缓存在如下的对象中<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">private</span> <span class="keyword">final</span> ThreadLocal<Map<Integer, Author>> cache = <span class="keyword">new</span> ThreadLocal<Map<Integer, Author>>() {</div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">protected</span> Map<Integer, Author> <span class="title">initialValue</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> HashMap<>();</div><div class="line"> }</div><div class="line"> };</div></pre></td></tr></table></figure></p>
<p>它使用 <code>Map<String, Object></code> 作为缓存机制。该map被保存一个 <code>ThreadLocal</code> 实例中。因此该类允许多个线程使用相同的变量,而不受其他线程的干扰。</p>
<p>2、author总是使用如下的方式进行检索<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">private</span> Author <span class="title">getOrCreate</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> id)</span> </span>{</div><div class="line"> Author author = cache.get().get(id);</div><div class="line"> <span class="keyword">if</span> (author == <span class="keyword">null</span>) {</div><div class="line"> author = <span class="keyword">new</span> Author();</div><div class="line"> cache.get().put(id, author);</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>3、<code>deserialize()</code>方法也将做如下的修改<br><code>descerialiser</code> 可以接收 <code>JsonPrimitive</code> 或 <code>JsonObject</code> 参数。当 <code>BookDeserialiser</code> 执行以下代码时,传递给 <code>AuthorDeserialiser</code> 的 <code>JsonElement</code> 将是 <code>JsonPrimitive</code> 的一个实例。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// This is executed within the BookDeserialiser</span></div><div class="line"> Author[] authors = context.deserialize(jsonObject.get(<span class="string">"authors"</span>), Author[].class);</div></pre></td></tr></table></figure></p>
<p>逻辑图如下:</p>
<img src="/2017/03/25/详解Java中的Gson反序列化/Delegating-Deserialisation-to-Context.png" alt="Delegating-Deserialisation-to-Context.png" title="">
<p><code>BookDeserialiser</code> 使用 <code>context</code> 代理author的反序列化,并提供了一个整形数组。对于该数组的每一个元素,该 <code>context</code> 都将调用下 <code>AuthorDeserialiser</code> 的 <code>deserialize()</code> 方法,并将该整形元素作为 <code>JsonPrimitive</code> 传递给方法。</p>
<p>另一个方面,当author被反序列化时,我们将得到一个包含author元素的 <code>JsonObject</code>。因此,在我们转换给定的 <code>JsonElement</code> 之前,我们需要确定它是否是正确的类型。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Only the ID is available</span></div><div class="line"> <span class="keyword">if</span> (json.isJsonPrimitive()) {</div><div class="line"> <span class="keyword">final</span> JsonPrimitive primitive = json.getAsJsonPrimitive();</div><div class="line"> <span class="keyword">final</span> Author author = getOrCreate(primitive.getAsInt());</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>在上面的例子中,只有id可用。 <code>JsonElement</code> 被转换为 <code>JsonPrimitive</code>,然后转换为int。该int用于从 <code>getOrCreate()</code> 方法中检索作者。</p>
<p>JsonElement可以是JsonObject类型,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The whole object is available</span></div><div class="line"> <span class="keyword">if</span> (json.isJsonObject()) {</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = json.getAsJsonObject();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author author = getOrCreate(jsonObject.get(<span class="string">"id"</span>).getAsInt());</div><div class="line"> author.setName(jsonObject.get(<span class="string">"name"</span>).getAsString());</div><div class="line"> <span class="keyword">return</span> author;</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>在这种情况下,在返回作者之前,该名称将添加到由 <code>getOrCreate()</code> 方法返回的作者实例中。</p>
<p>最后,如果给定的 <code>JsonElement</code> 实例既不是 <code>JsonPrimitive</code> 也不是 <code>JsonObject</code>,将抛出异常。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">throw</span> <span class="keyword">new</span> JsonParseException(<span class="string">"Unexpected JSON type: "</span> + json.getClass().getSimpleName());</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<p>译自 <a href="http://www.javacreed.com/gson-deserialiser-example/">GSON DESERIALISER EXAMPLE</a></p>
<p>在这篇文章中,我们将看到如何将复杂的JSON实体反序列化为已有的Java对象。我们将看到怎么使用GSON的<code>deserialiser</code>,以控制JSON实体映射到Java对象。</p>
<h1 id="一、-前言"><a href="#一、-前言" class="headerlink" title="一、 前言"></a>一、 前言</h1><p>请注意,在这篇文章中我们将使用术语解析或反序列化。</p>
<p>下面列出的所有代码均放在:<a href="https://java-creed-examples.googlecode.com/svn/gson/Gson Deserialiser Example"> https://java-creed-examples.googlecode.com/svn/gson/Gson Deserialiser Example</a>。</p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
<category term="gson" scheme="https://tunsuy.github.io/tags/gson/"/>
</entry>
<entry>
<title>深入详解Java中的Gson序列化</title>
<link href="https://tunsuy.github.io/2017/03/22/%E6%B7%B1%E5%85%A5%E8%AF%A6%E8%A7%A3Java%E4%B8%AD%E7%9A%84Gson%E5%BA%8F%E5%88%97%E5%8C%96/"/>
<id>https://tunsuy.github.io/2017/03/22/深入详解Java中的Gson序列化/</id>
<published>2017-03-22T09:46:38.000Z</published>
<updated>2017-04-19T09:50:04.077Z</updated>
<content type="html"><![CDATA[<p>译自 <a href="http://www.javacreed.com/gson-serialiser-example/" target="_blank" rel="external">GSON SERIALISER EXAMPLE</a></p>
<p>Java对象可以使用Gson API(Homepage)转换成JSON字符串。在本文中,我们将看到我们如何使用默认的Gson实现和自定义实现将Java对象转换为JSON字符串。</p>
<p>下面列出的所有代码均放在:<a href="http://java-creed-examples.googlecode.com/svn/gson/Gson Serialiser Example/" target="_blank" rel="external">http://java-creed-examples.googlecode.com/svn/gson/Gson Serialiser Example/</a></p>
<a id="more"></a>
<h1 id="一、-简单例子"><a href="#一、-简单例子" class="headerlink" title="一、 简单例子"></a>一、 简单例子</h1><p>看下面的一个类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String[] authors;</div><div class="line"> <span class="keyword">private</span> String isbn10;</div><div class="line"> <span class="keyword">private</span> String isbn13;</div><div class="line"> <span class="keyword">private</span> String title;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这是一个代表一本书的简单Java类。现在我们需要将这个类序列化成以下的JSON对象。<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"title"</span>: <span class="string">"Java Puzzlers: Traps, Pitfalls, and Corner Cases"</span>,</div><div class="line"> <span class="attr">"isbn-10"</span>: <span class="string">"032133678X"</span>,</div><div class="line"> <span class="attr">"isbn-13"</span>: <span class="string">"978-0321336781"</span>,</div><div class="line"> <span class="attr">"authors"</span>: [</div><div class="line"> <span class="string">"Joshua Bloch"</span>,</div><div class="line"> <span class="string">"Neal Gafter"</span></div><div class="line"> ]</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>Gson可以在没有任何特殊配置的情况下序列化Book类,只需使用默认配置即可。 Gson使用Java字段名作为JSON名称,并相应地对其值进行序列化。</p>
<p>但是我们仔细查看上面显示的JSON示例,我们将看到ISBN字段包含负号:isbn-10和isbn-13。不幸的是,我们无法使用默认的Gson配置获取这些字段名称。解决此问题的一种方法是使用注释。使用注释,我们可以定义JSON字段名称,而Gson将使用另外的字段名。另一种方法是使用 <code>JsonSerialiser</code>,如以下示例所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonArray;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonPrimitive;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializer;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookSerialiser</span> <span class="keyword">implements</span> <span class="title">JsonSerializer</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> JsonElement <span class="title">serialize</span><span class="params">(<span class="keyword">final</span> Book book, <span class="keyword">final</span> Type typeOfSrc, <span class="keyword">final</span> JsonSerializationContext context)</span> </span>{</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-10"</span>, book.getIsbn10());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-13"</span>, book.getIsbn13());</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonArray jsonAuthorsArray = <span class="keyword">new</span> JsonArray();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">final</span> String author : book.getAuthors()) {</div><div class="line"> <span class="keyword">final</span> JsonPrimitive jsonAuthor = <span class="keyword">new</span> JsonPrimitive(author);</div><div class="line"> jsonAuthorsArray.add(jsonAuthor);</div><div class="line"> }</div><div class="line"> jsonObject.add(<span class="string">"authors"</span>, jsonAuthorsArray);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> jsonObject;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>让我们一步步来解释下上面的过程</p>
<p>如果我们要序列化这个Java对象,我们首先需要创建 <code>JsonElement</code> 的正确实例。在我们的例子中,我们返回一个 <code>JsonObject</code> 的实例,因为我们的Book是一个对象,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div></pre></td></tr></table></figure></p>
<p>该对象将使用我们喜欢的名称填充我们的字段,如下所示<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The variable 'book' is passed as a parameter to the serialize() method</span></div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-10"</span>, book.getIsbn10());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-13"</span>, book.getIsbn13());</div></pre></td></tr></table></figure></p>
<p>使用 <code>addProperty()</code> 方法,我们可以添加任何的java基本类型(String和Number)。请注意,属性名称必须是唯一的,否则将替换以前的值。这可以看作是一个 <code>Map</code>,它将所有的值保存在以他们的名字作为的索引上。</p>
<p>更复杂的对象(如Java对象和数组)无法使用上述方法添加。 <code>JsonObject</code> 有另一个方法叫做 <code>add</code>,可以如下所示使用。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// The variable 'book' is passed as a parameter to the serialize() method</span></div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-10"</span>, book.getIsbn10());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn-13"</span>, book.getIsbn13());</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonArray jsonAuthorsArray = <span class="keyword">new</span> JsonArray();</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">final</span> String author : book.getAuthors()) {</div><div class="line"> <span class="keyword">final</span> JsonPrimitive jsonAuthor = <span class="keyword">new</span> JsonPrimitive(author);</div><div class="line"> jsonAuthorsArray.add(jsonAuthor);</div><div class="line"> }</div><div class="line"> jsonObject.add(<span class="string">"authors"</span>, jsonAuthorsArray);</div></pre></td></tr></table></figure></p>
<p>我们首先创建 <code>JsonArray</code> 并将所有author添加到里面。与Java不同,我们不需要在初始化时定义数组的大小。事实上,我们可以将JsonArray类作为一个列表而不是数组。最后,将 <code>jsonAuthorsArray</code> 变量添加到json对象。请注意,我们可以在添加其元素之前将 <code>jsonAuthorsArray</code> 添加到 <code>jsonObject</code>。</p>
<p>在我们可以使用我们自定义序列化类之前,我们需要用Gson注册,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.IOException;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> <span class="comment">// Configure GSON</span></div><div class="line"> <span class="keyword">final</span> GsonBuilder gsonBuilder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> gsonBuilder.registerTypeAdapter(Book.class, <span class="keyword">new</span> BookSerialiser());</div><div class="line"> gsonBuilder.setPrettyPrinting();</div><div class="line"> <span class="keyword">final</span> Gson gson = gsonBuilder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book javaPuzzlers = <span class="keyword">new</span> Book();</div><div class="line"> javaPuzzlers.setTitle(<span class="string">"Java Puzzlers: Traps, Pitfalls, and Corner Cases"</span>);</div><div class="line"> javaPuzzlers.setIsbn10(<span class="string">"032133678X"</span>);</div><div class="line"> javaPuzzlers.setIsbn13(<span class="string">"978-0321336781"</span>);</div><div class="line"> javaPuzzlers.setAuthors(<span class="keyword">new</span> String[] { <span class="string">"Joshua Bloch"</span>, <span class="string">"Neal Gafter"</span> });</div><div class="line"></div><div class="line"> <span class="comment">// Format to JSON</span></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(javaPuzzlers);</div><div class="line"> System.out.println(json);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>通过注册自定义的序列化类,每当类型为Book的对象被序列化时,Gson都将使用此序列化类。</p>
<p>在上面的例子中,我们还指示Gson格式化JSON对象,通过调用下面语言格式化打印。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">gsonBuilder.setPrettyPrinting();</div></pre></td></tr></table></figure></p>
<p>虽然这对于教程和调试是有用的,但请避免在生产环境中进行格式化打印,因为格式化可能会产生较大的JSON对象(在文本大小方面)。此外,还有轻微的性能成本,因为Gson必须格式化JSON对象并进行相应的缩进。</p>
<h1 id="二、-嵌套对象"><a href="#二、-嵌套对象" class="headerlink" title="二、 嵌套对象"></a>二、 嵌套对象</h1><p>在本例中,我们将介绍如何对嵌套对象进行序列化,即对象中的对象。这里我们将介绍一个新的实体,如下所示:<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> "title": "Java Puzzlers: Traps, Pitfalls, and Corner Cases",</div><div class="line"> "isbn": "032133678X",</div><div class="line"> "authors": [</div><div class="line"> {</div><div class="line"> "id": 1,</div><div class="line"> "name": "Joshua Bloch"</div><div class="line"> },</div><div class="line"> {</div><div class="line"> "id": 2,</div><div class="line"> "name": "Neal Gafter"</div><div class="line"> }</div><div class="line"> ]</div><div class="line">}</div><div class="line">`</div></pre></td></tr></table></figure></p>
<p>请注意,在上一个示例中,作者只是一个字符串数组,如下所示<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">"authors": [</div><div class="line"> "Joshua Bloch",</div><div class="line"> "Neal Gafter"</div><div class="line"> ]</div></pre></td></tr></table></figure></p>
<p>在这个例子中,作者是JSON对象,而不仅仅是原始图形,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="string">"id"</span>: <span class="number">1</span>,</div><div class="line"> <span class="string">"name"</span>: <span class="string">"Joshua Bloch"</span></div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>JSON author有一个id和一个name。因此Author类如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Author</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> id;</div><div class="line"> <span class="keyword">private</span> String name;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这个类很简单。它包括两个字段,两个都是 <code>JsonPrimitive</code> 类型。 Book类需要被修改为包含Author类对象的数组,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> Author[] authors;</div><div class="line"> <span class="keyword">private</span> String isbn;</div><div class="line"> <span class="keyword">private</span> String title;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>authors字段从String数组更改为Author数组。为了适应这种变化,必须修改 <code>BookSerialiser</code> 类,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializer;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookSerialiser</span> <span class="keyword">implements</span> <span class="title">JsonSerializer</span><<span class="title">Book</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> JsonElement <span class="title">serialize</span><span class="params">(<span class="keyword">final</span> Book book, <span class="keyword">final</span> Type typeOfSrc, <span class="keyword">final</span> JsonSerializationContext context)</span> </span>{</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn"</span>, book.getIsbn());</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonElement jsonAuthros = context.serialize(book.getAuthors());</div><div class="line"> jsonObject.add(<span class="string">"authors"</span>, jsonAuthros);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> jsonObject;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>author的序列化委托给上下文(<code>JsonSerializationContext</code>的一个实例),其作为参数传递给 <code>serialize()</code> 方法。上下文将序列化给定的对象并返回一个 <code>JsonElement</code> 类型对象。上下文将尝试找到可以序列化给定对象的序列化类,如果没有找到,则使用默认机制。这里我们暂时不会为Author类编写自定义的序列化类,而是使用默认的。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.IOException;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> <span class="comment">// Configure GSON</span></div><div class="line"> <span class="keyword">final</span> GsonBuilder gsonBuilder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> gsonBuilder.registerTypeAdapter(Book.class, <span class="keyword">new</span> BookSerialiser());</div><div class="line"> gsonBuilder.setPrettyPrinting();</div><div class="line"> <span class="keyword">final</span> Gson gson = gsonBuilder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author joshuaBloch = <span class="keyword">new</span> Author();</div><div class="line"> joshuaBloch.setId(<span class="number">1</span>);</div><div class="line"> joshuaBloch.setName(<span class="string">"Joshua Bloch"</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author nealGafter = <span class="keyword">new</span> Author();</div><div class="line"> nealGafter.setId(<span class="number">2</span>);</div><div class="line"> nealGafter.setName(<span class="string">"Neal Gafter"</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book javaPuzzlers = <span class="keyword">new</span> Book();</div><div class="line"> javaPuzzlers.setTitle(<span class="string">"Java Puzzlers: Traps, Pitfalls, and Corner Cases"</span>);</div><div class="line"> javaPuzzlers.setIsbn(<span class="string">"032133678X"</span>);</div><div class="line"> javaPuzzlers.setAuthors(<span class="keyword">new</span> Author[] { joshuaBloch, nealGafter });</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(javaPuzzlers);</div><div class="line"> System.out.println(json);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>下面对Author类编写自定义的序列化类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializer;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AuthorSerialiser</span> <span class="keyword">implements</span> <span class="title">JsonSerializer</span><<span class="title">Author</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> JsonElement <span class="title">serialize</span><span class="params">(<span class="keyword">final</span> Author author, <span class="keyword">final</span> Type typeOfSrc, <span class="keyword">final</span> JsonSerializationContext context)</span> </span>{</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"id"</span>, author.getId());</div><div class="line"> jsonObject.addProperty(<span class="string">"name"</span>, author.getName());</div><div class="line"></div><div class="line"> <span class="keyword">return</span> jsonObject;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>同样的,为了使用这个新的序列化类,我们需要注册它。</p>
<h1 id="三、-对象引用"><a href="#三、-对象引用" class="headerlink" title="三、 对象引用"></a>三、 对象引用</h1><p>看下面的例子:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.IOException;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Example1</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> IOException </span>{</div><div class="line"> <span class="comment">// Configure GSON</span></div><div class="line"> <span class="keyword">final</span> GsonBuilder gsonBuilder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> gsonBuilder.setPrettyPrinting();</div><div class="line"> <span class="keyword">final</span> Gson gson = gsonBuilder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author joshuaBloch = <span class="keyword">new</span> Author();</div><div class="line"> joshuaBloch.setId(<span class="number">1</span>);</div><div class="line"> joshuaBloch.setName(<span class="string">"Joshua Bloch"</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Author nealGafter = <span class="keyword">new</span> Author();</div><div class="line"> nealGafter.setId(<span class="number">2</span>);</div><div class="line"> nealGafter.setName(<span class="string">"Neal Gafter"</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book javaPuzzlers = <span class="keyword">new</span> Book();</div><div class="line"> javaPuzzlers.setTitle(<span class="string">"Java Puzzlers: Traps, Pitfalls, and Corner Cases"</span>);</div><div class="line"> javaPuzzlers.setIsbn(<span class="string">"032133678X"</span>);</div><div class="line"> javaPuzzlers.setAuthors(<span class="keyword">new</span> Author[] { joshuaBloch, nealGafter });</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book effectiveJava = <span class="keyword">new</span> Book();</div><div class="line"> effectiveJava.setTitle(<span class="string">"Effective Java (2nd Edition)"</span>);</div><div class="line"> effectiveJava.setIsbn(<span class="string">"0321356683"</span>);</div><div class="line"> effectiveJava.setAuthors(<span class="keyword">new</span> Author[] { joshuaBloch });</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Book[] books = <span class="keyword">new</span> Book[] { javaPuzzlers, effectiveJava };</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(books);</div><div class="line"> System.out.println(json);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这里我们有两个作者和两本书。其中一个作者在两本书之间共享。最后注意到,我们目前没有使用任何定制的序列化类。执行上述代码时会产生以下内容。<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line">[</div><div class="line"> {</div><div class="line"> <span class="attr">"authors"</span>: [</div><div class="line"> {</div><div class="line"> <span class="attr">"id"</span>: <span class="number">1</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Joshua Bloch"</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"id"</span>: <span class="number">2</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Neal Gafter"</span></div><div class="line"> }</div><div class="line"> ],</div><div class="line"> <span class="attr">"isbn"</span>: <span class="string">"032133678X"</span>,</div><div class="line"> <span class="attr">"title"</span>: <span class="string">"Java Puzzlers: Traps, Pitfalls, and Corner Cases"</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"authors"</span>: [</div><div class="line"> {</div><div class="line"> <span class="attr">"id"</span>: <span class="number">1</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Joshua Bloch"</span></div><div class="line"> }</div><div class="line"> ],</div><div class="line"> <span class="attr">"isbn"</span>: <span class="string">"0321356683"</span>,</div><div class="line"> <span class="attr">"title"</span>: <span class="string">"Effective Java (2nd Edition)"</span></div><div class="line"> }</div><div class="line">]</div></pre></td></tr></table></figure></p>
<p>我们有两个JSON book和三个JSON author。author:Joshua Bloch,如下所示,是重复的。<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"id"</span>: <span class="number">1</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Joshua Bloch"</span></div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>这增加了JSON对象的大小,特别是对于更复杂的对象。理想情况下,JSON对象只包含作者的ID,而不是整个对象。这类似于关系数据库,其中该书具有作者表的外键。</p>
<p>Book类被修改为增加一个方法,该方法仅返回作者ID,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Book</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> Author[] authors;</div><div class="line"> <span class="keyword">private</span> String isbn;</div><div class="line"> <span class="keyword">private</span> String title;</div><div class="line"></div><div class="line"> <span class="keyword">public</span> Author[] getAuthors() {</div><div class="line"> <span class="keyword">return</span> authors;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">public</span> <span class="keyword">int</span>[] getAuthorsIds() {</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span>[] ids = <span class="keyword">new</span> <span class="keyword">int</span>[authors.length];</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < ids.length; i++) {</div><div class="line"> ids[i] = authors[i].getId();</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> ids;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// Other methods removed for brevity</span></div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>当序列化书籍时,我们将使用作者的ID而不是作者对象,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializer;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookSerialiser</span> <span class="keyword">implements</span> <span class="title">JsonSerializer</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> JsonElement <span class="title">serialize</span><span class="params">(<span class="keyword">final</span> Book book, <span class="keyword">final</span> Type typeOfSrc, <span class="keyword">final</span> JsonSerializationContext context)</span> </span>{</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn"</span>, book.getIsbn());</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonElement jsonAuthros = context.serialize(book.getAuthorsIds());</div><div class="line"> jsonObject.add(<span class="string">"authors"</span>, jsonAuthros);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> jsonObject;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>使用该自定义序列化类,将得到如下输出内容:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.lang.reflect.Type;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.JsonElement;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonObject;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializationContext;</div><div class="line"><span class="keyword">import</span> com.google.gson.JsonSerializer;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BookSerialiser</span> <span class="keyword">implements</span> <span class="title">JsonSerializer</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> JsonElement <span class="title">serialize</span><span class="params">(<span class="keyword">final</span> Book book, <span class="keyword">final</span> Type typeOfSrc, <span class="keyword">final</span> JsonSerializationContext context)</span> </span>{</div><div class="line"> <span class="keyword">final</span> JsonObject jsonObject = <span class="keyword">new</span> JsonObject();</div><div class="line"> jsonObject.addProperty(<span class="string">"title"</span>, book.getTitle());</div><div class="line"> jsonObject.addProperty(<span class="string">"isbn"</span>, book.getIsbn());</div><div class="line"></div><div class="line"> <span class="keyword">final</span> JsonElement jsonAuthros = context.serialize(book.getAuthorsIds());</div><div class="line"> jsonObject.add(<span class="string">"authors"</span>, jsonAuthros);</div><div class="line"></div><div class="line"> <span class="keyword">return</span> jsonObject;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<p>译自 <a href="http://www.javacreed.com/gson-serialiser-example/">GSON SERIALISER EXAMPLE</a></p>
<p>Java对象可以使用Gson API(Homepage)转换成JSON字符串。在本文中,我们将看到我们如何使用默认的Gson实现和自定义实现将Java对象转换为JSON字符串。</p>
<p>下面列出的所有代码均放在:<a href="http://java-creed-examples.googlecode.com/svn/gson/Gson Serialiser Example/">http://java-creed-examples.googlecode.com/svn/gson/Gson Serialiser Example/</a></p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
<category term="gson" scheme="https://tunsuy.github.io/tags/gson/"/>
</entry>
<entry>
<title>深入详解Java中的Gson注解</title>
<link href="https://tunsuy.github.io/2017/03/20/%E6%B7%B1%E5%85%A5%E8%AF%A6%E8%A7%A3Java%E4%B8%AD%E7%9A%84Gson%E6%B3%A8%E8%A7%A3/"/>
<id>https://tunsuy.github.io/2017/03/20/深入详解Java中的Gson注解/</id>
<published>2017-03-20T07:14:37.000Z</published>
<updated>2017-04-19T07:18:27.286Z</updated>
<content type="html"><![CDATA[<p>译自 <a href="http://www.javacreed.com/gson-annotations-example/" target="_blank" rel="external">GSON ANNOTATIONS EXAMPLE</a></p>
<p>Gson提供了一组注解来简化序列化和反序列化过程。在本文中,我们将看到我们如何使用这些注释,以及如何简化Gson在Java对象和JSON对象之间的转换。</p>
<p>以下列出的所有代码在该链接可以找到:<a href="https://java-creed-examples.googlecode.com/svn/gson/Gson Annotations Example" target="_blank" rel="external">Gson Annotations Example</a>。</p>
<p>Gson提供了四个注解,如Java文档中所述。这些注解可以分为三类。下面每个类别将分别讨论。</p>
<a id="more"></a>
<h1 id="一、-自定义字段名称"><a href="#一、-自定义字段名称" class="headerlink" title="一、 自定义字段名称"></a>一、 自定义字段名称</h1><p>看下面的类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.annotations.SerializedName;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Box</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"w"</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> width;</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"h"</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> height;</div><div class="line"></div><div class="line"> <span class="meta">@SerializedName</span>(<span class="string">"d"</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> depth;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这个类有三个字段: 表示一个box的width,height和depth。这些字段用 <code>@SerializedName</code> 注解。此注解的参数(值)是序列化和反序列化对象时要使用的名称。例如,Java字段depth在JSON中表示为d。</p>
<p>Gson支持此注解,而不需要任何配置,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>{</div><div class="line"> <span class="keyword">final</span> GsonBuilder builder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> <span class="keyword">final</span> Gson gson = builder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Box box = <span class="keyword">new</span> Box();</div><div class="line"> box.setWidth(<span class="number">10</span>);</div><div class="line"> box.setHeight(<span class="number">20</span>);</div><div class="line"> box.setDepth(<span class="number">30</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(box);</div><div class="line"> System.out.printf(<span class="string">"Serialised: %s%n"</span>, json);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Box otherBox = gson.fromJson(json, Box.class);</div><div class="line"> System.out.printf(<span class="string">"Same box: %s%n"</span>, box.equals(otherBox));</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出如下所示:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Serialised: {<span class="string">"w"</span>:<span class="number">10</span>,<span class="string">"h"</span>:<span class="number">20</span>,<span class="string">"d"</span>:<span class="number">30</span>}</div><div class="line">Same box: <span class="keyword">true</span></div></pre></td></tr></table></figure></p>
<p>第一行是序列化的输出。请注意,当序列化Box Java对象时,Gson如何使用新名称作为JSON字段名称。同样适用于反序列化。 Gson将JSON字段重新映射到适当的Java字段名称。这由 <code>equals()</code> 方法验证。第二行打印为true,这意味着反序列化出来的对象与最初创建的对象相同。</p>
<h1 id="二、-字段控制"><a href="#二、-字段控制" class="headerlink" title="二、 字段控制"></a>二、 字段控制</h1><p>Gson提供了两种类型的转换:序列化(从Java到JSON)和反序列化(从JSON到Java)。标记为 <code>transient</code> 的Java字段不会被序列化和反序列化。因此,不应该序列化的敏感信息可以被标记为 <code>transient</code>,而Gson不会将其序列化为JSON。</p>
<p>Gson还提供更精细的序列化和反序列化控制和过滤。使用Gson,我们可以使用注解来独立地控制什么是序列化和反序列化。或者,我们可以使用自定义 <code>JsonDeserializer<T></code> 和自定义 <code>JsonSerializer<T></code>。虽然这些接口提供了完全的控制和灵活性,但本文中描述的注解方法更简单,因为它不需要额外的类,我们将在下面的示例中看到。</p>
<p>看下面的类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.annotations.Expose;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Expose</span>(deserialize = <span class="keyword">false</span>)</div><div class="line"> <span class="keyword">private</span> String accountNumber;</div><div class="line"></div><div class="line"> <span class="meta">@Expose</span></div><div class="line"> <span class="keyword">private</span> String iban;</div><div class="line"></div><div class="line"> <span class="meta">@Expose</span>(serialize = <span class="keyword">false</span>)</div><div class="line"> <span class="keyword">private</span> String owner;</div><div class="line"></div><div class="line"> <span class="meta">@Expose</span>(serialize = <span class="keyword">false</span>, deserialize = <span class="keyword">false</span>)</div><div class="line"> <span class="keyword">private</span> String address;</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String pin;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这里我们使用了 <code>@Expose</code> 注解,它具有两个可选元素:<code>deserialize</code> 和 <code>serialize</code>。通过这两个元素,我们可以独立地控制序列化和反序列化,如下表所示。元素的默认值都为true。</p>
<p>为了能够使用 <code>@Expose</code> 此注解,我们需要使用正确的配置。与 <code>@SerializedName</code> 注解不同,我们需要将Gson配置为仅expose注释的字段,并忽略其余的字段,如以下代码片段所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">final</span> GsonBuilder builder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> builder.excludeFieldsWithoutExposeAnnotation();</div><div class="line"> <span class="keyword">final</span> Gson gson = builder.create();</div></pre></td></tr></table></figure></p>
<p>没有这个配置,这个注释将被忽略,所有符合条件的字段被序列化和反序列化,包括字段引脚。以下是完整的例子。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.InputStreamReader;</div><div class="line"><span class="keyword">import</span> java.io.Reader;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"> <span class="keyword">final</span> GsonBuilder builder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> builder.excludeFieldsWithoutExposeAnnotation();</div><div class="line"> <span class="keyword">final</span> Gson gson = builder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Account account = <span class="keyword">new</span> Account();</div><div class="line"> account.setAccountNumber(<span class="string">"A123 45678 90"</span>);</div><div class="line"> account.setIban(<span class="string">"IBAN 11 22 33 44"</span>);</div><div class="line"> account.setOwner(<span class="string">"Albert Attard"</span>);</div><div class="line"> account.setPin(<span class="string">"1234"</span>);</div><div class="line"> account.setAddress(<span class="string">"Somewhere, Far Far Away"</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(account);</div><div class="line"> System.out.printf(<span class="string">"Serialised%n %s%n"</span>, json);</div><div class="line"></div><div class="line"> <span class="keyword">try</span> (<span class="keyword">final</span> Reader data = <span class="keyword">new</span> InputStreamReader(Main.class.getResourceAsStream(<span class="string">"account.json"</span>), <span class="string">"UTF-8"</span>)) {</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Account otherAccount = gson.fromJson(data, Account.class);</div><div class="line"> System.out.println(<span class="string">"Deserialised"</span>);</div><div class="line"> System.out.printf(<span class="string">" Account Number: %s%n"</span>, otherAccount.getAccountNumber());</div><div class="line"> System.out.printf(<span class="string">" IBAN: %s%n"</span>, otherAccount.getIban());</div><div class="line"> System.out.printf(<span class="string">" Owner: %s%n"</span>, otherAccount.getOwner());</div><div class="line"> System.out.printf(<span class="string">" Pin: %s%n"</span>, otherAccount.getPin());</div><div class="line"> System.out.printf(<span class="string">" Address: %s%n"</span>, otherAccount.getAddress());</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">Serialised</div><div class="line"> {<span class="string">"accountNumber"</span>:<span class="string">"A123 45678 90"</span>,<span class="string">"iban"</span>:<span class="string">"IBAN 11 22 33 44"</span>}</div><div class="line">Deserialised</div><div class="line"> Account Number: <span class="keyword">null</span></div><div class="line"> IBAN: IBAN <span class="number">11</span> <span class="number">22</span> <span class="number">33</span> <span class="number">44</span></div><div class="line"> Owner: Albert Attard</div><div class="line"> Pin: <span class="keyword">null</span></div><div class="line"> Address: <span class="keyword">null</span></div></pre></td></tr></table></figure></p>
<p>请注意,只有 <code>accountNumber</code> 和 <code>iban</code> 被序列化,如上表所示。此外,当反序列化以下JSON时,同样的只处理了 <code>owner</code> 和 <code>iban</code>。其他字段被忽略。<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">{</div><div class="line"> <span class="attr">"accountNumber"</span>: <span class="string">"A123 45678 90"</span>,</div><div class="line"> <span class="attr">"iban"</span>: <span class="string">"IBAN 11 22 33 44"</span>,</div><div class="line"> <span class="attr">"owner"</span>: <span class="string">"Albert Attard"</span>,</div><div class="line"> <span class="attr">"address"</span>: <span class="string">"Somewhere, Far Far Away"</span>,</div><div class="line"> <span class="attr">"pin"</span>: <span class="string">"1234"</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>使用 <code>@Expose</code> 注解,我们可以获得对串行化和反序列化的细粒度控制,而无需编写处理此逻辑的代码。</p>
<h1 id="三、-版本控制"><a href="#三、-版本控制" class="headerlink" title="三、 版本控制"></a>三、 版本控制</h1><p>Gson提供了两个注释,称为 <code>@Since</code> 和 <code>@Until</code>,可以用于控制在使用Gson在Java对象和JSON对象之间进行转换时,进行版本的标注。<code>@Since</code> 表示增加,<code>@Until</code> 表示删除。</p>
<p>考虑下面这样一个类,有四个字段。其中一个字段在版本1.2中被添加,另一个字段在版本0.9中被删除。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.annotations.Since;</div><div class="line"><span class="keyword">import</span> com.google.gson.annotations.Until;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SoccerPlayer</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String name;</div><div class="line"></div><div class="line"> <span class="meta">@Since</span>(<span class="number">1.2</span>)</div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> shirtNumber;</div><div class="line"></div><div class="line"> <span class="meta">@Until</span>(<span class="number">0.9</span>)</div><div class="line"> <span class="keyword">private</span> String country;</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String teamName;</div><div class="line"></div><div class="line"> <span class="comment">// Methods removed for brevity</span></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>与之前描述的@Expose注释类似,为了使此注释生效,我们需要进行正确的配置,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.gson.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> java.io.InputStreamReader;</div><div class="line"><span class="keyword">import</span> java.io.Reader;</div><div class="line"></div><div class="line"><span class="keyword">import</span> com.google.gson.Gson;</div><div class="line"><span class="keyword">import</span> com.google.gson.GsonBuilder;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> <span class="keyword">throws</span> Exception </span>{</div><div class="line"></div><div class="line"> <span class="keyword">final</span> GsonBuilder builder = <span class="keyword">new</span> GsonBuilder();</div><div class="line"> builder.setVersion(<span class="number">1.0</span>);</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Gson gson = builder.create();</div><div class="line"></div><div class="line"> <span class="keyword">final</span> SoccerPlayer account = <span class="keyword">new</span> SoccerPlayer();</div><div class="line"> account.setName(<span class="string">"Albert Attard"</span>);</div><div class="line"> account.setShirtNumber(<span class="number">10</span>); <span class="comment">// Since version 1.2</span></div><div class="line"> account.setTeamName(<span class="string">"Zejtun Corinthians"</span>);</div><div class="line"> account.setCountry(<span class="string">"Malta"</span>); <span class="comment">// Until version 0.9</span></div><div class="line"></div><div class="line"> <span class="keyword">final</span> String json = gson.toJson(account);</div><div class="line"> System.out.printf(<span class="string">"Serialised (version 1.0)%n %s%n"</span>, json);</div><div class="line"></div><div class="line"> <span class="keyword">try</span> (<span class="keyword">final</span> Reader data = <span class="keyword">new</span> InputStreamReader(Main.class.getResourceAsStream(<span class="string">"player.json"</span>), <span class="string">"UTF-8"</span>)) {</div><div class="line"> <span class="comment">// Parse JSON to Java</span></div><div class="line"> <span class="keyword">final</span> SoccerPlayer otherPlayer = gson.fromJson(data, SoccerPlayer.class);</div><div class="line"> System.out.println(<span class="string">"Deserialised (version 1.0)"</span>);</div><div class="line"> System.out.printf(<span class="string">" Name: %s%n"</span>, otherPlayer.getName());</div><div class="line"> System.out.printf(<span class="string">" Shirt Number: %s (since version 1.2)%n"</span>, otherPlayer.getShirtNumber());</div><div class="line"> System.out.printf(<span class="string">" Team: %s%n"</span>, otherPlayer.getTeamName());</div><div class="line"> System.out.printf(<span class="string">" Country: %s (until version 0.9)%n"</span>, otherPlayer.getCountry());</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>在这个例子中,我们将版本定义为1.0,这意味着只有 <code>name</code> 和 <code>teamName</code> 会被处理。版本1.2中添加的 <code>shirtNumber</code>,因此不符合条件,0.9版本后删除了 <code>country</code>。得到如下输出:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Serialised (version <span class="number">1.0</span>)</div><div class="line"> {<span class="string">"name"</span>:<span class="string">"Albert Attard"</span>,<span class="string">"teamName"</span>:<span class="string">"Zejtun Corinthians"</span>}</div><div class="line">Deserialised (version <span class="number">1.0</span>)</div><div class="line"> Name: Albert Attard</div><div class="line"> Shirt Number: <span class="number">0</span> (since version <span class="number">1.2</span>)</div><div class="line"> Team: Zejtun Corinthians</div><div class="line"> Country: <span class="keyword">null</span> (until version <span class="number">0.9</span>)</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<p>译自 <a href="http://www.javacreed.com/gson-annotations-example/">GSON ANNOTATIONS EXAMPLE</a></p>
<p>Gson提供了一组注解来简化序列化和反序列化过程。在本文中,我们将看到我们如何使用这些注释,以及如何简化Gson在Java对象和JSON对象之间的转换。</p>
<p>以下列出的所有代码在该链接可以找到:<a href="https://java-creed-examples.googlecode.com/svn/gson/Gson Annotations Example">Gson Annotations Example</a>。</p>
<p>Gson提供了四个注解,如Java文档中所述。这些注解可以分为三类。下面每个类别将分别讨论。</p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
<category term="gson" scheme="https://tunsuy.github.io/tags/gson/"/>
</entry>
<entry>
<title>Protobuf协议代码生成器之wire</title>
<link href="https://tunsuy.github.io/2017/03/15/Protobuf%E5%8D%8F%E8%AE%AE%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90%E5%99%A8%E4%B9%8Bwire/"/>
<id>https://tunsuy.github.io/2017/03/15/Protobuf协议代码生成器之wire/</id>
<published>2017-03-15T10:09:10.000Z</published>
<updated>2017-04-18T11:18:15.614Z</updated>
<content type="html"><![CDATA[<h1 id="一、-前言"><a href="#一、-前言" class="headerlink" title="一、 前言"></a>一、 前言</h1><p>首先要搞清楚 <code>Protocol Buffers</code> 和代码生成器。</p>
<p><code>Protocol Buffers</code> 只是一种数据传输协议格式,是Google定义的,它是与语言和平台均无关的,用于描述和传输数据的语言。开发人员可以在不同的环境中使用相同的模式开发。<code>Protocol Buffers</code>中的 <code>.proto</code> 源文件是人类可读的,可以包含注释。<code>Protocol Buffers</code>定义了一种紧凑的二进制格式,允许项目结构在不破坏现有客户端的情况下发展。</p>
<p>而代码生成器则是根据这个协议文档自动生成相应语言代码,任何组织机构都可以开发该工具。也就是说,使用代码生成器来读取 <code>.proto</code> 文件,并以你选择的语言生成源代码。这种方法有助于加快开发速度。。下文中将Google自身的工具称为标准工具,即 <code>protoc</code></p>
<a id="more"></a>
<p><code>Protocol Buffers</code> 标准使用:<a href="https://developers.google.com/protocol-buffers/docs/javatutorial#compiling-your-protocol-buffers" target="_blank" rel="external">Protocol Buffer Basics: Java</a></p>
<p><code>Protocol Buffers</code> 标准工具 <code>protoc</code> 会为你的 <code>Message</code> 中的每个可选或必需字段生成至少九种方法,以及至少十八种重复字段的方法!在非限制环境中拥有所有这些灵活性是非常好的。但是在Android环境中,Dalvik字节码格式在单个应用程序中强加了64K的方法数限制。所以这将大大限制业务代码方法数。也是就有了一些第三方组织自研的代码生成器。</p>
<h1 id="二、-wire简介"><a href="#二、-wire简介" class="headerlink" title="二、 wire简介"></a>二、 wire简介</h1><p>Wire是基于Google的 <a href="https://github.com/google/protobuf" target="_blank" rel="external">Protocol Buffers</a> 的新的开源实现。它适用于Android设备,但可用于运行Java语言代码的任何地方。</p>
<p>项目地址: <a href="https://github.com/square/wire" target="_blank" rel="external">https://github.com/square/wire</a></p>
<p>对于Android应用开发中的数据传输协议,我们希望其应该具有如下几个特性:<br>1、消息应包含最少数量的生成方法<br>2、消息应该是纯净的且是开发友好的数据对象<br>3、他们应该高度可读的<br>4、他们应该是不可改变的<br>5、他们应该有诸如 <code>equals</code>,<code>hashCode</code> 和 <code>toString</code> 等这些有用的方法<br>6、他们应该支持链式Builder模式<br>7、他们应该从.proto源文件继承文档<br>8、<code>Protocol Buffers</code>中的 <code>enums</code> 类型应该映射到Java中的 <code>enums</code><br>9、理想情况下,所有应用程序都可以使用基于Java的工具进行构建</p>
<p>wire正是在这样的需求之上实现的,它是构建在 <a href="https://github.com/square/protoparser/" target="_blank" rel="external">ProtoParser</a> 和 <a href="https://github.com/square/javapoet" target="_blank" rel="external">JavaWriter</a> 之上的。</p>
<p>google也针对Android这样的移动设备推出了自己的协议 <a href="https://github.com/android/platform_external_protobuf/tree/master/java/src/main/java/com/google/protobuf/nano" target="_blank" rel="external">nano</a>,这个协议有更少的方法产生,但是它不具有上述的所有需求。</p>
<h1 id="三、-wire应用"><a href="#三、-wire应用" class="headerlink" title="三、 wire应用"></a>三、 wire应用</h1><p>看下面的protobuf文件定义:<br><figure class="highlight protobuf"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">message</span> <span class="title">Person</span> </span>{</div><div class="line"> <span class="comment">// The customer's full name.</span></div><div class="line"> <span class="keyword">required</span> <span class="built_in">string</span> name = <span class="number">1</span>;</div><div class="line"> <span class="comment">// The customer's ID number.</span></div><div class="line"> <span class="keyword">required</span> <span class="built_in">int32</span> id = <span class="number">2</span>;</div><div class="line"> <span class="comment">// Email address for the customer.</span></div><div class="line"> <span class="keyword">optional</span> <span class="built_in">string</span> email = <span class="number">3</span>;</div><div class="line"> <span class="class"><span class="keyword">enum</span> <span class="title">PhoneType</span> </span>{</div><div class="line"> MOBILE = <span class="number">0</span>;</div><div class="line"> HOME = <span class="number">1</span>;</div><div class="line"> WORK = <span class="number">2</span>;</div><div class="line"> }</div><div class="line"> <span class="class"><span class="keyword">message</span> <span class="title">PhoneNumber</span> </span>{</div><div class="line"> <span class="comment">// The user's phone number.</span></div><div class="line"> <span class="keyword">required</span> <span class="built_in">string</span> number = <span class="number">1</span>;</div><div class="line"> <span class="comment">// The type of phone stored here.</span></div><div class="line"> <span class="keyword">optional</span> PhoneType type = <span class="number">2</span> [default = HOME];</div><div class="line"> }</div><div class="line"> <span class="comment">// A list of the user's phone numbers.</span></div><div class="line"> <span class="keyword">repeated</span> PhoneNumber phone = <span class="number">4</span>;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>通过wire生成的java部分代码如下所示:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Person</span> <span class="keyword">extends</span> <span class="title">Message</span> </span>{</div><div class="line"> <span class="comment">/** The customer's full name. */</span></div><div class="line"> <span class="meta">@ProtoField</span>(tag = <span class="number">1</span>, type = STRING, label = REQUIRED)</div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> String name;</div><div class="line"> <span class="comment">/** The customer's ID number. */</span></div><div class="line"> <span class="meta">@ProtoField</span>(tag = <span class="number">2</span>, type = INT32, label = REQUIRED)</div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> Integer id;</div><div class="line"> <span class="comment">/** Email address for the customer. */</span></div><div class="line"> <span class="meta">@ProtoField</span>(tag = <span class="number">3</span>, type = STRING)</div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> String email;</div><div class="line"> <span class="comment">/** A list of the user's phone numbers. */</span></div><div class="line"> <span class="meta">@ProtoField</span>(tag = <span class="number">4</span>, label = REPEATED)</div><div class="line"> <span class="keyword">public</span> <span class="keyword">final</span> List<PhoneNumber> phone;</div><div class="line"> <span class="function"><span class="keyword">private</span> <span class="title">Person</span><span class="params">(Builder builder)</span> </span>{</div><div class="line"> <span class="keyword">super</span>(builder);</div><div class="line"> <span class="keyword">this</span>.name = builder.name;</div><div class="line"> <span class="keyword">this</span>.id = builder.id;</div><div class="line"> <span class="keyword">this</span>.email = builder.email;</div><div class="line"> <span class="keyword">this</span>.phone = immutableCopyOf(builder.phone);</div><div class="line"> }</div><div class="line"> <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object other)</span> </span>{</div><div class="line"> <span class="keyword">if</span> (!(other <span class="keyword">instanceof</span> Person)) <span class="keyword">return</span> <span class="keyword">false</span>;</div><div class="line"> Person o = (Person) other;</div><div class="line"> <span class="keyword">return</span> equals(name, o.name)</div><div class="line"> && equals(id, o.id)</div><div class="line"> && equals(email, o.email)</div><div class="line"> && equals(phone, o.phone);</div><div class="line"> }</div><div class="line"> <span class="meta">@Override</span> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">int</span> result = hashCode;</div><div class="line"> <span class="keyword">if</span> (result == <span class="number">0</span>) {</div><div class="line"> result = name != <span class="keyword">null</span> ? name.hashCode() : <span class="number">0</span>;</div><div class="line"> result = result * <span class="number">37</span> + (id != <span class="keyword">null</span> ? id.hashCode() : <span class="number">0</span>);</div><div class="line"> result = result * <span class="number">37</span> + (email != <span class="keyword">null</span> ? email.hashCode() : <span class="number">0</span>);</div><div class="line"> result = result * <span class="number">37</span> + (phone != <span class="keyword">null</span> ? phone.hashCode() : <span class="number">0</span>);</div><div class="line"> hashCode = result;</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> result;</div><div class="line"> }</div><div class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Builder</span> <span class="keyword">extends</span> <span class="title">Message</span>.<span class="title">Builder</span><<span class="title">Person</span>> </span>{</div><div class="line"> <span class="comment">// not shown</span></div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p><code>Message</code> 类的实例只能由相应的嵌套 <code>Builder</code> 类创建。 Wire在每个构建器中为每个字段生成单个方法,以支持链接:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">Person person = <span class="keyword">new</span> Person.Builder()</div><div class="line"> .name(<span class="string">"Omar"</span>)</div><div class="line"> .id(<span class="number">1234</span>)</div><div class="line"> .email(<span class="string">"[email protected]"</span>)</div><div class="line"> .phone(Arrays.asList(<span class="keyword">new</span> PhoneNumber.Builder()</div><div class="line"> .number(<span class="string">"410-555-0909"</span>)</div><div class="line"> .type(PhoneType.MOBILE)</div><div class="line"> .build()))</div><div class="line"> .build();</div></pre></td></tr></table></figure></p>
<p>Wire通过为每个 <code>Message</code> 字段使用 <code>public final</code> 修饰来减少生成的方法的数量。数组被包装,所以 <code>Message</code> 实例是不可改变的。每个字段都用 <code>@ProtoField</code> 来注解,以便提供Wire执行序列化和反序列化所需要的元数据,比如:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@ProtoField</span>(tag = <span class="number">1</span>, type = STRING, label = REQUIRED)</div><div class="line"><span class="keyword">public</span> <span class="keyword">final</span> String name;</div></pre></td></tr></table></figure></p>
<p>可以直接使用这些字段来访问你的数据,比如:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (person.phone != <span class="keyword">null</span>) {</div><div class="line"> <span class="keyword">for</span> (PhoneNumber phone : person.phone)</div><div class="line"> <span class="keyword">if</span> (phone.type == PhoneType.MOBILE) {</div><div class="line"> sendSms(person.name, phone.number, message);</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>我们将上面创建的 <code>person</code> 实例进行序列化和反序列化,代码如下所示:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// 序列化</span></div><div class="line"><span class="keyword">byte</span>[] data = person.toByteArray();</div><div class="line"></div><div class="line"><span class="comment">//反序列化</span></div><div class="line">Wire wire = <span class="keyword">new</span> Wire();</div><div class="line">Person newPerson = wire.parseFrom(data, Person.class);</div></pre></td></tr></table></figure></p>
<p>wire使用了反射机制来实现一些功能,如序列化,反序列化和 <code>toString</code> 方法。并且缓存有关每个消息类的反射信息,以获得更好的性能。</p>
<p>在标准 <code>Protocol Buffers</code>(protoc)生成的代码中,你可以调用 <code>person.hasEmail()</code> 来查看是否已经设置了电子邮件地址。但是使用wire,你只需检查 <code>person.email == null</code>。对于诸如 <code>phone</code> 等重复字段,Wire还需要你的应用程序一次性获取或设置 <code>PhoneNumber</code> 实例列表,从而节省了大量方法。</p>
<p>Wire支持附加功能,如扩展名和未知字段。目前,它缺乏对一些高级功能的支持,包括自定义选项,服务和运行时自省等。也不支持目前已经过时的 <code>groups</code> 功能。</p>
]]></content>
<summary type="html">
<h1 id="一、-前言"><a href="#一、-前言" class="headerlink" title="一、 前言"></a>一、 前言</h1><p>首先要搞清楚 <code>Protocol Buffers</code> 和代码生成器。</p>
<p><code>Protocol Buffers</code> 只是一种数据传输协议格式,是Google定义的,它是与语言和平台均无关的,用于描述和传输数据的语言。开发人员可以在不同的环境中使用相同的模式开发。<code>Protocol Buffers</code>中的 <code>.proto</code> 源文件是人类可读的,可以包含注释。<code>Protocol Buffers</code>定义了一种紧凑的二进制格式,允许项目结构在不破坏现有客户端的情况下发展。</p>
<p>而代码生成器则是根据这个协议文档自动生成相应语言代码,任何组织机构都可以开发该工具。也就是说,使用代码生成器来读取 <code>.proto</code> 文件,并以你选择的语言生成源代码。这种方法有助于加快开发速度。。下文中将Google自身的工具称为标准工具,即 <code>protoc</code></p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
<category term="android" scheme="https://tunsuy.github.io/tags/android/"/>
<category term="protobuf" scheme="https://tunsuy.github.io/tags/protobuf/"/>
</entry>
<entry>
<title>使用Spring实现Java中的缓存</title>
<link href="https://tunsuy.github.io/2017/03/10/%E4%BD%BF%E7%94%A8Spring%E5%AE%9E%E7%8E%B0Java%E4%B8%AD%E7%9A%84%E7%BC%93%E5%AD%98/"/>
<id>https://tunsuy.github.io/2017/03/10/使用Spring实现Java中的缓存/</id>
<published>2017-03-10T04:11:14.000Z</published>
<updated>2017-04-18T04:14:32.496Z</updated>
<content type="html"><![CDATA[<p>译自 <a href="http://www.javacreed.com/caching-made-easy-with-spring/" target="_blank" rel="external">CACHING MADE EASY WITH SPRING</a></p>
<p>Spring3.1引入了一种新的简单的方法来缓存结果。在本文中,我们将看到如何在我们的项目中使用新的Spring缓存。本文的读者应该有一些关于Spring和依赖注入的基本知识(或者也称为:控制反转IOC)。</p>
<p>以下列出的所有代码均可在 <a href="http://code.google.com/p/java-creed-examples/source/checkout" target="_blank" rel="external">http://code.google.com/p/java-creed-examples/source/checkout</a> 中找到。</p>
<h1 id="一、-简单缓存"><a href="#一、-简单缓存" class="headerlink" title="一、 简单缓存"></a>一、 简单缓存</h1><p>看看如下的类:</p>
<a id="more"></a>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</div><div class="line"></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Worker</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">longTask</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> id)</span> </span>{</div><div class="line"> System.out.printf(<span class="string">"Running long task for id: %d...%n"</span>, id);</div><div class="line"> <span class="keyword">return</span> <span class="string">"Long task for id "</span> + id + <span class="string">" is done"</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">shortTask</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> id)</span> </span>{</div><div class="line"> System.out.printf(<span class="string">"Running short task for id: %d...%n"</span>, id);</div><div class="line"> <span class="keyword">return</span> <span class="string">"Short task for id "</span> + id + <span class="string">" is done"</span>;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这里我们有一个简单的Spring组件类,它有两个方法。一个名为 <code>longTask()</code> 方法:表示一个虚拟的耗时任务,而名为 <code>shortTask()</code> 的第二种方法可快速运行。两种方法的输出仅由这些方法的输入决定。因此,对于相同的输入,我们将始终得到相同的输出。这是非常重要的,否则我们不能应用缓存。</p>
<p>再看下对这个类的实际使用情况:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>{</div><div class="line"> <span class="keyword">final</span> String xmlFile = <span class="string">"META-INF/spring/app-context.xml"</span>;</div><div class="line"> <span class="keyword">try</span> (ClassPathXmlApplicationContext context = <span class="keyword">new</span> ClassPathXmlApplicationContext(xmlFile)) {</div><div class="line"></div><div class="line"> <span class="keyword">final</span> Worker worker = context.getBean(Worker.class);</div><div class="line"> worker.longTask(<span class="number">1</span>);</div><div class="line"> worker.longTask(<span class="number">1</span>);</div><div class="line"> worker.longTask(<span class="number">1</span>);</div><div class="line"> worker.longTask(<span class="number">2</span>);</div><div class="line"> worker.longTask(<span class="number">2</span>);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>在这里,我们创建了Spring环境并从Spring检索了一个Worker的实例。然后我们调用 <code>longTask()</code> 方法五次。这将产生以下输出:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">1</span>...</div><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">1</span>...</div><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">1</span>...</div><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">2</span>...</div><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">2</span>...</div></pre></td></tr></table></figure></p>
<p>请注意,此方法只接收两个不同的输入。参数值为1的输入调用了3次,参数值为2的输入调用了2次。由于该方法的输出仅由其输入决定,因此可以使用缓存,在下一个请求该输入值时直接返回该输出值,而不是重新运行该方法。</p>
<p>为了使用缓存,我们需要做如下的三件事</p>
<p>1、标记输出将被缓存的方法(或类)。<br>Spring 3.1添加了新的注释,启用方法缓存,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.cache.annotation.Cacheable;</div><div class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</div><div class="line"></div><div class="line"><span class="meta">@Component</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Worker</span> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Cacheable</span>(<span class="string">"task"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">longTask</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> id)</span> </span>{</div><div class="line"> System.out.printf(<span class="string">"Running long task for id: %d...%n"</span>, id);</div><div class="line"> <span class="keyword">return</span> <span class="string">"Long task for id "</span> + id + <span class="string">" is done"</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">shortTask</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> id)</span> </span>{</div><div class="line"> System.out.printf(<span class="string">"Running short task for id: %d...%n"</span>, id);</div><div class="line"> <span class="keyword">return</span> <span class="string">"Short task for id "</span> + id + <span class="string">" is done"</span>;</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>通过简单地将 <code>@Cacheable</code> 注释添加到方法签名中,具有相同参数值的此方法的重复请求将简单地返回缓存的值。 Spring允许我们缓存值,而无需编写处理这种情况的样板代码。请注意,此注释还将获取一个值,该值是缓存存储库的名称。</p>
<p>请注意,<code>@Cacheable</code> 的注释可以应用于一个类,这意味着该类的所有方法都被缓存。</p>
<p>2、启用Spring缓存<br>在Spring开始缓存我们的值之前,我们需要添加以下声明<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><?xml version="1.0" encoding="UTF-8"?></div><div class="line"><span class="tag"><<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">"http://www.springframework.org/schema/beans"</span></span></div><div class="line"> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></div><div class="line"> <span class="attr">xmlns:context</span>=<span class="string">"http://www.springframework.org/schema/context"</span></div><div class="line"> <span class="attr">xmlns:cache</span>=<span class="string">"http://www.springframework.org/schema/cache"</span></div><div class="line"> <span class="attr">xmlns:p</span>=<span class="string">"http://www.springframework.org/schema/p"</span></div><div class="line"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd</span></div><div class="line"> http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd</div><div class="line"> http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">context:annotation-config</span> /></span></div><div class="line"> <span class="tag"><<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">"com.javacreed.examples.sc"</span> /></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- Enables the caching through annotations --></span></div><div class="line"> <span class="tag"><<span class="name">cache:annotation-driven</span> /></span></div><div class="line"></div><div class="line"><span class="tag"></<span class="name">beans</span>></span></div></pre></td></tr></table></figure></p>
<p>有了这个声明,Spring会寻找任何被标记为可缓存的类或方法,并且将采取所有必要的操作来提供缓存。</p>
<p>3、配置要使用的缓存存储库。<br>在此代码可以工作之前,我们需要定义缓存存储库,如下所示。<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><?xml version="1.0" encoding="UTF-8"?></div><div class="line"><span class="tag"><<span class="name">beans</span> <span class="attr">xmlns</span>=<span class="string">"http://www.springframework.org/schema/beans"</span></span></div><div class="line"> <span class="attr">xmlns:xsi</span>=<span class="string">"http://www.w3.org/2001/XMLSchema-instance"</span></div><div class="line"> <span class="attr">xmlns:context</span>=<span class="string">"http://www.springframework.org/schema/context"</span></div><div class="line"> <span class="attr">xmlns:cache</span>=<span class="string">"http://www.springframework.org/schema/cache"</span></div><div class="line"> <span class="attr">xmlns:p</span>=<span class="string">"http://www.springframework.org/schema/p"</span></div><div class="line"> <span class="attr">xsi:schemaLocation</span>=<span class="string">"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd</span></div><div class="line"> http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-3.2.xsd</div><div class="line"> http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"></div><div class="line"></div><div class="line"> <span class="tag"><<span class="name">context:annotation-config</span> /></span></div><div class="line"> <span class="tag"><<span class="name">context:component-scan</span> <span class="attr">base-package</span>=<span class="string">"com.javacreed.examples.sc"</span> /></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- Enables the caching through annotations --></span></div><div class="line"> <span class="tag"><<span class="name">cache:annotation-driven</span> /></span></div><div class="line"></div><div class="line"> <span class="comment"><!-- Generic cache manager based on the JDK ConcurrentMap --></span></div><div class="line"> <span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"cacheManager"</span> <span class="attr">class</span>=<span class="string">"org.springframework.cache.support.SimpleCacheManager"</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"caches"</span>></span></div><div class="line"> <span class="tag"><<span class="name">set</span>></span></div><div class="line"> <span class="tag"><<span class="name">bean</span> <span class="attr">class</span>=<span class="string">"org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"</span> <span class="attr">p:name</span>=<span class="string">"task"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">set</span>></span></div><div class="line"> <span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"></<span class="name">bean</span>></span></div><div class="line"><span class="tag"></<span class="name">beans</span>></span></div></pre></td></tr></table></figure></p>
<p>缓存存储库是保存实际对象的地方。 Spring支持两种类型的存储库:一种基于 <code>JDK ConcurrentMap</code>,另一种在 <code>ehcache</code> 流行库中。这里我们使用 <code>JDK ConcurrentMap</code> 作为缓存存储库。存储库对代码的影响很小(如果有的话),并且存储库之间的切换很容易。在这个例子中,我们添加了一个名为 <code>task</code> 的缓存存储库。我们可以有多个存储库。请注意,此存储库的名称必须与之前的注释中显示的名称相同。</p>
<p>请注意,上述声明中所示的 <code>JDK ConcurrentMap</code> 类因Spring 3.1和3.2版本而异。这里我们使用的是Spring 3.2。在版本3.1中,类名如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">org.springframework.cache.concurrent.ConcurrentCacheFactoryBean</div></pre></td></tr></table></figure></p>
<p>那么该缓存到底是怎么工作的呢?</p>
<p>在配置为使用缓存时,Spring将标记为要缓存的对象包装到代理中。调用者将不会使用我们的对象,而是使用代理,如下图所示。</p>
<p>如果我们打印worker类的Spring环境返回的对象的规范名称,我们将看到如下。<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Worker class: com.javacreed.examples.sc.part1.Worker$$EnhancerByCGLIB$$4fa6f80b</div></pre></td></tr></table></figure></p>
<p>请注意,这不是我们创建的Worker类(规范名称:com.javacreed.examples.sc.part1.Worker),而是其他类。实际上,这个类是由Spring使用代码生成技术生成的,这里没有讨论。当我们从Worker类调用任何方法时,我们正在调用生成的代理中的方法。此代理持有我们的Worker类的实例。它会将任何请求转发给我们的对象并返回其响应,如下图所示。如果方法被标记为可缓存,那么代理将绕过请求,并返回缓存的值。如果代理没有给定输入的缓存值,它将发出请求并保存响应以备将来使用。</p>
<p>运行该例子,得到如下输出:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">1</span>...</div><div class="line">Running <span class="keyword">long</span> task <span class="keyword">for</span> id: <span class="number">2</span>...</div></pre></td></tr></table></figure></p>
<p>这里耗时方法(<code>longTask()</code>)实际上被调用两次。代理在其他时间返回缓存的结果。我们的第一节关于Spring缓存。我们看到,这是很容易启用。我们所需要做的就是按照上面列出的三个步骤进行,我们有缓存。在下一节中,我们将看到我们如何使用递归应用缓存。</p>
<h1 id="二、-缓存递归方法"><a href="#二、-缓存递归方法" class="headerlink" title="二、 缓存递归方法"></a>二、 缓存递归方法</h1><p>看下面的一个类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part2_1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.cache.annotation.Cacheable;</div><div class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</div><div class="line"></div><div class="line"><span class="meta">@Component</span>(<span class="string">"fibonacci"</span>)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Fibonacci</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> executions = <span class="number">0</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getExecutions</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> executions;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resetExecutions</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.executions = <span class="number">0</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Cacheable</span>(<span class="string">"fibonacci"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">valueAt</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> index)</span> </span>{</div><div class="line"> executions++;</div><div class="line"> <span class="keyword">if</span> (index < <span class="number">2</span>) {</div><div class="line"> <span class="keyword">return</span> <span class="number">1</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> valueAt(index - <span class="number">1</span>) + valueAt(index - <span class="number">2</span>);</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>该类实现斐波那契序列,并用给定的索引返回斐波那契数。斐波那契数是使用以下函数递归计算的:<code>fib(n)= fib(n-1)+ fib(n-2)</code>。此递归函数的基本情况是前两个斐波那契数为1。</p>
<p>请注意,此类还会跟踪调用 <code>valueAt()</code> 方法的次数。我们可以通过getter方法获得这个值。斐波那契类还启用了这个值的重置,使得计数器从0开始再次启动。</p>
<p>现在我们执行下这个例子:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part2_1;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>{</div><div class="line"> <span class="keyword">final</span> String xmlFile = <span class="string">"META-INF/spring/app-context.xml"</span>;</div><div class="line"> <span class="keyword">try</span> (ClassPathXmlApplicationContext context = <span class="keyword">new</span> ClassPathXmlApplicationContext(xmlFile)) {</div><div class="line"></div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> start = System.nanoTime();</div><div class="line"> <span class="keyword">final</span> Fibonacci sequence = context.getBean(<span class="string">"fibonacci"</span>, Fibonacci.class);</div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> fibNumber = sequence.valueAt(<span class="number">5</span>);</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> executions = sequence.getExecutions();</div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> timeTaken = System.nanoTime() - start;</div><div class="line"> System.out.printf(<span class="string">"The 5th Fibonacci number is: %d (%,d executions in %,d NS)%n"</span>, fibNumber, executions,</div><div class="line"> timeTaken);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">The <span class="number">5</span>th Fibonacci number is: <span class="number">8</span> (<span class="number">15</span> executions in <span class="number">17</span>,<span class="number">762</span>,<span class="number">022</span> NS)</div></pre></td></tr></table></figure></p>
<p>从输出可以看出:可缓存方法 <code>valueAt()</code> 被调用了15次。这看起来不正确。 <code>valueAt()</code> 方法应该只执行6次而不是15次。其他9次,应该直接返回缓存值。</p>
<p>问题出在哪里呢?</p>
<p>在 <code>main()</code> 方法中,我们通过Spring获得了一个 <code>Fibonacci</code> 类的实例。反过来,Spring将我们的对象包装成代理。因此在 <code>main()</code> 方法中,我们只能访问代理。但是在 <code>Fibonacci</code> 类中的 <code>valueAt()</code> 方法,调用自身(递归)。这不是通过代理调用 <code>valueAt()</code> 方法,而是直接从 <code>Fibonacci</code> 类调用。因此代理被绕过。这就是为什么没有使用缓存的原因</p>
<p>(注:如果我们再次调用 <code>sequence.valueAt(5)</code>,那么此时将直接返回缓存值,因为变量 <code>sequence</code> 是代理的斐波纳契的一个实例。)</p>
<p>怎么解决上面那个问题呢?</p>
<p>我们需要修改 <code>Fibonacci</code> 类并传递我们代理的引用,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part2_2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.cache.annotation.Cacheable;</div><div class="line"><span class="keyword">import</span> org.springframework.stereotype.Component;</div><div class="line"></div><div class="line"><span class="meta">@Component</span>(<span class="string">"fibonacci2"</span>)</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Fibonacci</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">int</span> executions = <span class="number">0</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getExecutions</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> executions;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resetExecutions</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">this</span>.executions = <span class="number">0</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="meta">@Cacheable</span>(<span class="string">"fibonacci"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">valueAt</span><span class="params">(<span class="keyword">final</span> <span class="keyword">long</span> index, <span class="keyword">final</span> Fibonacci callback)</span> </span>{</div><div class="line"> executions++;</div><div class="line"> <span class="keyword">if</span> (index < <span class="number">2</span>) {</div><div class="line"> <span class="keyword">return</span> <span class="number">1</span>;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> callback.valueAt(index - <span class="number">1</span>, callback) + callback.valueAt(index - <span class="number">2</span>, callback);</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>请注意,现在我们的 <code>valueAt()</code> 方法有两个参数,而不是一个。它需要Fibonacci类的一个实例,称为回调。此外,它不是调用 <code>valueAt()</code> 本身,而是调用回调的 <code>valueAt()</code>。同样的 <code>main()</code> 方法,我们还需要传递代理的 <code>Fibonacci</code> 类的一个实例,如下例所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part2_2;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>{</div><div class="line"> <span class="keyword">final</span> String xmlFile = <span class="string">"META-INF/spring/app-context.xml"</span>;</div><div class="line"> <span class="keyword">try</span> (ClassPathXmlApplicationContext context = <span class="keyword">new</span> ClassPathXmlApplicationContext(xmlFile)) {</div><div class="line"></div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> start = System.nanoTime();</div><div class="line"> <span class="keyword">final</span> Fibonacci sequence = context.getBean(<span class="string">"fibonacci2"</span>, Fibonacci.class);</div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> fibNumber = sequence.valueAt(<span class="number">5</span>, sequence);</div><div class="line"> <span class="keyword">final</span> <span class="keyword">int</span> executions = sequence.getExecutions();</div><div class="line"> <span class="keyword">final</span> <span class="keyword">long</span> timeTaken = System.nanoTime() - start;</div><div class="line"> System.out.printf(<span class="string">"The 5th Fibonacci number is: %d (%,d executions in %,d NS)%n"</span>, fibNumber, executions,</div><div class="line"> timeTaken);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">The <span class="number">5</span>th Fibonacci number is: <span class="number">8</span> (<span class="number">6</span> executions in <span class="number">18</span>,<span class="number">320</span>,<span class="number">003</span> NS)</div></pre></td></tr></table></figure></p>
<h1 id="三、-缓存实践"><a href="#三、-缓存实践" class="headerlink" title="三、 缓存实践"></a>三、 缓存实践</h1><p>看下面一个类:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part3;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Member</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> memberId;</div><div class="line"> <span class="keyword">private</span> <span class="keyword">final</span> String memberName;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Member</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> memberId, <span class="keyword">final</span> String memberName)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.memberId = memberId;</div><div class="line"> <span class="keyword">this</span>.memberName = memberName;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="comment">// Getters removed for brevity</span></div><div class="line"></div><div class="line"> <span class="meta">@Override</span></div><div class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> String.format(<span class="string">"[%d] %s"</span>, memberId, memberName);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这是一个简单的类,一个成员只有一个id(唯一标识一个成员)和一个名字。为了简单起见,我们将成员保存在如下格式的文本文件中。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="number">1</span>,Albert Attard</div><div class="line"><span class="number">2</span>,Mary Borg</div><div class="line"><span class="number">3</span>,Tony White</div><div class="line"><span class="number">4</span>,Jane Black</div></pre></td></tr></table></figure></p>
<p>现在有如下一个服务接口:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part3;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">MembersService</span> </span>{</div><div class="line"></div><div class="line"> <span class="function">Member <span class="title">getMemberWithId</span><span class="params">(<span class="keyword">int</span> id)</span></span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">saveMember</span><span class="params">(Member member)</span></span>;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>该接口暴露了两种方法,一个用于检索具有给定ID的成员的方法,另一个用于持久化对文件的任何修改。在main方法中,向 <code>MembersService</code> 的实现类发起多个请求。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> com.javacreed.examples.sc.part3;</div><div class="line"></div><div class="line"><span class="keyword">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Main</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(<span class="keyword">final</span> String[] args)</span> </span>{</div><div class="line"> <span class="keyword">final</span> String xmlFile = <span class="string">"META-INF/spring/app-context.xml"</span>;</div><div class="line"> <span class="keyword">try</span> (ClassPathXmlApplicationContext context = <span class="keyword">new</span> ClassPathXmlApplicationContext(xmlFile)) {</div><div class="line"></div><div class="line"> <span class="keyword">final</span> MembersService service = context.getBean(MembersService.class);</div><div class="line"></div><div class="line"> <span class="comment">// Load member with id 1</span></div><div class="line"> Member member = service.getMemberWithId(<span class="number">1</span>);</div><div class="line"> System.out.println(member);</div><div class="line"></div><div class="line"> <span class="comment">// Load member with id 1 again</span></div><div class="line"> member = service.getMemberWithId(<span class="number">1</span>);</div><div class="line"> System.out.println(member);</div><div class="line"></div><div class="line"> <span class="comment">// Edit member with id 1</span></div><div class="line"> member = <span class="keyword">new</span> Member(<span class="number">1</span>, <span class="string">"Joe Vella"</span>);</div><div class="line"> service.saveMember(member);</div><div class="line"></div><div class="line"> <span class="comment">// Load member with id 1 after it was modified</span></div><div class="line"> member = service.getMemberWithId(<span class="number">1</span>);</div><div class="line"> System.out.println(member);</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>得到如下的输出:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">Retrieving the member with id: [<span class="number">1</span>] from file: C:\javacreed\spring-cache\members.txt</div><div class="line">[<span class="number">1</span>] Albert Attard</div><div class="line">[<span class="number">1</span>] Albert Attard</div><div class="line">Retrieving the member with id: [<span class="number">1</span>] from file: C:\javacreed\spring-cache\members.txt</div><div class="line">[<span class="number">1</span>] Joe Vella</div></pre></td></tr></table></figure></p>
<p>这里我们发起了两个请求来检索ID为1的成员,但该方法实际上被调用了一次。在第二个请求中,返回缓存的值。然后我们用相同的id修改了成员。由于成员被修改,缓存无效。因此,当检索到具有相同id的成员时,我们再次调用了实际的方法,并从文件加载它。此值将被缓存,直到再次失效。</p>
<p>现在让我们看看这是如何实现的。 <code>getMemberWithId()</code> 类似于我们已经看到的其他方法。它用 <code>@Cacheable</code> 注释。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"> <span class="meta">@Cacheable</span>(<span class="string">"members"</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> Member <span class="title">getMemberWithId</span><span class="params">(<span class="keyword">final</span> <span class="keyword">int</span> id)</span> </span>{</div><div class="line"> System.out.printf(<span class="string">"Retrieving the member with id: [%d] from file: %s%n"</span>, id, dataFile.getAbsolutePath());</div><div class="line"> <span class="comment">// code removed for brevity</span></div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p><code>saveMember()</code> 需要使缓存无效。为了实现这一点,Spring提供了另一个名为:<code>@CacheEvict</code> 的注解,如下所示。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="meta">@Override</span></div><div class="line"> <span class="meta">@CacheEvict</span>(value = <span class="string">"members"</span>, allEntries = <span class="keyword">true</span>)</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">saveMember</span><span class="params">(<span class="keyword">final</span> Member member)</span> </span>{</div><div class="line"> <span class="comment">// code removed for brevity</span></div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>无论何时调用此方法,名为 <code>members</code> 的高速缓存存储库将从所有成员中清除(按照以下命令执行:<code>allEntries = true annotation optional</code> 参数)。因此,下一次调用 <code>getMemberWithId()</code> 方法时,将不得不从文件加载成员,从而读取新的更改。如果没有这个,<code>getMemberWithId()</code> 方法仍然会返回id为1的成员的旧版本。</p>
]]></content>
<summary type="html">
<p>译自 <a href="http://www.javacreed.com/caching-made-easy-with-spring/">CACHING MADE EASY WITH SPRING</a></p>
<p>Spring3.1引入了一种新的简单的方法来缓存结果。在本文中,我们将看到如何在我们的项目中使用新的Spring缓存。本文的读者应该有一些关于Spring和依赖注入的基本知识(或者也称为:控制反转IOC)。</p>
<p>以下列出的所有代码均可在 <a href="http://code.google.com/p/java-creed-examples/source/checkout">http://code.google.com/p/java-creed-examples/source/checkout</a> 中找到。</p>
<h1 id="一、-简单缓存"><a href="#一、-简单缓存" class="headerlink" title="一、 简单缓存"></a>一、 简单缓存</h1><p>看看如下的类:</p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
<category term="spring" scheme="https://tunsuy.github.io/tags/spring/"/>
</entry>
<entry>
<title>Python中的异步编程详解</title>
<link href="https://tunsuy.github.io/2017/03/05/Python%E4%B8%AD%E7%9A%84%E5%BC%82%E6%AD%A5%E7%BC%96%E7%A8%8B%E8%AF%A6%E8%A7%A3/"/>
<id>https://tunsuy.github.io/2017/03/05/Python中的异步编程详解/</id>
<published>2017-03-05T03:36:37.000Z</published>
<updated>2017-03-18T03:55:36.250Z</updated>
<content type="html"><![CDATA[<h1 id="一、-协程和线程"><a href="#一、-协程和线程" class="headerlink" title="一、 协程和线程"></a>一、 协程和线程</h1><p>多线程的Python程序总是让你看起来像是同时在运行多个函数。但是多线程有三大问题:</p>
<ul>
<li>它们需要特殊的工具来协调各个线程之间的安全。这让编写代码比单线程程序更加的苦难。并且这让代码变得更加的难以维护和扩展。</li>
<li>线程需要更多的内存,每个执行中的线程大概需要8M。这对于现在大部分计算机来说可能不算什么。但是如果你想让你的程序同时运行成千上万的功能,这可能就会导致有些线程不工作了。</li>
<li>开启一个线程的代价是很高的。如果你频繁的创建和销毁一个线程,那么这开销将会是很大的,将拖慢整个系统。</li>
</ul>
<a id="more"></a>
<p>Python用协程将解决这些问题。协程让你的Python程序看起来有很多同时工作的函数功能。它们被实现为作为生成器的扩展功能。开启一个生成器协程的代价就只相当于一个函数调用。一旦开启协程,它们的内存消耗少于1KB。</p>
<p>关于Python的协程发展,这篇文章讲的比较好 <a href="http://shangliuyan.github.io/2015/07/06/python%E7%9A%84%E5%8D%8F%E7%A8%8B/" target="_blank" rel="external">python的协程</a></p>
<p>下面先来看看Python中的生成器</p>
<h1 id="二、-生成器"><a href="#二、-生成器" class="headerlink" title="二、 生成器"></a>二、 生成器</h1><p>简单来说,生成器就是一个生产值的方法。一个方法通常返回一个值之后,内存调用栈中就会将该方法的调用信息给销毁了。当我们再次调用该方法时,又会从入口开始从头执行,它是一次性执行的。但是一个生成器能够 <code>yield</code> 一个值,并且暂停该方法的执行吗,同时将线程控制器交给调用者。当我们想要得到其他值的时候,又可以再次恢复这个方法的执行。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">simple_gen</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">yield</span> <span class="string">"Hello"</span></div><div class="line"> <span class="keyword">yield</span> <span class="string">"World"</span></div><div class="line"></div><div class="line"></div><div class="line">gen = simple_gen()</div><div class="line">print(next(gen))</div><div class="line">print(next(gen))</div></pre></td></tr></table></figure></p>
<p>注:一个生成器方法被调用时不会直接返回任何的值,而是当返回一个生成器对象(类似于迭代器)。我们可以对这个生成器调用 <code>next()</code> 方法来获取每一个值,或者运行 <code>for</code> 循环。</p>
<p>生成器有什么用呢?</p>
<p>假如你的老板要求你写一个方法来生成100以内的序列(一个 <code>range()</code> 的超级简化版本)。你可能这样实现它:你定义一个空列表,然后将数字添加进入,最后返回该列表。后来这个需求变更了,需要生成千万的序列。这时如果你在一个列表中存储千万的数据,这将导致内存溢出。这时,生成器可以解决这个问题:你可以生成这些数据,但是不用存储在列表中,下面是示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">generate_nums</span><span class="params">()</span>:</span></div><div class="line"> num = <span class="number">0</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> <span class="keyword">yield</span> num</div><div class="line"> num = num + <span class="number">1</span></div><div class="line"></div><div class="line"></div><div class="line">nums = generate_nums()</div><div class="line"></div><div class="line"><span class="keyword">for</span> x <span class="keyword">in</span> nums:</div><div class="line"> print(x)</div><div class="line"></div><div class="line"> <span class="keyword">if</span> x > <span class="number">9</span>:</div><div class="line"> <span class="keyword">break</span></div></pre></td></tr></table></figure></p>
<h1 id="三、-协程"><a href="#三、-协程" class="headerlink" title="三、 协程"></a>三、 协程</h1><p>在上一节中,我们已经看见了,使用生成器我们可以从方法上下文中拿到数据。那如果我们也想要传递一些数据给该方法上下文中的变量呢?这就是协程发挥的作用了。<code>yield</code> 关键字能够用来获取数据,也能作为一个表达式。我们能够对生成器对象使用 <code>send()</code> 方法来传递数据给方法。这就是所谓的 <code>“基于生成器的协程”</code>。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">coro</span><span class="params">()</span>:</span></div><div class="line"> hello = <span class="keyword">yield</span> <span class="string">"Hello"</span></div><div class="line"> <span class="keyword">yield</span> hello</div><div class="line"></div><div class="line"></div><div class="line">c = coro()</div><div class="line">print(next(c))</div><div class="line">print(c.send(<span class="string">"World"</span>))</div></pre></td></tr></table></figure></p>
<p>这段代码是怎样工作的呢?首先我们执行 <code>next(c)</code>, 第一次拿到 <code>coro()</code> 中的数据 <code>Hello</code> (此时 <code>coro()</code> 方法暂停,等待下一次恢复)。然后我们通过 <code>send()</code> 方法向方法 <code>coro()</code> 传递一个值 <code>World</code>,此时 <code>coro()</code> 方法恢复执行,并且将我们发送的数据赋值给 <code>hello</code> 变量。并开始往下执行直到遇到下一个 <code>yield</code> ,此时方法返回 <code>World</code> 。</p>
<p>Python的生成器是协程coroutine的一种形式,但它的局限性在于只能向它的直接调用者yield值。这意味着那些包含yield的代码不能想其他代码那样被分离出来放到一个单独的函数中。这也正是 <code>yield from</code> 要解决的。</p>
<h1 id="四、-yield-from"><a href="#四、-yield-from" class="headerlink" title="四、 yield from"></a>四、 yield from</h1><p> <code>yield from</code> 允许一个generator生成器将其部分操作委派给另一个生成器。其产生的主要动力在于使生成器能够很容易分为多个拥有send和throw方法的子生成器,像一个大函数可以分为多个子函数一样简单。示例:<br> <figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="meta">>>> </span><span class="function"><span class="keyword">def</span> <span class="title">accumulate</span><span class="params">()</span>:</span> <span class="comment"># 子生成器,将传进的非None值累加,传进的值若为None,则返回累加结果</span></div><div class="line"><span class="meta">... </span> tally = <span class="number">0</span></div><div class="line"><span class="meta">... </span> <span class="keyword">while</span> <span class="number">1</span>:</div><div class="line"><span class="meta">... </span> next = <span class="keyword">yield</span></div><div class="line"><span class="meta">... </span> <span class="keyword">if</span> next <span class="keyword">is</span> <span class="keyword">None</span>:</div><div class="line"><span class="meta">... </span> <span class="keyword">return</span> tally</div><div class="line"><span class="meta">... </span> tally += next</div><div class="line">...</div><div class="line"><span class="meta">>>> </span><span class="function"><span class="keyword">def</span> <span class="title">gather_tallies</span><span class="params">(tallies)</span>:</span> <span class="comment"># 外部生成器,将累加操作任务委托给子生成器</span></div><div class="line"><span class="meta">... </span> <span class="keyword">while</span> <span class="number">1</span>:</div><div class="line"><span class="meta">... </span> tally = <span class="keyword">yield</span> <span class="keyword">from</span> accumulate()</div><div class="line"><span class="meta">... </span> tallies.append(tally)</div><div class="line">...</div><div class="line"><span class="meta">>>> </span>tallies = []</div><div class="line"><span class="meta">>>> </span>acc = gather_tallies(tallies)</div><div class="line"><span class="meta">>>> </span>next(acc) <span class="comment"># 使累加生成器准备好接收传入值</span></div><div class="line"><span class="meta">>>> </span><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">4</span>):</div><div class="line"><span class="meta">... </span> acc.send(i)</div><div class="line">...</div><div class="line"><span class="meta">>>> </span>acc.send(<span class="keyword">None</span>) <span class="comment"># 结束第一次累加</span></div><div class="line"><span class="meta">>>> </span><span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">5</span>):</div><div class="line"><span class="meta">... </span> acc.send(i)</div><div class="line">...</div><div class="line"><span class="meta">>>> </span>acc.send(<span class="keyword">None</span>) <span class="comment"># 结束第二次累加</span></div><div class="line"><span class="meta">>>> </span>tallies <span class="comment"># 输出最终结果</span></div><div class="line">[<span class="number">6</span>, <span class="number">10</span>]</div></pre></td></tr></table></figure></p>
<p>基于生成器的协程在Python2.5以上就有了,但是在Python3.5,又有了更加灵活强大的协程支持 <code>async/await</code> 以及本地协程。</p>
<h1 id="五、-异步I-O"><a href="#五、-异步I-O" class="headerlink" title="五、 异步I/O"></a>五、 异步I/O</h1><p>从Python3.4起,有了一个叫做 <code>asyncio</code> 的新模块,提供了很多好的API来处理异步程序。我们可以使用协程和asyncio模块更加容易的处理异常程序。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> asyncio</div><div class="line"><span class="keyword">import</span> datetime</div><div class="line"><span class="keyword">import</span> random</div><div class="line"></div><div class="line"></div><div class="line"><span class="meta">@asyncio.coroutine</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">display_date</span><span class="params">(num, loop)</span>:</span></div><div class="line"> end_time = loop.time() + <span class="number">50.0</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> print(<span class="string">"Loop: {} Time: {}"</span>.format(num, datetime.datetime.now()))</div><div class="line"> <span class="keyword">if</span> (loop.time() + <span class="number">1.0</span>) >= end_time:</div><div class="line"> <span class="keyword">break</span></div><div class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> asyncio.sleep(random.randint(<span class="number">0</span>, <span class="number">5</span>))</div><div class="line"></div><div class="line"></div><div class="line">loop = asyncio.get_event_loop()</div><div class="line"></div><div class="line">asyncio.ensure_future(display_date(<span class="number">1</span>, loop))</div><div class="line">asyncio.ensure_future(display_date(<span class="number">2</span>, loop))</div><div class="line"></div><div class="line">loop.run_forever()</div></pre></td></tr></table></figure></p>
<p>这段代码中,我们创建了一个协程 <code>display_date(num, loop)</code> 。它使用一个 <code>yield from</code> 来等待子协程 <code>asyncio.sleep()</code> 的返回结果。<code>asyncio.sleep()</code> 是一个协程,在给定的时间之后完成。因此我们传递随机的时间给它。然后我们使用 <code>asyncio.ensure_future()</code> 在默认时间循环中调度 <code>display_date()</code> 协程的执行。</p>
<p>通过输出,我们可以看到两个协程在并发的执行。当我们使用 <code>yield from</code> 时,这事件循环知道它将耗时一会,所以它会自动中断这个协程的执行,转而去执行另外一个。因此看起来是并发的执行(但不是并行,因为事件循环是一个单线程)。</p>
<p>正如你知道的,<code>yield from</code> 是一个语法糖:<code>for x in asyncio.sleep(random.randint(0, 5)): yield x</code>。</p>
<h1 id="六、-原生协程"><a href="#六、-原生协程" class="headerlink" title="六、 原生协程"></a>六、 原生协程</h1><p>在Python3.5起,提供了 <code>async/await</code> 来支持原生协程。上一节的代码可以这样重写:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> asyncio</div><div class="line"><span class="keyword">import</span> datetime</div><div class="line"><span class="keyword">import</span> random</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">display_date</span><span class="params">(num, loop, )</span>:</span></div><div class="line"> end_time = loop.time() + <span class="number">50.0</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> print(<span class="string">"Loop: {} Time: {}"</span>.format(num, datetime.datetime.now()))</div><div class="line"> <span class="keyword">if</span> (loop.time() + <span class="number">1.0</span>) >= end_time:</div><div class="line"> <span class="keyword">break</span></div><div class="line"> <span class="keyword">await</span> asyncio.sleep(random.randint(<span class="number">0</span>, <span class="number">5</span>))</div><div class="line"></div><div class="line"></div><div class="line">loop = asyncio.get_event_loop()</div><div class="line"></div><div class="line">asyncio.ensure_future(display_date(<span class="number">1</span>, loop))</div><div class="line">asyncio.ensure_future(display_date(<span class="number">2</span>, loop))</div><div class="line"></div><div class="line">loop.run_forever()</div></pre></td></tr></table></figure></p>
<h1 id="七、-两种模式对比"><a href="#七、-两种模式对比" class="headerlink" title="七、 两种模式对比"></a>七、 两种模式对比</h1><p>原生协程和基于生成器的协程在功能上没有什么差异,除了使用的关键字不同而已。两者之间不能混用,比如在原生中使用 <code>yield/yield from</code> ,在基于生成器的协程中使用 <code>await</code> 。</p>
<p>尽管有使用上的不同,但是我们也可以对他们进行互操作。我们只需要增加 <code>@types.coroutine</code> 装饰器到旧的基于生成器协程上。也就是说,我们能够在原生协程中使用 <code>await</code> 来等待一个基于生成器的协程,在基于生成器的协程中使用 <code>yield from</code> 来等待一个原生协程。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> asyncio</div><div class="line"><span class="keyword">import</span> datetime</div><div class="line"><span class="keyword">import</span> random</div><div class="line"><span class="keyword">import</span> types</div><div class="line"></div><div class="line"></div><div class="line"><span class="meta">@types.coroutine</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_sleep_func</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">yield</span> <span class="keyword">from</span> asyncio.sleep(random.randint(<span class="number">0</span>, <span class="number">5</span>))</div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">def</span> <span class="title">display_date</span><span class="params">(num, loop, )</span>:</span></div><div class="line"> end_time = loop.time() + <span class="number">50.0</span></div><div class="line"> <span class="keyword">while</span> <span class="keyword">True</span>:</div><div class="line"> print(<span class="string">"Loop: {} Time: {}"</span>.format(num, datetime.datetime.now()))</div><div class="line"> <span class="keyword">if</span> (loop.time() + <span class="number">1.0</span>) >= end_time:</div><div class="line"> <span class="keyword">break</span></div><div class="line"> <span class="keyword">await</span> my_sleep_func()</div><div class="line"></div><div class="line"></div><div class="line">loop = asyncio.get_event_loop()</div><div class="line"></div><div class="line">asyncio.ensure_future(display_date(<span class="number">1</span>, loop))</div><div class="line">asyncio.ensure_future(display_date(<span class="number">2</span>, loop))</div><div class="line"></div><div class="line">loop.run_forever()</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<h1 id="一、-协程和线程"><a href="#一、-协程和线程" class="headerlink" title="一、 协程和线程"></a>一、 协程和线程</h1><p>多线程的Python程序总是让你看起来像是同时在运行多个函数。但是多线程有三大问题:</p>
<ul>
<li>它们需要特殊的工具来协调各个线程之间的安全。这让编写代码比单线程程序更加的苦难。并且这让代码变得更加的难以维护和扩展。</li>
<li>线程需要更多的内存,每个执行中的线程大概需要8M。这对于现在大部分计算机来说可能不算什么。但是如果你想让你的程序同时运行成千上万的功能,这可能就会导致有些线程不工作了。</li>
<li>开启一个线程的代价是很高的。如果你频繁的创建和销毁一个线程,那么这开销将会是很大的,将拖慢整个系统。</li>
</ul>
</summary>
<category term="python" scheme="https://tunsuy.github.io/categories/python/"/>
<category term="python" scheme="https://tunsuy.github.io/tags/python/"/>
</entry>
<entry>
<title>为Protobuf编译器protoc编写插件</title>
<link href="https://tunsuy.github.io/2017/02/20/%E4%B8%BAProtobuf%E7%BC%96%E8%AF%91%E5%99%A8protoc%E7%BC%96%E5%86%99%E6%8F%92%E4%BB%B6/"/>
<id>https://tunsuy.github.io/2017/02/20/为Protobuf编译器protoc编写插件/</id>
<published>2017-02-20T01:31:47.000Z</published>
<updated>2017-04-19T03:44:04.410Z</updated>
<content type="html"><![CDATA[<p>Google的 <code>Protocol Buffer</code> 是一种以二进制格式对消息进行编码和解码的库,可针对不同平台之间的紧凑性和可移植性进行优化。目前,核心库可以为 <code>C/C++</code>,<code>Java</code> 和 <code>Python</code> 生成代码,但通过编写Protobuf编译器的插件可以实现自动生成其他语言代码。</p>
<p>已经有一个支持第三方语言的插件列表,但是你可以更加自己的需要来编写插件。在这篇文章中,将实践举例如何编写一个Protobuf编译器的插件。</p>
<h1 id="一、-配置"><a href="#一、-配置" class="headerlink" title="一、 配置"></a>一、 配置</h1><p>在开始编写插件之前,我们需要安装 <code>Protocol Buffer</code> 编译器:<br><figure class="highlight sh"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yum install protobuf</div></pre></td></tr></table></figure></p>
<p>为了能够编译 <code>.proto</code> 文档,我们还需要安装相关语言的protobuf包, 这里我们用Python编写插件:<br><figure class="highlight sh"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pip install protobuf</div></pre></td></tr></table></figure></p>
<a id="more"></a>
<h1 id="二、-定义proto"><a href="#二、-定义proto" class="headerlink" title="二、 定义proto"></a>二、 定义proto</h1><p>先定义一个proto示例文件,以便后面演示<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">enum Greeting {</div><div class="line"> NONE = 0;</div><div class="line"> MR = 1;</div><div class="line"> MRS = 2;</div><div class="line"> MISS = 3;</div><div class="line">}</div><div class="line"></div><div class="line">message Hello {</div><div class="line"> required Greeting greeting = 1;</div><div class="line"> required string name = 2;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h1 id="三、-编写插件"><a href="#三、-编写插件" class="headerlink" title="三、 编写插件"></a>三、 编写插件</h1><p>编译器 <code>protoc</code> 的接口非常简单:编译器将在 <code>stdin</code> 上传递一个 <code>CodeGeneratorRequest</code> 消息,你的插件将在 <code>stdout</code> 的 <code>CodeGeneratorResponse</code> 中输出生成的代码。所以第一步是编写读取请求的代码并写一个空的响应:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div></pre></td><td class="code"><pre><div class="line"><span class="comment">#!/usr/bin/env python</span></div><div class="line"></div><div class="line"><span class="keyword">import</span> sys</div><div class="line"></div><div class="line"><span class="keyword">from</span> google.protobuf.compiler <span class="keyword">import</span> plugin_pb2 <span class="keyword">as</span> plugin</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">generate_code</span><span class="params">(request, response)</span>:</span></div><div class="line"> <span class="keyword">pass</span></div><div class="line"></div><div class="line"></div><div class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</div><div class="line"> <span class="comment"># Read request message from stdin</span></div><div class="line"> data = sys.stdin.read()</div><div class="line"></div><div class="line"> <span class="comment"># Parse request</span></div><div class="line"> request = plugin.CodeGeneratorRequest()</div><div class="line"> request.ParseFromString(data)</div><div class="line"></div><div class="line"> <span class="comment"># Create response</span></div><div class="line"> response = plugin.CodeGeneratorResponse()</div><div class="line"></div><div class="line"> <span class="comment"># Generate code</span></div><div class="line"> generate_code(request, response)</div><div class="line"></div><div class="line"> <span class="comment"># Serialise response message</span></div><div class="line"> output = response.SerializeToString()</div><div class="line"></div><div class="line"> <span class="comment"># Write to stdout</span></div><div class="line"> sys.stdout.write(output)</div></pre></td></tr></table></figure></p>
<p>编译器protoc遵循关于插件名称的命名约定,你可以在 <code>PATH</code> 中将代码保存在名为 <code>protoc-gen-custom</code> 的文件中,或使用你喜欢的任何名称保存(如 <code>my-plugin.py</code>),并将插件的名称和路径传递给 <code>--plugin</code> 命令行选项。</p>
<p>我们选择第二个选项,所以我们将把插件保存为 <code>my-plugin.py</code>,然后编译器的调用将如下所示(假设构建目录已经存在):<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">protoc --plugin=protoc-gen-custom=my-plugin.py --custom_out=./build hello.proto</div></pre></td></tr></table></figure></p>
<p>上面的命令不会产生任何输出,因为我们的插件什么都不做,现在要写一些有意义的输出。</p>
<p>让我们修改 <code>generate_code()</code> 函数来生成 <code>.proto</code> 文件的JSON表示。在这篇文章中,根据之前我们定义的proto文件内容,首先我们需要一个遍历AST并返回所有枚举,消息和嵌套类型的函数:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">traverse</span><span class="params">(proto_file)</span>:</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">_traverse</span><span class="params">(package, items)</span>:</span></div><div class="line"> <span class="keyword">for</span> item <span class="keyword">in</span> items:</div><div class="line"> <span class="keyword">yield</span> item, package</div><div class="line"></div><div class="line"> <span class="keyword">if</span> isinstance(item, DescriptorProto):</div><div class="line"> <span class="keyword">for</span> enum <span class="keyword">in</span> item.enum_type:</div><div class="line"> <span class="keyword">yield</span> enum, package</div><div class="line"></div><div class="line"> <span class="keyword">for</span> nested <span class="keyword">in</span> item.nested_type:</div><div class="line"> nested_package = package + item.name</div><div class="line"></div><div class="line"> <span class="keyword">for</span> nested_item <span class="keyword">in</span> _traverse(nested, nested_package):</div><div class="line"> <span class="keyword">yield</span> nested_item, nested_package</div><div class="line"></div><div class="line"> <span class="keyword">return</span> itertools.chain(</div><div class="line"> _traverse(proto_file.package, proto_file.enum_type),</div><div class="line"> _traverse(proto_file.package, proto_file.message_type),</div><div class="line"> )</div></pre></td></tr></table></figure></p>
<p>现在 <code>generate_code()</code> 代码如下所示:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> itertools</div><div class="line"><span class="keyword">import</span> json</div><div class="line"></div><div class="line"><span class="keyword">from</span> google.protobuf.descriptor_pb2 <span class="keyword">import</span> DescriptorProto, EnumDescriptorProto</div><div class="line"></div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">generate_code</span><span class="params">(request, response)</span>:</span></div><div class="line"> <span class="keyword">for</span> proto_file <span class="keyword">in</span> request.proto_file:</div><div class="line"> output = []</div><div class="line"></div><div class="line"> <span class="comment"># Parse request</span></div><div class="line"> <span class="keyword">for</span> item, package <span class="keyword">in</span> traverse(proto_file):</div><div class="line"> data = {</div><div class="line"> <span class="string">'package'</span>: proto_file.package <span class="keyword">or</span> <span class="string">'&lt;root&gt;'</span>,</div><div class="line"> <span class="string">'filename'</span>: proto_file.name,</div><div class="line"> <span class="string">'name'</span>: item.name,</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">if</span> isinstance(item, DescriptorProto):</div><div class="line"> data.update({</div><div class="line"> <span class="string">'type'</span>: <span class="string">'Message'</span>,</div><div class="line"> <span class="string">'properties'</span>: [{<span class="string">'name'</span>: f.name, <span class="string">'type'</span>: int(f.type)}</div><div class="line"> <span class="keyword">for</span> f <span class="keyword">in</span> item.field]</div><div class="line"> })</div><div class="line"></div><div class="line"> <span class="keyword">elif</span> isinstance(item, EnumDescriptorProto):</div><div class="line"> data.update({</div><div class="line"> <span class="string">'type'</span>: <span class="string">'Enum'</span>,</div><div class="line"> <span class="string">'values'</span>: [{<span class="string">'name'</span>: v.name, <span class="string">'value'</span>: v.number}</div><div class="line"> <span class="keyword">for</span> v <span class="keyword">in</span> item.value]</div><div class="line"> })</div><div class="line"></div><div class="line"> output.append(data)</div><div class="line"></div><div class="line"> <span class="comment"># Fill response</span></div><div class="line"> f = response.file.add()</div><div class="line"> f.name = proto_file.name + <span class="string">'.json'</span></div><div class="line"> f.content = json.dumps(output, indent=<span class="number">2</span>)</div></pre></td></tr></table></figure></p>
<p>对于 <code>protoc</code> 请求中的每个 <code>.proto</code> 文件,我们遍历所有项目(枚举,消息和嵌套类型),并在字典中写入一些信息。然后,我们向 <code>protoc</code> 的响应中添加一个新文件,我们设置文件名,在该例中等于原始文件名加上 <code>.json</code> 扩展名,以及字典的JSON表示形式的内容。</p>
<p>如果再次运行protobuf编译器,它将在build目录中输出一个名为 <code>hello.proto.json</code> 的文件,内容如下:<br><figure class="highlight json"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line">[</div><div class="line"> {</div><div class="line"> <span class="attr">"type"</span>: <span class="string">"Enum"</span>,</div><div class="line"> <span class="attr">"filename"</span>: <span class="string">"hello.proto"</span>,</div><div class="line"> <span class="attr">"values"</span>: [</div><div class="line"> {</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"NONE"</span>,</div><div class="line"> <span class="attr">"value"</span>: <span class="number">0</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"MR"</span>,</div><div class="line"> <span class="attr">"value"</span>: <span class="number">1</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"MRS"</span>,</div><div class="line"> <span class="attr">"value"</span>: <span class="number">2</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"MISS"</span>,</div><div class="line"> <span class="attr">"value"</span>: <span class="number">3</span></div><div class="line"> }</div><div class="line"> ],</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Greeting"</span>,</div><div class="line"> <span class="attr">"package"</span>: <span class="string">"&lt;root&gt;"</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"properties"</span>: [</div><div class="line"> {</div><div class="line"> <span class="attr">"type"</span>: <span class="number">14</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"greeting"</span></div><div class="line"> },</div><div class="line"> {</div><div class="line"> <span class="attr">"type"</span>: <span class="number">9</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"name"</span></div><div class="line"> }</div><div class="line"> ],</div><div class="line"> <span class="attr">"filename"</span>: <span class="string">"hello.proto"</span>,</div><div class="line"> <span class="attr">"type"</span>: <span class="string">"Message"</span>,</div><div class="line"> <span class="attr">"name"</span>: <span class="string">"Hello"</span>,</div><div class="line"> <span class="attr">"package"</span>: <span class="string">"&lt;root&gt;"</span></div><div class="line"> }</div><div class="line">]</div></pre></td></tr></table></figure></p>
<h1 id="四、-总结"><a href="#四、-总结" class="headerlink" title="四、 总结"></a>四、 总结</h1><p>在这篇文章中,我们通过创建一个 <code>Protocol Buffer</code> 插件来将 <code>.proto</code> 文件编译成JSON格式的简化表示。核心部分是从 <code>stdin</code> 读取protoc请求的接口代码,遍历AST并在stdout上写入响应。该例中我们只处理了 <code>.proto</code> 中所有的枚举,消息和嵌套类型,如果 <code>.proto</code> 文档有其他类型,还需要加相关的代码解析处理。</p>
<p>根据这样的思路,及插件编写过程,你可以定制任何你想要的输出形式。</p>
]]></content>
<summary type="html">
<p>Google的 <code>Protocol Buffer</code> 是一种以二进制格式对消息进行编码和解码的库,可针对不同平台之间的紧凑性和可移植性进行优化。目前,核心库可以为 <code>C/C++</code>,<code>Java</code> 和 <code>Python</code> 生成代码,但通过编写Protobuf编译器的插件可以实现自动生成其他语言代码。</p>
<p>已经有一个支持第三方语言的插件列表,但是你可以更加自己的需要来编写插件。在这篇文章中,将实践举例如何编写一个Protobuf编译器的插件。</p>
<h1 id="一、-配置"><a href="#一、-配置" class="headerlink" title="一、 配置"></a>一、 配置</h1><p>在开始编写插件之前,我们需要安装 <code>Protocol Buffer</code> 编译器:<br><figure class="highlight sh"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yum install protobuf</div></pre></td></tr></table></figure></p>
<p>为了能够编译 <code>.proto</code> 文档,我们还需要安装相关语言的protobuf包, 这里我们用Python编写插件:<br><figure class="highlight sh"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">pip install protobuf</div></pre></td></tr></table></figure></p>
</summary>
<category term="python" scheme="https://tunsuy.github.io/categories/python/"/>
<category term="protobuf" scheme="https://tunsuy.github.io/tags/protobuf/"/>
<category term="python" scheme="https://tunsuy.github.io/tags/python/"/>
<category term="json" scheme="https://tunsuy.github.io/tags/json/"/>
</entry>
<entry>
<title>高效Python编程之方法参数</title>
<link href="https://tunsuy.github.io/2017/02/18/%E9%AB%98%E6%95%88Python%E7%BC%96%E7%A8%8B%E4%B9%8B%E6%96%B9%E6%B3%95%E5%8F%82%E6%95%B0/"/>
<id>https://tunsuy.github.io/2017/02/18/高效Python编程之方法参数/</id>
<published>2017-02-18T03:31:28.000Z</published>
<updated>2017-03-18T04:05:41.079Z</updated>
<content type="html"><![CDATA[<h1 id="一、-可变数量参数"><a href="#一、-可变数量参数" class="headerlink" title="一、 可变数量参数"></a>一、 可变数量参数</h1><h2 id="1、-概述"><a href="#1、-概述" class="headerlink" title="1、 概述"></a>1、 概述</h2><p>可变数量参数是指参数前带 <code>*</code> 的。如 <code>*args</code>.<br>比如,你想要通过一些参数信息来打印日志。使用固定参数如下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(message, values)</span>:</span></div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> values:</div><div class="line"> print(message)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> values_str = <span class="string">', '</span>.join(str(x) <span class="keyword">for</span> x <span class="keyword">in</span> values)</div><div class="line"> print(<span class="string">'%s: %s'</span> % (message, values_str))</div><div class="line"></div><div class="line">log(<span class="string">'My numbers are'</span>, [<span class="number">1</span>, <span class="number">2</span>])</div><div class="line">log(<span class="string">'Hi there'</span>, [])</div><div class="line"></div><div class="line">>>></div><div class="line">My numbers are: <span class="number">1</span>, <span class="number">2</span></div><div class="line">Hi there</div></pre></td></tr></table></figure></p>
<p>可以看出,当你没有values值传递的时候,你也不得不传递一个 <code>[]</code> 。</p>
<a id="more"></a>
<p>最好的做法就是没有值,第二个参数就留空。那么你能够在最后一个参数前加 <code>*</code> 来达到这样的效果。那么最后一个参数,你传递多少个值都是合法的。如下所示:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(message, *values)</span>:</span> <span class="comment"># The only difference</span></div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> values:</div><div class="line"> print(message)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> values_str = <span class="string">', '</span>.join(str(x) <span class="keyword">for</span> x <span class="keyword">in</span> values)</div><div class="line"> print(<span class="string">'%s: %s'</span> % (message, values_str))</div><div class="line"></div><div class="line">log(<span class="string">'My numbers are'</span>, <span class="number">1</span>, <span class="number">2</span>)</div><div class="line">log(<span class="string">'Hi there'</span>) <span class="comment"># Much better</span></div><div class="line"></div><div class="line">>>></div><div class="line">My numbers are: <span class="number">1</span>, <span class="number">2</span></div><div class="line">Hi there</div></pre></td></tr></table></figure></p>
<p>如果你已经有了一个列表变量,想要传递给像log这样的可选参数方法。你能够直接在列表变量前加 <code>*</code> 传递给方法。这表示让Python将列表中的元素项依次传递给方法。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">favorites = [<span class="number">7</span>, <span class="number">33</span>, <span class="number">99</span>]</div><div class="line">log(<span class="string">'Favorite colors'</span>, *favorites)</div><div class="line"></div><div class="line">>>></div><div class="line">Favorite colors: <span class="number">7</span>, <span class="number">33</span>, <span class="number">99</span></div></pre></td></tr></table></figure></p>
<h2 id="2、-问题注意"><a href="#2、-问题注意" class="headerlink" title="2、 问题注意"></a>2、 问题注意</h2><p>接受可变位置的可变数量的参数有两个问题:</p>
<p>第一个问题就是可变参数在被传递到方法中时总是被转换为一个元组。这就意味着如果一个方法的参数是生成器前加 <code>*</code> 。那么该生成器参数将全部迭代完所有的元素,然后返回包含来自该生成器的所有元素组成的元组,这就有可能在数据量比较大的时候占用很大的内存,导致程序crash。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_generator</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(<span class="number">10</span>):</div><div class="line"> <span class="keyword">yield</span> i</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_func</span><span class="params">(*args)</span>:</span></div><div class="line"> print(args)</div><div class="line"></div><div class="line">it = my_generator()</div><div class="line">my_func(*it)</div><div class="line"></div><div class="line">>>></div><div class="line">(<span class="number">0</span>, <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>, <span class="number">7</span>, <span class="number">8</span>, <span class="number">9</span>)</div></pre></td></tr></table></figure></p>
<p>第二个问题就是参数是位置对应的,传递的参数需要根据参数位置来传递,如果中间某个参数没有,那么可变参数中的元素将被填充到那个没有传参的参数中,具体示例如下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(sequence, message, *values)</span>:</span></div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> values:</div><div class="line"> print(<span class="string">'%s: %s'</span> % (sequence, message))</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> values_str = <span class="string">', '</span>.join(str(x) <span class="keyword">for</span> x <span class="keyword">in</span> values)</div><div class="line"> print(<span class="string">'%s: %s: %s'</span> % (sequence, message, values_str))</div><div class="line"></div><div class="line">log(<span class="number">1</span>, <span class="string">'Favorites'</span>, <span class="number">7</span>, <span class="number">33</span>) <span class="comment"># New usage is OK</span></div><div class="line">log(<span class="string">'Favorite numbers'</span>, <span class="number">7</span>, <span class="number">33</span>) <span class="comment"># Old usage breaks</span></div><div class="line"></div><div class="line">>>></div><div class="line"><span class="number">1</span>: Favorites: <span class="number">7</span>, <span class="number">33</span></div><div class="line">Favorite numbers: <span class="number">7</span>: <span class="number">33</span></div></pre></td></tr></table></figure></p>
<h1 id="二、-关键字参数"><a href="#二、-关键字参数" class="headerlink" title="二、 关键字参数"></a>二、 关键字参数</h1><h2 id="1、-概述-1"><a href="#1、-概述-1" class="headerlink" title="1、 概述"></a>1、 概述</h2><p>跟其他程序语言一样,在Python中调用方法允许使用位置来传递参数。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">remainder</span><span class="params">(number, divisor)</span>:</span></div><div class="line"> <span class="keyword">return</span> number % divisor</div><div class="line"></div><div class="line"><span class="keyword">assert</span> remainder(<span class="number">20</span>, <span class="number">7</span>) == <span class="number">6</span></div></pre></td></tr></table></figure></p>
<p>在Python中所有的位置参数也都可以使用关键字来传递,方法定义中的关键字也就是方法调用中的赋值变量。关键字参数能够以任意的位置来传递,也能够同位置参数混合使用。下面的调用都是等效的:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">remainder(<span class="number">20</span>, <span class="number">7</span>)</div><div class="line">remainder(<span class="number">20</span>, divisor=<span class="number">7</span>)</div><div class="line">remainder(number=<span class="number">20</span>, divisor=<span class="number">7</span>)</div><div class="line">remainder(divisor=<span class="number">7</span>, number=<span class="number">20</span>)</div></pre></td></tr></table></figure></p>
<h2 id="2、-问题注意-1"><a href="#2、-问题注意-1" class="headerlink" title="2、 问题注意"></a>2、 问题注意</h2><p>位置参数必现在关键字参数之前被指定,看下面就是一个违法的调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">remainder(number=<span class="number">20</span>, <span class="number">7</span>)</div><div class="line"></div><div class="line">>>></div><div class="line">SyntaxError: non-keyword arg after keyword arg</div></pre></td></tr></table></figure></p>
<p>每一个参数只能被指定一次:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">remainder(<span class="number">20</span>, number=<span class="number">7</span>)</div><div class="line"></div><div class="line">>>></div><div class="line">TypeError: remainder() got multiple values <span class="keyword">for</span> argument <span class="string">'number'</span></div></pre></td></tr></table></figure></p>
<h2 id="3、-优点"><a href="#3、-优点" class="headerlink" title="3、 优点"></a>3、 优点</h2><p>使用关键字参数让程序可读性更好,通过参数名即可知道传递的参数的作用。</p>
<p>关键字参数可以指定默认的值,这对于某些逻辑是很有作用的。在调用的时候则可以不用传递参数,那么该方法将使用默认的值。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">flow_rate</span><span class="params">(weight_diff, time_diff, period=<span class="number">1</span>)</span>:</span></div><div class="line"> <span class="keyword">return</span> (weight_diff / time_diff) * period</div></pre></td></tr></table></figure></p>
<p>调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">flow_per_second = flow_rate(weight_diff, time_diff)</div><div class="line">flow_per_hour = flow_rate(weight_diff, time_diff, period=<span class="number">3600</span>)</div></pre></td></tr></table></figure></p>
<p>有利于程序的扩展性。可以对增加的关键字使用默认值,达到向后兼容的效果,不需要改动已有的代码,示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">flow_rate</span><span class="params">(weight_diff, time_diff,</span></span></div><div class="line"> period=<span class="number">1</span>, units_per_kg=<span class="number">1</span>):</div><div class="line"> <span class="keyword">return</span> ((weight_diff * units_per_kg) / time_diff) * period</div></pre></td></tr></table></figure></p>
<p>新增的调用逻辑:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">pounds_per_hour = flow_rate(weight_diff, time_diff,</div><div class="line"> period=<span class="number">3600</span>, units_per_kg=<span class="number">2.2</span>)</div></pre></td></tr></table></figure></p>
<h1 id="三、-动态默认参数"><a href="#三、-动态默认参数" class="headerlink" title="三、 动态默认参数"></a>三、 动态默认参数</h1><p>有时候你可能需要一个动态的默认参数值。先来看一个列子:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(message, when=datetime.now<span class="params">()</span>)</span>:</span></div><div class="line"> print(<span class="string">'%s: %s'</span> % (when, message))</div><div class="line"></div><div class="line">log(<span class="string">'Hi there!'</span>)</div><div class="line">sleep(<span class="number">0.1</span>)</div><div class="line">log(<span class="string">'Hi again!'</span>)</div><div class="line"></div><div class="line">>>></div><div class="line"><span class="number">2014</span><span class="number">-11</span><span class="number">-15</span> <span class="number">21</span>:<span class="number">10</span>:<span class="number">10.371432</span>: Hi there!</div><div class="line"><span class="number">2014</span><span class="number">-11</span><span class="number">-15</span> <span class="number">21</span>:<span class="number">10</span>:<span class="number">10.371432</span>: Hi again!</div></pre></td></tr></table></figure></p>
<p>我们发现这个时间是一样的,这是因为 <code>datetime.now()</code> 只执行了一次:当这个函数被定义的时候。这是因为当程序启动的时候,加载模块,这个模块包含的代码也被加载了,那么这个默认参数值就被确认了。</p>
<p>一般的做法是给这个参数赋 <code>None</code> 值,然后在代码文档注释中说明。具体动态默认值在程序中指定。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(message, when=None)</span>:</span></div><div class="line"> <span class="string">"""Log a message with a timestamp.</span></div><div class="line"></div><div class="line"> Args:</div><div class="line"> message: Message to print.</div><div class="line"> when: datetime of when the message occurred.</div><div class="line"> Defaults to the present time.</div><div class="line"> """</div><div class="line"> when = datetime.now() <span class="keyword">if</span> when <span class="keyword">is</span> <span class="keyword">None</span> <span class="keyword">else</span> when</div><div class="line"> print(<span class="string">'%s: %s'</span> % (when, message))</div></pre></td></tr></table></figure></p>
<p>这时输出就是动态的参数值了:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">log(<span class="string">'Hi there!'</span>)</div><div class="line">sleep(<span class="number">0.1</span>)</div><div class="line">log(<span class="string">'Hi again!'</span>)</div><div class="line"></div><div class="line">>>></div><div class="line"><span class="number">2014</span><span class="number">-11</span><span class="number">-15</span> <span class="number">21</span>:<span class="number">10</span>:<span class="number">10.472303</span>: Hi there!</div><div class="line"><span class="number">2014</span><span class="number">-11</span><span class="number">-15</span> <span class="number">21</span>:<span class="number">10</span>:<span class="number">10.573395</span>: Hi again!</div></pre></td></tr></table></figure></p>
<p>使用None作为参数默认值时很重要的,特别是当你的参数是可变的时候。比如,你想要加载一个data,并使用json编码。如果编码失败,你想要返回一个空的字典。你可能这样做:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">decode</span><span class="params">(data, default={})</span>:</span></div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> <span class="keyword">return</span> json.loads(data)</div><div class="line"> <span class="keyword">except</span> ValueError:</div><div class="line"> <span class="keyword">return</span> default</div></pre></td></tr></table></figure></p>
<p>这个效果之前一个列子一样,所有的调用使用的都是同样的默认值,这会导致无法预期的效果:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">foo = decode(<span class="string">'bad data'</span>)</div><div class="line">foo[<span class="string">'stuff'</span>] = <span class="number">5</span></div><div class="line">bar = decode(<span class="string">'also bad'</span>)</div><div class="line">bar[<span class="string">'meep'</span>] = <span class="number">1</span></div><div class="line">print(<span class="string">'Foo:'</span>, foo)</div><div class="line">print(<span class="string">'Bar:'</span>, bar)</div><div class="line"></div><div class="line">>>></div><div class="line">Foo: {<span class="string">'stuff'</span>: <span class="number">5</span>, <span class="string">'meep'</span>: <span class="number">1</span>}</div><div class="line">Bar: {<span class="string">'stuff'</span>: <span class="number">5</span>, <span class="string">'meep'</span>: <span class="number">1</span>}</div></pre></td></tr></table></figure></p>
<p>可以发现,两个对象的值都是一样的,改变一个影响了另一个。这是因为两个都是同一个默认值,指向的是同一个对象。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">assert</span> foo <span class="keyword">is</span> bar</div></pre></td></tr></table></figure></p>
<p>使用 <code>None</code> 作为默认值可以解决这个问题<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">decode</span><span class="params">(data, default=None)</span>:</span></div><div class="line"> <span class="string">"""Load JSON data from a string.</span></div><div class="line"></div><div class="line"> Args:</div><div class="line"> data: JSON data to decode.</div><div class="line"> default: Value to return if decoding fails.</div><div class="line"> Defaults to an empty dictionary.</div><div class="line"> """</div><div class="line"> <span class="keyword">if</span> default <span class="keyword">is</span> <span class="keyword">None</span>:</div><div class="line"> default = {}</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> <span class="keyword">return</span> json.loads(data)</div><div class="line"> <span class="keyword">except</span> ValueError:</div><div class="line"> <span class="keyword">return</span> default</div></pre></td></tr></table></figure></p>
<p>现在调用可以发现是正确的了:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">foo = decode(<span class="string">'bad data'</span>)</div><div class="line">foo[<span class="string">'stuff'</span>] = <span class="number">5</span></div><div class="line">bar = decode(<span class="string">'also bad'</span>)</div><div class="line">bar[<span class="string">'meep'</span>] = <span class="number">1</span></div><div class="line">print(<span class="string">'Foo:'</span>, foo)</div><div class="line">print(<span class="string">'Bar:'</span>, bar)</div><div class="line"></div><div class="line">>>></div><div class="line">Foo: {<span class="string">'stuff'</span>: <span class="number">5</span>}</div><div class="line">Bar: {<span class="string">'meep'</span>: <span class="number">1</span>}</div></pre></td></tr></table></figure></p>
<h1 id="四、-强制使用关键字参数"><a href="#四、-强制使用关键字参数" class="headerlink" title="四、 强制使用关键字参数"></a>四、 强制使用关键字参数</h1><p>比如,你在处理两个数相除时,有时候可能想要忽略 <code>ZeroDivisionError</code> 异常,返回无穷大。有时候想要忽略 <code>OverflowError</code> 异常,返回0.<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">safe_division</span><span class="params">(number, divisor, ignore_overflow,</span></span></div><div class="line"> ignore_zero_division):</div><div class="line"> <span class="keyword">try</span>:</div><div class="line"> <span class="keyword">return</span> number / divisor</div><div class="line"> <span class="keyword">except</span> OverflowError:</div><div class="line"> <span class="keyword">if</span> ignore_overflow:</div><div class="line"> <span class="keyword">return</span> <span class="number">0</span></div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> <span class="keyword">raise</span></div><div class="line"> <span class="keyword">except</span> ZeroDivisionError:</div><div class="line"> <span class="keyword">if</span> ignore_zero_division:</div><div class="line"> <span class="keyword">return</span> float(<span class="string">'inf'</span>)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> <span class="keyword">raise</span></div></pre></td></tr></table></figure></p>
<p>调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">result = safe_division(<span class="number">1</span>, <span class="number">10</span>**<span class="number">500</span>, <span class="keyword">True</span>, <span class="keyword">False</span>)</div><div class="line">print(result)</div><div class="line"></div><div class="line">>>></div><div class="line"><span class="number">0.0</span></div></pre></td></tr></table></figure></p>
<p>或者:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">result = safe_division(<span class="number">1</span>, <span class="number">0</span>, <span class="keyword">False</span>, <span class="keyword">True</span>)</div><div class="line">print(result)</div><div class="line"></div><div class="line">>>></div><div class="line">inf</div></pre></td></tr></table></figure></p>
<p>但是你会发现只看调用,是非常不直观的,不知道每个参数是什么意思,这时可以用关键字参数来指示:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">safe_division_b</span><span class="params">(number, divisor,</span></span></div><div class="line"> ignore_overflow=False,</div><div class="line"> ignore_zero_division=False):</div><div class="line"> <span class="comment"># ...</span></div></pre></td></tr></table></figure></p>
<p>调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">safe_division_b(<span class="number">1</span>, <span class="number">10</span>**<span class="number">500</span>, ignore_overflow=<span class="keyword">True</span>)</div><div class="line">safe_division_b(<span class="number">1</span>, <span class="number">0</span>, ignore_zero_division=<span class="keyword">True</span>)</div></pre></td></tr></table></figure></p>
<p>但是还有一个问题就是,这个关键字参数是可选,你任然可以使用位置参数来调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">safe_division_b(<span class="number">1</span>, <span class="number">10</span>**<span class="number">500</span>, <span class="keyword">True</span>, <span class="keyword">False</span>)</div></pre></td></tr></table></figure></p>
<p>那么可不可以强制调用者使用关键字呢?在Python3中可以这样做:在参数中加一个 <code>*</code> 参数,表示之前的参数是位置参数,之后的参数是关键字参数,必须强制表明。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">safe_division_c</span><span class="params">(number, divisor, *,</span></span></div><div class="line"> ignore_overflow=False,</div><div class="line"> ignore_zero_division=False):</div><div class="line"> <span class="comment"># ...</span></div></pre></td></tr></table></figure></p>
<p>现在使用位置参数来调用将报错:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">safe_division_c(<span class="number">1</span>, <span class="number">10</span>**<span class="number">500</span>, <span class="keyword">True</span>, <span class="keyword">False</span>)</div><div class="line"></div><div class="line">>>></div><div class="line">TypeError: safe_division_c() takes <span class="number">2</span> positional arguments but <span class="number">4</span> were given</div></pre></td></tr></table></figure></p>
<p>正确调用:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">safe_division_c(<span class="number">1</span>, <span class="number">0</span>, ignore_zero_division=<span class="keyword">True</span>) <span class="comment"># OK</span></div><div class="line"></div><div class="line"><span class="keyword">try</span>:</div><div class="line"> safe_division_c(<span class="number">1</span>, <span class="number">0</span>)</div><div class="line"><span class="keyword">except</span> ZeroDivisionError:</div><div class="line"> <span class="keyword">pass</span> <span class="comment"># Expected</span></div></pre></td></tr></table></figure></p>
<h1 id="五、-可变数量关键字参数"><a href="#五、-可变数量关键字参数" class="headerlink" title="五、 可变数量关键字参数"></a>五、 可变数量关键字参数</h1><p>将数量不定的可变数量关键字参数传递给方法时,可以使用 <code>**</code> 参数。<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">print_args</span><span class="params">(*args, **kwargs)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">'Positional:'</span>, args</div><div class="line"> <span class="keyword">print</span> <span class="string">'Keyword: '</span>, kwargs</div><div class="line"></div><div class="line">print_args(<span class="number">1</span>, <span class="number">2</span>, foo=<span class="string">'bar'</span>, stuff=<span class="string">'meep'</span>)</div><div class="line"></div><div class="line">>>></div><div class="line">Positional: (<span class="number">1</span>, <span class="number">2</span>)</div><div class="line">Keyword: {<span class="string">'foo'</span>: <span class="string">'bar'</span>, <span class="string">'stuff'</span>: <span class="string">'meep'</span>}</div></pre></td></tr></table></figure></p>
<p>调用的时候,会将传递的所有的关键字参数传递给 <code>kwargs</code> 参数,Python会将其转化成一个字典。</p>
<h1 id="六、-参数顺序"><a href="#六、-参数顺序" class="headerlink" title="六、 参数顺序"></a>六、 参数顺序</h1><p>几种方法参数的定义顺序为:位置参数,关键字参数,非关键字可变长参数(<code>*args</code>),可变数量关键字参数(<code>**kwargs</code>)。</p>
<p>根据传递的参数顺序来依次匹配,先逐级匹配,如果还有剩余的参数再匹配下一级参数。</p>
]]></content>
<summary type="html">
<h1 id="一、-可变数量参数"><a href="#一、-可变数量参数" class="headerlink" title="一、 可变数量参数"></a>一、 可变数量参数</h1><h2 id="1、-概述"><a href="#1、-概述" class="headerlink" title="1、 概述"></a>1、 概述</h2><p>可变数量参数是指参数前带 <code>*</code> 的。如 <code>*args</code>.<br>比如,你想要通过一些参数信息来打印日志。使用固定参数如下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">log</span><span class="params">(message, values)</span>:</span></div><div class="line"> <span class="keyword">if</span> <span class="keyword">not</span> values:</div><div class="line"> print(message)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> values_str = <span class="string">', '</span>.join(str(x) <span class="keyword">for</span> x <span class="keyword">in</span> values)</div><div class="line"> print(<span class="string">'%s: %s'</span> % (message, values_str))</div><div class="line"></div><div class="line">log(<span class="string">'My numbers are'</span>, [<span class="number">1</span>, <span class="number">2</span>])</div><div class="line">log(<span class="string">'Hi there'</span>, [])</div><div class="line"></div><div class="line">&gt;&gt;&gt;</div><div class="line">My numbers are: <span class="number">1</span>, <span class="number">2</span></div><div class="line">Hi there</div></pre></td></tr></table></figure></p>
<p>可以看出,当你没有values值传递的时候,你也不得不传递一个 <code>[]</code> 。</p>
</summary>
<category term="python" scheme="https://tunsuy.github.io/categories/python/"/>
<category term="python" scheme="https://tunsuy.github.io/tags/python/"/>
</entry>
<entry>
<title>深入了解Python中的装饰器</title>
<link href="https://tunsuy.github.io/2017/02/07/%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3Python%E4%B8%AD%E7%9A%84%E8%A3%85%E9%A5%B0%E5%99%A8/"/>
<id>https://tunsuy.github.io/2017/02/07/深入了解Python中的装饰器/</id>
<published>2017-02-07T03:39:34.000Z</published>
<updated>2017-03-18T03:53:59.645Z</updated>
<content type="html"><![CDATA[<h1 id="一、-概述"><a href="#一、-概述" class="headerlink" title="一、 概述"></a>一、 概述</h1><p>Python的装饰器是AOP编程的一种实现,其他很多语言也都支持装饰器模式。<br>注:AOP是指面向切面编程,详见 <a href="https://en.wikipedia.org/wiki/Aspect-oriented_programming" target="_blank" rel="external">AOP概念</a></p>
<p>一个装饰器允许你增加、修改或者完全修改一个方法或者函数的逻辑。使用装饰器,将与业务无关的逻辑移到装饰器中,这将会让你的代码更加的干净紧凑。</p>
<a id="more"></a>
<h1 id="二、-装饰器举例"><a href="#二、-装饰器举例" class="headerlink" title="二、 装饰器举例"></a>二、 装饰器举例</h1><p>最经典的例子当然是Python内建的装饰器:<code>@staticmethod</code> 和 <code>@classmethod</code> 。这些装饰器将一个类中的方法转换为静态方法(该方法的第一个参数不是 <code>self</code>),和类方法(该方法的第一个参数是 <code>cls</code>)。</p>
<p>如下所示:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span><span class="params">(object)</span>:</span></div><div class="line"><span class="meta"> @classmethod</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">foo</span><span class="params">(cls)</span>:</span></div><div class="line"> <span class="keyword">print</span> cls.__name__</div><div class="line"></div><div class="line"><span class="meta"> @staticmethod</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">bar</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">'I have no use for the instance or class'</span></div><div class="line"></div><div class="line"></div><div class="line">A.foo()</div><div class="line">A.bar()</div></pre></td></tr></table></figure></p>
<p>输出:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">A</div><div class="line">I have no use <span class="keyword">for</span> the instance <span class="keyword">or</span> <span class="class"><span class="keyword">class</span></span></div></pre></td></tr></table></figure></p>
<h1 id="三、-装饰器定义"><a href="#三、-装饰器定义" class="headerlink" title="三、 装饰器定义"></a>三、 装饰器定义</h1><p>装饰器就是一个接受一个可调用对象(被装饰的目标)的可调用对象,返回一个和源目标(被装饰对象)接受相同参数的可调用对象(装饰器)。</p>
<p>下面来解释下这段文字:</p>
<p>首先,什么是可调用对象呢?一个可调用对象在Python中就是包含一个叫 <code>call()</code> 方法的对象。具体来说就是可以是代码块、方法或者类。你也可以给你自己的类实现这个 <code>call()</code> 方法,这样你的类实例就变成了一个可调用对象,为了检测一个对象是否是可调用的,你可以在命令行中使用内建的 <code>callable()</code> 方法测试下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">callable(len)</div><div class="line"><span class="keyword">True</span></div><div class="line"></div><div class="line">callable(<span class="string">'123'</span>)</div><div class="line"><span class="keyword">False</span></div></pre></td></tr></table></figure></p>
<p>注意:<code>callable()</code> 方法在Python3.0被移除了,但是在Python3.2又重新加入了。</p>
<h1 id="四、-函数装饰器"><a href="#四、-函数装饰器" class="headerlink" title="四、 函数装饰器"></a>四、 函数装饰器</h1><p>一个函数装饰器是用来装饰一个函数或者方法的。假设我们想要在每个执行之前都先输出一个字符串”Yeah, it works!”。下面以非装饰器的方式来实现它。<br>原始方法:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">foo</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">'foo() here'</span></div><div class="line"></div><div class="line">foo()</div></pre></td></tr></table></figure></p>
<p>输出:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">foo() here</div></pre></td></tr></table></figure></p>
<p>下面以一种很丑陋的方式来实现我们的需求<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">original_foo = foo</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">decorated_foo</span><span class="params">()</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">'Yeah, it works!'</span></div><div class="line"> original_foo()</div><div class="line"></div><div class="line">foo = decorated_foo</div><div class="line">foo()</div></pre></td></tr></table></figure></p>
<p>输出:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Yeah, it works!</div><div class="line">foo() here</div></pre></td></tr></table></figure></p>
<p>这种方式有几个问题:</p>
<ul>
<li>它会增加很多工作</li>
<li>用中间名字污染了名字空间,如:<code>original_foo()</code> 和 <code>decorated_foo()</code></li>
<li>你不得不对每一个你想要增加这个需求的方法增加同样的逻辑</li>
</ul>
<p>然而用迭代器同样能够实现这样的需求,并且是可以重用的。如下所示:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">yeah_it_works</span><span class="params">(f)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">decorated</span><span class="params">(*args, **kwargs)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">'Yeah, it works'</span></div><div class="line"> <span class="keyword">return</span> f(*args, **kwargs)</div><div class="line"> <span class="keyword">return</span> decorated</div></pre></td></tr></table></figure></p>
<p>注:<code>yeah_it_works()</code> 是一个接受一个可调用对象f的函数。返回一个接受任何类型和数量的参数的可调用对象(嵌套函数 <code>decorated</code>)<br>现在我们可以在任何函数中重用它<br><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">@yeah_it_works</div><div class="line">def f1()</div><div class="line"> print 'f1() here'</div><div class="line"></div><div class="line">@yeah_it_works</div><div class="line">def f2()</div><div class="line"> print 'f3() here'</div><div class="line"></div><div class="line">@yeah_it_works</div><div class="line">def f3()</div><div class="line"> print 'f3() here'</div><div class="line"></div><div class="line">f1()</div><div class="line">f2()</div><div class="line">f3()</div></pre></td></tr></table></figure></p>
<p>输出:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">Yeah, it works</div><div class="line">f1() here</div><div class="line">Yeah, it works</div><div class="line">f2() here</div><div class="line">Yeah, it works</div><div class="line">f3() here</div></pre></td></tr></table></figure></p>
<h1 id="五、-类装饰器"><a href="#五、-类装饰器" class="headerlink" title="五、 类装饰器"></a>五、 类装饰器</h1><p>类装饰器是装饰整个类。它们在类定义时被替换。你能够对一个装饰器类增加或者减少方法,甚至将迭代器应用到所有的类的方法中。</p>
<p>假设我们要跟踪一个类抛出的所有异常,让我们假设已经有了一个函数装饰器叫 <code>track_exceptions_decorator</code> ,如果没有类装饰器,那么我们需要对每个方法应用这个函数装饰器或者采用 <code>metaclasses</code>。示例:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span><span class="params">(object)</span>:</span></div><div class="line"><span class="meta"> @track_exceptions_decorator</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">f1</span><span class="params">()</span>:</span></div><div class="line"> ...</div><div class="line"></div><div class="line"><span class="meta"> @track_exceptions_decorator</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">f2</span><span class="params">()</span>:</span></div><div class="line"> ...</div><div class="line"> .</div><div class="line"> .</div><div class="line"> .</div><div class="line"><span class="meta"> @track_exceptions_decorator</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">f100</span><span class="params">()</span>:</span></div><div class="line"> ...</div></pre></td></tr></table></figure></p>
<p>一个类装饰器可以达到相同的效果:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">track_exception</span><span class="params">(cls)</span>:</span></div><div class="line"> <span class="comment"># Get all callable attributes of the class</span></div><div class="line"> callable_attributes = {k:v <span class="keyword">for</span> k, v <span class="keyword">in</span> cls.__dict__.items() <span class="keyword">if</span> callable(v)}</div><div class="line"> <span class="comment"># Decorate each callable attribute of to the input class</span></div><div class="line"> <span class="keyword">for</span> name, func <span class="keyword">in</span> callable_attributes.items():</div><div class="line"> decorated = track_exceptions_decorator(func)</div><div class="line"> setattr(cls, name, decorated)</div><div class="line"> <span class="keyword">return</span> cls</div><div class="line"></div><div class="line"><span class="meta">@track_exceptions</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">A</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">f1</span><span class="params">(self)</span>:</span></div><div class="line"> print(<span class="string">'1'</span>)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">f2</span><span class="params">(self)</span>:</span></div><div class="line"> print(<span class="string">'2'</span>)</div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<h1 id="一、-概述"><a href="#一、-概述" class="headerlink" title="一、 概述"></a>一、 概述</h1><p>Python的装饰器是AOP编程的一种实现,其他很多语言也都支持装饰器模式。<br>注:AOP是指面向切面编程,详见 <a href="https://en.wikipedia.org/wiki/Aspect-oriented_programming">AOP概念</a></p>
<p>一个装饰器允许你增加、修改或者完全修改一个方法或者函数的逻辑。使用装饰器,将与业务无关的逻辑移到装饰器中,这将会让你的代码更加的干净紧凑。</p>
</summary>
<category term="python" scheme="https://tunsuy.github.io/categories/python/"/>
<category term="python" scheme="https://tunsuy.github.io/tags/python/"/>
</entry>
<entry>
<title>深入详解Java的NIO之Buffer</title>
<link href="https://tunsuy.github.io/2016/12/18/%E6%B7%B1%E5%85%A5%E8%AF%A6%E8%A7%A3Java%E7%9A%84NIO%E4%B9%8BBuffer/"/>
<id>https://tunsuy.github.io/2016/12/18/深入详解Java的NIO之Buffer/</id>
<published>2016-12-18T12:03:09.000Z</published>
<updated>2017-03-21T09:39:00.535Z</updated>
<content type="html"><![CDATA[<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>Buffer是一个写入通道或者从通道中读取的数据块。Buffer是Java I/O和Java NIO的主要差异之一。对于早期的Java I/O,数据是基于流的方式进行读写的,而现在NIO是基于buffer的方式来读写的。NIO中的通道跟流是一个意思。</p>
<h1 id="二、-Buffer特性"><a href="#二、-Buffer特性" class="headerlink" title="二、 Buffer特性"></a>二、 Buffer特性</h1><ul>
<li>Buffer是Java NIO的基本构成组件。</li>
<li>Buffer提供了一个固定大小的容器来读写数据。</li>
<li>每个buffer都是可读的,但是只有被选中的buffer才是可写的。</li>
<li>Buffer是通道的端点。</li>
<li>在一个只读buffer中,内容是不可变的,但是它的 mark, position, limit 是可变的。</li>
<li>默认的buffer是线程不安全的</li>
</ul>
<a id="more"></a>
<p>Buffer的读写模式如图所示:</p>
<img src="/2016/12/18/深入详解Java的NIO之Buffer/JavaNIOBuffer.png" alt="JavaNIOBuffer.png" title="">
<h1 id="三、-Buffer的类型"><a href="#三、-Buffer的类型" class="headerlink" title="三、 Buffer的类型"></a>三、 Buffer的类型</h1><p>每个基本类型都有一个buffer类型想对应。所有的buffer类都实现了 Buffer 接口。大部分使用的类型是 <code>ByteBuffer</code>。下面是在Java NIO包中可用的buffer类型:</p>
<ul>
<li>ByteBuffer</li>
<li>CharBuffer</li>
<li>ShortBuffer</li>
<li>IntBuffer</li>
<li>LongBuffer</li>
<li>FloatBuffer</li>
<li>DoubleBuffer</li>
<li>MappedByteBuffer<br>如图所示:</li>
</ul>
<img src="/2016/12/18/深入详解Java的NIO之Buffer/java-nio-buffer-classes.png" alt="java-nio-buffer-classes.png" title="">
<p>这些类都不能直接实例化,因为他们都是抽象类,但是每个都包含了静态工厂方法来创建适当类的实例。</p>
<h1 id="四、-Buffer的基本属性"><a href="#四、-Buffer的基本属性" class="headerlink" title="四、 Buffer的基本属性"></a>四、 Buffer的基本属性</h1><p>Buffer有四个基本的属性,下面简单介绍下</p>
<p>1、容量Capacity<br>Buffer是一个固定大小的类型,这个最大固定大小就叫做capacity。一旦这个buffer是满了,那么就应该在写入它之前清空它。一旦capacity被设置,在它的生命周期内就不能再被改变了。</p>
<p>2、上界Limit<br>对于写模式,limit 是等于buffer的 capacity的。对于读模式,limit是buffer的最后填充索引的下一个。当buffer被写入时,limit会自增。limit满足下面的条件: <code>0 <= limit <= capacity</code></p>
<p>3、位置Position<br>Position是buffer中下一个要被读或者写的元素的索引。当buffer被创建时,position被初始化为0。当读写时,position是自增的。position总是处于0和limit之间。</p>
<p>4、标记Mark<br>Mark就像是buffer中为position设置的书签一样。调用 <code>mark()</code> 来设定 <code>mark = position</code>,调用 <code>reset()</code> 来设定 <code>position = mark</code> 。</p>
<h1 id="五、-Buffer相关操作"><a href="#五、-Buffer相关操作" class="headerlink" title="五、 Buffer相关操作"></a>五、 Buffer相关操作</h1><p>1、flip()<br>该方法被用来为 <code>get()</code> 操作准备一个 buffer ,或者是用来准备一个新的写序列。<code>flip()</code> 方法设置 <code>limit</code> 等于buffer的当前 <code>position</code>,然后将 <code>position = 0</code>。</p>
<p>2、clear()<br>该方法用来为 <code>put()</code> 操作准备一个 buffer,或者用来准备一个新的读序列。<code>clear()</code> 方法设置 <code>limit = capacity</code> 和 <code>position = 0</code>。</p>
<p>3、rewind()<br>该方法用来再次读取数据。它设置 <code>position = 0</code>。</p>
<h1 id="六、-Buffer的创建"><a href="#六、-Buffer的创建" class="headerlink" title="六、 Buffer的创建"></a>六、 Buffer的创建</h1><p>Buffer是通过 <code>allocation</code> 或者 <code>wrapping</code> 来创建的。<code>allocation</code> 创建一个buffer对象,并且分配指定容量的私有空间来容纳数据元素。<code>wrapping</code> 创建一个buffer对象,但是不分配任何的空间来保存数据元素,它使用你提供的数组空间来存储buffer的数据元素。</p>
<p>举例,创建一个能够容纳100个字符的 <code>CharBuffer</code>:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">CharBuffer charBuffer = CharBuffer.allocate (<span class="number">100</span>);</div></pre></td></tr></table></figure></p>
<p>上面这种方式,隐式的从堆上分配了一个char数组来存储这100个字符。如果你想要使用你自己创建的数组来存储,则使用 <code>wrap()</code> 方法:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">char</span> [] myArray = <span class="keyword">new</span> <span class="keyword">char</span> [<span class="number">100</span>];</div><div class="line">CharBuffer charbuffer = CharBuffer.wrap (myArray);</div></pre></td></tr></table></figure></p>
<p>这意味着通过调用 <code>put()</code> 方法对buffer做的变更都会反映到这个数组中。同样的,对这个数组做的任何改变都将反映到buffer中。</p>
<p>你也能够通过指定偏移和长度值作为 <code>position</code> 和 <code>limit</code> 来构造buffer:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">char</span> [] myArray = <span class="keyword">new</span> <span class="keyword">char</span> [<span class="number">100</span>];</div><div class="line">CharBuffer charbuffer = CharBuffer.wrap (myArray , <span class="number">12</span>, <span class="number">42</span>);</div></pre></td></tr></table></figure></p>
<p>注:</p>
<ul>
<li>这个方法不会创建一个buffer,而是指向改数组的一个子集</li>
<li>这个方法拥有这个数组整体的访问权,偏移和长度参数仅仅只是设置初始状态。</li>
<li>调用这个buffer的 <code>clear()</code> 方法将使它的 <code>limit</code> 范围变为这个数组的所有元素</li>
<li>调用 <code>slice()</code> 方法能够创建一个buffer指向这个数组的一部分</li>
</ul>
<p>通过 <code>allocate()</code> 或者 <code>wrap()</code> 方法创建的buffer都不是直接在内存中开辟空间的,而是指向一个已有的数组。使用 <code>hasArray()</code> 方法告诉你这个buffer是否有一个相关联的数组,如果返回 <code>true</code>,则可以使用 <code>array()</code> 方法返回一个数组的引用给buffer。如果返回 <code>false</code>,就不要调用 <code>array()</code> 和 <code>arrayOffset()</code>,否则将抛出 <code>UnsupportedOperationException</code> 异常。</p>
<h1 id="七、访问Buffer"><a href="#七、访问Buffer" class="headerlink" title="七、访问Buffer"></a>七、访问Buffer</h1><p>每个buffer类都提供了 <code>get()</code> 和 <code>put()</code> 方法来访问buffer,比如:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="class"><span class="keyword">class</span> <span class="title">ByteBuffer</span> <span class="keyword">extends</span> <span class="title">Buffer</span> <span class="keyword">implements</span> <span class="title">Comparable</span></span></div><div class="line">{</div><div class="line"> <span class="comment">// This is a partial API listing</span></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">byte</span> <span class="title">get</span><span class="params">()</span></span>;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">byte</span> <span class="title">get</span> <span class="params">(<span class="keyword">int</span> index)</span></span>;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> ByteBuffer <span class="title">put</span> <span class="params">(<span class="keyword">byte</span> b)</span></span>;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> ByteBuffer <span class="title">put</span> <span class="params">(<span class="keyword">int</span> index, <span class="keyword">byte</span> b)</span></span>;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p> 访问API有绝对和相对之分,调用相对访问API时,<code>position</code> 会自动的向前进一个,调用绝对访问API时,指定索引位置的内容会被覆盖掉或者按指定索引返回内容。</p>
<h1 id="八、-Buffer的比较"><a href="#八、-Buffer的比较" class="headerlink" title="八、 Buffer的比较"></a>八、 Buffer的比较</h1><p> 所有的buffer类都提供了一个自定义的 <code>equals()</code> 方法 和 <code>compareTo()</code> 方法,比如:<br> <figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (buffer1.equals (buffer2)) {</div><div class="line"> doSomething();</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>两个buffer按照下面的规则进行比较:</p>
<ul>
<li>包含的对象必须是同等类型的</li>
<li>两个buffer必须包含有相等数量的元素, 也就是说,buffer的容量不需要相同,buffer中剩余的数据索引不需要相同,但是buffer中剩余元素的数量(从 <code>position</code> 到 <code>limit</code>)必须相同</li>
<li>从 <code>get()</code> 返回的剩余数据元素的序列在每个缓冲区中必须相同。</li>
</ul>
<p>buffer还支持使用<code>compareTo()</code>方法进行词典比较。该方法返回一个正数、负数或者0。比如:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> (buffer1.compareTo (buffer2) > <span class="number">0</span>) {</div><div class="line"> doSomething();</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>参考文章 <a href="http://howtodoinjava.com/java-7/nio/java-nio-2-0-working-with-buffers/" target="_blank" rel="external">Working With Buffers</a></p>
]]></content>
<summary type="html">
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>Buffer是一个写入通道或者从通道中读取的数据块。Buffer是Java I/O和Java NIO的主要差异之一。对于早期的Java I/O,数据是基于流的方式进行读写的,而现在NIO是基于buffer的方式来读写的。NIO中的通道跟流是一个意思。</p>
<h1 id="二、-Buffer特性"><a href="#二、-Buffer特性" class="headerlink" title="二、 Buffer特性"></a>二、 Buffer特性</h1><ul>
<li>Buffer是Java NIO的基本构成组件。</li>
<li>Buffer提供了一个固定大小的容器来读写数据。</li>
<li>每个buffer都是可读的,但是只有被选中的buffer才是可写的。</li>
<li>Buffer是通道的端点。</li>
<li>在一个只读buffer中,内容是不可变的,但是它的 mark, position, limit 是可变的。</li>
<li>默认的buffer是线程不安全的</li>
</ul>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
</entry>
<entry>
<title>深入详解Java的NIO之Channel</title>
<link href="https://tunsuy.github.io/2016/12/13/%E6%B7%B1%E5%85%A5%E8%AF%A6%E8%A7%A3Java%E7%9A%84NIO%E4%B9%8BChannel/"/>
<id>https://tunsuy.github.io/2016/12/13/深入详解Java的NIO之Channel/</id>
<published>2016-12-13T09:30:58.000Z</published>
<updated>2017-03-21T09:43:58.901Z</updated>
<content type="html"><![CDATA[<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>通道是java.nio在buffer之后的第二个主要创新。通道提供与I/O服务的直接连接。通道是一种在字节缓冲区和通道另一端的实体(通常是文件或套接字)之间高效传输数据的介质。通常通道与操作系统文件描述符具有一对一的关系。通道类提供了支持平台无关性所需的抽象,但仍然具有对现代操作系统的本机I/O建模的能力。通道是网关,通过它可以以最小的开销访问操作系统的本地I/O服务。</p>
<a id="more"></a>
<h1 id="二、-Channel接口"><a href="#二、-Channel接口" class="headerlink" title="二、 Channel接口"></a>二、 Channel接口</h1><p>通道与缓冲区不同,通道 API 主要由接口指定。不同的操作系统上通道实现(Channel Implementation)会有根本性的差异,所以通道 API 仅仅描述了可以做什么。因此很自然地,通道实现经常使用操作系统的本地代码。通道接口允许您以一种受控且可移植的方式来访问底层的 I/O服务。</p>
<p>通道类簇图如下:</p>
<img src="/2016/12/13/深入详解Java的NIO之Channel/nio-channel.png" alt="nio-channel.png" title="">
<p><code>Channel</code> 作为NIO通道类的顶层类,是一个接口,如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">package</span> java.nio.channels;</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Channel</span></span></div><div class="line">{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">isOpen</span><span class="params">()</span></span>;</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">close</span><span class="params">()</span> <span class="keyword">throws</span> IOException</span>;</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>从 Channel 接口引申出的其他接口都是面向字节的子接口,包括 Writable ByteChannel 和ReadableByteChannel。这也正好支持了我们之前所学的:通道只能在字节缓冲区上操作。</p>
<h1 id="三、-打开通道"><a href="#三、-打开通道" class="headerlink" title="三、 打开通道"></a>三、 打开通道</h1><p>作为我们都知道的,I/O 分为两大类:文件I/O 和 流I/O。因此,这也就对应两种通道类型:<code>FileChannel</code>类 和 <code>SocketChannel</code>类</p>
<p><code>FileChannel</code> 对象只能通过在一个打开的<code>RandomAccessFile</code>,<code>FileInputStream</code> 或 <code>FileOutputStream</code> 对象上调用 <code>getChannel()</code> 方法获得。示例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">RandomAccessFile raf = <span class="keyword">new</span> RandomAccessFile (<span class="string">"somefile"</span>, <span class="string">"r"</span>);</div><div class="line">FileChannel fc = raf.getChannel();</div></pre></td></tr></table></figure></p>
<p>相对于 <code>FileChannel</code>,<code>SocketChannel</code> 自身有工厂方法来创建新的socket通道,示例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//How to open SocketChannel</span></div><div class="line">SocketChannel sc = SocketChannel.open();</div><div class="line">sc.connect(<span class="keyword">new</span> InetSocketAddress(<span class="string">"somehost"</span>, someport));</div><div class="line"></div><div class="line"><span class="comment">//How to open ServerSocketChannel</span></div><div class="line">ServerSocketChannel ssc = ServerSocketChannel.open();</div><div class="line">ssc.socket().bind (<span class="keyword">new</span> InetSocketAddress (somelocalport));</div><div class="line"></div><div class="line"><span class="comment">//How to open DatagramChannel</span></div><div class="line">DatagramChannel dc = DatagramChannel.open();</div></pre></td></tr></table></figure></p>
<h1 id="四、-使用通道"><a href="#四、-使用通道" class="headerlink" title="四、 使用通道"></a>四、 使用通道</h1><p>通过实现以下接口的相关方法,可以实现读写操作:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ReadableByteChannel</span> <span class="keyword">extends</span> <span class="title">Channel</span></span></div><div class="line">{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span> <span class="params">(ByteBuffer dst)</span> <span class="keyword">throws</span> IOException</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">WritableByteChannel</span> <span class="keyword">extends</span> <span class="title">Channel</span></span></div><div class="line">{</div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">write</span> <span class="params">(ByteBuffer src)</span> <span class="keyword">throws</span> IOException</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">ByteChannel</span> <span class="keyword">extends</span> <span class="title">ReadableByteChannel</span>, <span class="title">WritableByteChannel</span></span></div><div class="line">{</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>通道可以是单向或者双向的,一个通道类只实现 <code>ReadableByteChannel</code> 接口,那么它就是单向的只读通道;如果一个通道类只实现 <code>WritableByteChannel</code> 接口,那么它就是单向的只写通道。如果一个通道类两者都实现了,那么它就是双向的可读写通道。</p>
<p>文件通道和socket通道都实现了这三个接口,都是双向通道。</p>
<p>对于文件通道来说,有一点需要注意:通过 <code>FileInputStream</code> 对象的 <code>getChannel()</code> 方法获得的 <code>FileChannel</code> 对象是只读的,但是因为 <code>FileChannel</code> 实现了 <code>ByteChannel</code> ,所以从接口层面上是双向的。如果在 <code>FileChannel</code> 上调用 <code>write()</code> 方法将抛出 <code>NonWritableChannelException</code> 异常。因此,请记住,当通道连接到特定的I/O服务时,通道实例的功能将受其连接的服务的特性约束:连接到只读文件的通道实例无法写入,即使该通道实例所属的类可能具有write()方法。示例:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">FileInputStream input = <span class="keyword">new</span> FileInputStream (<span class="string">"readOnlyFile.txt"</span>);</div><div class="line">FileChannel channel = input.getChannel();</div><div class="line"></div><div class="line"><span class="comment">// This will compile but will throw an IOException</span></div><div class="line"><span class="comment">// because the underlying file is read-only</span></div><div class="line">channel.write (buffer);</div></pre></td></tr></table></figure></p>
<p><code>ByteChannel</code> 的 <code>read()</code> 和 <code>write()</code> 方法以ByteBuffer对象作为参数。返回传输的字节数,可以小于缓冲区中的字节数,甚至为零。如果执行了部分传输,则可以将缓冲区重新传递到通道,以在其中断的地方继续传输数据。如此重复,直到缓冲区的 <code>hasRemaining()</code> 方法返回false。</p>
<h1 id="五、工作模式"><a href="#五、工作模式" class="headerlink" title="五、工作模式"></a>五、工作模式</h1><p>通道可以工作在阻塞或非阻塞模式。非阻塞模式下,所请求的操作可以立即完成,也可以返回表示什么都没有完成的结果。</p>
<p>只有面向流的信道,例如套接字和管道,可以被置于非阻塞模式。</p>
<p><code>FileChannel</code> 总是阻塞的,因此不能被设置成非阻塞模式</p>
<h1 id="六、-关闭通道"><a href="#六、-关闭通道" class="headerlink" title="六、 关闭通道"></a>六、 关闭通道</h1><p>想要关闭通道,需要使用 <code>close()</code> 方法。一个被关闭的通道不能再被使用。</p>
<p>可以对一个通道多次调用 <code>close()</code> 方法,对一个已关闭的通道调用 <code>close()</code> 方法时,什么都不做,直接返回。</p>
<p>可以使用 <code>isOpen()</code> 放来检查一个通道的打开状态。</p>
]]></content>
<summary type="html">
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>通道是java.nio在buffer之后的第二个主要创新。通道提供与I/O服务的直接连接。通道是一种在字节缓冲区和通道另一端的实体(通常是文件或套接字)之间高效传输数据的介质。通常通道与操作系统文件描述符具有一对一的关系。通道类提供了支持平台无关性所需的抽象,但仍然具有对现代操作系统的本机I/O建模的能力。通道是网关,通过它可以以最小的开销访问操作系统的本地I/O服务。</p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
</entry>
<entry>
<title>自编译mongo3.2测试</title>
<link href="https://tunsuy.github.io/2016/12/09/%E8%87%AA%E7%BC%96%E8%AF%91mongo3-2%E6%B5%8B%E8%AF%95/"/>
<id>https://tunsuy.github.io/2016/12/09/自编译mongo3-2测试/</id>
<published>2016-12-09T02:13:44.000Z</published>
<updated>2017-03-14T05:14:10.767Z</updated>
<content type="html"><![CDATA[<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>公司将mongodb删除机制进行了自定义,改动了相关源码,故需要配合测试mongodb的功能是否正常</p>
<h1 id="二、-检查服务器"><a href="#二、-检查服务器" class="headerlink" title="二、 检查服务器"></a>二、 检查服务器</h1><h2 id="1、-检查mongo启动情况"><a href="#1、-检查mongo启动情况" class="headerlink" title="1、 检查mongo启动情况"></a>1、 检查mongo启动情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">netstat -anpt | grep mong* | grep <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span></div><div class="line">mongoa --port <span class="number">27020</span>(相应的端口)——进入mongo</div></pre></td></tr></table></figure>
<a id="more"></a>
<h2 id="2、-集群数据库情况"><a href="#2、-集群数据库情况" class="headerlink" title="2、 集群数据库情况"></a>2、 集群数据库情况</h2><p><code>cat /etc/hosts | grep db</code><br>——3个数据库分片(sh0,sh1,sh2)<br>——2个统计分片(statis-sh0,statis-sh1)</p>
<p>也可以这样查看:</p>
<ul>
<li>进入mongos(169.100下)</li>
<li>进入config数据库中<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.shards.find()</div></pre></td></tr></table></figure>
</li>
</ul>
<h1 id="三、-主备切换测试"><a href="#三、-主备切换测试" class="headerlink" title="三、 主备切换测试"></a>三、 主备切换测试</h1><h2 id="1、-shard0:"><a href="#1、-shard0:" class="headerlink" title="1、 shard0:"></a>1、 shard0:</h2><ul>
<li>随便进入一个sh0节点服务器<br>通过/etc/hosts可以查看到(169.100下)</li>
<li>查看sh0集群情况: <code>rs.status()</code><br>找到sh0主节点服务器,并进入, 执行<code>rs.stepDown()</code>——切换主备</li>
<li>检查主备切换是否成功<code>rs.status()</code>——集群情况</li>
</ul>
<h2 id="2、-依次对shard1、shard2集群进行主备切换"><a href="#2、-依次对shard1、shard2集群进行主备切换" class="headerlink" title="2、 依次对shard1、shard2集群进行主备切换"></a>2、 依次对shard1、shard2集群进行主备切换</h2><p>也可以关注下主备切换的日志情况<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">find / -name mongo*.log(找到各mongo进程log)</div></pre></td></tr></table></figure></p>
<h2 id="四、-chunk迁移测试"><a href="#四、-chunk迁移测试" class="headerlink" title="四、 chunk迁移测试"></a>四、 chunk迁移测试</h2><p>(以迁移customer.info表为例)</p>
<h2 id="1、-查看迁移之前的chunk情况"><a href="#1、-查看迁移之前的chunk情况" class="headerlink" title="1、 查看迁移之前的chunk情况"></a>1、 查看迁移之前的chunk情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line">use config</div><div class="line">db.chunks.find({<span class="string">"_id"</span>:<span class="regexp">/customer.info-did_10000*/</span>, <span class="string">"ns"</span>:<span class="string">"customer.info"</span>}).pretty()</div><div class="line">{</div><div class="line"> <span class="string">"_id"</span> : <span class="string">"customer.info-did_10000custmid_100"</span>,</div><div class="line"> <span class="string">"lastmod"</span> : Timestamp(<span class="number">135</span>, <span class="number">0</span>),</div><div class="line"> <span class="string">"lastmodEpoch"</span> : ObjectId(<span class="string">"54ae3eaaf3a2f2e7d3726927"</span>),</div><div class="line"> <span class="string">"ns"</span> : <span class="string">"customer.info"</span>,</div><div class="line"> <span class="string">"min"</span> : {</div><div class="line"> <span class="string">"did"</span> : NumberLong(<span class="number">10000</span>),</div><div class="line"> <span class="string">"custmid"</span> : NumberLong(<span class="number">100</span>)</div><div class="line"> },</div><div class="line"> <span class="string">"max"</span> : {</div><div class="line"> <span class="string">"did"</span> : NumberLong(<span class="number">10000</span>),</div><div class="line"> <span class="string">"custmid"</span> : NumberLong(<span class="number">1948</span>)</div><div class="line"> },</div><div class="line"> <span class="string">"shard"</span> : <span class="string">"shard0"</span></div><div class="line">}</div><div class="line">{</div><div class="line"> <span class="string">"_id"</span> : <span class="string">"customer.info-did_10000custmid_1948"</span>,</div><div class="line"> <span class="string">"lastmod"</span> : Timestamp(<span class="number">136</span>, <span class="number">0</span>),</div><div class="line"> <span class="string">"lastmodEpoch"</span> : ObjectId(<span class="string">"54ae3eaaf3a2f2e7d3726927"</span>),</div><div class="line"> <span class="string">"ns"</span> : <span class="string">"customer.info"</span>,</div><div class="line"> <span class="string">"min"</span> : {</div><div class="line"> <span class="string">"did"</span> : NumberLong(<span class="number">10000</span>),</div><div class="line"> <span class="string">"custmid"</span> : NumberLong(<span class="number">1948</span>)</div><div class="line"> },</div><div class="line"> <span class="string">"max"</span> : {</div><div class="line"> <span class="string">"did"</span> : NumberLong(<span class="number">10039</span>),</div><div class="line"> <span class="string">"custmid"</span> : NumberLong(<span class="number">451</span>)</div><div class="line"> },</div><div class="line"> <span class="string">"shard"</span> : <span class="string">"shard2"</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="2、-执行迁移命令"><a href="#2、-执行迁移命令" class="headerlink" title="2、 执行迁移命令"></a>2、 执行迁移命令</h2><figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"> sh.moveChunk("customer.info", {did:10000,custmid:100}, "shard1")</div><div class="line">#或者</div><div class="line"> db.adminCommand({moveChunk:"customer.info", find:{did:10000,custmid:100}, to:"shard1"})</div></pre></td></tr></table></figure>
<p>PS:一般执行命令都有这两种方式</p>
<ul>
<li>sh. 方式可以不用切换数据库执行</li>
<li>db. 方式必须在admin数据库下<br>PS:迁移条件必须包含片键</li>
</ul>
<h2 id="3、-查看迁移情况"><a href="#3、-查看迁移情况" class="headerlink" title="3、 查看迁移情况"></a>3、 查看迁移情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">use config</div><div class="line">db.chunks.find({<span class="string">"_id"</span>:<span class="regexp">/customer.info-did_10000*/</span>, <span class="string">"ns"</span>:<span class="string">"customer.info"</span>}).pretty()</div></pre></td></tr></table></figure>
<p>也可以关注下主备切换的日志情况<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">find / -name mongo*.log(找到各mongo进程log)</div></pre></td></tr></table></figure></p>
<h1 id="五、-chunk分裂"><a href="#五、-chunk分裂" class="headerlink" title="五、 chunk分裂"></a>五、 chunk分裂</h1><h2 id="1、-查看chunk分裂前情况"><a href="#1、-查看chunk分裂前情况" class="headerlink" title="1、 查看chunk分裂前情况"></a>1、 查看chunk分裂前情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">use config</div><div class="line">db.chunks.find({<span class="string">"_id"</span>:<span class="regexp">/customer.info-did_10000*/</span>, <span class="string">"ns"</span>:<span class="string">"customer.info"</span>}).pretty()</div></pre></td></tr></table></figure>
<h2 id="2、-指定分裂点"><a href="#2、-指定分裂点" class="headerlink" title="2、 指定分裂点"></a>2、 指定分裂点</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">sh.splitAt(<span class="string">"customer.info"</span>, {<span class="string">"did"</span>:<span class="number">10000</span>, <span class="string">"custmid"</span>:<span class="number">500</span>})</div></pre></td></tr></table></figure>
<h2 id="3、-检查分裂情况"><a href="#3、-检查分裂情况" class="headerlink" title="3、 检查分裂情况"></a>3、 检查分裂情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">db.chunks.find({<span class="string">"_id"</span>:<span class="regexp">/customer.info-did_10000*/</span>, <span class="string">"ns"</span>:<span class="string">"customer.info"</span>}).pretty()</div></pre></td></tr></table></figure>
<h1 id="六、-其他"><a href="#六、-其他" class="headerlink" title="六、 其他"></a>六、 其他</h1><h2 id="1、-重启moa服务"><a href="#1、-重启moa服务" class="headerlink" title="1、 重启moa服务"></a>1、 重启moa服务</h2><p>PS:因为有的服务可能有缓存,所有在操作了数据库之后需要重启服务才能重新读取数据库数据</p>
<h2 id="2、-检查各模块功能情况"><a href="#2、-检查各模块功能情况" class="headerlink" title="2、 检查各模块功能情况"></a>2、 检查各模块功能情况</h2><p>PS:因为涉及到php、go、c的mongo库,所以需要覆盖以下测试</p>
<ul>
<li>web端、PC端、运营、统计</li>
<li>主要关注点<br>——涉及数据库读写操作的功能<br>——大量读写操作情况<br>——导入导出情况<br>——私有云服务器接入情况<br>——数据删除情况<br>——数据恢复情况</li>
</ul>
<h2 id="3、-检查服务器资源情况"><a href="#3、-检查服务器资源情况" class="headerlink" title="3、 检查服务器资源情况"></a>3、 检查服务器资源情况</h2><p>PS:可以写一个小的脚本(超过一定的阈值则输出到log里面),然后一直在后台运行,然后操作业务<br>最后测试完之后,检查log文件中的资源记录情况</p>
]]></content>
<summary type="html">
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>公司将mongodb删除机制进行了自定义,改动了相关源码,故需要配合测试mongodb的功能是否正常</p>
<h1 id="二、-检查服务器"><a href="#二、-检查服务器" class="headerlink" title="二、 检查服务器"></a>二、 检查服务器</h1><h2 id="1、-检查mongo启动情况"><a href="#1、-检查mongo启动情况" class="headerlink" title="1、 检查mongo启动情况"></a>1、 检查mongo启动情况</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">netstat -anpt | grep mong* | grep <span class="number">0.0</span><span class="number">.0</span><span class="number">.0</span></div><div class="line">mongoa --port <span class="number">27020</span>(相应的端口)——进入mongo</div></pre></td></tr></table></figure>
</summary>
<category term="mongodb" scheme="https://tunsuy.github.io/categories/mongodb/"/>
<category term="linux" scheme="https://tunsuy.github.io/tags/linux/"/>
<category term="mongodb" scheme="https://tunsuy.github.io/tags/mongodb/"/>
</entry>
<entry>
<title>App三方交互逻辑实践</title>
<link href="https://tunsuy.github.io/2016/11/16/App%E4%B8%89%E6%96%B9%E4%BA%A4%E4%BA%92%E9%80%BB%E8%BE%91%E5%AE%9E%E8%B7%B5/"/>
<id>https://tunsuy.github.io/2016/11/16/App三方交互逻辑实践/</id>
<published>2016-11-16T07:28:06.000Z</published>
<updated>2017-03-14T05:14:09.342Z</updated>
<content type="html"><![CDATA[<p>项目地址:<br><a href="https://github.com/tunsuy/AppInteractive" target="_blank" rel="external">https://github.com/tunsuy/AppInteractive</a></p>
<h1 id="一、-背景介绍"><a href="#一、-背景介绍" class="headerlink" title="一、 背景介绍"></a>一、 背景介绍</h1><p>之前有一个公司项目与第三方应用交互的逻辑需要全面测试,那么怎样才能测试到整个交互的所有环节呢</p>
<p>那就是需要一个实际的第三方,并且这个第三方需要包括server端和app端</p>
<a id="more"></a>
<p>既然是测试,那么我们只需要将公司项目跟三方有交互的接口做个全面的测试即可,不在乎这个三方是怎样的形式</p>
<p>那么这就好办了,自己实现一个这个的三方项目</p>
<h1 id="二、-交互逻辑图"><a href="#二、-交互逻辑图" class="headerlink" title="二、 交互逻辑图"></a>二、 交互逻辑图</h1><p>1、口袋助理跳转第三方app逻辑示意图:</p>
<img src="/2016/11/16/App三方交互逻辑实践/moa2app.jpg" alt="moa2app.jpg" title="">
<p>2、第三方跳转口袋助理逻辑示意图:</p>
<img src="/2016/11/16/App三方交互逻辑实践/app2moa.jpg" alt="app2moa.jpg" title="">
<h1 id="三、-实现"><a href="#三、-实现" class="headerlink" title="三、 实现"></a>三、 实现</h1><p>server端:go语言实现<br>app端:iOS</p>
<p>具体实现见github项目地址</p>
<p>根据相关协议从代码层面详细的测试每个交互接口:</p>
<ul>
<li>兼容性</li>
<li>容错性</li>
<li>健壮性</li>
</ul>
]]></content>
<summary type="html">
<p>项目地址:<br><a href="https://github.com/tunsuy/AppInteractive">https://github.com/tunsuy/AppInteractive</a></p>
<h1 id="一、-背景介绍"><a href="#一、-背景介绍" class="headerlink" title="一、 背景介绍"></a>一、 背景介绍</h1><p>之前有一个公司项目与第三方应用交互的逻辑需要全面测试,那么怎样才能测试到整个交互的所有环节呢</p>
<p>那就是需要一个实际的第三方,并且这个第三方需要包括server端和app端</p>
</summary>
<category term="ios" scheme="https://tunsuy.github.io/categories/ios/"/>
<category term="ios" scheme="https://tunsuy.github.io/tags/ios/"/>
<category term="go" scheme="https://tunsuy.github.io/tags/go/"/>
<category term="服务端" scheme="https://tunsuy.github.io/tags/%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
</entry>
<entry>
<title>mongodb集群3.0.X升级3.2.X</title>
<link href="https://tunsuy.github.io/2016/10/15/mongodb%E9%9B%86%E7%BE%A43-0-X%E5%8D%87%E7%BA%A73-2-X/"/>
<id>https://tunsuy.github.io/2016/10/15/mongodb集群3-0-X升级3-2-X/</id>
<published>2016-10-15T02:07:58.000Z</published>
<updated>2017-03-14T05:14:10.036Z</updated>
<content type="html"><![CDATA[<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>该文章主要根据官方英文文档来操作的</p>
<p>官方文档:<a href="https://docs.mongodb.com/manual/release-notes/3.2-upgrade/" target="_blank" rel="external">https://docs.mongodb.com/manual/release-notes/3.2-upgrade/</a></p>
<h1 id="二、-升级准备工作"><a href="#二、-升级准备工作" class="headerlink" title="二、 升级准备工作"></a>二、 升级准备工作</h1><p>1、下载二进制文件<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">wget https:<span class="comment">//www.mongodb.com/dr/fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.2.10.tgz</span></div></pre></td></tr></table></figure></p>
<p>2、将该文件放入所有的节点服务器下并解压<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">tar -zxvf XXXX</div></pre></td></tr></table></figure></p>
<a id="more"></a>
<p>3、备份configdb(可选)<br>在每个configdb服务器执行:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">cp -r /home/moa/db/configdb/data/ <span class="regexp">/home/m</span>oa/db/configdb/data_bak/</div></pre></td></tr></table></figure></p>
<p>4、关闭均衡器</p>
<ul>
<li>查看均衡器状态: <code>sh.getBalancerState()</code></li>
<li>关闭: <code>sh.stopBalancer()</code></li>
<li>检查数据没有在迁移<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">use config</div><div class="line"><span class="keyword">while</span>( sh.isBalancerRunning() ) {</div><div class="line"> print(<span class="string">"waiting..."</span>);</div><div class="line"> sleep(<span class="number">1000</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<h1 id="三、-开始升级"><a href="#三、-开始升级" class="headerlink" title="三、 开始升级"></a>三、 开始升级</h1><h2 id="1、-首先升级分片0"><a href="#1、-首先升级分片0" class="headerlink" title="1、 首先升级分片0"></a>1、 首先升级分片0</h2><p>1.1 升级分片0集群中的secondary节点</p>
<ul>
<li>停掉该mongod0进程—kill pid</li>
<li><p>使用最新mongodb进程替换掉mongod0 </p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">#备份:</div><div class="line">mv /usr/local/mongodb/bin/mongod0 /usr/local/mongodb/bin/mongod0.bak </div><div class="line"></div><div class="line">#替换:</div><div class="line">cp /home/mongodb-linux-x86_64-rhel62-3.2.10/bin/mongod /usr/local/mongodb/bin/mongod0</div></pre></td></tr></table></figure>
</li>
<li><p>启动该mongod0进程—<code>/etc/init.d/mongod0 restart</code></p>
</li>
</ul>
<p>按照1.1方法依次升级其他的secondary节点<br>ps:在确认上一个secondary状态正常的情况再进行: <code>rs.status()</code></p>
<p>1.2 升级分片0集群中的primary节点</p>
<ul>
<li>停掉primary—<code>rs.stepDown()</code></li>
<li>检查是否产生了新的primary—<code>rs.status()</code><br>ps:在其他两个节点查看</li>
<li>按照1的方法升级其他分片</li>
</ul>
<p>1.3 更新选举协议<br>在primary节点执行以下操作:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">cfg=rs.conf();</div><div class="line">cfg.protocolVersion=<span class="number">1</span>;</div><div class="line">rs.reconfig(cfg);</div></pre></td></tr></table></figure></p>
<h2 id="2、-升级分片1"><a href="#2、-升级分片1" class="headerlink" title="2、 升级分片1"></a>2、 升级分片1</h2><p>按照1中的方法进行</p>
<h2 id="3、-升级config-服务器"><a href="#3、-升级config-服务器" class="headerlink" title="3、 升级config 服务器"></a>3、 升级config 服务器</h2><ul>
<li>顺序:需要按照mongos中配置的逆序升级<figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">cat /home/moa/db/mongos/mongos.conf</div><div class="line">——configdb=config0.moadb.com,config1.moadb.com,config2.moadb.com</div></pre></td></tr></table></figure>
</li>
</ul>
<p>所以升级顺序应该是:config2、config1、config0</p>
<ul>
<li>升级:按照1中方法进行(只是这里变成了mongocfgd)</li>
</ul>
<h2 id="4、-升级mognos服务器"><a href="#4、-升级mognos服务器" class="headerlink" title="4、 升级mognos服务器"></a>4、 升级mognos服务器</h2><p>按照1中方法进行(只是这样换成了mongos)</p>
<h2 id="5、-替换掉所以的mongo工具(可选)"><a href="#5、-替换掉所以的mongo工具(可选)" class="headerlink" title="5、 替换掉所以的mongo工具(可选)"></a>5、 替换掉所以的mongo工具(可选)</h2><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">cp ./mongodb-linux-x86_64-rhel62<span class="number">-3.2</span><span class="number">.10</span>/bin<span class="comment">/* /usr/local/mongodb/bin/</span></div></pre></td></tr></table></figure>
]]></content>
<summary type="html">
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>该文章主要根据官方英文文档来操作的</p>
<p>官方文档:<a href="https://docs.mongodb.com/manual/release-notes/3.2-upgrade/">https://docs.mongodb.com/manual/release-notes/3.2-upgrade/</a></p>
<h1 id="二、-升级准备工作"><a href="#二、-升级准备工作" class="headerlink" title="二、 升级准备工作"></a>二、 升级准备工作</h1><p>1、下载二进制文件<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">wget https:<span class="comment">//www.mongodb.com/dr/fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel62-3.2.10.tgz</span></div></pre></td></tr></table></figure></p>
<p>2、将该文件放入所有的节点服务器下并解压<br><figure class="highlight js"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">tar -zxvf XXXX</div></pre></td></tr></table></figure></p>
</summary>
<category term="mongodb" scheme="https://tunsuy.github.io/categories/mongodb/"/>
<category term="linux" scheme="https://tunsuy.github.io/tags/linux/"/>
<category term="mongodb" scheme="https://tunsuy.github.io/tags/mongodb/"/>
</entry>
<entry>
<title>深入理解Java中的线程安全和集合</title>
<link href="https://tunsuy.github.io/2016/10/09/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E4%B8%AD%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E5%92%8C%E9%9B%86%E5%90%88/"/>
<id>https://tunsuy.github.io/2016/10/09/深入理解Java中的线程安全和集合/</id>
<published>2016-10-09T11:20:00.000Z</published>
<updated>2017-03-14T10:09:22.742Z</updated>
<content type="html"><![CDATA[<p>翻译于<a href="http://www.codejava.net/java-core/collections/understanding-collections-and-thread-safety-in-java" target="_blank" rel="external">http://www.codejava.net/java-core/collections/understanding-collections-and-thread-safety-in-java</a></p>
<p>注:不是原文翻译,有些自己的理解改动</p>
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>为什么大部分的collection类都是线程不安全的呢?<br>正如你知道的,大部分的collection类如:<code>ArrayList、LinkedList、HashMap、HashSet、TreeMap、TreeSet</code> 等等都是线程不安全的。事实上,在 <code>java.util</code> 包中的所有collection类(除了 <code>Vector</code> 和 <code>HashTable</code> )都是线程不安全的。为什么呢?</p>
<p>原因:同步的代价是很大的。</p>
<a id="more"></a>
<p><code>Vector</code> 和<code>HashTable</code> 是java早期就存在的两个集合,他们一开始就被设计为线程安全的(如果你看源码,你会发现他们的所有方法都是 <code>synchronized</code> 的)。然而,很快在多线程程序中它们就被暴露出性能是很低下的,因为同步是需要加锁的,这很耗性能。</p>
<p>这也是为什么在后来新的collections( <code>List、Set、Map</code> 等)都提供了在单线程应用中非并发性的控制来获得最大的性能。</p>
<p>下面的程序比较了 <code>Vector</code> 和 <code>ArrayList</code> 的性能( <code>Vector</code> 是线程安全的,而 <code>ArrayList</code> 不是):</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> java.util.*;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * This test program compares performance of Vector versus ArrayList</div><div class="line"> * <span class="doctag">@author</span> www.codejava.net</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CollectionsThreadSafeTest</span> </span>{</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testVector</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">long</span> startTime = System.currentTimeMillis();</div><div class="line"></div><div class="line"> Vector<Integer> vector = <span class="keyword">new</span> Vector<>();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10_000_000</span>; i++) {</div><div class="line"> vector.addElement(i);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">long</span> endTime = System.currentTimeMillis();</div><div class="line"></div><div class="line"> <span class="keyword">long</span> totalTime = endTime - startTime;</div><div class="line"></div><div class="line"> System.out.println(<span class="string">"Test Vector: "</span> + totalTime + <span class="string">" ms"</span>);</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testArrayList</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">long</span> startTime = System.currentTimeMillis();</div><div class="line"></div><div class="line"> List<Integer> list = <span class="keyword">new</span> ArrayList<>();</div><div class="line"></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10_000_000</span>; i++) {</div><div class="line"> list.add(i);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">long</span> endTime = System.currentTimeMillis();</div><div class="line"></div><div class="line"> <span class="keyword">long</span> totalTime = endTime - startTime;</div><div class="line"></div><div class="line"> System.out.println(<span class="string">"Test ArrayList: "</span> + totalTime + <span class="string">" ms"</span>);</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> CollectionsThreadSafeTest tester = <span class="keyword">new</span> CollectionsThreadSafeTest();</div><div class="line"></div><div class="line"> tester.testVector();</div><div class="line"></div><div class="line"> tester.testArrayList();</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<p>该程序测试了分别给这两种类型增加1000000个元素所耗费的时间,结果如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Test Vector: <span class="number">9266</span> ms</div><div class="line">Test ArrayList: <span class="number">4588</span> ms</div></pre></td></tr></table></figure></p>
<p>说明在大数据量下,ArrayList的性能比Vector的好两倍以上。</p>
<h1 id="二、-快速失败"><a href="#二、-快速失败" class="headerlink" title="二、 快速失败"></a>二、 快速失败</h1><p>快速失败又叫做”<code>Fail-Fast</code>“策略。当使用集合时,你需要理解它们的迭代器的并发策略:<code>Fail-Fast Iterators</code></p>
<p>看下面的代码片段:Strings列表的迭代<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">List<String> listNames = Arrays.asList(<span class="string">"Tom"</span>, <span class="string">"Joe"</span>, <span class="string">"Bill"</span>, <span class="string">"Dave"</span>, <span class="string">"John"</span>);</div><div class="line"></div><div class="line">Iterator<String> iterator = listNames.iterator();</div><div class="line"></div><div class="line"><span class="keyword">while</span> (iterator.hasNext()) {</div><div class="line"> String nextName = iterator.next();</div><div class="line"> System.out.println(nextName);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这里我们使用了集合的迭代器来遍历集合元素。加入listNames是被两个线程所共享:正在迭代遍历的目前的线程和另外一个线程。当第一个线程正在迭代遍历的时候,第二个线程也在修改这个集合(增加或者减少元素)。那么结果会怎样呢?</p>
<p>第一个线程将抛出这个异常 <code>ConcurrentModificationException</code>并立刻失败。所以这就叫做 <code>fail-fast iterators</code>。</p>
<p>迭代器为什么会这么快的失败呢?这是因为当遍历一个正在修改的集合时是非常危险的:这个迭代器被生成之后,这个集合可能有更多、更少或者没有元素,以至于导致无法预期或者不一致的结果。这应该要尽可能早的避免,因此这个迭代器必现抛出一个异常来结束目前线程的执行。</p>
<p>下面的例子模拟了抛出 <code>ConcurrentModificationException</code> 异常的情况:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> java.util.*;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * This test program illustrates how a collection's iterator fails fast</div><div class="line"> * and throw ConcurrentModificationException</div><div class="line"> * <span class="doctag">@author</span> www.codejava.net</div><div class="line"> *</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">IteratorFailFastTest</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> List<Integer> list = <span class="keyword">new</span> ArrayList<>();</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">IteratorFailFastTest</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10_000</span>; i++) {</div><div class="line"> list.add(i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">runUpdateThread</span><span class="params">()</span> </span>{</div><div class="line"> Thread thread1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">10_000</span>; i < <span class="number">20_000</span>; i++) {</div><div class="line"> list.add(i);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> });</div><div class="line"></div><div class="line"> thread1.start();</div><div class="line"> }</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">runIteratorThread</span><span class="params">()</span> </span>{</div><div class="line"> Thread thread2 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>{</div><div class="line"> ListIterator<Integer> iterator = list.listIterator();</div><div class="line"> <span class="keyword">while</span> (iterator.hasNext()) {</div><div class="line"> Integer number = iterator.next();</div><div class="line"> System.out.println(number);</div><div class="line"> }</div><div class="line"> }</div><div class="line"> });</div><div class="line"></div><div class="line"> thread2.start();</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</div><div class="line"> IteratorFailFastTest tester = <span class="keyword">new</span> IteratorFailFastTest();</div><div class="line"></div><div class="line"> tester.runIteratorThread();</div><div class="line"> tester.runUpdateThread();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>代码中,这个 <code>thread1</code> 正在迭代遍历 <code>list</code>,而 <code>thread2</code> 也在继续增加元素到这个集合,这就导致了 <code>ConcurrentModificationException</code> 抛出。</p>
<p>注意:迭代器的 <code>fail-fast</code> 行为只是用来帮助更容易的找到或者诊断bug,我们不应该在程序中依赖并操作这个 <code>ConcurrentModificationException</code> 因为 <code>fail-fast</code> 不被保障的。这就是说当这个异常抛出后,我们的程序应该立即停止运行。</p>
<h1 id="三、-同步装饰器"><a href="#三、-同步装饰器" class="headerlink" title="三、 同步装饰器"></a>三、 同步装饰器</h1><h2 id="1、-简介"><a href="#1、-简介" class="headerlink" title="1、 简介"></a>1、 简介</h2><p>目前我们已经知道了这些基本的集合类的实现都是线程不安全的,是为了最大的提高在单线程程序中的性能。那么如果我们要用在多线程应用中呢?</p>
<p>当然我们不应该在并发的上下文中使用线程不安全的集合类,因为这将导致无法预期和不一致的结果。我们可以通过同步代码块来手动的控制我们的代码。然而使用线程安全的集合类总是比我们手动写同步代码块是更好的。</p>
<p>可能你已经知道的,Java的 <code>Collections</code> 库给我们提供了创建线程安全的集合的工厂方法。这些方法就是来自下面的形式:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Collections.synchronizedXXX(collection)</div></pre></td></tr></table></figure></p>
<p>这些工厂方法装饰指定的集合,然后返回线程安全的实现。这里的 <code>XXX</code> 可以是 <code>Collection, List, Map, Set, SortedMap</code> 和 <code>SortedSet</code>。下面就是一个例子:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">List<String> safeList = Collections.synchronizedList(<span class="keyword">new</span> ArrayList<>());</div></pre></td></tr></table></figure></p>
<p>如果我们已经有一个存在的线程不安全的集合对象,我们也可以使用这个来装饰它:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Map<Integer, String> unsafeMap = <span class="keyword">new</span> HashMap<>();</div><div class="line">Map<Integer, String> safeMap = Collections.synchronizedMap(unsafeMap);</div></pre></td></tr></table></figure></p>
<p>这些工厂方法都是以一个相同的接口实现了对指定的集合的线程安全实现的装饰,所以叫做 <code>'synchronized wrappers'</code>。实际上,就是这些线程不安全的集合将所有的工作都委托给了这个装饰器集合来处理。</p>
<h2 id="2、-注意"><a href="#2、-注意" class="headerlink" title="2、 注意"></a>2、 注意</h2><p>当使用一个同步集合的迭代器的时候,我们需要使用同步块来保证迭代器的安全,因为这个迭代器不是线程安全的。看下面的例子:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">List<String> safeList = Collections.synchronizedList(<span class="keyword">new</span> ArrayList<>());</div><div class="line"></div><div class="line"><span class="comment">// adds some elements to the list</span></div><div class="line"></div><div class="line">Iterator<String> iterator = safeList.iterator();</div><div class="line"></div><div class="line"><span class="keyword">while</span> (iterator.hasNext()) {</div><div class="line"> String next = iterator.next();</div><div class="line"> System.out.println(next);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>虽然 <code>safeList</code> 是线程安全的,但是他的迭代器不是,因此我们需要手动增加同步块:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">synchronized</span> (safeList) {</div><div class="line"> <span class="keyword">while</span> (iterator.hasNext()) {</div><div class="line"> String next = iterator.next();</div><div class="line"> System.out.println(next);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>同样的需要注意:同步集合的迭代器也是 <code>fail-fast</code> 的。</p>
<p>虽然同步装饰器可以安全的用在多线程应用中,但是有一些缺点需要说明,在下面将介绍</p>
<h1 id="四、-并发集合"><a href="#四、-并发集合" class="headerlink" title="四、 并发集合"></a>四、 并发集合</h1><h2 id="1、-简介-1"><a href="#1、-简介-1" class="headerlink" title="1、 简介"></a>1、 简介</h2><p>同步集合的一个缺点就是它们的同步机制是使用的它们自己作为锁对象。这就意味着一个线程正在遍历这个集合的时候,在这个集合其他方法块的其他线程就得等待,这就导致程序性能下降。</p>
<p>这就是为什么说Java5及以上介绍说 <code>concurrent collections</code>(并发集合)的性能比同步装饰器好。并发集合是在 <code>java.util.concurrent</code> 包中。它们根据它们的线程机制被分为了三个组。</p>
<h2 id="2、-copy-on-write-collections"><a href="#2、-copy-on-write-collections" class="headerlink" title="2、 copy-on-write collections"></a>2、 copy-on-write collections</h2><p>第一个组叫 <code>copy-on-write collections</code> :这种线程安全的集合是在不可变的数组中存储值的;这个集合的值的任何改变,结果都是返回一个新的数组用来反射到新的值; 这种集合适合于读操作远远多于写操作的情况;有两种实现:<code>CopyOnWriteArrayList</code> 和 <code>CopyOnWriteArraySet</code>。</p>
<p>注意:这种集合有一个 <code>iterators</code> 快照,不会抛出 <code>ConcurrentModificationException</code> 异常。因为这种集合是基于不可变数组的,所以一个线程能够读取集合中的数据而不用担心其他线程改变了它们。</p>
<h2 id="3、-CAS-collections"><a href="#3、-CAS-collections" class="headerlink" title="3、 CAS collections"></a>3、 CAS collections</h2><p>第二组叫 <code>Compare-And-Swap or CAS collections</code>:这组集合是基于 <code>Compare-And-Swap (CAS)</code> 算法实现的线程安全。CAS算法是这样的:</p>
<p>为了执行计算或者更新变量的值,它对变量在本地做了一份复制,计算的时候使用的是这个本地的值。当它想要更新这个变量的值时,它会用本地的值跟这个变量进行对比,如果它们是相同的,则用新的值更新这个变量。</p>
<p>如果它们是不相同的,那么说明这个变量被其他线程改变了,这种情况下,<code>CAS</code> 线程会尝试使用这个新值进行计算,或者放弃,或者继续。<code>CAS</code> 包括 <code>ConcurrentLinkedQueue</code> 和 <code>ConcurrentSkipListMap</code>。</p>
<p>注意:<code>CAS</code> 集合的迭代器是弱一致性的。就是指它可能只会反射这个集合中的一些而不是所有的变化。弱一致性不会抛出 <code>ConcurrentModificationException</code>。</p>
<h2 id="4、-special-lock-object"><a href="#4、-special-lock-object" class="headerlink" title="4、 special lock object"></a>4、 special lock object</h2><p>第三组叫 <code>concurrent collections using a special lock object (java.util.concurrent.lock.Lock)</code>:<br>这个机制是比经典的同步机制更加的灵活。</p>
<p>这个锁跟经典的同步有相同的行为,但是一个线程在以下几种情况也能够获取它:锁目前不被持有,超时或者线程没有被中断。</p>
<p>不同于同步代码,一个锁对象在代码块或者方法被执行的时候就被持有。而这个 <code>lock</code> 是在 <code>unlock()</code> 方法被调用时才持有。这个机制很有用的一些实现就是将集合分成部分,从而可以分别持有。为了提高并发,比如 <code>LinkedBlockingQueue</code> ,这个队列的头和尾被分开 <code>lock</code>,因此这个元素能够并行的增减。</p>
<p>一些集合使用包含 <code>ConcurrentHashMap</code> 的锁和 <code>BlockingQueue</code> 的大部分实现。</p>
<p>这个组中的集合也是弱一致性的迭代器,不会抛出 <code>ConcurrentModificationException</code>。</p>
]]></content>
<summary type="html">
<p>翻译于<a href="http://www.codejava.net/java-core/collections/understanding-collections-and-thread-safety-in-java">http://www.codejava.net/java-core/collections/understanding-collections-and-thread-safety-in-java</a></p>
<p>注:不是原文翻译,有些自己的理解改动</p>
<h1 id="一、-简介"><a href="#一、-简介" class="headerlink" title="一、 简介"></a>一、 简介</h1><p>为什么大部分的collection类都是线程不安全的呢?<br>正如你知道的,大部分的collection类如:<code>ArrayList、LinkedList、HashMap、HashSet、TreeMap、TreeSet</code> 等等都是线程不安全的。事实上,在 <code>java.util</code> 包中的所有collection类(除了 <code>Vector</code> 和 <code>HashTable</code> )都是线程不安全的。为什么呢?</p>
<p>原因:同步的代价是很大的。</p>
</summary>
<category term="java" scheme="https://tunsuy.github.io/categories/java/"/>
<category term="java" scheme="https://tunsuy.github.io/tags/java/"/>
</entry>
<entry>
<title>redmine-数据统计</title>
<link href="https://tunsuy.github.io/2016/10/08/redmine-%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1/"/>
<id>https://tunsuy.github.io/2016/10/08/redmine-数据统计/</id>
<published>2016-10-08T12:11:06.000Z</published>
<updated>2017-03-31T09:58:35.049Z</updated>
<content type="html"><![CDATA[<h1 id="一、-项目说明"><a href="#一、-项目说明" class="headerlink" title="一、 项目说明"></a>一、 项目说明</h1><p>语言:python语言</p>
<p>1、项目地址:<br><a href="https://github.com/tunsuy/redmine_statis" target="_blank" rel="external">https://github.com/tunsuy/redmine_statis</a></p>
<p>2、统计维度: </p>
<ul>
<li>个人迭代bug数</li>
<li>模块迭代bug数</li>
<li>个人网上问题数</li>
<li>个人迭代工作粒度</li>
<li>个人遗留问题数</li>
</ul>
<p>如果要加上其他维度的统计,也是非常简单的</p>
<a id="more"></a>
<h1 id="二、-redmine表说明"><a href="#二、-redmine表说明" class="headerlink" title="二、 redmine表说明"></a>二、 redmine表说明</h1><p>1、表issues<br>—用来存放issue的标准字段。</p>
<p>2、表custom_fields<br>—该表字段都和创建自定义字段的web页面看到的选择项很像。</p>
<p>3、表custom_values<br>—该表可以用custom_field_id字段和custom_fields表的id关联。 而customized_id 可以和issues表的id相关联</p>
<h1 id="三、-表关联"><a href="#三、-表关联" class="headerlink" title="三、 表关联"></a>三、 表关联</h1><p>1、三个表issues, custom_fields和custom_values在一起表达了这么个关系。</p>
<p>2、一个issue的标准字段来自issues表,扩展字段来自custom_fields表,而custom_values和前custom_fields表关联,一起表示一个issue的某个自定义字段的值。</p>
<p>3、当表示issue的自定义字段时,<code>custom_fields.type</code>的值是 ‘IssueCustomField’ 而<code>custom_values.customized_type</code>的值是’Issue’.</p>
<p>4、所有issue的自定义字段值<br>可以先将custom_fields表和custom_values表关联,获得如下结果:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">select</span> customized_id <span class="keyword">as</span> issue_id,custom_field_id,<span class="keyword">type</span>,<span class="keyword">name</span>,default_value,<span class="keyword">value</span> <span class="keyword">from</span> custom_fields a <span class="keyword">inner</span> <span class="keyword">join</span> custom_values b <span class="keyword">on</span> a.id =b.custom_field_id <span class="keyword">and</span> a.type = <span class="string">'IssueCustomField'</span> <span class="keyword">and</span> b.customized_type=<span class="string">'Issue'</span> <span class="keyword">limit</span> <span class="number">2</span>;</div></pre></td></tr></table></figure></p>
<p>由此可以看出redmine的设计是用记录行数来表示扩展字段的值,所以可以不受mysql表字段的限制。</p>
<h1 id="四、-访问权限"><a href="#四、-访问权限" class="headerlink" title="四、 访问权限"></a>四、 访问权限</h1><p>基本知识了解:<br>1、授予用户redmine_static 在指定ip下 以 密码 moatest 访问 bitnami_redmine 的 select和excute操作<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">grant</span> <span class="keyword">select</span>,excute <span class="keyword">on</span> bitnami_redmine.* <span class="keyword">to</span> <span class="string">'redmine_static'</span>@<span class="string">'200.200.169.162'</span> <span class="keyword">identified</span> <span class="keyword">by</span> <span class="string">'moatest'</span></div></pre></td></tr></table></figure></p>
<p>2、查询mysql所有用户<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">select</span> <span class="keyword">user</span>,host,<span class="keyword">password</span> <span class="keyword">from</span> mysql.user;</div></pre></td></tr></table></figure></p>
<p>3、刷新权限设置<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">flush</span> <span class="keyword">privileges</span>;</div></pre></td></tr></table></figure></p>
<p>4、查询 指定IP 下 用户redmine_static 的数据库权限<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">show</span> <span class="keyword">grants</span> <span class="keyword">for</span> <span class="string">'redmine_static'</span>@<span class="string">'200.200.169.162'</span>\G</div></pre></td></tr></table></figure></p>
<p>5、取消用户的操作权限<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">revoke</span> <span class="keyword">select</span> <span class="keyword">on</span> bitnami_redmine.* <span class="keyword">from</span> <span class="string">'redmine_static'</span>@<span class="string">'200.200.169.162'</span> <span class="keyword">identified</span> <span class="keyword">by</span> <span class="string">'moatest'</span>;</div></pre></td></tr></table></figure></p>
<p>6、授予所有操作权限<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">grant</span> all <span class="keyword">privileges</span> <span class="keyword">on</span> bitnami_redmine.* <span class="keyword">to</span> <span class="string">'redmine_static'</span>@<span class="string">'200.200.169.162'</span> <span class="keyword">identified</span> <span class="keyword">by</span> <span class="string">'moatest'</span>;</div></pre></td></tr></table></figure></p>
<p>7、删除用户<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">drop</span> <span class="keyword">user</span> redmine_static@<span class="string">'%'</span>;</div></pre></td></tr></table></figure></p>
<p>8、创建用户<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">create</span> <span class="keyword">user</span> redmine_static@<span class="string">'%'</span> <span class="keyword">identified</span> <span class="keyword">by</span> <span class="string">'moatest'</span>;</div></pre></td></tr></table></figure></p>
<h1 id="五、-redmine统计说明"><a href="#五、-redmine统计说明" class="headerlink" title="五、 redmine统计说明"></a>五、 redmine统计说明</h1><p>1、在redmine服务器中新增一个mysql用户:<br>——该用户只能在169.162中以用户名和密码的方式访问<br>——见如上2说明 </p>
<h1 id="六、-使用"><a href="#六、-使用" class="headerlink" title="六、 使用"></a>六、 使用</h1><p>1、切换python环境:<code>pyenv activate venv2710</code><br>2、切换到项目路径<br>3、执行:python main.py [统计开始时间 统计结束时间]<br><figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">eg:python main.py <span class="number">2016</span><span class="number">-9</span><span class="number">-1</span> <span class="number">2016</span><span class="number">-12</span><span class="number">-31</span></div></pre></td></tr></table></figure></p>
]]></content>
<summary type="html">
<h1 id="一、-项目说明"><a href="#一、-项目说明" class="headerlink" title="一、 项目说明"></a>一、 项目说明</h1><p>语言:python语言</p>
<p>1、项目地址:<br><a href="https://github.com/tunsuy/redmine_statis">https://github.com/tunsuy/redmine_statis</a></p>
<p>2、统计维度: </p>
<ul>
<li>个人迭代bug数</li>
<li>模块迭代bug数</li>
<li>个人网上问题数</li>
<li>个人迭代工作粒度</li>
<li>个人遗留问题数</li>
</ul>
<p>如果要加上其他维度的统计,也是非常简单的</p>
</summary>
<category term="redmine" scheme="https://tunsuy.github.io/categories/redmine/"/>
<category term="python" scheme="https://tunsuy.github.io/tags/python/"/>
<category term="redmine" scheme="https://tunsuy.github.io/tags/redmine/"/>
</entry>
<entry>
<title>实现浏览器中实时查看iOS日志</title>
<link href="https://tunsuy.github.io/2016/09/23/%E5%AE%9E%E7%8E%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E4%B8%AD%E5%AE%9E%E6%97%B6%E6%9F%A5%E7%9C%8BiOS%E6%97%A5%E5%BF%97/"/>
<id>https://tunsuy.github.io/2016/09/23/实现浏览器中实时查看iOS日志/</id>
<published>2016-09-23T07:32:34.000Z</published>
<updated>2017-03-14T05:14:10.678Z</updated>