-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
1129 lines (1027 loc) · 194 KB
/
atom.xml
File metadata and controls
1129 lines (1027 loc) · 194 KB
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><![CDATA[whtis's blog]]></title>
<link href="/atom.xml" rel="self"/>
<link href="whtis.github.io/"/>
<updated>2019-11-02T08:54:23.018Z</updated>
<id>whtis.github.io/</id>
<author>
<name><![CDATA[whtis]]></name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title><![CDATA[利用ios备份导出微信文本聊天记录]]></title>
<link href="whtis.github.io/2019/11/02/%E5%88%A9%E7%94%A8ios%E5%A4%87%E4%BB%BD%E5%AF%BC%E5%87%BA%E5%BE%AE%E4%BF%A1%E6%96%87%E6%9C%AC%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95/"/>
<id>whtis.github.io/2019/11/02/利用ios备份导出微信文本聊天记录/</id>
<published>2019-11-02T06:53:36.000Z</published>
<updated>2019-11-02T08:54:23.018Z</updated>
<content type="html"><![CDATA[<h3 id="目的">目的</h3><p>众所周知,源于微信的安全性和ios的封闭性,想要把微信聊天记录导出为可编辑的文本内容不是一件容易的事。itunes和微信自带的备份,可以将聊天内容恢复到同一个微信号登录的手机上,却无法将这些内容提取为可随便编辑的文本内容。在查阅了网上大量靠谱不靠谱的方法后,作者总结了一个可行的流程,根据这个流程,可以实现:将微信聊天的文本内容导出为txt文本文件,之后的用途,嘿嘿,你可以生成和某人聊天的文字云,也可以对文本内容的词频进行分析,等等等等~~~</p>
<h3 id="注意">注意</h3><p><strong>本文所有步骤,全部基于<code>mac</code>+<code>ios</code>+<code>itunes</code>实现</strong>,android由于文件系统的开放性,可以有各种获取微信聊天内容的方法,作者在查阅资料的时候,也见过类似的博文,详见底部参考文章。工具:</p>
<ul>
<li>Mac: mac os 10.14.6</li>
<li>itunes</li>
<li>IMazing: 2.10.4 破解版,这里不放链接了=。=</li>
<li><a href="https://sqlitebrowser.org/" target="_blank" rel="external">DB Browser for SQLite</a></li>
<li><a href="http://wxbackup.imxfd.com/" target="_blank" rel="external">wxbackup</a></li>
</ul>
<h3 id="正文">正文</h3><p>整体的思路:1、使用itunes备份手机。2、使用Imazing提取微信文件夹。3、使用DB Browser for SQLite读取特定账号的微信聊天记录数据表,导出。上述流程最麻烦的就是提取微信的文件夹,作者下载试用的包括但不限于:微信管理大师(x),楼月微信聊天记录导出恢复助手(x),weback(x),果备份(x)…… 这些有些是收费的,价格不菲,然而并没有卵用,有些是跟不上时代,很久远不更新的了,所以能找到一款可以提取的软件真的不容易=。=。另外,github上相关开源项目,没有一个可用的,特别是这个<strong><a href="https://github.com/tsycnh/WeChatExporter" target="_blank" rel="external">WeChatExporter</a></strong>在查阅资料过程中出镜率很高,但实测无效。不过可以顺便了解一下nwjs哈哈哈。</p>
<h4 id="itunes备份">itunes备份</h4><p>打开itunes,连接手机备份。这一步没啥说的。只有一点可以了解下,mac itunes备份的默认路径为:<code>/Users/${whtis}/Library/Application Support/MobileSync</code></p>
<h4 id="导出微信文件夹">导出微信文件夹</h4><ul>
<li>微信位于<strong>文件系统–>app–>AppDomain-com.tencent.xin–>Documents</strong></li>
</ul>
<p><img src="imazing备份.png" alt="imazing备份"></p>
<ul>
<li><p>图中红框部分,每个人不同。因为我手机前后登过3个微信号,所以有三个文件夹。那么问题来了,如何确定哪个文件夹是我们想要导出微信记录的微信号的内容呢?文件夹的命名很关键,网上很多人也提到,文件夹的命名是<code>md5(微信号)</code>生成的,32位小写加密,这里要注意的是,<strong>md5加密的微信号,不是个人自定义的,而是腾讯原始的,以wxid_xxxxxx打头的这一串字符</strong>。如果你自定义了微信号,那么可以看一下<code>wxbackup</code>这个软件的使用。本文后面也会提到。</p>
</li>
<li><p>进入到特定微信号的文件中,找到DB文件夹,可以看到<code>MM.sqlite</code>这个文件,我们微信聊天的文本文件,就存在这个里面。</p>
</li>
<li><p>下载<a href="https://sqlitebrowser.org/" target="_blank" rel="external">DB Browser for SQLite</a>,将<code>MM.sqlite</code>打开,执行sql: <code>SELECT * from Chat_03af72c4831cc6ae3XXXXX</code>,可以看到如下图结果,Chat后跟的这串字符,即<code>md5(想导出人wxid)</code>。</p>
<p><img src="wechat.png" alt="聊天记录数据表示例"></p>
<p>其中,createTime是该条记录的时间戳,des中,0表示自己,Message就是微信聊天记录了。如示例图所示,导出csv,至此,主流程结束。</p>
</li>
</ul>
<h4 id="文本处理">文本处理</h4><p>这里多啰嗦几句,导出的文本中包含到红包链接、语音链接、撤回消息提醒、表情发送失败提醒等。为了获得纯净的文本,需要将这部分内容去除。最好的方式就是正则替换了。这里提供我处理文本时用的一些正则表达式:</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><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">"<\?xml[\s\S]*?</msg>\n"</div><div class="line"></div><div class="line">"<msg>(.|\n)*?</msg>"</div><div class="line"></div><div class="line">"<img[\s\S]*?</_wc_custom_link_>"$</div><div class="line"></div><div class="line">"<sysmsg[\s\S]*?</sysmsg>"</div><div class="line"></div><div class="line">^((https|http|ftp|rtsp|mms)?:\/\/)[^\s]+</div><div class="line"></div><div class="line"><voipinvitemsg>.*?/voipextinfo></div><div class="line"></div><div class="line"><voiplocalinfo><wordingtype>\d+</wordingtype><duration>\d+</duration></voiplocalinfo></div><div class="line"></div><div class="line">"因内容受保护,表情未能成功发送.*</a>"</div></pre></td></tr></table></figure>
<h4 id="其它">其它</h4><p>我们最终的目的是要获得一个可阅读的文档,因此还有一些后置工作要做,这里也放几个链接,都是处理过程中用的到的:</p>
<ul>
<li><a href="https://jingyan.baidu.com/article/63acb44afb1e2561fcc17e3f.html" target="_blank" rel="external">excel批量将时间戳转化为时间</a></li>
<li><a href="https://jingyan.baidu.com/article/fcb5aff7aa222cedaa4a718e.html" target="_blank" rel="external">excel替换某一列的内容</a></li>
<li><a href="https://jingyan.baidu.com/article/d7130635c59a6113fdf47504.html" target="_blank" rel="external">word分栏</a></li>
</ul>
<h3 id="参考资料">参考资料</h3><h4 id="wxbackup_微信聊天记录备份软件">wxbackup 微信聊天记录备份软件</h4><p>好东西,不解释。全网唯一能用的软件,而且免费,链接:<a href="https://zhuanlan.zhihu.com/p/32511173。文中提到的,查看微信原始id的,可以直接在导出记录界面看到相应人的wxid。唯一美中不足的是,当导出的聊天记录过多时,浏览器会卡死,期待作者优化2333。" target="_blank" rel="external">https://zhuanlan.zhihu.com/p/32511173。文中提到的,查看微信原始id的,可以直接在导出记录界面看到相应人的wxid。唯一美中不足的是,当导出的聊天记录过多时,浏览器会卡死,期待作者优化2333。</a></p>
<h4 id="微信聊天记录分析,Python_之_WorldCloud)"><a href="[https://52xpp.com/2018/03/20/%E5%BE%AE%E4%BF%A1%E8%81%8A%E5%A4%A9%E8%AE%B0%E5%BD%95%E5%88%86%E6%9E%90%EF%BC%8Cpython-%E4%B9%8B-worldcloud/](https://52xpp.com/2018/03/20/微信聊天记录分析,python-之-worldcloud/">微信聊天记录分析,Python 之 WorldCloud</a>)</h4><p>本文主要参考之一,强烈建议阅读。</p>
<h3 id="后记">后记</h3><p>在博客长草了一年多以后,能静下心来再写博客不容易,以后更要多进行总结,一边记录,一边成长吧。做人嘛,开心就好。</p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>]]></content>
<summary type="html">
<![CDATA[<h3 id="目的">目的</h3><p>众所周知,源于微信的安全性和ios的封闭性,想要把微信聊天记录导出为可编辑的文本内容不是一件容易的事。itunes和微信自带的备份,可以将聊天内容恢复到同一个微信号登录的手机上,却无法将这些内容提取为可随便编辑的文本内容。在查阅了网上大]]>
</summary>
<category term="原创" scheme="whtis.github.io/tags/%E5%8E%9F%E5%88%9B/"/>
<category term="ios" scheme="whtis.github.io/tags/ios/"/>
<category term="wechat" scheme="whtis.github.io/tags/wechat/"/>
<category term="python" scheme="whtis.github.io/categories/python/"/>
</entry>
<entry>
<title><![CDATA[最近要做的几件事]]></title>
<link href="whtis.github.io/2018/05/17/%E6%9C%80%E8%BF%91%E8%A6%81%E5%81%9A%E7%9A%84%E5%87%A0%E4%BB%B6%E4%BA%8B/"/>
<id>whtis.github.io/2018/05/17/最近要做的几件事/</id>
<published>2018-05-17T12:14:17.169Z</published>
<updated>2018-01-24T18:23:00.055Z</updated>
<content type="html"><![CDATA[<h3 id="写在前面">写在前面</h3><p>有时候想到要做一些跟技术有关的东西,但很多时候都会忘记。一直觉得<strong>兴趣是最好的老师</strong>这句话很对,既然有兴趣,那么最好动手做一做。写这篇文章,提醒自己的同时也是希望认识志同道合的朋友,虽然刚开始做的东西很low,但慢慢积累,总能获得成就感不是?<br><br>这篇文章会不断更新,我也想看看,我最后到底能做几件自己喜欢的事情,嘻嘻(●’◡’●)</p>
<h5 id="2016-09-19日凌晨">2016-09-19日凌晨</h5><ul>
<li>学习安卓开发,写一个app,具体需求如下:(已实现第一步)<ul>
<li><del>能够实现实时监测手机短信的功能。当手机A收到新短信后,能立刻调用系统短信app将短信内容发送给B</del></li>
<li>如果有可能,转发这一步调用微信完成(省钱啊)</li>
<li><del>界面,能凑合用就行,别出bug就行(没写。。)</del></li>
</ul>
</li>
<li>给这博客添加几个功能:<ul>
<li><del>添加网易云音乐插件(最终还是觉得不加好一些)</del></li>
<li><del>添加一个新分区,类似于微信朋友圈或者qq空间发状态的功能。(因为自己有时候有些话憋不住,按照博文发出来又太短了=。=)</del></li>
<li><del>之前好像见过博客同步instagram的功能,如果上面两个都完成了,可以在博客上添加一个专门的图片分区(看过效果,类似于图床,没啥实用性,还是直接上传instagram,等积累到一定量直接放链接就行)</del></li>
</ul>
</li>
</ul>
<h5 id="2016-11-07日夜">2016-11-07日夜</h5><ul>
<li><del>续上次的需求,学习微信公众号开发(转发手机短信到公众号再下发)</del></li>
<li><del>学习javascript,自己写一个简易的网站(博客新分区的提高要求)</del></li>
</ul>
<h5 id="2017-6-19">2017-6-19</h5><ul>
<li>接去年11月的需求,学习微信公众号开发,建立自己的微信公众号,提供信息的聚合,包括新闻和招聘信息</li>
<li>学习<code>spring</code>框架,运用所学的知识在腾讯云上搭建一个自己的网站。采用技术为<code>java+spring+mybatis</code></li>
</ul>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h3 id="写在前面">写在前面</h3><p>有时候想到要做一些跟技术有关的东西,但很多时候都会忘记。一直觉得<strong>兴趣是最好的老师</strong>这句话很对,既然有兴趣,那么最好动手做一做。写这篇文章,提醒自己的同时也是希望认识志同道合的朋友,虽然刚开始做的东]]>
</summary>
<category term="待完成" scheme="whtis.github.io/tags/%E5%BE%85%E5%AE%8C%E6%88%90/"/>
<category term="未完成" scheme="whtis.github.io/categories/%E6%9C%AA%E5%AE%8C%E6%88%90/"/>
</entry>
<entry>
<title><![CDATA[我的父亲]]></title>
<link href="whtis.github.io/2018/01/25/%E6%88%91%E7%9A%84%E7%88%B6%E4%BA%B2/"/>
<id>whtis.github.io/2018/01/25/我的父亲/</id>
<published>2018-01-24T18:18:16.000Z</published>
<updated>2018-01-24T19:07:40.317Z</updated>
<content type="html"><![CDATA[<p></p><p style="text-indent:2em">终于有时间写点关于父亲的文字了。</p><br><a id="more"></a><p></p>
<p></p><p style="text-indent:2em">写了这么多年的作文,记忆中唯一有关父亲的作文还是小学二年级的家庭作业。一转眼,也快二十年了,当时的作文是没得写,现在的文章却是不知道从哪里开始写了。</p>
<p></p><p style="text-indent:2em">父亲有两个孩子,人们都说养孩子最忌讳的就是偏爱,但父亲却毫不掩饰对我的喜爱,每当父亲喝多了和母亲聊天,父亲都会说他喜欢他的小儿子。其中缘由呢,大概是哥哥小时候在老家生活过一段时间,而我从小在父亲身边,小时候又喜欢黏着他的缘故吧。</p>
<p></p><p style="text-indent:2em">正因为我小时候一直在父亲身边,父亲干啥事都喜欢带我一起。父亲去打麻将,我就在旁边吸二手烟;父亲去牧民家里做客,我就在他们聊天的时候吃羊肉;父亲去山里收羊,我就帮着一起赶羊……</p>
<p></p><p align="center">(一)</p><p></p>
<p></p><p style="text-indent:2em">2000年前后,冬,三九天,这个时候山上的野兔又冷又饿,容易上套。父亲就和他几个朋友约好一起去山上套兔子。作为跟屁虫的我,当然是一起去了。然而父亲高估了我的抗寒能力,我坐在摩托车的后座,刚到山里,被凛冽的寒风一吹,就觉得自己要冻死了,即使又穿了父亲的外套,我还是感觉这风再猛烈一点我就被吹飞了。不争气的我开始抱着父亲哭,说我冷,要回家。但来这么一趟,烧了这么多汽油,空手而归的话父亲的朋友肯定不干。父亲只能把我放在一座废桥下面,让我等他。可以想象的是,五六岁的我,独自一人在山里,耳边只有寒风呼呼的吹,得恐惧成什么样。而且那时候也听说了狼这种生物,心里更是害怕到了极点。在我反复从桥下跑到桥上又跑回桥下的焦急等待中,我终于听到了父亲的声音“幺,回家了”。是的,可以回家了!当父亲出现的那一刻,幼小的我懂得了安全感这个词的意思。父亲,就是安全感的代名词。此后无论我是因为丢钱而惊慌失措,还是因为在陌生环境里惴惴不安,只要见到父亲或者听到他的声音,一种安全感就会从心底涌起,让我不再慌张,不再恐惧,能够从容处理任何事情。</p>
<p></p><p align="center">(二)</p><p></p>
<p></p><p style="text-indent:2em">父亲很喜欢打麻将,而且喜欢带着我一起,尽管幼小的我不知道麻将的规则,也不知道这里面的乐趣,但父亲就喜欢让我坐在他旁边看他玩。父亲一玩几个小时,乐在其中,而我,肯定不会老老实实陪着他坐几个小时。于是每当父亲去别人家打麻将时,我就会先在父亲边上坐一段时间,然后就去开别人家的电视看。</p>
<p></p><p style="text-indent:2em">又一次的,当父亲结束麻将桌上的奋战,准备带着电视机前的我离开的时候,电视机的主人惊讶的说到她的隐形眼镜镜片找不到了。自然,嫌疑最大的是我,因为隐形眼镜的眼镜盒就放在电视机旁边。幼小的我除了说我没动过那个眼镜盒外,也不知道说啥了。父亲在认真问我是否动过那个眼镜盒后,提出赔偿电视机的主人。300元人民币,我记得很清楚,2000年前后,我的学费还只是五六十元人民币。出了门,本以为父亲会生气揍我,但父亲只是再次询问了我是否动了那个眼镜盒,得到否定的答案后,父亲只是对我说电视机主人的人品不怎么样,就没再提过这个事了。当然,父亲以后再没有带我去过那里。</p>
<p></p><p style="text-indent:2em">年幼的我本来以为会是一场腥风血雨,但父亲没有用巴掌发泄自己的愤怒,反而教给了年幼的孩子一个道理:在自己的孩子和外人之间,父亲会坚定不移的相信自己的孩子。大概这也是现在的我,无论是在家人或是外人面前,都不喜欢讲谎话的原因吧。</p>
<p></p><p style="text-indent:2em">随着我的成长,我无法一直在父亲身边做那个癞皮狗了。从小学四年级开始,我只能在每年的寒暑假才能见到父亲了,和父亲的交流,也越来越局限在吃穿住上面了。还记得父亲在得知我中考考了全州第二的时候破天荒的陪我玩了一把游戏机;也记得从不允许我去网吧的父亲主动找到网管让我上网查高考成绩,在我告诉他我被北邮录取了以后,父亲还要我拿他手机发短信做个确认。</p>
<p></p><p style="text-indent:2em">小时候的我觉得父亲是个高高在上的大人,当我长大后才觉得父亲更像一个小孩子。父亲会因为我总打妈妈电话而不打他电话拒绝和我说话,也会因为给我钱的数目而和我展开辩论……</p>
<p></p><p style="text-indent:2em">寒暑假回家的我,也不再热衷跟着父亲到处晃悠,父亲这个时候就开发了新套路。这个套路就是从早上起床到晚上睡觉,只要是我在家的时候,父亲干个啥事都会叫我。早上我喜欢赖床到吃早饭,父亲就会一边做早饭,一边来叫我起床,还骗我说早饭已经好了。起了床,立刻叫我洗脸……次数多了,我经常就会装作听不见,然后父亲又开发了新招数:掀被子。掀完了还一边笑一边问我为啥还不起床。折腾了半天,终于吃完早饭,我可以消停点了。但没过一会你就会听到:”幺,给我倒杯水”、“幺,去把脏水桶倒了”、“幺,我要睡觉了啊,你还不洗脸洗脚啊”……我有时候会埋怨,这些事情哥哥也能做啊,凭啥每次都要我去做。父亲就会说我是小儿子,这种事就该我去做。但现在细细想来,父亲这样做,还是因为对我的喜爱啊。</p>
<p></p><p style="text-indent:2em">在自己儿子面前如此逗的父亲,只会在儿子面前炫耀自己赚钱的成果,却从来不会提自己在外面赚钱的辛苦,更不会提自己悲伤的往事。</p>
<p></p><p style="text-indent:2em">除非,他喝酒了。</p>
<p></p><p style="text-indent:2em">小学二年级,语文老师布置了作文,叙事文,能说清楚一件事就行了。至于主题,记不住了。那天父亲喝了点酒,在我吵着让妈妈教我如何做作业的声音中,父亲大笔一挥,开始了他唯一一次教儿子学习的表演,结果却不如人意。在父亲提笔写了100多字以后,妈妈看了一眼,就开始嘲笑他,说他尽说些自己小时候的淘气事,根本不是什么作文。父亲喝酒上头,就开始和妈妈理论,开始了口头上的叙事。什么小时候打猪草去卖;别人上学他在外面玩,别人放学他跟别人一起回家;接下来就是由于学习偏科:数学接近满分,语文考零分,没有继续上学,13岁开始外出打拼……说到情深出,父亲竟然哭了。可能是被当时不懂事的我嘲笑了,此后很长一段时间,我没有再见过父亲掉眼泪。直到奶奶去世……</p>
<p></p><p style="text-indent:2em">父亲家小时候很穷,所以父亲才会早早外出打拼。有了我和哥哥以后,为了赚钱,父亲更是辛苦。虽然这样,但父亲一般都不会让我和哥哥去帮他分担,我和哥哥得到的指令都是好好学习就行。年幼的我总是觉得父亲的辛苦是应该的,而且有时候还会轻视他的辛苦,总是说着自己以后绝对不会做这些事情来赚钱。但父亲对于这些事情却乐此不疲:贩牛羊肉、卖五金杂货、开黑车拉人……</p>
<p></p><p style="text-indent:2em">我初中毕业回家玩耍的时候正巧碰上父亲要去山里收一群羊,然后把这群羊从山里赶到山脚下。因为平时在学校听说父亲经常干这事赚钱,就想跟着父亲体验一把。父亲抵不过我的软磨硬泡,我又坐到了摩托车的后座上,向着距家100多公里的山里进发。这次是夏天,没有套兔子那次的寒冷,我坐在后座上认真欣赏路边的风景(其实除了树就是沙子),这风景直到我们快到山口才发生改变(一条高山融水形成的河流拦住了我们的去路,由于夏天和山势的缘故河流非常湍急)。父亲倒是非常淡定,一边骑车顺着河流走,一边告诉我说某个地方河流会变窄,摩托车可以从那里横穿过去。等我们到了父亲说的那个地方,我才发现事实上没有那么简单,由于河道变窄,水流流速更快,横跨水面的有几块大石头,人倒是可以跳过去,但摩托车是问题多多。父亲让我先去了对岸,然后我在对岸全程观摩父亲的车技:在摩托车横穿河流失去平衡即将被冲倒的瞬间,父亲用脚做支点,给摩托车了一个作用力,保证车在倾倒前冲到了对岸。车是到了对岸,但父亲的鞋子,也湿透了。牧民家在进山后要过两座山,这距离摩托车跑起来也就不到一个小时,但我们徒步赶羊走了快三个小时。当我们把羊赶出山口的时候,月亮已经悄悄探出了头……</p>
<p></p><p style="text-indent:2em">如此的事情,数不胜数,但父亲从未在电话中向我们提起,也不会在饭桌上诉说。而作为儿子的我,也总是以为,等到我毕业有了工作,可以有足够的时间去回报父亲的辛苦。</p>
<p></p><p style="text-indent:2em">最近跟父亲圈子的人接触,他们说的最多的就是父亲喜欢夸自己的两个儿子,可是,父亲你又怎么会知道,没有你那聪明的基因,你那两个儿子又怎么能够读到研究生?“慧极必伤,情深不寿”,老天是有多么嫉妒你,才会在你两个儿子即将成才的时候把你带走?你走的又是多么的急,都没有告诉你的儿子,如何在没有你的陪伴下走接下来的人生路!</p>
<p></p><p style="text-indent:2em">曾经我一直在幻想这样一个场景:你喝了点小酒,开始和我算我从小到大花了多少钱,我掏出银行卡放在你面前,对你说到“爸拿去,算完了以后你去取就行“,现在我要准备这么一张银行卡了,可是你却等不到取钱的时候了。</p>
<p></p><p style="text-indent:2em">以前我总是说你怎么孝敬爷爷我就怎么孝敬你,可你却不声不响的走在了爷爷的前面;以前我总说以后要学你一包烟一瓶酒,可你却还没等到我和你同桌对饮就离开了这喧嚣的世间;以前你总说要给我找个漂亮的女生,可现在八字还没一撇你就撒手不管了;以前你总说要开着车带我们一起出去旅游,可现在我刚拿到驾照等着你教我开车你却对我不闻不问了。</p>
<p></p><p style="text-indent:2em">人家都说我们两兄弟孝顺,可你连让我孝顺你的机会都不给我。哥哥说要整理你的照片,我才发现,这么久了,这个家里长久以来最被我忽视的人是你,因为你是家里的顶梁柱,所以我默认了你的坚强。这些天看到一些老年人头发花白,我就在心里想象你头发白了的话会是什么样子,可是我想象不出来。</p>
<p></p><p style="text-indent:2em">我有一个好爸爸,他和我没有所谓的代沟,他特别喜欢叫我的名字玩,他干啥事都喜欢带着我,他会在我每次离家的时候都多拿几百块钱给我,他每次都会在电话里都会告诉我如果没钱了找他要;他遇到谁都会跟人家说他的儿子多么多么优秀,他会在想喝酒的时候问儿子能不能给他买只因为儿子发脾气说他再喝酒就不理他;他还没有开着车带着家人去旅游,他还没有拿到他儿子答应还他的钱,他还没有回老家安家,他还没有看到两个儿子成家立业。但是他困了,所以他先睡了。</p>
<p></p><p style="text-indent:2em">爸,好好休息吧,不打扰你了。事情我们都会一件一件去做,我想说的只是,我想你了。</p>
]]></content>
<summary type="html">
<![CDATA[<p></p><p style="text-indent:2em">终于有时间写点关于父亲的文字了。</p><br>]]>
</summary>
<category term="other" scheme="whtis.github.io/categories/other/"/>
</entry>
<entry>
<title><![CDATA[这篇博文,我不想写技术了。。]]></title>
<link href="whtis.github.io/2017/11/27/%E8%BF%99%E7%AF%87%E5%8D%9A%E6%96%87%EF%BC%8C%E6%88%91%E4%B8%8D%E6%83%B3%E5%86%99%E6%8A%80%E6%9C%AF%E4%BA%86%E3%80%82%E3%80%82/"/>
<id>whtis.github.io/2017/11/27/这篇博文,我不想写技术了。。/</id>
<published>2017-11-27T12:23:14.000Z</published>
<updated>2018-01-24T18:23:00.055Z</updated>
<content type="html"><![CDATA[<p>最近闷的很,特别是发现打游戏太浪费时间然后远离王者荣耀后,更加的烦闷了。</p>
<p>但在刷社交软件的时候,看到了些让我醍醐灌顶的句子。懒得查原出处了,只是记录下我看到这些句子后的想法吧。</p>
<blockquote>
<p>1、中国的强大,是资本的强大,与平民没多大关系,我们只是凑巧的,与这一事件处于同一时代。</p>
</blockquote>
<p>看到这句话和接下来该作者的分析,我发现这真是透过现象看本质的绝佳例子。在当今社会,一切向钱看已经是不争的事实。学术圈的浮躁,商业圈的谎言,一切的一切只是为了能够使自身的利益最大化。</p>
<p>我们一直强调的中华传统美德,在资本面前,无影无踪。信任,这个历史上让人最着迷的东西,在现代社会已经快成为杀人工具了。中学学到唐朝时,特别憧憬生活在那种天朝大国,万方朝拜时代,那时候夜不闭户,老幼皆有所养。印象特别深刻的是长途跋涉的书生可以单凭借宿从家乡赶到长安。反观现在的天朝,经济总量跃居全球第二,但人呢?开车撞人说没钱赔,转身买房买车;好友为了救她被她男友杀害,她安心过年发自拍;妈妈说自己的孩子在幼儿园被性侵,国家的第一反应不是控制有关人员,而是控制舆论;大火烧死了几十人,原因是因为违建,于是寒冬腊月,断水断电砸玻璃,强制几十万人几天内撤离首都。谁说人心都是肉长的?在权钱的支配下,连人这个身份都可以放弃,人心算什么?</p>
<blockquote>
<p>2、我们生活在一个最好的时代,也是一个最坏的时代。</p>
</blockquote>
<p>一个时代的好坏,不能因为几件事情而定性。</p>
<p>这个时代,强调联系。小到人与人,大到国与国。就算你不想主动联系,凭借现代化的高科技,他们也能让你被联系。因此,这个时代里,没有人能够“采菊东篱下,悠然见南山”,没有人能“处江湖之远”。因此,这个时代的好坏,取决于这个时代的每个人。</p>
<p>倘若大多数人,放弃了对真理的追寻,偏居一隅,秉持“个人自扫门前雪,哪管他人瓦上霜”的处世哲学,谨言慎行,这个时代一定是一个崩坏的时代。可悲的是,在当下权钱至上的环境里,越来越多的人,甚至连偏居一隅都无法做到了。</p>
<blockquote>
<p>3、你要承认的是,当前你获得的所有,都是建立在剥削比你更弱小的人的基础上的</p>
</blockquote>
<p>人人平等,这只能是个口号。让你同情工人可以,但让你放弃豪华舒适的生活去和工人打成一片,你绝对不乐意。你现在对于比你层次更高人的不满意,只是因为你内心极度渴望成为他们中的一员。</p>
<blockquote>
<p>4、移民:二等洋人和四等汉人,你愿意做哪个?</p>
</blockquote>
<p>世界之间的距离越来越小。国内环境差,就可以去其他国家了。殊不知,除去移民的难度,那边的生活也没有想象中的美好。于是,在二等洋人和四等汉人之间做个选择吧,为啥要做这个选择呢,因为归根结底,生活是你自己的事。如果二等洋人和四等汉人是一个级别,你如果在国内是三等汉人的水平,那就没必要去国外了。</p>
<blockquote>
<p>5、评价一个人的好坏,在于你是否相信他,他是否会倾听你。这个道理,放在国家层面,也是成立的。</p>
</blockquote>
<p>这句话,是我说的。</p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章不接受任何形式的转载</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<p>最近闷的很,特别是发现打游戏太浪费时间然后远离王者荣耀后,更加的烦闷了。</p>
<p>但在刷社交软件的时候,看到了些让我醍醐灌顶的句子。懒得查原出处了,只是记录下我看到这些句子后的想法吧。</p>
<blockquote>
<p>1、中国的强大,是资本的强大,与平民没多大]]>
</summary>
<category term="想法,社会" scheme="whtis.github.io/tags/%E6%83%B3%E6%B3%95%EF%BC%8C%E7%A4%BE%E4%BC%9A/"/>
<category term="随笔" scheme="whtis.github.io/categories/%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title><![CDATA[spring4学习笔记(五)--AOP面向切面编程]]></title>
<link href="whtis.github.io/2017/11/04/spring4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%BA%94%EF%BC%89-AOP%E9%9D%A2%E5%90%91%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B/"/>
<id>whtis.github.io/2017/11/04/spring4学习笔记(五)-AOP面向切面编程/</id>
<published>2017-11-04T14:14:48.000Z</published>
<updated>2017-11-23T09:25:11.608Z</updated>
<content type="html"><![CDATA[<h2 id="AOP_简介">AOP 简介</h2><ul>
<li>AOP(Aspect-Oriented Programming,面向切面编程):是对传统的<code>OOP</code>(Object-Oriented Programming 面向对象编程)的补充。</li>
<li>AOP 的主要编程对象是<code>切面(aspect)</code>,和<code>切面模块化横切关注点</code>。</li>
<li>在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊对象(切面)里。</li>
<li>AOP的好处:<ul>
<li>每个事物逻辑位于一个位置,代码不分散,便于维护和升级</li>
<li>业务模块更简洁,只包含核心业务代码</li>
</ul>
</li>
<li>AOP 很适合做日志、参数验证等工作。</li>
</ul>
<h2 id="AOP_术语">AOP 术语</h2><ul>
<li>切面(Aspect): 横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象</li>
<li>通知(Advice): 切面必须要完成的工作</li>
<li>目标(Target): 被通知的对象</li>
<li>代理(Proxy): 向目标对象应用通知之后创建的对象</li>
<li>连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点;相对点表示的方位。</li>
<li>切点(pointcut):每个类都拥有多个连接点:一个类的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP 通过切点定位到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过 <code>org.springframework.aop.Pointcut</code>接口进行描述,它使用类和方法作为连接点的查询条件。</li>
</ul>
<h2 id="Spring_AOP">Spring AOP</h2><h3 id="AspectJ">AspectJ</h3><p>在 Spring2.0以上版本中, 可以使用基于AspectJ注解或基于XML配置的AOP</p>
<h4 id="在Spring中启用AspectJ注解支持">在Spring中启用AspectJ注解支持</h4><ul>
<li>要在Spring应用中使用AspectJ注解, 必须在classpath下包含AspectJ类库: aopalliance.jar、aspectj.weaver.jar 和 spring-aspects.jar</li>
<li>要在Spring IOC容器中启用AspectJ注解支持, 只要在Bean配置文件中定义一个空的XML元素 <aop:aspectj-autoproxy><br>当 Spring IOC容器侦测到Bean配置文件中的 <aop:aspectj-autoproxy> 元素时, 会自动为与AspectJ切面匹配的Bean创建代理.</aop:aspectj-autoproxy></aop:aspectj-autoproxy></li>
</ul>
<h4 id="用AspectJ注解声明切面">用AspectJ注解声明切面</h4><ul>
<li>要在Spring中声明AspectJ切面, 只需要在IOC容器中将切面声明为Bean实例。 当在Spring IOC容器中初始化AspectJ切面之后, Spring IOC容器就会为那些与AspectJ切面相匹配的 Bean 创建代理。</li>
<li>在 AspectJ 注解中, 切面只是一个带有<code>@Aspect</code> 注解的Java类。</li>
<li>通知是标注有某种注解的简单的 Java 方法。</li>
<li>AspectJ 支持 5 种类型的通知注解:<ul>
<li><code>@Before</code>: 前置通知, 在方法执行之前执行</li>
<li><code>@After</code>: 后置通知, 在方法执行之后执行</li>
<li><code>@AfterRunning</code>: 返回通知, 在方法返回结果之后执行</li>
<li><code>@AfterThrowing</code>: 异常通知, 在方法抛出异常之后</li>
<li><code>@Around</code>: 环绕通知, 围绕着方法执行</li>
</ul>
</li>
<li>切入点表达式的写法一般是<code>execution(...)</code>,我们可以通过声明一个标有<code>#Pointcut(exection(...))</code>的空方法,来对切入点表达式进行<strong>合并</strong>。</li>
</ul>
<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">@Pointcut</span>(<span class="string">"exection(...) || exection(...)"</span>)</div><div class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">loggingPoeration</span><span class="params">()</span></span>{}</div></pre></td></tr></table></figure>
<ul>
<li>切入点方法的访问控制符同时也控制着这个切入点的可见性。如果切入点要在多个切面中共用,最好将它们集中在一个公共的类中。在这种情况下,它们必须被声明为<code>public</code>。在引入这个切入点时,必须将类名也包括在内。如果类没有与这个切面放在同一个包中,还必须包含包名。</li>
<li>其他通知可以通过方法名称引入该切入点。<ul>
<li>为了让通知访问当前连接点的细节,我们可以在通知方法中声明一个类型为<code>JoinPoint</code>的参数,从而访问到方法名称、参数值、异常通知这些东西。</li>
</ul>
</li>
</ul>
<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="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">loggingPoeration</span><span class="params">(JointPoint jointPoint)</span></span>{</div><div class="line"> String methodName = jointPoint.getSignature().getName();</div><div class="line"> System.out.println(Arrays.toString(jointPoint.getArgs()));</div><div class="line">}</div></pre></td></tr></table></figure>
<ul>
<li>一个切点可以包含一个或多个通知。</li>
<li>在同一个连接点上应用不止一个切面时,除非明确指定,否则它们的优先级是不确定的。<ul>
<li>切面的优先级可以通过实现<code>Ordered</code>接口或利用<code>@Order</code>注解指定。</li>
<li>使用<code>@Orader</code>注解,序号越小,优先级越高。</li>
</ul>
</li>
</ul>
<h4 id="用基于XML配置文件声明切面">用基于XML配置文件声明切面</h4><ul>
<li>正常情况下,基于注解的声明要优先于基于XML的声明。通过AspectJ注解,切面可以与AspectJ兼容,而基于XML的配置则是Spring专有的。由于 AspectJ得到越来越多的AOP框架支持,所以以注解风格编写的切面将会有更多重用的机会。</li>
<li>一个完整的<code>AspectJ</code>基于xml文件的配置如下:</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">aop:config</span>></span></div><div class="line"> <span class="tag"><<span class="name">aop:pointcut</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">expression</span>=<span class="string">"execution(xxx)"</span>/></span></div><div class="line"> <span class="tag"><<span class="name">aop:aspect</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">ref</span>=<span class="string">""</span> <span class="attr">order</span>=<span class="string">""</span>></span></div><div class="line"> <span class="tag"><<span class="name">aop:before</span> <span class="attr">method</span>=<span class="string">"xxx"</span> <span class="attr">pointcut-ref</span>=<span class="string">""</span>/></span></div><div class="line"> <span class="tag"><<span class="name">aop:after</span> <span class="attr">method</span>=<span class="string">""</span> <span class="attr">pointcut-ref</span>=<span class="string">""</span>/></span></div><div class="line"> <span class="tag"></<span class="name">aop:aspect</span>></span></div><div class="line"><span class="tag"></<span class="name">aop:config</span>></span></div></pre></td></tr></table></figure>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h2 id="AOP_简介">AOP 简介</h2><ul>
<li>AOP(Aspect-Oriented Programming,面向切面编程):是对传统的<code>OOP</code>(Object-Oriented Programming 面向对象编程)的补充。</l]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="spring4" scheme="whtis.github.io/tags/spring4/"/>
<category term="java web" scheme="whtis.github.io/categories/java-web/"/>
</entry>
<entry>
<title><![CDATA[给hexo博客nexT主题添加Gitalk评论系统]]></title>
<link href="whtis.github.io/2017/10/19/%E7%BB%99hexo%E5%8D%9A%E5%AE%A2nexT%E4%B8%BB%E9%A2%98%E6%B7%BB%E5%8A%A0Gitalk%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F/"/>
<id>whtis.github.io/2017/10/19/给hexo博客nexT主题添加Gitalk评论系统/</id>
<published>2017-10-19T06:58:47.000Z</published>
<updated>2017-11-23T09:25:11.608Z</updated>
<content type="html"><![CDATA[<h2 id="写在前面">写在前面</h2><p><strong>注意,此插件基于github开发,授权阶段需要获取的权限不局限于<code>issues</code>,还包括编辑你代码的权限,因此对这应用授权时需要十分小心</strong><br><img src="authorization.jpg" alt="authorization"></p>
<p>前段时间因为<code>hexo</code>的<code>nexT</code>主题崩了(载入博客时显示全白,<a href="https://www.zhihu.com/question/52455581" target="_blank" rel="external">原因在这</a>),换了<code>material</code>主题,这主题很有安卓的风格,我也写过对它的一些折腾:<a href="hexo博客主题--material折腾笔记">hexo博客主题–material折腾笔记</a>。<br>这些都不是重点,重点的是今年6月份,多说离开了。为此我专门注册了<code>disqus</code>,搭配着<code>material</code>主题进行使用。使用期间,让我很郁闷的是连黄图哥都懒得评论QAQ,再加上前几天博客莫名的多出来了很多有规律的访问(没错,是爬虫),所以为了防止攻击,我又回归了<code>nexT</code>的怀抱。<br>下面主要记录下我将评论系统<code>Gitalk</code>集成到<code>nexT</code>的过程。</p>
<h3 id="为什么选择Gitalk">为什么选择<code>Gitalk</code></h3><p>多说消失后,原先与多说竞争最激烈的<code>disqus</code>看起来似乎是最大赢家。然而在天朝的环境中,它没有像想象中那样一跃成为主流的评论系统。而在国内,随着多说的消失,<code>畅言</code>、<code>网易云跟帖</code>等等产品都跃跃欲试,想取代多说。然而现实是,网易云跟帖活了不到两个月官方就宣布不再为第三方博客提供服务,畅言虽然很活跃,但鉴于国内的大环境,我还是决定寻找一款国外的评论系统。<br><code>nexT</code>主题集成了很多第三方评论系统,其中有一款很和我意:<code>gitment</code>。它由国内大神编写,基于<code>GitHub Issues</code>编写。详细的内容可以<a href="https://imsun.net/posts/gitment-introduction/" target="_blank" rel="external">看这里</a>。然而不幸的是,作者似乎弃坑了,不再进行维护,而且查看<code>issues</code>发现<code>gitment</code>在移动端貌似使用不了。<br>然后,我就发现了<code>Gitalk</code>,这个评论系统和<code>gitment</code>类似,且有人一直在维护,移动端也可以使用。因此我决定模仿<code>gitment</code>将<code>Gitalk</code>集成到<code>nexT</code>主题中。</p>
<h3 id="Gitalk和nexT主题目录的简要介绍"><code>Gitalk</code>和<code>nexT</code>主题目录的简要介绍</h3><p>在实际修改之前,我们先了解下Gitalk和nexT项目的目录文件,这方便我们理解后续集成的思路和使用的方法。</p>
<h4 id="Gitalk_简介">Gitalk 简介</h4><blockquote>
<p>Gitalk 是一个基于 Github Issue 和 Preact 开发的评论插件。</p>
<ul>
<li>特性:<ul>
<li>使用 Github 登录</li>
<li>支持多语言 [en, zh-CN, zh-TW, es-ES, fr]</li>
<li>无干扰模式(设置 distractionFreeMode 为 true 开启)</li>
<li>快捷键提交评论 (cmd|ctrl + enter)</li>
</ul>
</li>
</ul>
</blockquote>
<p>以上内容摘自github,详细的内容请查看<a href="https://github.com/gitalk/gitalk/blob/master/readme-cn.md" target="_blank" rel="external">Gitalk中文文档</a>。</p>
<h4 id="nexT_目录说明">nexT 目录说明</h4><p>nexT 主题的源码结构非常清晰,下面对目录树进行简要说明。<strong>注意:主题迭代频繁,博主使用的版本是5.1.3,开发前请确认自己使用的版本目录树是否与本文所写一致</strong></p>
<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><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">├── languages 博客语言配置</div><div class="line">├── layout 博客的整体布局</div><div class="line">│ ├── _custom 用户自定义配置文件(仅支持对主界面布局进行修改)</div><div class="line">│ ├── _macro </div><div class="line">│ ├── _partials 博客组件的配置</div><div class="line">│ ├── _scripts</div><div class="line">│ └── _third-party 第三方插件</div><div class="line">├── scripts</div><div class="line">│ └── tags</div><div class="line">├── source 博客使用的样式目录</div><div class="line">│ ├── css </div><div class="line">│ ├── fonts</div><div class="line">│ ├── images</div><div class="line">│ ├── js</div><div class="line">│ └── lib</div><div class="line">└── test</div></pre></td></tr></table></figure>
<h3 id="集成的步骤">集成的步骤</h3><p>如果要达到我们最初的目的,需要修改以下内容:</p>
<ul>
<li><code>layout/_partials/comments.swig</code></li>
<li><code>layout/_third-party/comments/index.swig</code></li>
<li><code>_config.yml</code><br>还需要在<code>layout/_third-party/comments/</code>路径下,添加我们自己的文件<code>gitalk.swig</code>。下面我们一步一步来。</li>
</ul>
<h4 id="编写我们自己的插件文件gitalk-swig">编写我们自己的插件文件<code>gitalk.swig</code></h4><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><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">{% if not (theme.duoshuo and theme.duoshuo.shortname) and not theme.duoshuo_shortname %}</div><div class="line">{% if theme.gitalk.enable and theme.gitalk.client_id %}</div><div class="line"><!-- LOCAL: You can save these files to your site and update links --></div><div class="line"> {% set CommentsClass = "Gitalk" %}</div><div class="line"> <link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css"></div><div class="line"> <script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script></div><div class="line"><!-- END LOCAL --></div><div class="line"></div><div class="line"> {% if page.comments %}</div><div class="line"> <script type="text/javascript"></div><div class="line"> function renderGitalk(){</div><div class="line"> var gitalk = new {{CommentsClass}}({</div><div class="line"> owner: '{{ theme.gitalk.github_user }}',</div><div class="line"> repo: '{{ theme.gitalk.github_repo }}',</div><div class="line"> clientID: '{{ theme.gitalk.client_id }}',</div><div class="line"> clientSecret: '{{ theme.gitalk.client_secret }}',</div><div class="line"> admin: '{{ theme.gitalk.admin }}',</div><div class="line"> {% if theme.gitalk.distractionFreeMode %}</div><div class="line"> distractionFreeMode: '{{ theme.gitalk.distractionFreeMode }}'</div><div class="line"> {% endif %}</div><div class="line"> });</div><div class="line"> gitalk.render('gitalk-container');</div><div class="line"> }</div><div class="line"> renderGitalk();</div><div class="line"> </script></div><div class="line"> {% endif %}</div><div class="line"></div><div class="line">{% endif %}</div><div class="line">{% endif %}</div></pre></td></tr></table></figure>
<h4 id="把编写好的插件添加到hexo主题中">把编写好的插件添加到hexo主题中</h4><p>在<code>layout/_third-party/comments/index.swig</code>中添加下面语句:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">{% include 'gitalk.swig' %}</div></pre></td></tr></table></figure></p>
<h4 id="根据条件判断是否启用该插件">根据条件判断是否启用该插件</h4><p>在<code>layout/_partials/comments.swig</code>中相应的地方加入如下语句:<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></pre></td><td class="code"><pre><div class="line">{% elseif theme.gitalk.enable %}</div><div class="line"> <div class="comments" id="comments"></div><div class="line"> <div id="gitalk-container"></div></div><div class="line"> </div></div></pre></td></tr></table></figure></p>
<h4 id="在主题配置文件中配置有关Gitalk的信息">在主题配置文件中配置有关<code>Gitalk</code>的信息</h4><p>编辑<code>_config.yml</code>文件,在需要配置<code>gitalk</code>的地方加入如下内容:</p>
<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></pre></td><td class="code"><pre><div class="line">gitalk:</div><div class="line"> enable: true</div><div class="line"> github_user: # owner</div><div class="line"> github_repo: # repo</div><div class="line"> client_id: # clientID</div><div class="line"> client_secret: # clientSecret </div><div class="line"> admin: # Github repository collaborators. (Ensure having write access to this repository)</div><div class="line"> distractionFreeMode: # Facebook-like distraction free mode</div></pre></td></tr></table></figure>
<p>至此,大功告成。<strong>但需要注意的是:</strong> 当使用<code>hexo s -d</code>本地调试时你可能会看到</p>
<figure class="highlight plain"><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">未找到相关的 Issues 进行评论</div><div class="line">请联系 @whtis 初始化创建</div></pre></td></tr></table></figure>
<p>出现了这个提示不要紧,你只要部署后,使用github登录就好了。</p>
<h2 id="博客的其他修改">博客的其他修改</h2><ul>
<li>因为很喜欢<code>material</code>主题同步<code>bing</code>每日壁纸的功能,所以也把hexo的背景图片与bing壁纸进行同步。实现这个功能,只需要编辑<code>source/css/_custom/custom.styl</code>文件,加入如下内容即可:<figure class="highlight css"><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="selector-tag">html</span>, <span class="selector-tag">body</span> {</div><div class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</div><div class="line"> <span class="attribute">background-image</span>: <span class="built_in">url</span>(<span class="string">"http://api.dujin.org/bing/1920.php"</span>);</div><div class="line"> <span class="attribute">background-repeat</span>: no-repeat;</div><div class="line"> <span class="attribute">background-attachment</span>:fixed;</div><div class="line"> <span class="attribute">background-position</span>:<span class="number">50%</span> <span class="number">50%</span>;</div><div class="line"> <span class="attribute">background-size</span>: cover;</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ul>
<p>其中,image的url是其他博主提供的<a href="https://www.dujin.org/fenxiang/jiaocheng/3618.html" target="_blank" rel="external">api接口</a>,目前看来还是很稳定的。后续也可以考虑自己抓取bing的壁纸,有关这部分内容,可以参考这篇博文:<a href="http://www.cnblogs.com/liujianshe1990-/archive/2017/10/07/7635339.html" target="_blank" rel="external">获取Bing每日图片API接口</a></p>
<ul>
<li>将<code>post</code>、<code>sidebar</code>和<code>header-inner</code>透明度调成80%,这是为了较好的突出背景。实现方法:编辑<code>source/css/_custom/custom.styl</code>文件,加入如下内容即可:</li>
</ul>
<figure class="highlight css"><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="selector-class">.header-inner</span> {</div><div class="line"> <span class="attribute">opacity</span>: <span class="number">0.8</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="selector-class">.main-inner</span> {</div><div class="line"> <span class="attribute">opacity</span>: <span class="number">0.8</span>;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="selector-class">.sidebar</span> {</div><div class="line"> <span class="attribute">opacity</span>: <span class="number">0.8</span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<ul>
<li>其他修改。参考文章见文末。</li>
</ul>
<h2 id="后记">后记</h2><p>看了<code>next</code>的<code>issues</code>发现已经有人在两个月前<code>Pull requests</code>了,但还在<code>open</code>状态,怪不得我<code>clone</code>的时候找不到这个插件。</p>
<p><del>最后,有同学如果也研究过hexo的,欢迎教我怎么把<strong>文章那块(<code>post-block</code>)调成半透明的</strong>。。。楞是没有找到修改哪里才能生效⊙﹏⊙</del></p>
<p>最后的最后,附一个比较全面的hexo折腾总结:<a href="http://blog.csdn.net/qq_33699981/article/details/72716951" target="_blank" rel="external">hexo的next主题个性化教程:打造炫酷网站</a></p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h2 id="写在前面">写在前面</h2><p><strong>注意,此插件基于github开发,授权阶段需要获取的权限不局限于<code>issues</code>,还包括编辑你代码的权限,因此对这应用授权时需要十分小心</strong><br><img src="auth]]>
</summary>
<category term="hexo,Gitalk,nexT" scheme="whtis.github.io/tags/hexo%EF%BC%8CGitalk%EF%BC%8CnexT/"/>
<category term="hexo" scheme="whtis.github.io/categories/hexo/"/>
</entry>
<entry>
<title><![CDATA[spring4学习笔记(四)--Spring-4.x新特性:泛型依赖注入]]></title>
<link href="whtis.github.io/2017/10/13/spring4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E5%9B%9B%EF%BC%89--Spring-4-x%E6%96%B0%E7%89%B9%E6%80%A7%EF%BC%9A%E6%B3%9B%E5%9E%8B%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5/"/>
<id>whtis.github.io/2017/10/13/spring4学习笔记(四)--Spring-4-x新特性:泛型依赖注入/</id>
<published>2017-10-13T07:09:57.000Z</published>
<updated>2017-10-20T08:42:09.363Z</updated>
<content type="html"><![CDATA[<p>Spring4 添加了泛型依赖注入的功能。具体的,从下面这张图来看:<br><img src="spring4.x泛型依赖注入.jpg" alt="spring4.x泛型依赖注入"></p>
<p>假使我们创建了两个泛型基类<code>BaseService<T></code>和<code>BaseRepository<T></code>,并分别进行了实现<code>UserService<User></code>和<code>UserRepository<User></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></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BaseService</span><<span class="title">T</span>> </span>{</div><div class="line"></div><div class="line"> <span class="meta">@Autowired</span></div><div class="line"> <span class="keyword">protected</span> BaseRepository baseRepository;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">()</span> </span>{</div><div class="line"> System.out.println(baseRepository);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>而<code>UserService<User></code>类仅仅实现了<code>BaseService</code>的接口,没有重写<code>add</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></pre></td><td class="code"><pre><div class="line"><span class="meta">@service</span></div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserService</span><<span class="title">User</span>> <span class="title">implement</span> <span class="title">BaseService</span></span>{</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<p><code>UserRepository<User></code>与<code>UserService<User></code>类似。将这些Bean交给spring IOC进行管理,创建<code>main</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></pre></td><td class="code"><pre><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>{</div><div class="line"> ApplicationContext ctx = <span class="keyword">new</span> ClassPathXmlApplicationContext(<span class="string">"xxx.xml"</span>);</div><div class="line"> UserService userService = (UserService) ctx.getBean(<span class="string">"userService"</span>);</div><div class="line"> <span class="comment">//调用userService的add方法</span></div><div class="line"> UserService.add();</div><div class="line"> }</div><div class="line"></div><div class="line">}</div></pre></td></tr></table></figure>
<p>可以看到<code>System.out.println(baseRepository);</code>的输出结果是一个<code>userRepository</code>类。<br>由此可知,spring4自动将<code>User</code>类传入了<code>baseRepository</code>的继承类中。</p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<p>Spring4 添加了泛型依赖注入的功能。具体的,从下面这张图来看:<br><img src="spring4.x泛型依赖注入.jpg" alt="spring4.x泛型依赖注入"></p>
<p>假使我们创建了两个泛型基类<code>BaseService<T>]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="spring4" scheme="whtis.github.io/tags/spring4/"/>
<category term="java web" scheme="whtis.github.io/categories/java-web/"/>
</entry>
<entry>
<title><![CDATA[spring4学习笔记(三)--SpEL:Spring表达式语言]]></title>
<link href="whtis.github.io/2017/10/13/spring4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%89%EF%BC%89--SpEL-Spring%E8%A1%A8%E8%BE%BE%E5%BC%8F%E8%AF%AD%E8%A8%80/"/>
<id>whtis.github.io/2017/10/13/spring4学习笔记(三)--SpEL-Spring表达式语言/</id>
<published>2017-10-13T07:03:21.000Z</published>
<updated>2017-10-20T08:49:35.933Z</updated>
<content type="html"><![CDATA[<h2 id="spEL:Spring_表达式语言">spEL:Spring 表达式语言</h2><ul>
<li>是一个支持运行时查询和操作对象图的强大的表达式语言。</li>
<li>语法类似于EL:SpEL使用<code>#{...}</code>作为定界符,所有在大框号中的字符都将被认为是<code>SpEL</code></li>
<li>SpEL为Bean的属性进行动态赋值提供了便利。</li>
<li>通过SpEL可以实现:<ul>
<li>通过 bean 的 <code>id</code> 对 bean 进行引用</li>
<li>调用方法以及引用对象中的属性</li>
<li>计算表达式的值</li>
<li>正则表达式的匹配</li>
</ul>
</li>
</ul>
<h3 id="SpEL_可以表示基本的字面值">SpEL 可以表示基本的字面值</h3> <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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"dataSource"</span> <span class="attr">class</span>=<span class="string">"com.mchange.v2.c3p0.ComboPooledDataSource"</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"user"</span> <span class="attr">value</span>=<span class="string">"#{2}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">value</span>=<span class="string">"#{3.14}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"driverClass"</span> <span class="attr">value</span>=<span class="string">"#{false}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"jdbcUrl"</span> <span class="attr">value</span>=<span class="string">"#{“whtis”}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h3 id="SpEL_引用_bean、属性和方法">SpEL 引用 bean、属性和方法</h3><ul>
<li>引用其他对象:</li>
</ul>
<figure class="highlight xml"><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"><!-- 通过 value 属性和 SpEL 配置 bean 之间的应用关系 --></span></div><div class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"prefix"</span> <span class="attr">value</span>=<span class="string">"#{prefixGenerator}"</span>></span><span class="tag"></<span class="name">property</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>引用其他对象的属性:</li>
</ul>
<figure class="highlight xml"><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"><!-- 通过 value 属性和 SpEL 配置 bean 之间的应用关系 --></span></div><div class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"suffix"</span> <span class="attr">value</span>=<span class="string">"#{sequenceGenerator2.suffix}"</span>></span><span class="tag"></<span class="name">property</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>调用其他方法,还可以链式操作</li>
</ul>
<figure class="highlight xml"><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"><!-- 通过 value 属性和 SpEL 配置 suffix 属性值为另一个 bean 的方法的返回值 --></span></div><div class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"suffix"</span> <span class="attr">value</span>=<span class="string">"#{sequenceGenerator2.toString()}"</span>></span><span class="tag"></<span class="name">property</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>调用静态方法或静态属性:通过<code>T()</code>调用一个类的静态方法,它将返回一个 <code>Class Object</code>,然后再调用相应的方法或属性。</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"initValue"</span> <span class="attr">value</span>=<span class="string">#{T(java.lang.Math).PI}</span>></span><span class="tag"></<span class="name">property</span>></span></div></pre></td></tr></table></figure>
<h3 id="SpEL支持的运算符号">SpEL支持的运算符号</h3><ul>
<li>算数运算符:<code>+,-,×,/,%,^</code></li>
<li>加号还可以用作字符串连接</li>
<li>比较运算符:<code><,>,==,<=,>=,lt,gt,eq,le,ge</code></li>
<li>逻辑运算符:<code>and,or,not,|</code></li>
<li>if-else运算符:<code>?:(temary),?:(Elvis)</code></li>
<li>if-else的变体</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">constructor-arg</span> <span class="attr">value</span>=<span class="string">"#{kenny.song:'Greensleeves'}"</span></span></div></pre></td></tr></table></figure>
<ul>
<li>正则表达式:<code>matches</code></li>
</ul>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h2 id="spEL:Spring_表达式语言">spEL:Spring 表达式语言</h2><ul>
<li>是一个支持运行时查询和操作对象图的强大的表达式语言。</li>
<li>语法类似于EL:SpEL使用<code>#{...}</code>作为定界符,所有在大框号中]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="spring4" scheme="whtis.github.io/tags/spring4/"/>
<category term="java web" scheme="whtis.github.io/categories/java-web/"/>
</entry>
<entry>
<title><![CDATA[spring4学习笔记(二)--spring中Bean的配置]]></title>
<link href="whtis.github.io/2017/10/13/spring4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%BA%8C%EF%BC%89--spring%E4%B8%ADBean%E7%9A%84%E9%85%8D%E7%BD%AE/"/>
<id>whtis.github.io/2017/10/13/spring4学习笔记(二)--spring中Bean的配置/</id>
<published>2017-10-13T03:54:06.000Z</published>
<updated>2017-10-20T08:49:18.062Z</updated>
<content type="html"><![CDATA[<h1 id="配置形式">配置形式</h1><h2 id="基于xml文件的方式">基于xml文件的方式</h2><h3 id="Bean的配置方式">Bean的配置方式</h3><h4 id="通过全类名(反射)">通过全类名(反射)</h4><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></pre></td><td class="code"><pre><div class="line"><!--</div><div class="line"> 通过全类名配置bean</div><div class="line"> class:bean 的全类名,通过反射的方式在 IOC 容器中创建 Bean。所以要求 Bean 中必须有无参数的构造器。</div><div class="line"> id: 表示容器中的Bean,唯一。</div><div class="line"> <Bean id="" class=""></Bean></div><div class="line"> --></div></pre></td></tr></table></figure>
<h4 id="依赖注入的方式">依赖注入的方式</h4><h5 id="构造器注入">构造器注入</h5><ul>
<li>通过构造方法注入 Bean 的属性值或依赖的对象,它保证了 Bean 实例在实例化后就可以使用。</li>
<li>构造器注入在<code><constructor-arg></code>元素里声明属性,该元素没有<code>name</code>属性</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 通过构造方法来配置 Bean 属性 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="comment"><!-- 使用构造器注入属性值可以指定参数的位置和参数的类型! 以区分重载的构造器!--></span></div><div class="line"> <span class="tag"><<span class="name">constructor-arg</span> <span class="attr">value</span>=<span class="string">""</span> (<span class="attr">type</span>=<span class="string">""</span> <span class="attr">or</span> <span class="attr">index</span>=<span class="string">""</span>) ></span><span class="tag"></<span class="name">constructor-arg</span>></span></div><div class="line"> <span class="tag"><<span class="name">constructor-arg</span>></span></div><div class="line"> <span class="comment"><!-- 字面值注入,基本数据类型及其封装类、String等类型都可以采取字面值注入的方式 --></span></div><div class="line"> <span class="tag"><<span class="name">value</span>></span>250<span class="tag"></<span class="name">value</span>></span></div><div class="line"> <span class="comment"><!-- 如果字面值包含特殊字符可以使用<![CDATA[]]>包裹起来 --></span></div><div class="line"> <span class="tag"><></span><![CDATA[<beijing>]]><span class="tag"><></span></div><div class="line"> <span class="tag"></<span class="name">constructor-arg</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h5 id="属性注入">属性注入</h5><ul>
<li>属性注入即通过<code>setter</code>方法注入Bean的属性值或依赖的对象</li>
<li>属性注入使用 <code><property></code>元素,使用<code>name</code>属性指定 Bean 的属性名称,<code>value</code> 属性或<code><value></code> 子节点指定属性值。</li>
<li>属性注入是实际应用中最常用的注入方式。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 通过全类名的方式来配置 bean --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">""</span> <span class="attr">value</span>=<span class="string">""</span> ></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h6 id="引用其他_Bean">引用其他 Bean</h6><ul>
<li>在 Bean 的配置文件中,可以通过<code><ref></code>元素或者<code>ref</code>属性为Bean的属性或构造器参数指定对Bean的引用</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">""</span> <span class="attr">value</span>=<span class="string">""</span> <span class="attr">ref</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="comment"><!--</span></div><div class="line"> <property name="" value="" ></div><div class="line"> <ref bean=""/></div><div class="line"> </property></div><div class="line"> --></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>也可以在属性或构造器里包含Bean的声明,这样的Bean称为内部Bean</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="comment"><!-- 内部 Bean,不能被外部引用,只能内部使用 --></span></div><div class="line"> <span class="tag"><<span class="name">bean</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="tag"><<span class="name">constructor-arg</span> <span class="attr">value</span>=<span class="string">""</span> ></span><span class="tag"></<span class="name">constructor-arg</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">property</span>></span> </div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div><div class="line">//构造器注入类似</div></pre></td></tr></table></figure>
<h6 id="注入参数详解:null和级联属性">注入参数详解:null和级联属性</h6><ul>
<li>赋<code>null</code>值</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">constructor-arg</span>></span><span class="tag"><<span class="name">null</span>/></span><span class="tag"></<span class="name">constructor-arg</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>级联属性</li>
</ul>
<figure class="highlight xml"><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"><!-- 为级联属性赋值。注意:属性(car)需要先初始化才能为级联属性赋值,否则会出错 --></span></div><div class="line"><span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"car.maxPrice"</span> <span class="attr">value</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">property</span>></span></div></pre></td></tr></table></figure>
<h6 id="集合属性">集合属性</h6><ul>
<li>在Spring中可以通过一组内置的xml标签(<code><list>、<set>、<map></code>)来配置集合属性。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"list"</span>></span></div><div class="line"> <span class="comment"><!-- 使用list为list类型的成员变量--></span></div><div class="line"> <span class="tag"><<span class="name">list</span> <span class="attr">value</span>=<span class="string">""</span>></span></div><div class="line"> <span class="tag"><<span class="name">ref</span> <span class="attr">bean</span>=<span class="string">""</span>/></span></div><div class="line"> <span class="tag"></<span class="name">list</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"></div><div class="line">//set类似</div><div class="line"></div><div class="line"><span class="comment"><!-- 配置map属性值 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"map"</span>></span></div><div class="line"> <span class="comment"><!-- 使用map节点及 map 的 entry 子节点配置 Map类型的成员变量 --></span></div><div class="line"> <span class="tag"><<span class="name">map</span>></span></div><div class="line"> <span class="tag"><<span class="name">entry</span> <span class="attr">key</span>=<span class="string">""</span> <span class="attr">value</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">entry</span>></span></div><div class="line"> <span class="tag"></<span class="name">map</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></pre></td></tr></table></figure>
<ul>
<li>使用<code><props></code>定义<code>java.util.Properties</code>,该标签使用多个<code><prop></code>作为子标签,每个<code><prop></code>标签必须定义<code>key</code>属性。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 配置properties属性值 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> ></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"properties"</span>></span></div><div class="line"> <span class="comment"><!-- 使用props节点及 props 的 prop 子节点配置 props 类型的成员变量 --></span></div><div class="line"> <span class="tag"><<span class="name">props</span>></span></div><div class="line"> <span class="tag"><<span class="name">prop</span> <span class="attr">key</span>=<span class="string">"user"</span>></span>root<span class="tag"></<span class="name">prop</span>></span></div><div class="line"> <span class="tag"><<span class="name">prop</span> <span class="attr">key</span>=<span class="string">"password"</span>></span>123456<span class="tag"></<span class="name">prop</span>></span></div><div class="line"> <span class="tag"><<span class="name">prop</span> <span class="attr">key</span>=<span class="string">"jdbcUrl"</span>></span>jdbc:mysql:///test<span class="tag"></<span class="name">prop</span>></span></div><div class="line"> <span class="tag"><<span class="name">prop</span> <span class="attr">key</span>=<span class="string">"driver"</span>></span>com.mysql.jdbc.Driver<span class="tag"></<span class="name">prop</span>></span></div><div class="line"> <span class="tag"></<span class="name">props</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></pre></td></tr></table></figure>
<ul>
<li>使用<code>utility scheme</code>定义集合</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 配置独立的集合 bean,以供多个 bean 使用,需要引入util命名空间 --></span></div><div class="line"><span class="tag"><<span class="name">util:list</span> <span class="attr">id</span>=<span class="string">"list"</span>></span></div><div class="line"> <span class="tag"><<span class="name">ref</span> <span class="attr">bean</span>=<span class="string">""</span>/></span></div><div class="line"><span class="tag"></<span class="name">util:list</span>></span></div></pre></td></tr></table></figure>
<ul>
<li>使用<code>p</code>命名空间</li>
</ul>
<figure class="highlight xml"><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"><!-- 通过p命名空间为 bean 的属性赋值,需要先导入 p 命名空间,相对于传统的配置方法,较简捷 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> <span class="attr">p:xxx</span>=<span class="string">""</span> <span class="attr">p:zzz-ref</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h6 id="使用外部属性文件">使用外部属性文件</h6><ul>
<li>在配置文件里配置 Bean 时,有时需要在Bean的配置里混入<strong>系统部署的细节信息</strong>(例如:文件路径,数据源配置信息等)。而这些部署细节实际上需要和Bean配置分离。</li>
<li>Spring提供了一个<code>PropertyPlaceholderConfigurer</code>的<code>BeanFactory后置处理器</code>,这个处理器允许用户将Bean配置的部分内容外移到<code>属性文件</code>中。可以在Bean配置文件里使用形式为<code>${var}</code>的变量,<code>PropertyPlaceholderConfigurer</code>从属性文件里加载属性,并使用这些属性来替换变量。</li>
<li><p>Spring还允许在属性文件中使用<code>${propName}</code>,以实现属性之间的相互引用。</p>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 导入属性文件 --></span></div><div class="line"><span class="tag"><<span class="name">context:property-placeholder</span> <span class="attr">location</span>=<span class="string">"classpath"</span><span class="attr">db.properties</span>"/></span></div><div class="line"></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"dataSource"</span> <span class="attr">class</span>=<span class="string">"com.mchange.v2.c3p0.ComboPooledDataSource"</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"user"</span> <span class="attr">value</span>=<span class="string">"${user}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"password"</span> <span class="attr">value</span>=<span class="string">"${password}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"driverClass"</span> <span class="attr">value</span>=<span class="string">"${driverClass}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"> <span class="tag"><<span class="name">property</span> <span class="attr">name</span>=<span class="string">"jdbcUrl"</span> <span class="attr">value</span>=<span class="string">"${jdbcUrl}"</span>></span><span class="tag"></<span class="name">property</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
</li>
</ul>
<h5 id="工厂方法注入(不常用)">工厂方法注入(不常用)</h5><h4 id="通过工厂方法(静态工厂方法&实例工厂方法)">通过工厂方法(静态工厂方法&实例工厂方法)</h4><h5 id="通过调用静态工厂方法创建Bean">通过调用静态工厂方法创建Bean</h5><ul>
<li>调用静态工厂方法创建Bean是将<code>对象创建的过程封装到静态方法中</code>。当客户端需要对象时,只需要简单地调用静态方法,而不关心创建对象的细节。</li>
<li>要声明通过静态方法创建的Bean,需要在Bean的<code>class</code>属性里指定拥有该工厂的方法的类,同时在<code>factory-method</code>属性里指定工厂方法的名称。最后,使用<code><constructor-arg></code>元素为该方法传递方法参数。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 通过静态工厂方法来配置 Bean. 注意不是配置静态工厂方法实例,而是配置 bean 实例 --></span></div><div class="line"><span class="comment"><!--</span></div><div class="line"> class 属性: 指向静态方法的全类名</div><div class="line"> factory-method: 指向静态工厂方法的名字</div><div class="line"> constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg 来配置参数</div><div class="line"> --></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">"StaticCarFactory"</span> <span class="attr">factory-method</span>=<span class="string">"getCar"</span>></span></div><div class="line"> <span class="tag"><<span class="name">constructor-arg</span> <span class="attr">value</span>=<span class="string">"xxx"</span>></span><span class="tag"></<span class="name">constructor-arg</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<p>相应的静态类如下:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StaticCarFactory</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">static</span> Map<String, Car> carMap = <span class="keyword">new</span> HashMap();</div><div class="line"></div><div class="line"> <span class="keyword">static</span> {</div><div class="line"> carMap.put(<span class="string">"Audi"</span>, <span class="keyword">new</span> Car(<span class="string">"Audi"</span>,<span class="string">"300000"</span>));</div><div class="line"> carMap.put(<span class="string">"Ford"</span>, <span class="keyword">new</span> Car(<span class="string">"Ford"</span>, <span class="string">"400000"</span>));</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> Car <span class="title">getCar</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">return</span> carMap.get(name);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h5 id="通过调用实例工厂方法创建Bean">通过调用实例工厂方法创建Bean</h5><ul>
<li>实例工厂方法:<code>将对象的创建过程封装到另外一个对象实例的方法里</code>。当客户端需要请求对象时,只需要简单的调用该实例方法而不需要关心对象的创建细节。</li>
<li>要声明通过实例实例工厂方法创建的Bean<ul>
<li>在bean的<code>factory-bean</code>属性里指定拥有该工厂方法的Bean</li>
<li>在<code>factory-method</code>属性里指定该工厂方法的名称</li>
<li>使用<code>constructor-arg</code>元素为工厂方法传递方法参数</li>
</ul>
</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- 配置工厂的实例 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">"carFactory"</span> <span class="attr">class</span>=<span class="string">"InstanceCarFactory"</span>></span><span class="tag"></<span class="name">bean</span>></span></div><div class="line"><span class="comment"><!-- 通过实例工厂方法来配置 bean --></span></div><div class="line"><span class="comment"><!--</span></div><div class="line"> class 属性: 指向实例方法的 bean</div><div class="line"> factory-method: 指向实例工厂方法的名字</div><div class="line"> constructor-arg:如果工厂方法需要传入参数,则使用constructor-arg 来配置参数</div><div class="line"> --></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">factory-bean</span>=<span class="string">"carFactory"</span> <span class="attr">factory-method</span>=<span class="string">"getCar"</span>></span></div><div class="line"> <span class="tag"><<span class="name">constructor-arg</span> <span class="attr">value</span>=<span class="string">"Ford"</span>></span><span class="tag"></<span class="name">constructor-arg</span>></span></div><div class="line"><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<p>相应的实例类如下:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">InstanceCarFactory</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> Map<String, Car> map = <span class="keyword">null</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">InstanceCarFactory</span><span class="params">()</span> </span>{</div><div class="line"> map = <span class="keyword">new</span> HashMap<String, Car>();</div><div class="line"> map.put(<span class="string">"Audi"</span>, <span class="keyword">new</span> Car(<span class="string">"Audi"</span>, <span class="string">"300000"</span>));</div><div class="line"> map.put(<span class="string">"Ford"</span>, <span class="keyword">new</span> Car(<span class="string">"Ford"</span>, <span class="string">"400000"</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> Car <span class="title">getCar</span><span class="params">(String name)</span> </span>{</div><div class="line"> <span class="keyword">return</span> map.get(name);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="FactoryBean">FactoryBean</h4><h4 id="Spring_bean自动装配(装配引用)">Spring bean自动装配(装配引用)</h4><ul>
<li><p>spring IOC容器可以自动装配bean,需要做的仅仅是在<code><bean></code>的autowire属性里指定自动装配的模式。</p>
<ul>
<li><code>byType</code>:根据bean的类型和当前bean的属性的类型尽心自动装配。若 IOC 容器中有1个以上的类型匹配的bean,则抛异常。</li>
<li><code>byName</code>:根据bean的名字和当前bean的setter风格的属性名进行自动装配,若有匹配的,则进行自动装配,若没有匹配的,则不装配</li>
<li><code>constructor</code>:不推荐使用<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> <span class="attr">autowire</span>=<span class="string">"byName"</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><p>在Bean配置文件里设置<code>autowire</code>属性进行自动装配将会装配Bean的所有属性。然而,若只希望装配个别属性时,<code>autowire</code>就不够灵活了。</p>
</li>
<li><code>autowire</code>属性只能选择一种进行配置</li>
<li>一般情况下不建议使用自动装配功能</li>
</ul>
<h4 id="bean_之间的关系:继承;依赖">bean 之间的关系:继承;依赖</h4><h5 id="bean_的继承">bean 的继承</h5><ul>
<li>Spring允许继承bean的配置,被继承的bean称为<code>父bean</code>,继承这个父bean的bean称为<code>子bean</code></li>
<li>子bean从父bean中继承配置,包括bean的属性配置</li>
<li>子bean也可以覆盖从父bean中继承过来的配置</li>
<li>父bean可以作为配置模板,也可以作为Bean实例,若只想把父bean作为模板,可以设置<code><bean></code>的<code>abstract</code>属性为<code>true</code>,这样Spring将不会实例化这个Bean</li>
<li>并不是<code><bean></code>元素里的所有属性都会被继承,比如:<code>autowire</code>、<code>abstract</code>等</li>
<li>也可以忽略父Bean的class属性,让子bean指定自己的类,而共享相同的属性配置。但此时<strong>abstract必须设置为true</strong></li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!-- bean 配置的继承:使用 bean 的 parent 属性指定哪个 bean 的配置 --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">parent</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">bean</span>></span></div><div class="line"></div><div class="line"><span class="comment"><!-- 抽象bean: bean 的 abstract 属性为 true 的bean。这样的 bean 不能被 IOC 容器实例化,只能用来被继承设置</span></div><div class="line">若某一个 bean 的 class 属性没有被指定,则该 bean 必须是一个抽象 bean --></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">abstract</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h5 id="bean_的依赖">bean 的依赖</h5><ul>
<li>Spring 允许用户通过<code>depends-on</code>属性设定Bean前置依赖的Bean,前置依赖的Bean会在本Bean实例化之前创建好。</li>
<li>如果前置依赖于多个Bean,则可以通过逗号,空格、或的方式配置Bean的名称。</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> <span class="attr">depends-on</span>=<span class="string">“”</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h5 id="bean的作用域:singleton;prototype;WEB_环境作用域">bean的作用域:singleton;prototype;WEB 环境作用域</h5><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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!--</span></div><div class="line"> 使用 bean 的 scope 属性来配置 bean 的作用域</div><div class="line"> singleton: 默认值。容器初始时创建 bean 的实例,在整个容器的生命周期内只创建这一个 bean。单例的。</div><div class="line"> prototype: 原型的。容器初始化时不创建 bean 的实例。而在每次请求时都创建一个新的 Bean 实例,并返回。</div><div class="line"> --></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">id</span>=<span class="string">""</span> <span class="attr">class</span>=<span class="string">""</span> <span class="attr">scope</span>=<span class="string">""</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<h2 id="基于注解的方式">基于注解的方式</h2><h3 id="基于注解配置_Bean">基于注解配置 Bean</h3><h4 id="在_classpath_中扫描组件">在 classpath 中扫描组件</h4><ul>
<li>组件扫描:Spring 能够从<code>classpath</code>下自动扫描,侦测和实例化具有特定注解的组件。</li>
<li>特定组件包括:<ul>
<li><code>@Component</code>:基本注解,标识了一个受 Spring 管理的组件</li>
<li><code>@Respository</code>:标识持久层组件</li>
<li><code>@Service</code>:标识服务层(业务层)组件</li>
<li><code>@Controller</code>:标识表现层组件<br><strong>以上均为建议标识内容</strong></li>
</ul>
</li>
<li>对于扫描到的组件,Spring有<code>默认的命名策略</code>:使用非限定类名,第一个字母小写。<code>也可以在注解中通过value属性值标识组件的名称</code>。</li>
<li><p>当在组件类上使用了特定的注解之后,还需要在Spring的配置文件中声明:<code><context:component-scan></code>:</p>
<ul>
<li>base-package 属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里及其子包中的所有类。</li>
<li>当需要扫描多个包时,可以使用逗号分隔。</li>
<li>如果仅希望扫描特定的类而非基包下的所有类,可使用<code>resource-pattern</code>属性过滤特定的类,示例:</li>
<li><code>use-default-filters</code>属性指定是否使用默认的过滤策略。</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">context:</span></span></div><div class="line"> <span class="attr">component-scan</span> <span class="attr">base-package</span>=<span class="string">"com.whtis.spring.beans"</span></div><div class="line"> <span class="attr">resource-pattern</span>=<span class="string">"autowire/*.class"</span> <span class="attr">use-default-filters</span>=<span class="string">"false"</span>></div><div class="line"><span class="tag"></<span class="name">context:component-scan</span>></span></div></pre></td></tr></table></figure>
<ul>
<li><code><context:include-filter></code> 子节点表示要包含的目标类</li>
<li><code><context:exclude-filter></code> 子节点表示要排除在外的目标类</li>
<li><code><context:component-scan></code> 下可以拥有若干个<code><context:include-filter></code>和<code><context:exclude-filter></code>子节点</li>
<li><code><context:include-filter></code>和<code><context:exclude-filter></code>子节点支持多种类型的过滤表达式:<ul>
<li><code>annotation</code>: 所有标注了<code>XxxAnnotation</code>的类。该类型采用目标类是否标注了某个注解进行过滤。</li>
<li><code>assinable</code>:所有继承或扩展<code>XxxService</code>的类。该类型采用目标类是否继承或扩展某个特定类进行过滤。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="组件装配">组件装配</h4><p>如果一个组件中包含其他组件,仅靠配置<code>classpath</code>会导致无法找到组件中的其他组件从而报<code>nullPointException</code>错误。</p>
<ul>
<li><code><context:component-scan></code>元素还会自动注册<code>AutowiredAnnotationBeanPostProcessor</code>实例,该实例可以自动装配具有<code>@Autowired</code>和<code>@Resource</code>、<code>@Inject</code>注解的属性。<ul>
<li><code>@Autowired</code>注解自动装配具有兼容类型的单个Bean属性<ul>
<li>构造器,普通字段(即使是非<code>public</code>),一切具有参数的方法都可以应用<code>@Autowired</code>注解。</li>
<li>默认情况下,所有使用<code>@Autowired</code>注解的属性都需要被设置。当<code>Spring</code>找不到匹配的Bean装配属性时,会抛出异常。<strong>若某一属性允许不被设置,可以设置@Autowired注解的<code>required</code>属性为<code>false</code>。</strong></li>
<li>默认情况下,当 IOC 容器里存在多个类型兼容的Bean时,通过类型的自动装配将无法工作。此时可以在<code>@Qualifier</code>注解里提供Bean的名称。Spring允许对方法的入参标注<code>@Qualifiter</code>以指定注入Bean的名称。</li>
<li><code>@Autowired</code> 注解也可以应用在<code>数组类型</code>的属性上,此时Spring将会把所有匹配的Bean进行自动装配</li>
<li><code>@Autowired</code> 注解也可以应用在<code>集合属性</code>上,此时Spring读取该集合的类型信息,然后自动装配所有与之兼容的Bean</li>
<li><code>@Autowired</code> 注解也可以应用在<code>java.util.Map</code>上,若该Map的键值为String,那么Spring将自动装配与Map值类型兼容的Bean,此时Bean的名称作为键值。<ul>
<li>Spring 还支持<code>@Resource</code>和<code>@Inject</code>注解,这两个注解和<code>@Autowired</code>注解的功能类似</li>
</ul>
</li>
<li><code>@Resource</code> 注解要求提供一个 Bean 名称的属性,若该属性为空,则自动采用标注处的变量或方法名作为 Bean 的名称。</li>
<li><code>@Inject</code>和<code>Autowired</code>注解一样也是按类型匹配注入的Bean,但没有<code>required</code>属性。<br><strong>建议使用<code>Autowired</code>注解</strong></li>
</ul>
</li>
</ul>
</li>
</ul>
<h1 id="IOC_容器中_Bean_的生命周期">IOC 容器中 Bean 的生命周期</h1><ul>
<li>Spring IOC 容器可以管理Bean的生命周期,Spring允许在Bean生命周期的特定点执行定制的任务。</li>
<li>管理过程:<ul>
<li>通过构造器或工厂方法创建Bean实例</li>
<li>为Bean的属性设置值和对其他Bean的引用</li>
<li>调用Bean的初始化方法</li>
<li>Bean可以使用了</li>
<li>当容器关闭时,调用Bean的销毁方法</li>
</ul>
</li>
<li>在Bean的声明里设置<code>init-method</code>和<code>destory-method</code>属性,为Bean指定初始化和销毁方法。</li>
<li><p>创建Bean的后置处理器</p>
<ul>
<li>Bean后置处理器允许在调用初始化方法前后对Bean进行额外的处理</li>
<li>Bean后置处理器对IOC容器里的所有Bean实例逐一处理,而非单一实例。其典型应用是:检查Bean属性的正确性或根据特定的标准更改Bean的属性</li>
<li>对Bean后置处理器而言,需要实现<code>org.springframework.beans.factory.config</code>的<code>BeanPostProcessor</code>接口。在初始化方法被调用前后,Spring将每个Bean实例分别传递给上述接口的以下两个方法:<ul>
<li><code>postProcessBeforeInitialization()</code></li>
<li><code>postProcessAfterInitialization()</code></li>
</ul>
</li>
</ul>
<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></pre></td><td class="code"><pre><div class="line"><span class="comment"><!--</span></div><div class="line"> 实现 BeanPostProcessor 接口,并具体实现</div><div class="line"> public Object postProcessBeforeInitialization(Object o, String s) init-method 之前被调用</div><div class="line"> public Object postProcessAfterInitialization(Object o, String s) init-method 之后被调用</div><div class="line"> 的实现</div><div class="line"></div><div class="line"> bean: bean 实例本身</div><div class="line"> beanName: IOC 容器配置的 bean 的名字</div><div class="line"> 返回值:是实际上返回给用户的那个 Bean,注意: 可以在以上两个方法中修改返回的 bean,甚至返回一个新的 bean</div><div class="line"> --></div><div class="line"><span class="comment"><!-- 配置 Bean 的后置处理器: 不需要配置 id, IOC 容器自动识别是一个 BeanPostProcessor --></span></div><div class="line"><span class="tag"><<span class="name">bean</span> <span class="attr">class</span>=<span class="string">"myBeanPostProcessor"</span>></span><span class="tag"></<span class="name">bean</span>></span></div></pre></td></tr></table></figure>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
</li>
</ul>
]]></content>
<summary type="html">
<![CDATA[<h1 id="配置形式">配置形式</h1><h2 id="基于xml文件的方式">基于xml文件的方式</h2><h3 id="Bean的配置方式">Bean的配置方式</h3><h4 id="通过全类名(反射)">通过全类名(反射)</h4><figure class="h]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="spring4" scheme="whtis.github.io/tags/spring4/"/>
<category term="java web" scheme="whtis.github.io/categories/java-web/"/>
</entry>
<entry>
<title><![CDATA[spring4 学习笔记(一)--spring 简介]]></title>
<link href="whtis.github.io/2017/10/06/spring4%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89--spring%20%E7%AE%80%E4%BB%8B/"/>
<id>whtis.github.io/2017/10/06/spring4学习笔记(一)--spring 简介/</id>
<published>2017-10-06T11:19:15.000Z</published>
<updated>2017-10-13T07:34:46.277Z</updated>
<content type="html"><![CDATA[<h3 id="spring4_HelloWorld">spring4 HelloWorld</h3><ul>
<li>spring 是一个IOC(DI)和AOP容器框架</li>
<li>具体描述<ul>
<li>轻量级:<strong>Spring是非侵入式的</strong>-基于Spring开发的应用中的对象可以不依赖<code>Spring</code>的<code>API</code></li>
<li>依赖注入</li>
<li>面向切面编程(AOP)</li>
<li>容器</li>
<li>框架</li>
<li>一站式</li>
</ul>
</li>
</ul>
<h3 id="IOC和DI">IOC和DI</h3><ul>
<li>IOC(Inversion of Control):其思想是<strong>反转资源获取的方向</strong>。传统的资源查找方式要求组件向容器发起请求查找资源。作为回应,容器适时的返回资源。而应用了IOC以后,则是<strong>容器主动地将资源推送给它所管理的组件,组件所要做的仅是选择一种合适的方式来接受资源</strong>。这种行为也成为查找的被动形式。</li>
<li>DI(Dependency Injection):IOC的另一种表述方式:即:<strong>组件以一些预先定义好的方式(例如:setter方法)接受来自如容器的资源注入</strong>。相对于IOC来说,这种表述更直接。<ul>
<li>IOC<ul>
<li>分离接口与实现</li>
<li>工厂模式</li>
<li>采用反转控制</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="Spring容器">Spring容器</h3><ul>
<li>在Spring IOC 容器读取Bean配置创建Bean实例之前,必须对它进行实例化。只有在容器实例化后,才可以从IOC容器里获取Bean实例并使用。</li>
<li>Spring提供了两种类型的IOC容器实现<ul>
<li>BeanFactory: IOC容器的基本实现</li>
<li>ApplicationContext:提供了更多的高级特性,是BeanFactory的子接口。</li>
<li>BeanFactory是Spring框架的基础设施,面向Spring本身;ApplicationContext面向使用Spring框架的开发者,几乎所有的应用场合都直接使用ApplicationContext而非底层的BeanFactory</li>
<li>无论使用何种方式,配置文件是相同的</li>
</ul>
</li>
</ul>
<h4 id="ApplicationContext的主要实现类">ApplicationContext的主要实现类</h4><ul>
<li>ClassPathXmlApplicationContext:从类路径下加载配置文件</li>
<li>FileSystemXmlApplicationContext: 从文件系统中加载配置文件</li>
<li>ConfigurableApplicationContext扩展于ApplicationContext,新增加两个主要方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭上下文的能力。</li>
<li>ApplicationContext在初始化上下文的时候就实例化所有单例的Bean。(Bean的作用域)</li>
<li>WebApplicationContext是专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作。</li>
</ul>
<h3 id="从_IOC_容器中获取_Bean_实例">从 IOC 容器中获取 Bean 实例</h3><ul>
<li>BeanFactory 中的方法,常用的有两种:<ul>
<li>用id定位到 IOC 容器中的 Bean</li>
<li>利用类型返回 IOC 容器中的 Bean。但要求 IOC 容器中必须只能有一个该类型的bean</li>
</ul>
</li>
</ul>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h3 id="spring4_HelloWorld">spring4 HelloWorld</h3><ul>
<li>spring 是一个IOC(DI)和AOP容器框架</li>
<li>具体描述<ul>
<li>轻量级:<strong>Spring是非侵入式的</strong]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="spring4" scheme="whtis.github.io/tags/spring4/"/>
<category term="java web" scheme="whtis.github.io/categories/java-web/"/>
</entry>
<entry>
<title><![CDATA[使用python爬虫框架scrapy对实时新闻进行抓取并存入数据库]]></title>
<link href="whtis.github.io/2017/07/28/%E4%BD%BF%E7%94%A8python%E7%88%AC%E8%99%AB%E6%A1%86%E6%9E%B6scrapy%E5%AF%B9%E5%AE%9E%E6%97%B6%E6%96%B0%E9%97%BB%E8%BF%9B%E8%A1%8C%E6%8A%93%E5%8F%96%E5%B9%B6%E5%AD%98%E5%85%A5%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
<id>whtis.github.io/2017/07/28/使用python爬虫框架scrapy对实时新闻进行抓取并存入数据库/</id>
<published>2017-07-28T14:15:20.000Z</published>
<updated>2017-07-29T06:36:53.372Z</updated>
<content type="html"><![CDATA[<h3 id="写在前面">写在前面</h3><p>每天的新闻更新很快,如果要全面了解非常困难,更可恶的是一些门户网站还经常取一些乱七八糟的标题,点进去是文不对题。所以萌生了一个想法:自己抓取不同门户网站的新闻更新信息,然后将这些内容进行整合,推送一些当日的热点新闻。<br>想的很简单,真的做起来,发现自己还是太年轻,到写这篇博客为止,我也就是完成了一个基本的抓取框架,连内容都没有获得多少。不过本来就是抱着学习<code>python</code>的想法使用<code>scrapy</code>来抓取新闻,既然已经有了个差不多的框架,也该写写使用<code>scrapy</code>过程中的遇到的一些问题了。</p>
<h3 id="明确需求和技术路线">明确需求和技术路线</h3><ul>
<li>需求:很简单,抓取每天最新的新闻内容(文本),按照一定的格式存入数据库<code>mysql</code>中。</li>
<li>技术路线:之前一直在使用<code>java</code>语言开发的<code>webmagic</code>开源爬虫框架进行爬虫开发。但一直听说<code>python</code>下有个大名鼎鼎的爬虫框架<code>scrapy</code>,所以抱着学习的态度,使用<code>scrapy</code>进行爬虫的开发。</li>
</ul>
<h3 id="简单的技术介绍和入门">简单的技术介绍和入门</h3><p>之前只是使用<code>python</code>写过一些简单的脚本,这次为了使用<code>scrapy</code>,看了一遍<strong>廖雪峰大大</strong>的<code>python3</code><a href="https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000" target="_blank" rel="external">教程</a><br>看完后没记住多少,不过有个概念就是了。然后开始查看一些讲<code>scrapy</code>入门的博客,自己实现博客中的例子,算是对<code>scrapy</code>有个大概的了解。我复现的例子是这个:<a href="http://www.jianshu.com/p/fa614bea98eb" target="_blank" rel="external">【图文详解】scrapy安装与真的快速上手——爬取豆瓣9分榜单</a></p>
<h3 id="需求的再分析及让scrapy与需求相关">需求的再分析及让<code>scrapy</code>与需求相关</h3><ul>
<li>需求分析:新闻内容很多,获取的方式也不同。最笨的方式就是复现浏览器的行为:针对不同的信息源获取不同的页面进行解析;其次好一点的方法:由于某些新闻网站提供了rss订阅接口,因此可以解析此接口页面获得内容。下面分析下这两种方法的优缺点:<ul>
<li>模拟浏览器行为:<ul>
<li>优点:能够精准解析到需要的内容</li>
<li>缺点:解析难度高,主流新闻网站都有一系列反爬虫机制;代码与页面结构高度相关,如页面改版,代码改动较大</li>
</ul>
</li>
<li>rss接口:<ul>
<li>优点:不同网站的接口页面结构基本相同,可以使用一套解析逻辑进行解析</li>
<li>缺点:并不是所有网站都会提供该接口,且该接口返回的数据,有些仅有正文摘要,还需要再进一次网站获取完整的正文内容</li>
</ul>
</li>
</ul>
</li>
<li>明确需求下的<code>scrapy</code>:上一节提到的入门内容如果看完后,应该对<code>scrapy</code>会有一个大概的整体认识。在我这需求下,似乎单独创建一个爬虫无法完成所有的需求。因此我创建了两个爬虫文件:<code>rss_news_spider.py</code>和<code>web_news_spider.py</code>,下面会对这两个文件进行详细解释。</li>
</ul>
<h3 id="开始写爬虫">开始写爬虫</h3><h4 id="使用scrapy基础模板来爬取rss接口的新闻内容">使用scrapy基础模板来爬取rss接口的新闻内容</h4><p>终于,振奋人心的时刻到了。可以开始我们的爬虫了。首先我们创建<code>runRss.py</code>,然后写上如下启动爬虫的代码:</p>
<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"><span class="comment"># -*- coding: utf-8 -*-</span></div><div class="line"></div><div class="line"><span class="comment"># run rssSpider</span></div><div class="line"><span class="keyword">from</span> scrapy <span class="keyword">import</span> cmdline</div><div class="line"></div><div class="line">cmdline.execute(<span class="string">"scrapy crawl rss"</span>.split())</div></pre></td></tr></table></figure>
<p>是不是很熟悉,对,这条命令启动了名为<code>rss</code>的爬虫(<strong>注意,在scrapy中创建的每一个爬虫必须有自己唯一的<code>name</code>,否则无法启动</strong>)。我的<code>rss</code>爬虫位于<code>rss_news_spider.py</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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">RssSpider</span><span class="params">(scrapy.Spider)</span>:</span></div><div class="line"> name = <span class="string">'rss'</span></div><div class="line"> start_urls = [</div><div class="line"> <span class="string">'http://www.wyzxwk.com/e/web/?type=rss2&classid=0'</span>,</div><div class="line"> ]</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">parse</span><span class="params">(self, response)</span>:</span></div><div class="line"> <span class="keyword">if</span> response.url.find(<span class="string">'rss'</span>) != <span class="number">-1</span>:</div><div class="line"> <span class="keyword">yield</span> Request(response.url, callback=self.parse_rss)</div><div class="line"> <span class="keyword">elif</span> response.url.find(<span class="string">'Article'</span>) != <span class="number">-1</span>:</div><div class="line"> <span class="keyword">yield</span> Request(response.url, callback=self.parse_details)</div><div class="line"> <span class="keyword">else</span>:</div><div class="line"> self.logger.info(<span class="string">'start urls is not useful, please check it!'</span>)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">parse_rss</span><span class="params">(self, response)</span>:</span></div><div class="line"> selector = scrapy.Selector(response)</div><div class="line"> contents = selector.xpath(<span class="string">'//channel/item'</span>)</div><div class="line"> <span class="keyword">for</span> content <span class="keyword">in</span> contents:</div><div class="line"> title = content.xpath(<span class="string">'title/text()'</span>).extract()[<span class="number">0</span>]</div><div class="line"> link = content.xpath(<span class="string">'link/text()'</span>).extract()[<span class="number">0</span>]</div><div class="line"> ……</div><div class="line"> article = ArticleItem()</div><div class="line"> article[<span class="string">'table_name'</span>] = <span class="string">'article_'</span> + table_name.lower()</div><div class="line"> article[<span class="string">'title'</span>] = title</div><div class="line"> article[<span class="string">'url'</span>] = link</div><div class="line"> <span class="keyword">if</span> link <span class="keyword">is</span> <span class="keyword">not</span> <span class="keyword">None</span>:</div><div class="line"> <span class="keyword">yield</span> scrapy.http.Request(url=link, meta={<span class="string">'item'</span>: article}, callback=self.parse_details)</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">parse_details</span><span class="params">(self, response)</span>:</span></div><div class="line"> article = response.meta[<span class="string">'item'</span>]</div><div class="line"> selector = scrapy.Selector(response)</div><div class="line"> content = selector.xpath(<span class="string">'//div[@class="m-article s-shadow"]/article/p'</span>).extract().__str__()</div><div class="line"> content = content.replace(<span class="string">"', '"</span>, <span class="string">""</span>).replace(<span class="string">"\\u3000"</span>, <span class="string">" "</span>).replace(<span class="string">"'"</span>, <span class="string">""</span>).replace(<span class="string">"\\xa0"</span>, <span class="string">""</span>)</div><div class="line"> .replace(<span class="string">"▍"</span>, <span class="string">""</span>)</div><div class="line"> article[<span class="string">'content'</span>] = content</div><div class="line"> <span class="keyword">return</span> article</div></pre></td></tr></table></figure></p>
<p>来认真看下这段代码:</p>
<ul>
<li>设置<code>name</code>、<code>start_urls</code>和<code>parse</code>函数我们都很熟悉。那<code>parse_rss</code>和<code>parse_details</code>是干嘛用的?<ul>
<li>在本文的例子中,我们用了<a href="http://www.wyzxwk.com/" target="_blank" rel="external">乌有之乡</a>的<code>rss</code>接口作为例子。这个<code>rss</code>接口的特点是给出的正文内容是 摘要,如果要查看详细的正文内容需要访问相应的<code>url</code>,因此两个函数的作用就非常明显了:<code>parse_rss</code>函数是处理接口页面的信息并获取正文的链接,然后使用<code>parse_details</code>函数对正文做解析。</li>
<li>这个地方比较重要的是函数之间如何传递参数?也就是我想把<code>parse_rss</code>中解析到的内容传递给<code>parse_details</code>。从代码中可以看出,我们在传递链接的时候使用的<code>scrapy.http.Request</code>中可以附加参数:<code>url</code>是链接;<code>meta</code>是一个字典,可以包含所有希望下一个函数获得的内容;<code>callback</code>是回调函数,用于指定这些内容由哪个函数进行处理。</li>
</ul>
</li>
<li><code>start_urls</code>中注释的url是干啥用的?<ul>
<li>如我们在需求分析中所说,rss接口页面结构是相同的,因此理论上来说,在<code>parse_details</code>之前的逻辑,适用于任何实现了rss接口的网站。</li>
</ul>
</li>
</ul>
<p>现在有个问题?如果我爬取的网站比较多,那么<code>parse_details</code>的逻辑我是不是要根据不同的网站去分别实现?按道理来说是这样的,但<code>scrapy</code>还为我们提供了另外一种实现思路。下面的内容主要参考了如下几篇博客:</p>
<ul>
<li><a href="http://www.code123.cc/1431.html" target="_blank" rel="external">Python爬虫框架Scrapy教程(1)—入门</a></li>
<li><a href="http://www.code123.cc/1432.html" target="_blank" rel="external">Python爬虫框架Scrapy教程(2)—动态可配置</a></li>
<li><a href="http://www.code123.cc/1434.html" target="_blank" rel="external">Python爬虫框架Scrapy教程(3)—使用Redis和SQLAlchemy</a></li>
</ul>
<h4 id="读取数据库配置动态生成爬虫">读取数据库配置动态生成爬虫</h4><p>我们知道,爬虫的核心其实就是发送请求与接收请求。那么如果我们要从网络上获取到新闻内容,浏览器发送这两个请求就可以:<br>1、请求新闻的列表页,解析出每一篇新闻的详细链接<br>2、访问每一篇新闻的详细链接,然后解析返回的内容,就可以得到新闻的标题、发表时间、正文等内容了。<br>(<strong>以上两步其实是爬虫领域很经典的列表+详情页爬取逻辑</strong>)<br>在这个过程中,浏览器(或者代码)的执行步骤是相同的,不同的只是访问的链接,以及对不同链接内容的解析(主要是<code>xpath</code>和<code>正则表达式</code>)。因此假设访问的链接和解析方法都能够动态加载(从数据库中读取),那么我们就可以写一个爬虫来加载这些配置,实现多个网站内容的抓取了。<br>我们创建一个<code>runWeb.py</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></pre></td><td class="code"><pre><div class="line"><span class="comment"># run webSpider</span></div><div class="line">settings = Settings()</div><div class="line">settings.set(<span class="string">"ITEM_PIPELINES"</span>, {</div><div class="line"> <span class="comment"># 'pipelines.DuplicatesPipeline': 200,</span></div><div class="line"> <span class="comment"># 'pipelines.CountDropPipline': 100,</span></div><div class="line"> <span class="string">'pipelines.DataBasePipeline'</span>: <span class="number">300</span>,</div><div class="line">})</div><div class="line"><span class="comment"># crawl settings</span></div><div class="line">settings.set(<span class="string">"USER_AGENT"</span>,</div><div class="line"> <span class="string">"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari"</span></div><div class="line"> <span class="string">"/537.36"</span>)</div><div class="line"></div><div class="line">process = CrawlerProcess(settings)</div><div class="line"></div><div class="line">db = DBSession()</div><div class="line">rules = db.query(Rule).filter(Rule.enable == <span class="number">1</span>)</div><div class="line"><span class="keyword">for</span> rule <span class="keyword">in</span> rules:</div><div class="line"> process.crawl(WebSpider, rule)</div><div class="line">process.start()</div></pre></td></tr></table></figure></p>
<p>加载完自定义<code>settings</code>后,对每一个<code>rule</code>生成一个进程,运行<code>webSpider</code>。我的<code>webSpider</code>在<code>web_news_spider</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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">WebSpider</span><span class="params">(CrawlSpider)</span>:</span></div><div class="line"> name = <span class="string">"web"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(self, rule)</span>:</span></div><div class="line"> self.rule = rule</div><div class="line"> self.name = rule.site_name</div><div class="line"> self.allowed_domains = rule.allow_domains.split(<span class="string">","</span>)</div><div class="line"> self.start_urls = rule.start_urls.split(<span class="string">","</span>)</div><div class="line"> rule_list = []</div><div class="line"> <span class="comment"># 添加`下一页`的规则</span></div><div class="line"> <span class="keyword">if</span> rule.next_page:</div><div class="line"> rule_list.append(Rule(LinkExtractor(restrict_xpaths=rule.next_page)))</div><div class="line"> <span class="comment"># 添加抽取文章链接的规则</span></div><div class="line"> rule_list.append(Rule(LinkExtractor(</div><div class="line"> allow=[rule.allow_url],</div><div class="line"> restrict_xpaths=[rule.extract_from]),</div><div class="line"> callback=<span class="string">'parse_item'</span>, follow=<span class="keyword">True</span>))</div><div class="line"> self.rules = tuple(rule_list)</div><div class="line"> super(WebSpider, self).__init__()</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">parse_item</span><span class="params">(self, response)</span>:</span></div><div class="line"> self.log(<span class="string">'Hi, this is a web page! %s'</span> % response.url)</div><div class="line"></div><div class="line"> content = infos.xpath(self.rule.content_xpath).extract()</div><div class="line"> article[<span class="string">"content"</span>] = content[<span class="number">0</span>] <span class="keyword">if</span> content <span class="keyword">else</span> <span class="string">""</span></div><div class="line"></div><div class="line"> publish_time = infos.xpath(self.rule.publish_time_xpath).extract()</div><div class="line"> article[<span class="string">"publish_time"</span>] = publish_time[<span class="number">0</span>] <span class="keyword">if</span> publish_time <span class="keyword">else</span> <span class="string">""</span></div><div class="line"> ……</div></pre></td></tr></table></figure></p>
<p>其中<code>__init__</code>函数很关键,除了从数据库读取配置外,它使用<code>Rule(LinkExtractor(……))</code>指定了对新闻列表页的解析规则:<code>allow</code>指定了加入下载队列中url的格式;<code>restrict_xpaths</code>指定了寻找url的页面范围;<code>callback</code>指定了处理后续url(新闻详情页)的方法。</p>
<ul>
<li><code>allow</code>和<code>restrict_xpaths</code>从<code>正则表达式</code>和<code>xpath</code>共同对后续帖子详情页url进行限制;</li>
<li><code>callback</code>指定的是解析详情页的方法,顾名思义,列表页的处理是由爬虫自己处理的(使用参数,这也是我没搞懂的地方,因为不是自己处理,在爬虫无法抽取详情页url时很难定位到哪里出错了)</li>
</ul>
<p>如果你的<code>allow</code>和<code>restrict_xpaths</code>设置无误,且列表页返回的信息很完整,你就可以使用<code>parse_item</code>函数制定解析详情页的逻辑了。<br><strong>1、初始接触这种方法时,极其建议使用已经有的例子进行学习,上面提到的博客中的代码,在本文书写时仍能正常运行</strong><br><strong>2、这种方法对动态加载的列表页是无效的</strong></p>
<h3 id="把抓取到的内容存入数据库">把抓取到的内容存入数据库</h3><p>网上大部分使用的<code>mysql</code>驱动是<code>mysqlDb</code>,但是它不支持<code>python3.x</code>。因此本文使用的是<code>pymysql</code>,<code>orm</code>框架是<a href="https://www.sqlalchemy.org/" target="_blank" rel="external"><code>SQLAlchemy</code></a><br>本文仅对一些缺少资料地方进行说明:</p>
<ul>
<li><code>SQLAlchemy</code>连接数据库的方法(参考自官方文档):<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="comment"># 初始化数据库连接:</span></div><div class="line">engine = create_engine(<span class="string">'mysql+pymysql://user:passwd@localhost:3306/spider?charset=utf8mb4'</span>)</div><div class="line"></div><div class="line"><span class="comment"># 创建DBSession类型:</span></div><div class="line">DBSession = sessionmaker(bind=engine)</div></pre></td></tr></table></figure>
</li>
</ul>
<p>需要注意的是,初始化<code>engine</code>时需要指定数据库。本文使用的代码,数据库信息存储在<code>scrapy</code>的默认<code>settings</code>中。</p>
<ul>
<li>SQLAlchemy存储信息时动态指定表名<br>我们有这个一个需求,不同的网站内容我们需要存储到不同的表当中。但学习<code>SQLAlchemy</code>用法的时候,我们可以看到,在创建<code>model</code>类时就必须指定<code>__tablename__</code>,否则无法存储。<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"><span class="class"><span class="keyword">class</span> <span class="title">ArticleModel</span><span class="params">(Base)</span>:</span></div><div class="line"> __tablename__ = <span class="string">'none'</span></div><div class="line"></div><div class="line"> aid = Column(Integer, primary_key=<span class="keyword">True</span>)</div><div class="line"> title = Column(String)</div><div class="line"> url = Column(String)</div><div class="line"> ……</div></pre></td></tr></table></figure>
</li>
</ul>
<p>那我们总不能创建多个除了<code>__tablename__</code>不同其他都相同的<code>modle</code>类吧。还好,我们可以使用如下方法在向数据库插入数据的时候(pipeline中)动态指定表名:<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">ArticleModel.__table__.name = item[<span class="string">'table_name'</span>]</div><div class="line">a = ArticleModel(title=item[<span class="string">"title"</span>].encode(<span class="string">"utf-8"</span>),</div><div class="line"> url=item[<span class="string">"url"</span>],</div><div class="line"> ……</div><div class="line"> )</div></pre></td></tr></table></figure></p>
<h3 id="其他">其他</h3><p>本来以为<code>mysql</code>中的<code>timestamp</code>对应于<code>python</code>中的<code>date</code>类型,今天向数据库中插入时间戳的时候,才发现对应的是<code>str</code>类型。<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">pub_float = time.mktime(time.strptime(pubDate, <span class="string">'%a, %d %b %Y %X %z'</span>))</div><div class="line">timestamp = time.strftime(<span class="string">'%Y-%m-%d %X'</span>, time.localtime(pub_float))</div></pre></td></tr></table></figure></p>
<blockquote>
<blockquote>
<blockquote>
<p>type(timestamp) → str</p>
</blockquote>
</blockquote>
</blockquote>
<h3 id="后记">后记</h3><p>本文所写内容为这段时间学习成果之记录,开头所提到的需求离实际完成还很远。文中提到的第二种方法在具体到本文中需求中使用时并没有成功,具体原因由于本人水平所限,并未查出来。如果有对这方面感兴趣的可以一同研究学习。本文提到的完整代码可以在我的<a href="https://github.com/whtis/news_scrapy" target="_blank" rel="external">github</a>上找到。</p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h3 id="写在前面">写在前面</h3><p>每天的新闻更新很快,如果要全面了解非常困难,更可恶的是一些门户网站还经常取一些乱七八糟的标题,点进去是文不对题。所以萌生了一个想法:自己抓取不同门户网站的新闻更新信息,然后将这些内容进行整合,推送一些当日的热点新闻。<br>想的]]>
</summary>
<category term="python,scrapy,mysql,新闻抓取" scheme="whtis.github.io/tags/python%EF%BC%8Cscrapy%EF%BC%8Cmysql%EF%BC%8C%E6%96%B0%E9%97%BB%E6%8A%93%E5%8F%96/"/>
<category term="python" scheme="whtis.github.io/categories/python/"/>
</entry>
<entry>
<title><![CDATA[使用ubuntu过程中的一些命令集合]]></title>
<link href="whtis.github.io/2017/06/19/%E4%BD%BF%E7%94%A8ubuntu%E8%BF%87%E7%A8%8B%E4%B8%AD%E7%9A%84%E4%B8%80%E4%BA%9B%E5%91%BD%E4%BB%A4%E9%9B%86%E5%90%88/"/>
<id>whtis.github.io/2017/06/19/使用ubuntu过程中的一些命令集合/</id>
<published>2017-06-19T01:57:27.000Z</published>
<updated>2017-06-19T06:50:37.000Z</updated>
<content type="html"><![CDATA[<h1 id="ubuntu命令备忘">ubuntu命令备忘</h1><p>用ubuntu时,经常会用到一些有用的命令,但回头好久不用又忘了。而我又是那种不喜欢查手册的人,百度一下又得费时,所以干脆开篇博客记下来,以后直接来这查好了。</p>
<h2 id="ubuntu使用tar命令备份系统命令合集"><code>ubuntu</code>使用<code>tar</code>命令备份系统命令合集</h2><p>这里的备份和恢复都是针对同一硬盘下的备份与恢复。</p>
<h3 id="备份">备份</h3><ul>
<li><code>tar cvpzf backup.tgz --exclude=/proc --exclude=/lost+found --exclude=/backup.tgz --exclude=/mnt --exclude=/sys /</code></li>
<li>或者是 <code>tar cvpjf backup.tgz.gz2 --exclude=/proc --exclude=/lost+found --exclude=/backup.tgz --exclude=/mnt --exclude=/sys /</code><br>两条命令的区别在于第二条的压缩率高于第一条。</li>
</ul>
<h3 id="恢复">恢复</h3><ul>
<li><code>tar xvpfz backup.tgz -C /</code> or <code>tar xvpfj backup.tar.bz2 -C /</code></li>
</ul>
<h2 id="ubuntu使用dd命令备份硬盘命令合集"><code>ubuntu</code>使用<code>dd</code>命令备份硬盘命令合集</h2><p>这里的备份和恢复可以在不同硬盘下进行,但是要注意硬盘大小的问题,即恢复的硬盘大小不得小于备份的硬盘大小。这些操作请在<code>live CD</code>环境下进行。</p>
<h3 id="备份-1">备份</h3><ul>
<li><code>fdisk -u -l</code> 查看磁盘信息</li>
<li><code>dd if=/dev/sda of=/ubuntu/media/xxx/ghost.img</code> 备份到外接u盘中</li>
<li><code>kill -USR1 `pgrep ^dd` </code>显示上一条命令的进度(请在另外的终端中运行)</li>
<li><code>dd if=/ubuntu/media/xxx/ghost.img of=/dev/sda</code> 恢复到指定的硬盘中<tab> kill -USR1 <code>pgrep ^dd</code> </tab>
</li>
</ul>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h1 id="ubuntu命令备忘">ubuntu命令备忘</h1><p>用ubuntu时,经常会用到一些有用的命令,但回头好久不用又忘了。而我又是那种不喜欢查手册的人,百度一下又得费时,所以干脆开篇博客记下来,以后直接来这查好了。</p>
<h2 id="ubuntu使用ta]]>
</summary>
<category term="ubuntu" scheme="whtis.github.io/tags/ubuntu/"/>
<category term="收集整理" scheme="whtis.github.io/tags/%E6%94%B6%E9%9B%86%E6%95%B4%E7%90%86/"/>
<category term="ubuntu" scheme="whtis.github.io/categories/ubuntu/"/>
</entry>
<entry>
<title><![CDATA[ubuntu16.04删除当前用户密码文件/etc/shadow后无法进入系统的解决方法]]></title>
<link href="whtis.github.io/2017/04/08/ubuntu16-04%E5%88%A0%E9%99%A4%E5%BD%93%E5%89%8D%E7%94%A8%E6%88%B7%E5%AF%86%E7%A0%81%E6%96%87%E4%BB%B6-etc-shadow%E5%90%8E%E6%97%A0%E6%B3%95%E8%BF%9B%E5%85%A5%E7%B3%BB%E7%BB%9F%E7%9A%84%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95/"/>
<id>whtis.github.io/2017/04/08/ubuntu16-04删除当前用户密码文件-etc-shadow后无法进入系统的解决方法/</id>
<published>2017-04-08T07:48:02.000Z</published>
<updated>2017-04-08T08:55:00.000Z</updated>
<content type="html"><![CDATA[<h3 id="写在前面">写在前面</h3><p>最近项目用到了dspace,在windows中向dspace中导入item时会报解压错误的<code>error</code>,后来经过排查,确定是windows平台编码问题导致。于是转战ubuntu,在安装了16.04版本,配置了<code>java</code>环境、安装编译器等等一系列工作、差不多可以进行开发的时候,我手贱删除了这个文件<code>/etc/shadow</code>,为什么会删除它,这就涉及到安装shadowsocks了,这里不详说。反正是在编辑<code>/etc/shadowsocks.json</code>文件时,我觉得<code>/etc/shadow</code>可能是个临时文件,就用root权限删除了。这样一删不要紧,重启机器以后傻眼了,输入正确的用户名密码后,无法登陆到图形界面,提示也是说密码不对。类似于下图:<br><img src="http://7xnttb.com1.z0.glb.clouddn.com//558d1f9e0e1dcda9c9001b15c0b4d63d.png" alt="ubuntu密码错误"></p>
<h4 id="原因">原因</h4><p>解决这个问题之前,需要了解下ubuntu登陆图形界面时的用户认证过程。以下是个人理解,可能会有错误,还请指正。</p>
<ul>
<li>ubuntu会在<code>/etc</code>目录下保存用户的登陆信息,当然是加密后的,但是不像我们认为的那样只有一个文件,而是包含两个文件:<code>/etc/passwd</code>、<code>/etc/shadow</code>,登陆时ubuntu会自动比对两个文件的信息,如果一直则允许用户登陆,否则就会拒绝。我因为删除了<code>/etc/shadow</code>这个文件,所以在比对时会出错。</li>
</ul>
<h4 id="解决">解决</h4><p>找到了原因,就很好办了。网上有很多遗忘了ubuntu密码后找回的教程,但是很遗憾,我所能找到的没有一个教程是完全正确的,因为大多数都是互相抄,这种现象确实可悲。以下解决思路是我本人亲测,转载时请附带ubuntu版本信息及出处。</p>
<ul>
<li><del>第一种解决方法:既然我是误删了<code>/etc/shadow</code>,那么我想办法在命令行登陆,拷贝<code>/etc/passwd</code>文件为<code>/etc/shadow</code>不就可以绕过俩文件校验了么?(事实证明这个想法行不通,如果你查看两个文件的内容,你会发现实际上两个文件的内容是不同的,但登陆时具体怎么校验的,没有深入研究过)</del></li>
<li><del>第二种方法:想办法在命令行登陆,使用命令行更改用户密码,然后就可以使用原用户名+新密码登陆了(这种方法看似可行,而且命令行中更改密码时也会提示更改成功,但仍然无法登陆图形界面。ps:此时可以在shell中登陆)</del></li>
<li>第三种方法:想办法在命令行登陆,然后创建一个新的用户,将老用户文件拷贝到新用户目录下,重启后就可以使用新用户登陆了。(事实证明这种方法可行,唯一的问题是之前用户的某些配置会丢失,这也没办法了。。)</li>
</ul>
<h5 id="实际操作">实际操作</h5><p>可能有人注意到,上面的解决方法都有一点是相同的,<strong>想办法在命令行登陆</strong>。但当机器出现这问题时,<code>CTRL+ALT+F2~F6</code>都已经无法进入命令行了。下面说下正确的做法,截图来自网络。<br>1、开机按ESC,出现如下界面<br><img src="http://7xnttb.com1.z0.glb.clouddn.com//ef0ff36421090d6e1227c8c3cdfabefb.png" alt="ubuntu开机画面"></p>
<p>2、按回车键进入如下界面,然后选中有recovery mode的选项<br><img src="http://7xnttb.com1.z0.glb.clouddn.com//5ce0970a7d9d6cc2bcd68b4ae256fb47.png" alt="ubuntu-recovery-mode"></p>
<p>3、这一步不要按回车,按下<code>e</code>进行编辑状态,找到图中红色框的<code>recovery nomodeset</code>并删除,修改<code>ro</code>为<code>rw</code>(不改的话进入命令行无法输入命令),并在后面输入<code>quiet splash rw init=/bin/bash</code>后,按<code>F10</code>或<code>CTRL+X</code>重启<br><img src="http://7xnttb.com1.z0.glb.clouddn.com//e512c666c1780ade758e424890e8973c.png" alt="edit-grub"><br><strong>注意,网上大多数教程就是这一步操作不对,导致重启后要么无法进入命令行,要么无法编辑</strong></p>
<p>4、重启后如果看到下图,则说明我们可以开始尝试上面提到的三种方法了。<br><img src="http://7xnttb.com1.z0.glb.clouddn.com//4c37c3c29de17d562411db499f3a3bd1.png" alt="root-temp"></p>
<h4 id="后记">后记</h4><ul>
<li>请注意本文全程的解决是基于ubuntu没有开启<code>root</code>用户图形界面登陆的前提,关于如何开启root用户登陆,可以自行<code>google</code>。如果开启了<code>root</code>用户,则以上的讨论都可以省略,因为以<code>root</code>用户登陆后,更改用户等操作都不会遇到任何困难。因此强烈建议:<code>使用ubuntu桌面版时请开启root用户登陆,方便遇到问题后能及时解决。</code></li>
<li>附上我在测试以上三种方法时,使用的一些<code>ubuntu</code>指令:<ul>
<li>递归拷贝用户目录:<code>sudo cp -r sourceFold_path targetFold_path</code></li>
<li>递归指定用户目录所有者:<code>sudo chown user usergroup</code></li>
<li>增加用户并且在<code>/home</code>目录下生成相应文件夹:<code>sudo adduser user</code>,该命令会自动创建用户主目录,创建用户同名的组</li>
<li>修改密码:<code>sudo passwd</code>,然后输入两次密码即可。如果要修改特定用户的密码,可以在后面加上用户名 <code>sudo passwd user</code></li>
</ul>
</li>
</ul>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h3 id="写在前面">写在前面</h3><p>最近项目用到了dspace,在windows中向dspace中导入item时会报解压错误的<code>error</code>,后来经过排查,确定是windows平台编码问题导致。于是转战ubuntu,在安装了16.04版本,配]]>
</summary>
<category term="ubuntu" scheme="whtis.github.io/tags/ubuntu/"/>
<category term="搜集整理" scheme="whtis.github.io/tags/%E6%90%9C%E9%9B%86%E6%95%B4%E7%90%86/"/>
<category term="ubuntu" scheme="whtis.github.io/categories/ubuntu/"/>
</entry>
<entry>
<title><![CDATA[一个可以链式使用正则的类]]></title>
<link href="whtis.github.io/2017/03/23/%E4%B8%80%E4%B8%AA%E5%8F%AF%E4%BB%A5%E9%93%BE%E5%BC%8F%E4%BD%BF%E7%94%A8%E6%AD%A3%E5%88%99%E7%9A%84%E7%B1%BB/"/>
<id>whtis.github.io/2017/03/23/一个可以链式使用正则的类/</id>
<published>2017-03-23T07:52:01.000Z</published>
<updated>2017-06-19T01:58:35.000Z</updated>
<content type="html"><![CDATA[<p>最近使用webMgaic爬虫框架的时候,发现链式调用真的很好用,能省掉很多套路性的代码,所以也自己动手写了一个可以链式调用进行正则筛选的工具类。下面上代码:</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></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> java.util.regex.*;</div><div class="line"></div><div class="line"><span class="comment">/**</span></div><div class="line"> * Created by wht on 2017/3/18.</div><div class="line"> */</div><div class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RexString</span> </span>{</div><div class="line"></div><div class="line"> <span class="keyword">private</span> String string = <span class="string">""</span>;</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">RexString</span><span class="params">()</span> </span>{</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> <span class="title">RexString</span><span class="params">(String string)</span> </span>{</div><div class="line"> <span class="keyword">this</span>.string = string;</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">public</span> RexString <span class="title">getRex</span><span class="params">(String rex)</span> </span>{</div><div class="line"> Pattern p = Pattern.compile(rex);</div><div class="line"> Matcher m = p.matcher(string);</div><div class="line"> <span class="keyword">if</span> (m.find()) {</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> RexString(m.group());</div><div class="line"> }</div><div class="line"> <span class="keyword">return</span> <span class="keyword">new</span> RexString();</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> String <span class="title">toString</span><span class="params">()</span> </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.string;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>使用:</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></pre></td><td class="code"><pre><div class="line">RexString rexString = <span class="keyword">new</span> RexString(<span class="string">"xxx"</span>);</div><div class="line">RexString rs = rexString.getRex(rex1).getRex(rex2).getRex(rex3) ···</div><div class="line">String result = rs.toString();</div></pre></td></tr></table></figure>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<p>最近使用webMgaic爬虫框架的时候,发现链式调用真的很好用,能省掉很多套路性的代码,所以也自己动手写了一个可以链式调用进行正则筛选的工具类。下面上代码:</p>
<figure class="highlight java"><table><tr><td class="g]]>
</summary>
<category term="java" scheme="whtis.github.io/tags/java/"/>
<category term="总结,原创" scheme="whtis.github.io/tags/%E6%80%BB%E7%BB%93%EF%BC%8C%E5%8E%9F%E5%88%9B/"/>
<category term="java" scheme="whtis.github.io/categories/java/"/>
</entry>
<entry>
<title><![CDATA[hexo博客主题--material折腾笔记]]></title>
<link href="whtis.github.io/2016/11/20/hexo%E5%8D%9A%E5%AE%A2%E4%B8%BB%E9%A2%98-material%E6%8A%98%E8%85%BE%E7%AC%94%E8%AE%B0/"/>
<id>whtis.github.io/2016/11/20/hexo博客主题-material折腾笔记/</id>
<published>2016-11-20T12:56:17.000Z</published>
<updated>2016-11-20T13:21:22.000Z</updated>
<content type="html"><![CDATA[<p> 好久没怎么照看博客了,昨天突然发现,以前用的<code>Next</code>主题挂了。因为用的别人的东西,不知道问题。本着折腾的想法,重新换了一个主题<code>material</code>。该主题算是满足了我大部分的需求吧。但还是有吐槽的地方……(虽然不会写js,不妨碍我吐槽。。原作者看到估计会气的吐血)</p>
<h4 id="添加谷歌和百度统计代码">添加谷歌和百度统计代码</h4><p>按照<a href="https://material.vss.im/expert/" target="_blank" rel="external">使用文档</a>可以自行添加代码,但我添加百度统计代码时报错,所以直接把百度统计代码写到<code>layout/_partial/head.ejs</code>文件中了。</p>
<h4 id="在文章详情页添加回主页的方法">在文章详情页添加回主页的方法</h4><ul>
<li>可以直接点击缩略图返回首页</li>
<li>分享按钮下增加回主页</li>
</ul>
<h4 id="此主题的一些想法">此主题的一些想法</h4><ul>
<li>好的方面<ul>
<li>背景支持bing图片,这个想法点赞</li>
<li>每篇文章都提供缩略图<code>thumbnail</code>的设置,很棒</li>
</ul>
</li>
<li>可以改进的地方<ul>
<li>侧边栏很不完美。包括样式和调出方式(鼠标点击改为鼠标滑过会好很多,尝试过更改,然而太菜,没有找到正确修改的方法)</li>
<li>缺少文章分类页,看github上的issue,感觉作者根本没想到这个需求,但我之前用的主题都有,强迫症伤不起</li>
</ul>
</li>
</ul>
<p>突然想到,改了作者的文件,以后<code>pull</code>的时候又有得折腾了。anyway,自己菜就得忍受这些问题了。</p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<p> 好久没怎么照看博客了,昨天突然发现,以前用的<code>Next</code>主题挂了。因为用的别人的东西,不知道问题。本着折腾的想法,重新换了一个主题<code>material</code>。该主题算是满足了我大部分的需求吧。但还是有吐槽的地方…]]>
</summary>
<category term="原创" scheme="whtis.github.io/tags/%E5%8E%9F%E5%88%9B/"/>
<category term="hexo" scheme="whtis.github.io/tags/hexo/"/>
<category term="hexo" scheme="whtis.github.io/categories/hexo/"/>
</entry>
<entry>
<title><![CDATA[将百度搜索结果的长url链接转换为真实的url地址]]></title>
<link href="whtis.github.io/2016/09/19/%E5%B0%86%E7%99%BE%E5%BA%A6%E6%90%9C%E7%B4%A2%E7%BB%93%E6%9E%9C%E7%9A%84%E9%95%BFurl%E9%93%BE%E6%8E%A5%E8%BD%AC%E6%8D%A2%E4%B8%BA%E7%9C%9F%E5%AE%9E%E7%9A%84url%E5%9C%B0%E5%9D%80/"/>
<id>whtis.github.io/2016/09/19/将百度搜索结果的长url链接转换为真实的url地址/</id>
<published>2016-09-18T16:37:15.000Z</published>
<updated>2017-06-19T01:58:49.000Z</updated>
<content type="html"><![CDATA[<h3 id="写在前面">写在前面</h3><p>最近写爬虫的时候,想调用百度的搜索结果,于是有了这个问题。需要注意的是,本篇内容转化是仅针对百度跳转到非百度链接的,百度内部链接的跳转,如百度搜索主入口跳转到百度知道、百度文库等情况不再讨论之列。</p>
<h3 id="问题描述">问题描述</h3><ul>
<li>百度搜索入口:<code>https://www.baidu.com/s?wd=</code>,后面加上需要的搜索内容即可。如需要进行更加精确的搜索,可以使用百度的<a href="http://imgs.xinhuanet.com/js/baidu/gj.htm" target="_blank" rel="external">高级搜索</a>功能。</li>
<li>百度搜索结果示例:<code>https://www.baidu.com/link?url=NsJrxOylR5il9kO2p7IJZu9RdvqdWgobSo1W7n29FIu&amp;wd=&amp;eqid=e4c01daf0006800b0000000357debb6c</code>,实际的url为<code>www.whtis.com</code>。</li>
<li>原因:百度为什么要实行加密跳转,网上有很多有意思的文章,感兴趣可以看看。这里仅从技术角度进行说明。<br><br>百度的这种跳转采用的是302重定向。虽然有时候状态码不是302,但原理差不多一样:当你点击了百度的加密链接后,会向百度内部的服务器发送请求,这个时候服务器会根据你提交的百度链接使用重定向技术自动跳转到相应的真实url。</li>
</ul>
<h3 id="拿到真实的URL链接">拿到真实的URL链接</h3><p>网上有很多博客都提到过这事,既然知道了302跳转的原理,那么可以采用这种思路拿到真实的URL:模拟一个请求,在百度服务器进行重定向的时候获得重定向的地址即可。<br><br>原理很简单,但是我在网上找了很多,基本都是互相抄,抄的代码怎么说呢,用的工具都是httpclient,但是都是httpclient已经废弃的方法,虽然不报错,但是没法实现这功能了。所以这也告诉我们写博客的人,一定要注明自己的代码基于的是哪个版本的工具,虽然这只是一点小事,但却能让自己和他人节约很多时间。<br><br>下面上代码:</p>
<p><strong>使用版本:httpclient 4.3.3</strong><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></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">getRealUrl</span><span class="params">(String url)</span> </span>{</div><div class="line"></div><div class="line"> String realUrl = <span class="string">""</span>;</div><div class="line"></div><div class="line"> HttpClient client = HttpClientBuilder.create().build();</div><div class="line"> RequestConfig config = RequestConfig.custom()</div><div class="line"> <span class="comment">//禁止自动重定向</span></div><div class="line"> .setRedirectsEnabled(<span class="keyword">false</span>)</div><div class="line"> .build();</div><div class="line"><span class="comment">//302跳转</span></div><div class="line"> HttpGet get = <span class="keyword">new</span> HttpGet(url);</div><div class="line"> get.setConfig(config);</div><div class="line"> <span class="keyword">try</span> {</div><div class="line"> <span class="comment">//对url进行utf-8解码</span></div><div class="line"> url = URLDecoder.decode(url, <span class="string">"utf-8"</span>);</div><div class="line"> HttpResponse rs = client.execute(get);</div><div class="line"> Header h = rs.getFirstHeader(<span class="string">"Location"</span>);</div><div class="line"> realUrl = h.getValue().trim();</div><div class="line"> } <span class="keyword">catch</span> (IOException e) {</div><div class="line"> e.printstacktrace();</div><div class="line"> <span class="keyword">return</span> url;</div><div class="line"> }</div><div class="line"> <span class="keyword">if</span> (realUrl.equals(<span class="string">""</span>)) {</div><div class="line"> <span class="keyword">return</span> url;</div><div class="line"> } <span class="keyword">else</span> {</div><div class="line"> <span class="keyword">return</span> realUrl;</div><div class="line"> }</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<hr>
<div align="center" style="color:red;width=80px;height:90px;" onmouseout="this.style.border='1px solid blue'" onmouseover="this.style.border='none'"><br><p style="font-weight:bold;font-style:italic;">本文章首发<a href="http://www.whtis.com" target="_blank" rel="external">www.whtis.com</a>,转载请注明出处</p><br></div>
]]></content>
<summary type="html">
<![CDATA[<h3 id="写在前面">写在前面</h3><p>最近写爬虫的时候,想调用百度的搜索结果,于是有了这个问题。需要注意的是,本篇内容转化是仅针对百度跳转到非百度链接的,百度内部链接的跳转,如百度搜索主入口跳转到百度知道、百度文库等情况不再讨论之列。</p>
<h3 id="问题描]]>
</summary>
<category term="java,原创" scheme="whtis.github.io/tags/java%EF%BC%8C%E5%8E%9F%E5%88%9B/"/>
<category term="java" scheme="whtis.github.io/categories/java/"/>
</entry>
<entry>
<title><![CDATA[PostgreSQL使用手记]]></title>
<link href="whtis.github.io/2016/09/14/PostgreSQL%E4%BD%BF%E7%94%A8%E6%89%8B%E8%AE%B0/"/>
<id>whtis.github.io/2016/09/14/PostgreSQL使用手记/</id>
<published>2016-09-14T14:29:13.000Z</published>
<updated>2017-03-23T09:58:18.000Z</updated>
<content type="html"><![CDATA[<h3 id="写在前面">写在前面</h3><p><blockcode><br>  因为项目需要,要将本来的<code>mybatis+mysql</code>改为<code>mybatis+postgresql</code>,使用途中难免遇到坑,以下是我遇到的,记录下来供参考。好久没写blog,感觉都快长草了,目前在想要不要开一个分类,写点日常废话😂<br></blockcode><br>  好了,言归正传。</p>
<h3 id="为什么要用PostgreSQL">为什么要用PostgreSQL</h3><p>为什么要用呢,上面已经说了,项目需要。postgresql经常被拿来和mysql进行比较,postgresql可以用来存储较大的对象数据(文本、视频、图片等),符合项目要求。</p>
<h3 id="什么是PostgreSQL">什么是PostgreSQL</h3><p>念都不知道怎么念的一个词,确实第一感觉没有mysql好。看下官方的说法:</p>
<blockquote>
<p>PostgreSQL支持大部分 SQL标准并且提供了许多其他现代特性:复杂查询、外键、触发器、视图、事务完整性、MVCC。同样,PostgreSQL 可以用许多方法扩展,比如, 通过增加新的数据类型、函数、操作符、聚集函数、索引。免费使用、修改、和分发 PostgreSQL,不管是私用、商用、还是学术研究使用。(摘自百度百科)</p>
</blockquote>
<h3 id="使用版本">使用版本</h3><p>本文总结基于PostgreSQL 9.4版本</p>
<h3 id="安装及使用">安装及使用</h3><p>推荐下阮一峰写的这篇博客<a href="http://www.ruanyifeng.com/blog/2013/12/getting_started_with_postgresql.html" target="_blank" rel="external">PostgreSQL新手入门</a></p>
<h3 id="使用中出现的错误及解决方案">使用中出现的错误及解决方案</h3><h4 id="错误一:_java-sql-SQLException:_No_suitable_driver_found_for_jdbc:postgresql://yourhost:port">错误一: <code>java.sql.SQLException: No suitable driver found for jdbc:postgresql://yourhost:port</code></h4><ul>
<li>可能的错误原因:<ul>
<li>使用的postgresql的驱动与当前使用的jdk版本不兼容。具体兼容情况参见官方文档,本文仅作摘录:</li>