-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
541 lines (294 loc) · 310 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>zhiheng's blog</title>
<icon>https://www.gravatar.com/avatar/287d1512b5f4a1b17e98d4a1aaa7f45f</icon>
<subtitle>喜于分享,勤于积累;欢迎关注我的微信公众号:治恒说说</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://dddreams.github.io/"/>
<updated>2021-12-06T06:10:14.799Z</updated>
<id>https://dddreams.github.io/</id>
<author>
<name>Shure</name>
<email>[email protected]</email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>github精选-github加速访问神器</title>
<link href="https://dddreams.github.io/211206-github%E7%B2%BE%E9%80%89-github%E5%8A%A0%E9%80%9F%E8%AE%BF%E9%97%AE%E7%A5%9E%E5%99%A8.html"/>
<id>https://dddreams.github.io/211206-github精选-github加速访问神器.html</id>
<published>2021-12-06T05:12:49.000Z</published>
<updated>2021-12-06T06:10:14.799Z</updated>
<content type="html"><![CDATA[<p>最近分享了很多 Github 的项目或工具后,有很多同学反映 Github 就是一个神器的网站,要么打不开,要么打开了速度也慢的要命,文件下载到一半网络断了,气的人没脾气。还有更奇怪的是有些公司尽然把 Github 给禁了,他还是个软件公司。好吧,有这样烦恼的同学看过来,今天推荐一个 Github 加速神器<code>FastGithub</code>。</p><p>它主要解决 GitHub 打不开、用户头像无法加载、releases 无法上传下载、git-clone、git-pull、git-push 失败等问题。他的原理就是拿到 Github 大量的 IP 数据,来检测哪些 IP 可用,哪些 IP 访问速度最佳,然后进行解析,修改本机的dns服务指向 FastGithub 自身,解析匹配的域名为 FastGithub 自身的 IP,请求不受污染的dns服务(dnscrypt-proxy)获取域名的IP 选择最优的 IP 进行 ssh 或 https 反向代理。</p><p>软件可以傻瓜式使用,不用任何设置,只需要双击运行即可,与代理工具、vpn等不冲突(因为不是同一种类型),也不修改你的系统网络配置。下面是软件目录和 UI 界面。</p><p><img title src="https://s2.loli.net/2021/12/06/B1epZDTgrJ3adiq.png" alt="fastgithub-1.png" data-align="center" width="578"></p><p><img title src="https://s2.loli.net/2021/12/06/nHK5pFhx2mEIBVR.png" alt="fastgithub-3.png" data-align="center" width="561"></p><p>并且跨平台,支持Windows、Linux、Mac平台,根据自己的环境下载相应安装包。</p><p><img title src="https://s2.loli.net/2021/12/06/Wx8fCd1X6IwmQTa.png" alt="fastgithub-2.png" data-align="center" width="583"></p><p>开源地址:<a href="https://github.com/dotnetcore/FastGithub" target="_blank" rel="noopener">https://github.com/dotnetcore/FastGithub</a></p><p>下载地址:<a href="https://github.com/dotnetcore/FastGithub/releases" target="_blank" rel="noopener">https://github.com/dotnetcore/FastGithub/releases</a></p><p>有人说,这是一个死循环,访问不了 Github,如何获取到 FastGithub?</p><p>好的,奉上国内下载地址:<a href="https://gitee.com/jiulang/fast-github" target="_blank" rel="noopener">https://gitee.com/jiulang/fast-github</a>。gitee 上不是最新版本,下载后再去 Github 下载最新版本即可。</p><p><img title src="https://s2.loli.net/2021/12/06/f5IxRew7tXnLCkz.jpg" alt data-align="center" width="284"></p><center>更多文章请关注微信公众号: 治恒说说</center>]]></content>
<summary type="html">
<p>最近分享了很多 Github 的项目或工具后,有很多同学反映 Github 就是一个神器的网站,要么打不开,要么打开了速度也慢的要命,文件下载到一半网络断了,气的人没脾气。还有更奇怪的是有些公司尽然把 Github 给禁了,他还是个软件公司。好吧,有这样烦恼的同学看过来,今
</summary>
<category term="Github" scheme="https://dddreams.github.io/categories/Github/"/>
<category term="Github" scheme="https://dddreams.github.io/tags/Github/"/>
<category term="软件" scheme="https://dddreams.github.io/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>github精选-一款高颜值的Redis客户端</title>
<link href="https://dddreams.github.io/211204-github%E7%B2%BE%E9%80%89-%E4%B8%80%E6%AC%BE%E9%AB%98%E9%A2%9C%E5%80%BC%E7%9A%84Redis%E5%AE%A2%E6%88%B7%E7%AB%AF.html"/>
<id>https://dddreams.github.io/211204-github精选-一款高颜值的Redis客户端.html</id>
<published>2021-12-04T14:31:06.000Z</published>
<updated>2021-12-06T01:27:41.216Z</updated>
<content type="html"><![CDATA[<p>作为开发者,Redis大家都不陌生,一个开源的、高性能的、基于内存运行的键值对数据库,常用来做缓存,Session共享服务器等,进大厂面试必问的内容,公众号后台回复<code>Redis</code>领取关于 Redis 的面试资料。</p><p>好了言归正传,既然他是个数据库,那么我们在使用过程中就会和关系型数据库一样,要进行增删改查这些操作,可惜的是 Redis 官方没有提供可视化工具,便于我们管理 Redis,要对他进行操作就只能这样:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">cd /sur/local/redis</span><br><span class="line">./redis-cli -h 127.0.0.1 -p 6379</span><br><span class="line">127.0.0.1:6379> auth 123456 # 如果设置密码</span><br><span class="line">127.0.0.1:6379> </span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看所有的 key</span></span><br><span class="line">127.0.0.1:6379> keys *</span><br><span class="line"><span class="meta">#</span><span class="bash"> 新增键值对</span></span><br><span class="line">127.0.0.1:6379> set test 'shure'</span><br><span class="line">OK</span><br><span class="line"><span class="meta">#</span><span class="bash"> 查看某个 key 的值</span></span><br><span class="line">127.0.0.1:6379> get test</span><br><span class="line">"shure"</span><br><span class="line"><span class="meta">#</span><span class="bash"> 删除键值对</span></span><br><span class="line">127.0.0.1:6379> del test</span><br><span class="line">(integer) 1</span><br><span class="line"><span class="meta">#</span><span class="bash"> 清空单个库的数据</span></span><br><span class="line">127.0.0.1:6379> select 0</span><br><span class="line">127.0.0.1:6379> flushdb</span><br><span class="line"><span class="meta">#</span><span class="bash"> 清空整个库的数据</span></span><br><span class="line">127.0.0.1:6379> flushall</span><br></pre></td></tr></table></figure><p>或者有时候编写脚本操作一些常用的数据,可以直接使用命令:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">redis-cli -h 127.0.0.1 -p 6379 -a 123456 keys "login:*" |xargs redis-cli -h 127.0.0.1 -p 6379 -a 123456 del</span><br><span class="line"><span class="meta">#</span><span class="bash"><span class="comment"># </span></span></span><br><span class="line">-a 密码</span><br><span class="line">-n 指定数据库</span><br><span class="line">-h ip地址</span><br><span class="line">-p 端口</span><br></pre></td></tr></table></figure><p>命令操作起来的确很装逼,但是很繁琐,而且很容易忘记,所有就涌现出了很多第三方的客户端工具,常用的有<code>Redis Desktop Manager</code>、<code>medis</code>、<code>RedisPlus</code>、<code>FastoRedis</code>、<code>Red</code>(Mac上常用的工具)<code>Redis Insight</code>、<code>Iedis2</code>(Idea 插件)等等,而今天我要推荐这款高颜值的Redis客户端工具<code>AnotherRedisDesktopManager</code>。</p><p>先来看看他的样子</p><p><img title src="https://s2.loli.net/2021/12/06/f12Ag5TqbI9Uamr.jpg" alt width="547" data-align="center"></p><p><img title src="https://s2.loli.net/2021/12/06/OX9hRpFICougjtK.jpg" alt="1.jpg" data-align="center" width="548"></p><p><img title src="https://s2.loli.net/2021/12/06/OIntKTS2gdj7ZYJ.jpg" alt width="548" data-align="center"></p><p><img title src="https://s2.loli.net/2021/12/06/xiwQsDY38dHeRSk.jpg" alt data-align="center" width="556"></p><p><img title src="https://s2.loli.net/2021/12/06/JvqOMDP3G8fyYrg.jpg" alt data-align="center" width="556"></p><p>有黑暗主题、可切换简体中文、新建连接支持SSH跳板机、SSH模式、集群模式、可以查看Redis状态、增删改操作方便直观,并且可以修改过期时间,数据类型,当然也可以一键切换至命令行模式,更是跨平台可以运行在Windows、Linux、Mac平台,就算加载大数据量也不会卡死。</p><p>就是这样一款高颜值,又功能强大的客户端工具,你不赶快试试?</p><p>开源地址:<a href="https://github.com/qishibo/AnotherRedisDesktopManager" target="_blank" rel="noopener">https://github.com/qishibo/AnotherRedisDesktopManager</a></p><p>下载地址:<a href="https://github.com/qishibo/AnotherRedisDesktopManager/releases" target="_blank" rel="noopener">https://github.com/qishibo/AnotherRedisDesktopManager/releases</a></p><p>国内下载:<a href="https://gitee.com/qishibo/AnotherRedisDesktopManager/releases" target="_blank" rel="noopener">https://gitee.com/qishibo/AnotherRedisDesktopManager/releases</a></p><p><img title src="https://s2.loli.net/2021/12/06/f5IxRew7tXnLCkz.jpg" alt data-align="center" width="284"></p><center>更多文章请关注微信公众号: 治恒说说</center>]]></content>
<summary type="html">
<p>作为开发者,Redis大家都不陌生,一个开源的、高性能的、基于内存运行的键值对数据库,常用来做缓存,Session共享服务器等,进大厂面试必问的内容,公众号后台回复<code>Redis</code>领取关于 Redis 的面试资料。</p>
<p>好了言归正传,既然他是个
</summary>
<category term="Github" scheme="https://dddreams.github.io/categories/Github/"/>
<category term="Github" scheme="https://dddreams.github.io/tags/Github/"/>
<category term="软件" scheme="https://dddreams.github.io/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>Python读取Excel中的图片(二)</title>
<link href="https://dddreams.github.io/211203-Python%E8%AF%BB%E5%8F%96Excel%E4%B8%AD%E7%9A%84%E5%9B%BE%E7%89%87%EF%BC%88%E4%BA%8C%EF%BC%89.html"/>
<id>https://dddreams.github.io/211203-Python读取Excel中的图片(二).html</id>
<published>2021-12-03T06:36:51.000Z</published>
<updated>2021-12-03T07:15:24.913Z</updated>
<content type="html"><![CDATA[<p>继上一篇 <a href="https://dddreams.github.io/211116-%E4%BD%BF%E7%94%A8Python%E8%AF%BB%E5%8F%96Excel%E4%B8%AD%E7%9A%84%E5%9B%BE%E7%89%87%E5%B9%B6%E5%AF%B9%E5%BA%94%E5%88%B0%E8%AE%B0%E5%BD%95.html">使用Python读取Excel中的图片并对应到记录</a> ,经过实践之后,又发现了问题,便有了今天这篇。</p><h2 id="1、经过实践后发现的问题"><a href="#1、经过实践后发现的问题" class="headerlink" title="1、经过实践后发现的问题"></a>1、经过实践后发现的问题</h2><p>代码经过实践后,发现还是有问题,有些图片还是对应不到相应的记录,于是又开始了一波debugger,发现不是代码的锅,而是Excel解压后<code>drawing1.xml</code>的锅,来看看我们解析xml的代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">_f</span><span class="params">(subElementObj)</span>:</span></span><br><span class="line"> <span class="keyword">for</span> anchor <span class="keyword">in</span> subElementObj:</span><br><span class="line"> xdr_from = anchor.getElementsByTagName(<span class="string">'xdr:from'</span>)[<span class="number">0</span>]</span><br><span class="line"> col = xdr_from.childNodes[<span class="number">0</span>].firstChild.data <span class="comment"># 获取标签间的数据</span></span><br><span class="line"> row = xdr_from.childNodes[<span class="number">2</span>].firstChild.data</span><br><span class="line"> embed = anchor.getElementsByTagName(<span class="string">'xdr:pic'</span>)[<span class="number">0</span>].getElementsByTagName(<span class="string">'xdr:blipFill'</span>)[<span class="number">0</span>].getElementsByTagName(<span class="string">'a:blip'</span>)[<span class="number">0</span>].getAttribute(<span class="string">'r:embed'</span>) <span class="comment"># 获取属性</span></span><br><span class="line"> image_info[(int(row), int(col))] = img_dict.get(int(embed.replace(<span class="string">'rId'</span>, <span class="string">''</span>)), {}).get(img_feature)</span><br></pre></td></tr></table></figure><p>要解析的xml文档部分内容:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">xdr:pic</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">xdr:blipFill</span>></span></span><br><span class="line"> ...</span><br><span class="line"> <span class="tag"><<span class="name">a:blip</span> <span class="attr">r:embed</span>=<span class="string">"rId1"</span> <span class="attr">cstate</span>=<span class="string">"print"</span>></span></span><br><span class="line"> ...</span><br><span class="line"> <span class="tag"></<span class="name">a:blip</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">xdr:blipFill</span>></span></span><br><span class="line"><span class="tag"></<span class="name">xdr:pic</span>></span></span><br></pre></td></tr></table></figure><p>获取到<code><a:blip></code>元素的<code>r:embed</code>属性,即对应团片的序号,实际上,如果Excel内容是从其他地方复制过来的,他的序号与图片的序号对应不上,导致的问题,遗憾的是没找到什么原因,不知道Excel中是如何对应的,有兴趣的同学可以研究下。</p><h2 id="2、另一种方式的实现"><a href="#2、另一种方式的实现" class="headerlink" title="2、另一种方式的实现"></a>2、另一种方式的实现</h2><p>另一种方式是使用<code>openpyxl</code>和<code>openpyxl_image_loader</code>库,按行读取,loader 图片进行保存,完整代码见:<a href="https://github.com/dddreams/read-excel-image/blob/master/new_read_data.pyhttps://github.com/dddreams/read-excel-image/blob/master/new_read_data.py" target="_blank" rel="noopener">new_read_data.py</a>。</p><h2 id="3、新增的需求"><a href="#3、新增的需求" class="headerlink" title="3、新增的需求"></a>3、新增的需求</h2><ul><li><p>循环读取在某个目录下的多个文件))</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> root, dirs, files <span class="keyword">in</span> os.walk(source_root):</span><br><span class="line"> <span class="keyword">for</span> file <span class="keyword">in</span> files:</span><br><span class="line"> print(os.path.join(root, file))</span><br></pre></td></tr></table></figure></li><li><p>leader 要求照片大于200K不入库,于是添加了压缩图片的功能,我将压缩图片的代码分离了出来<a href="https://github.com/dddreams/read-excel-image/blob/master/compress_image.py" target="_blank" rel="noopener">compress_image.py</a>。</p></li><li><p>将有问题的数据记录下来,写入 Excel,于是有了写入Excel的代码。</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">wb = Workbook()</span><br><span class="line">ws = wb.create_sheet(<span class="string">"存在问题的数据"</span>, <span class="number">0</span>)</span><br><span class="line">index = <span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(len(error_data)):</span><br><span class="line"> index = index + <span class="number">1</span></span><br><span class="line"> arr_list = error_data[i].split(<span class="string">"|"</span>)</span><br><span class="line"> <span class="keyword">for</span> j <span class="keyword">in</span> range(len(arr_list)):</span><br><span class="line"> ws.cell(row = index, column= j+<span class="number">1</span>, value = arr_list[j])</span><br><span class="line">wb.save(target_root + <span class="string">'存在问题的数据.xlsx'</span>)</span><br></pre></td></tr></table></figure></li><li><p>照片使用电话号码命名,并生成日志,写入文件。</p></li></ul><h2 id="4、存在的问题"><a href="#4、存在的问题" class="headerlink" title="4、存在的问题"></a>4、存在的问题</h2><p>由于原始数据中存在照片未采集的记录,但是提取到的数据中这些记录都有对应的照片,原来<code>image_loader = SheetImageLoader(ws)</code>每次读完不会清空字典,所以就会把上一个文件中对应行的照片读取到当前文件的这一行,经过搜索查找发现是<code>openpyxl-image-loader</code>的问题,相关<code>issues</code>地址:<a href="https://github.com/ultr4nerd/openpyxl-image-loader/issues/9" target="_blank" rel="noopener">images should not be static variable of SheetImageLoader</a> 。所以在每次循环结束将<code>image_loader</code> 清空即可,添加这行代码:</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">image_loader._images.clear()</span><br></pre></td></tr></table></figure><h2 id="5、通过VB导出图片"><a href="#5、通过VB导出图片" class="headerlink" title="5、通过VB导出图片"></a>5、通过VB导出图片</h2><p>其实提取Excel中的图片可以使用VB实现,直接在Excel的sheet上右键【查看代码】然后粘贴一下代码执行就会将图片导出来,并且能以任一列的值命名。</p><figure class="highlight vb"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Sub</span> 导出图片()</span><br><span class="line"> <span class="keyword">On</span> <span class="keyword">Error</span> <span class="keyword">Resume</span> <span class="keyword">Next</span></span><br><span class="line"> MkDir ThisWorkbook.Path & <span class="string">"\图片"</span></span><br><span class="line"> <span class="keyword">For</span> <span class="keyword">Each</span> pic <span class="keyword">In</span> ActiveSheet.Shapes</span><br><span class="line"> <span class="keyword">If</span> pic.Type = <span class="number">13</span> <span class="keyword">Then</span></span><br><span class="line"> RN = pic.TopLeftCell.Offset(<span class="number">0</span>, <span class="number">-3</span>).Value</span><br><span class="line"> pic.Copy</span><br><span class="line"> <span class="keyword">With</span> ActiveSheet.ChartObjects.Add(<span class="number">0</span>, <span class="number">0</span>, pic.Width, pic.Height).Chart <span class="comment">'创建图片</span></span><br><span class="line"> .Parent.<span class="keyword">Select</span></span><br><span class="line"> .Paste</span><br><span class="line"> .Export ThisWorkbook.Path & <span class="string">"\图片\"</span> & RN & <span class="string">".jpg"</span></span><br><span class="line"> .Parent.Delete</span><br><span class="line"> <span class="keyword">End</span> <span class="keyword">With</span></span><br><span class="line"> <span class="keyword">End</span> <span class="keyword">If</span></span><br><span class="line"> <span class="keyword">Next</span></span><br><span class="line"> MsgBox <span class="string">"导出图片完成! "</span></span><br><span class="line"><span class="keyword">End</span> <span class="keyword">Sub</span></span><br></pre></td></tr></table></figure><h2 id="6、总结"><a href="#6、总结" class="headerlink" title="6、总结"></a>6、总结</h2><p>经过不断的折腾,发现条条大路通罗马才是真理,不管你用什么方式实现,发现问题、解决问题才是最重要的经历。</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<p>继上一篇 <a href="https://dddreams.github.io/211116-%E4%BD%BF%E7%94%A8Python%E8%AF%BB%E5%8F%96Excel%E4%B8%AD%E7%9A%84%E5%9B%BE%E7%89%87%E5%B9
</summary>
<category term="Python" scheme="https://dddreams.github.io/categories/Python/"/>
<category term="Python" scheme="https://dddreams.github.io/tags/Python/"/>
<category term="Excel" scheme="https://dddreams.github.io/tags/Excel/"/>
</entry>
<entry>
<title>github精选-Linux命令大全</title>
<link href="https://dddreams.github.io/211124-github%E7%B2%BE%E9%80%89-Linux%E5%91%BD%E4%BB%A4%E5%A4%A7%E5%85%A8.html"/>
<id>https://dddreams.github.io/211124-github精选-Linux命令大全.html</id>
<published>2021-11-24T07:31:41.000Z</published>
<updated>2021-11-24T09:10:24.554Z</updated>
<content type="html"><![CDATA[<p> 对于程序员,和 Linux 打交道那是一项必不可少的技能,有很多同学问我,Linux命令这么多,该怎么从入门到精通呢?今天给大家推荐一个学习和查询Linux命令的好工具<code>linux-command</code>.</p><p>先来看看效果</p><p><img src="https://user-images.githubusercontent.com/1680273/123261829-ce830300-d529-11eb-8cea-a39059b972dd.gif" alt="linux-command"></p><p>该仓库搜集了570多个Linux命令,内容包含 Linux 命令手册、详解、学习等,非常值得收藏的 Linux 命令速查手册。并且作者生成了一个web网站,方便大家使用,地址:<a href="https://git.io/linux" target="_blank" rel="noopener">https://git.io/linux</a>。由于网络的原因这个地址可能访问不到,大家可以访问:<a href="https://wangchujiang.com/linux-command" target="_blank" rel="noopener">https://wangchujiang.com/linux-command</a>。其中包含了Linux命令有:</p><p><img src="https://i.loli.net/2021/11/24/nsJ1do8L4TGgftW.png" alt="1234566.png"></p><p>不仅于此,作者还提供了微信小程序版、Chrome插件、Alfred 版本、Dash 版本、命令行工具等方式进行检索。下载方式请移步仓库地址。</p><p>仓库还整理了很多Linux学习资料,包括各种资讯、文章、技术,知识相关、软件工具等,是一个学习Linux的不错选择。</p><p>开源地址:<a href="https://github.com/jaywcjlove/linux-command" target="_blank" rel="noopener">https://github.com/jaywcjlove/linux-command</a></p><p>网站地址:<a href="https://wangchujiang.com/linux-command/" target="_blank" rel="noopener">https://wangchujiang.com/linux-command/</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<p> 对于程序员,和 Linux 打交道那是一项必不可少的技能,有很多同学问我,Linux命令这么多,该怎么从入门到精通呢?今天给大家推荐一个学习和查询Linux命令的好工具<code>linux-command</code>.</p>
<p>先来看看效果</p>
<p><im
</summary>
<category term="Github" scheme="https://dddreams.github.io/categories/Github/"/>
<category term="Github" scheme="https://dddreams.github.io/tags/Github/"/>
<category term="工具" scheme="https://dddreams.github.io/tags/%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>github精选-PeaZip一款好用又免费的压缩软件</title>
<link href="https://dddreams.github.io/211120-github%E7%B2%BE%E9%80%89-PeaZip%E4%B8%80%E6%AC%BE%E5%A5%BD%E7%94%A8%E5%8F%88%E5%85%8D%E8%B4%B9%E7%9A%84%E5%8E%8B%E7%BC%A9%E8%BD%AF%E4%BB%B6.html"/>
<id>https://dddreams.github.io/211120-github精选-PeaZip一款好用又免费的压缩软件.html</id>
<published>2021-11-20T14:46:17.000Z</published>
<updated>2021-11-20T14:19:41.721Z</updated>
<content type="html"><![CDATA[<p>先问大家一个问题,你们电脑上常用的压缩软件是什么呢?<code>WinRaR</code>?还是<code>BandiZip</code>?或者国产的<strong>好压</strong>、还是<strong>360压缩</strong>?如果是后两者,那么我建议你是时候换一款压缩软件了。</p><h3 id="1、介绍"><a href="#1、介绍" class="headerlink" title="1、介绍"></a>1、介绍</h3><p>今天介绍的便是一款好用又免费而且没有广告的压缩软件<code>PeaZip</code>,跨平台Windows和Linux都可以运行,它本身有一种原生的压缩格式叫做 pea。除此之外,完全支持7Z, 7Z-sfx, BZip2, GZip/TGZ, PAQ8F, PAQ8JD, PAQ8L, PEA, QUAD, split (.001), TAR, ZIP等格式;支持浏览或解压缩以下格式:CE, ARJ, CAB, CHM, CPIO, ISO, Java archives (JAR, EAR, WAR), Linux installers (DEB, PET/PUP, RPM, SLP), LHA, LZH, Open Office file types, PAK/PK3/PK4, RAR, Windows installers (NSIS, some MSI), WIM, XPI, Z/TZ。这些格式不能支持压缩,说原因,大家也明白,这些格式都是不开放的。</p><h3 id="2、软件界面"><a href="#2、软件界面" class="headerlink" title="2、软件界面"></a>2、软件界面</h3><p>界面长这样子</p><p><img src="https://tva1.sinaimg.cn/large/9b21a2d4ly8gwlwa46d6aj20oh0e275v.jpg" alt="1.png" style="zoom:80%;"></p><p>你可以轻松切换到中文界面,打开【Options】菜单选择【Localization】,然后选择<code>chs.txt</code>(简体中文)或者<code>cht.txt</code>(繁体中文)就变为下面这样了。</p><p><img src="https://tva1.sinaimg.cn/large/9b21a2d4ly8gwlwhke0dcj20oi0e40uc.jpg" alt="2.png" style="zoom:80%;"></p><h3 id="3、有哪些不错的功能"><a href="#3、有哪些不错的功能" class="headerlink" title="3、有哪些不错的功能"></a>3、有哪些不错的功能</h3><h5 id="1、智能解压"><a href="#1、智能解压" class="headerlink" title="1、智能解压"></a>1、智能解压</h5><p><img src="https://cdn.sspai.com/2019/12/24/b9f3bc1e6456b1502996762363ebbac7.png" alt="像这样" style="zoom:80%;"></p><p>如果在一个压缩文件中包含很多文件,就像上图,当我们在未打开这个压缩软件时先点击「解压到当前文件夹」时,一定会在下一秒为刚刚做出的操作而后悔,因为这些文件迅速的占领了你的视野,就像下图这样:</p><p><img src="https://cdn.sspai.com/2019/12/24/acf5196799ffa78b7c9d7a43f868c4d7.png" alt="解压后" style="zoom:80%;"></p><p>但是如果遇到内置一个单独文件夹的压缩包时,当我们像以往一样使用「解压到(压缩文件名称的)文件夹」时就会遇到令人抓狂的「套娃」模式。这时,智能解压就显得十分重要。当解压一个我们不知道内部是什么的文件夹时,使用智能解压,就可以优雅地将压缩包中的目标文件整理到当前目录的同名文件夹里。不用担心是否会套娃,也不用担心一堆文件突然塞满你的文件夹。</p><h5 id="2、快速拖放"><a href="#2、快速拖放" class="headerlink" title="2、快速拖放"></a>2、快速拖放</h5><p>若你习惯于直接将压缩包内文件「拖出来」来进行解压操作,那你大概常常会在压缩软件的解压进度条走完后遇到又一个进度条:将操作文件从临时文件夹复制到目标拖放目录。</p><p>这种情况常见于从一个特别大的压缩文件里面提取一个小文件,在 WinRAR 上我们需要等待它完全解压、转存到临时文件夹,然后再让它自动把你要的文件放到你想要的位置上去……这一切无疑都太耗费时间了。</p><p>而 Peazip 在解压大文件时会使用「快速拖放」功能,让你的文件被直接解压到目标拖放目录,而非从临时文件夹处中转。既不需要你改变操作习惯,同时有效减少你的等待时间。</p><h5 id="3、密码管理器"><a href="#3、密码管理器" class="headerlink" title="3、密码管理器"></a>3、密码管理器</h5><p><img src="https://tva3.sinaimg.cn/large/9b21a2d4ly8gwlwh7dk3gj20gn0bwq33.jpg" alt="密码管理器" style="zoom: 80%;"></p><p>如果你有密码管理的需求,PeaZip 是一个不错的选择。</p><h3 id="4、开源地址"><a href="#4、开源地址" class="headerlink" title="4、开源地址"></a>4、开源地址</h3><p>github地址:<a href="https://github.com/peazip/PeaZip" target="_blank" rel="noopener">https://github.com/peazip/PeaZip</a></p><p>下载地址:<a href="https://peazip.github.io/" target="_blank" rel="noopener">https://peazip.github.io/</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<p>先问大家一个问题,你们电脑上常用的压缩软件是什么呢?<code>WinRaR</code>?还是<code>BandiZip</code>?或者国产的<strong>好压</strong>、还是<strong>360压缩</strong>?如果是后两者,那么我建议你是时候换
</summary>
<category term="Github" scheme="https://dddreams.github.io/categories/Github/"/>
<category term="Github" scheme="https://dddreams.github.io/tags/Github/"/>
<category term="软件" scheme="https://dddreams.github.io/tags/%E8%BD%AF%E4%BB%B6/"/>
</entry>
<entry>
<title>使用Python读取Excel中的图片并对应到记录</title>
<link href="https://dddreams.github.io/211116-%E4%BD%BF%E7%94%A8Python%E8%AF%BB%E5%8F%96Excel%E4%B8%AD%E7%9A%84%E5%9B%BE%E7%89%87%E5%B9%B6%E5%AF%B9%E5%BA%94%E5%88%B0%E8%AE%B0%E5%BD%95.html"/>
<id>https://dddreams.github.io/211116-使用Python读取Excel中的图片并对应到记录.html</id>
<published>2021-11-16T13:40:58.000Z</published>
<updated>2021-11-16T12:34:59.631Z</updated>
<content type="html"><![CDATA[<h2 id="1、需求"><a href="#1、需求" class="headerlink" title="1、需求"></a>1、需求</h2><p> 有这样一个需求,将采集在 Excel 中的人员信息(包含照片)导入到 Mysql 库。需求很简单,就是读取 Excel 中的数据插入到 Mysql 中的表,问题在于照片怎么读取?</p><h2 id="2、分析"><a href="#2、分析" class="headerlink" title="2、分析"></a>2、分析</h2><p> 对于读取文本数据,直接按行读取即可;对于图片,常用做法是,将 Excel 文件的后缀名改为 zip,然后解压文件,对应文件名目录下有一个<code>\xl\media</code>的目录,里面便是我们要照片的图片,然而,他的文件名、文件顺序都是乱的,确定不出图片对应的记录,怎么办呢?其实目录中还有这样一个 xml 文件<code>\xl\drawings\drawing1.xml</code>其中就有图片与记录的对应关系,只要解析 xml 文件就可以了。知道了这个思路,那我们来一步步的实现他。</p><h2 id="3、实现"><a href="#3、实现" class="headerlink" title="3、实现"></a>3、实现</h2><p>1、复制并修改指定目录下的文件类型名,将excel后缀名修改为.zip</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">copy_change_file_name</span><span class="params">(file_path, new_type=<span class="string">'.zip'</span>)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> isfile_exist(file_path):</span><br><span class="line"> <span class="keyword">return</span> <span class="string">''</span></span><br><span class="line"> extend = os.path.splitext(file_path)[<span class="number">1</span>] <span class="comment"># 获取文件拓展名</span></span><br><span class="line"> <span class="keyword">if</span> extend != <span class="string">'.xlsx'</span> <span class="keyword">and</span> extend != <span class="string">'.xls'</span>:</span><br><span class="line"> print(<span class="string">"It's not a excel file! %s"</span> % file_path)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> file_name = os.path.basename(file_path) <span class="comment"># 获取文件名</span></span><br><span class="line"> new_name = str(file_name.split(<span class="string">'.'</span>)[<span class="number">0</span>]) + new_type <span class="comment"># 新的文件名,命名为:xxx.zip</span></span><br><span class="line"></span><br><span class="line"> dir_path = os.path.dirname(file_path) <span class="comment"># 获取文件所在目录</span></span><br><span class="line"> new_path = os.path.join(dir_path, new_name) <span class="comment"># 新的文件路径</span></span><br><span class="line"> <span class="keyword">if</span> os.path.exists(new_path):</span><br><span class="line"> os.remove(new_path)</span><br><span class="line"> shutil.copyfile(file_path, new_path)</span><br><span class="line"> <span class="keyword">return</span> new_path <span class="comment"># 返回新的文件路径,压缩包</span></span><br></pre></td></tr></table></figure><p>2、解压文件</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">unzip_file</span><span class="params">(zipfile_path)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> isfile_exist(zipfile_path):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> <span class="keyword">if</span> os.path.splitext(zipfile_path)[<span class="number">1</span>] != <span class="string">'.zip'</span>:</span><br><span class="line"> print(<span class="string">"It's not a zip file! %s"</span> % zipfile_path)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> file_zip = zipfile.ZipFile(zipfile_path, <span class="string">'r'</span>)</span><br><span class="line"> file_name = os.path.basename(zipfile_path) <span class="comment"># 获取文件名</span></span><br><span class="line"> zipdir = os.path.join(os.path.dirname(zipfile_path), str(file_name.split(<span class="string">'.'</span>)[<span class="number">0</span>])) <span class="comment"># 获取文件所在目录</span></span><br><span class="line"> <span class="keyword">for</span> files <span class="keyword">in</span> file_zip.namelist():</span><br><span class="line"> file_zip.extract(files, os.path.join(zipfile_path, zipdir)) <span class="comment"># 解压到指定文件目录</span></span><br><span class="line"> file_zip.close()</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br></pre></td></tr></table></figure><p>3、读取解压后的文件夹,打印图片路径</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_img</span><span class="params">(zipfile_path)</span>:</span></span><br><span class="line"> img_dict = dict()</span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> isfile_exist(zipfile_path):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"> dir_path = os.path.dirname(zipfile_path) <span class="comment"># 获取文件所在目录</span></span><br><span class="line"> file_name = os.path.basename(zipfile_path) <span class="comment"># 获取文件名</span></span><br><span class="line"> pic_dir = <span class="string">'xl'</span> + os.sep + <span class="string">'media'</span> <span class="comment"># excel变成压缩包后,再解压,图片在media目录</span></span><br><span class="line"> pic_path = os.path.join(dir_path, str(file_name.split(<span class="string">'.'</span>)[<span class="number">0</span>]), pic_dir)</span><br><span class="line"></span><br><span class="line"> file_list = os.listdir(pic_path)</span><br><span class="line"> <span class="keyword">for</span> file <span class="keyword">in</span> file_list:</span><br><span class="line"> filepath = os.path.join(pic_path, file)</span><br><span class="line"> print(filepath)</span><br><span class="line"> img_index = int(re.findall(<span class="string">r'image(\d+)\.'</span>, filepath)[<span class="number">0</span>])</span><br><span class="line"> img_dict[img_index] = dict(img_index=img_index, img_path=filepath)</span><br><span class="line"> <span class="keyword">return</span> img_dict</span><br></pre></td></tr></table></figure><p>4、解析xml 文件,获取图片在excel表格中的索引位置信息</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_img_pos_info</span><span class="params">(zip_file_path, img_dict, img_feature)</span>:</span></span><br><span class="line"> <span class="string">"""解析xml 文件,获取图片在excel表格中的索引位置信息"""</span></span><br><span class="line"> os.path.dirname(zip_file_path)</span><br><span class="line"> dir_path = os.path.dirname(zip_file_path) <span class="comment"># 获取文件所在目录</span></span><br><span class="line"> file_name = os.path.basename(zip_file_path) <span class="comment"># 获取文件名</span></span><br><span class="line"> xml_dir = <span class="string">'xl'</span> + os.sep + <span class="string">'drawings'</span> + os.sep + <span class="string">'drawing1.xml'</span></span><br><span class="line"> xml_path = os.path.join(dir_path, str(file_name.split(<span class="string">'.'</span>)[<span class="number">0</span>]), xml_dir)</span><br><span class="line"> image_info_dict = parse_xml(xml_path, img_dict, img_feature=img_feature) <span class="comment"># 解析xml 文件, 返回图片索引位置信息</span></span><br><span class="line"> <span class="keyword">return</span> image_info_dict</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 重命名解压获取图片位置,及图片表格索引信息</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_img_info</span><span class="params">(excel_file_path, img_feature)</span>:</span></span><br><span class="line"> <span class="keyword">if</span> img_feature <span class="keyword">not</span> <span class="keyword">in</span> [<span class="string">"img_index"</span>, <span class="string">"img_path"</span>, <span class="string">"img_base64"</span>]:</span><br><span class="line"> <span class="keyword">raise</span> Exception(<span class="string">'图片返回参数错误, ["img_index", "img_path", "img_base64"]'</span>)</span><br><span class="line"> zip_file_path = copy_change_file_name(excel_file_path)</span><br><span class="line"> <span class="keyword">if</span> zip_file_path != <span class="string">''</span>:</span><br><span class="line"> <span class="keyword">if</span> unzip_file(zip_file_path):</span><br><span class="line"> img_dict = read_img(zip_file_path)</span><br><span class="line"> image_info_dict = get_img_pos_info(zip_file_path, img_dict, img_feature)</span><br><span class="line"> <span class="keyword">return</span> image_info_dict</span><br><span class="line"> <span class="keyword">return</span> dict()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment"># 解析xml文件并获取对应图片位置</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">parse_xml</span><span class="params">(file_name, img_dict, img_feature=<span class="string">'img_path'</span>)</span>:</span></span><br><span class="line"> <span class="comment"># 得到文档对象</span></span><br><span class="line"> image_info = dict()</span><br><span class="line"> dom_obj = xmldom.parse(file_name)</span><br><span class="line"> <span class="comment"># 得到元素对象</span></span><br><span class="line"> element = dom_obj.documentElement</span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">_f</span><span class="params">(subElementObj)</span>:</span></span><br><span class="line"> <span class="keyword">for</span> anchor <span class="keyword">in</span> subElementObj:</span><br><span class="line"> xdr_from = anchor.getElementsByTagName(<span class="string">'xdr:from'</span>)[<span class="number">0</span>]</span><br><span class="line"> col = xdr_from.childNodes[<span class="number">0</span>].firstChild.data <span class="comment"># 获取标签间的数据</span></span><br><span class="line"> row = xdr_from.childNodes[<span class="number">2</span>].firstChild.data</span><br><span class="line"> embed = \</span><br><span class="line"> anchor.getElementsByTagName(<span class="string">'xdr:pic'</span>)[<span class="number">0</span>].getElementsByTagName(<span class="string">'xdr:blipFill'</span>)[<span class="number">0</span>].getElementsByTagName(<span class="string">'a:blip'</span>)[<span class="number">0</span>].getAttribute(<span class="string">'r:embed'</span>) <span class="comment"># 获取属性</span></span><br><span class="line"> image_info[(int(row), int(col))] = img_dict.get(int(embed.replace(<span class="string">'rId'</span>, <span class="string">''</span>)), {}).get(img_feature)</span><br><span class="line"></span><br><span class="line"> sub_twoCellAnchor = element.getElementsByTagName(<span class="string">"xdr:twoCellAnchor"</span>)</span><br><span class="line"> sub_oneCellAnchor = element.getElementsByTagName(<span class="string">"xdr:oneCellAnchor"</span>)</span><br><span class="line"> _f(sub_twoCellAnchor)</span><br><span class="line"> _f(sub_oneCellAnchor)</span><br><span class="line"> <span class="keyword">return</span> image_info</span><br></pre></td></tr></table></figure><p>5、读取包含图片的excel数据</p><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">read_excel_info</span><span class="params">(file_path, img_col_index, img_feature=<span class="string">'img_path'</span>)</span>:</span></span><br><span class="line"> img_info_dict = get_img_info(file_path, img_feature)</span><br><span class="line"> book = xlrd.open_workbook(file_path)</span><br><span class="line"> sheet = book.sheet_by_index(<span class="number">0</span>)</span><br><span class="line"> head = dict()</span><br><span class="line"> <span class="keyword">for</span> i, v <span class="keyword">in</span> enumerate(sheet.row(<span class="number">0</span>)):</span><br><span class="line"> head[i] = v.value</span><br><span class="line"> info_list = []</span><br><span class="line"> <span class="keyword">for</span> row_num <span class="keyword">in</span> range(sheet.nrows):</span><br><span class="line"> d = dict()</span><br><span class="line"> <span class="keyword">for</span> col_num <span class="keyword">in</span> range(sheet.ncols):</span><br><span class="line"> <span class="keyword">if</span> row_num == <span class="number">0</span>:</span><br><span class="line"> <span class="keyword">continue</span></span><br><span class="line"> <span class="keyword">if</span> <span class="string">'empty:'</span> <span class="keyword">in</span> sheet.cell(row_num, col_num).__str__():</span><br><span class="line"> <span class="keyword">if</span> col_num <span class="keyword">in</span> img_col_index:</span><br><span class="line"> d[head[col_num]] = img_info_dict.get((row_num, col_num))</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> d[head[col_num]] = sheet.cell(row_num, col_num).value</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> d[head[col_num]] = sheet.cell(row_num, col_num).value</span><br><span class="line"> <span class="keyword">if</span> d:</span><br><span class="line"> info_list.append(d)</span><br><span class="line"> <span class="keyword">return</span> info_list</span><br></pre></td></tr></table></figure><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> re</span><br><span class="line"><span class="keyword">import</span> xml.dom.minidom <span class="keyword">as</span> xmldom</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">import</span> zipfile</span><br><span class="line"><span class="keyword">import</span> shutil</span><br><span class="line"><span class="keyword">import</span> xlrd</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> path = <span class="string">r'E:\\员工信息.xlsx'</span></span><br><span class="line"> data = read_excel_info(path, img_col_index=[<span class="number">4</span>])</span><br><span class="line"> print(data)</span><br></pre></td></tr></table></figure><p>完整代码:<a href="https://github.com/dddreams/read-excel-image/blob/master/read_users.py" target="_blank" rel="noopener">https://github.com/dddreams/read-excel-image/blob/master/read_users.py</a></p><h2 id="4、处理图片对应不到的问题"><a href="#4、处理图片对应不到的问题" class="headerlink" title="4、处理图片对应不到的问题"></a>4、处理图片对应不到的问题</h2><p> 有时会出现图片对应不到记录的情况,这是因为 Excel 中图片不在单独的单元格内,指定的列中取不到图片,这就需要调整 Excel 内的数据了,但是如果数据量比较大,调整起来就比较麻烦了。其实,我们可以借助 Excel 宏的功能,批量修改图片大小,批量让其居中,位于单元格内部,这样在解析 xml 时就能对应到每条记录了。</p><p>打开 Excel 按下 <code>Alt + F11</code> ,然后点击【插入】菜单,选择【模块】复制下面代码:</p><figure class="highlight vbscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Sub</span> dq()</span><br><span class="line"><span class="keyword">Dim</span> shp As Shape</span><br><span class="line"><span class="keyword">For</span> <span class="keyword">Each</span> shp <span class="keyword">In</span> ActiveSheet.Shapes</span><br><span class="line">shp.<span class="built_in">Left</span> = (shp.TopLeftCell.Width - shp.Width) / <span class="number">2</span> + shp.TopLeftCell.<span class="built_in">Left</span></span><br><span class="line">shp.Top = (shp.TopLeftCell.Height - shp.Height) / <span class="number">2</span> + shp.TopLeftCell.Top</span><br><span class="line"><span class="keyword">Next</span></span><br><span class="line"><span class="keyword">End</span> <span class="keyword">Sub</span></span><br></pre></td></tr></table></figure><p>然后返回 Excel 【开始】菜单中点击【查找和选择】选择【定位条件】中【对象】,将选中所有的图片,然后按下<code>Alt+F8</code> 点击【执行】就可以批量将图片在所在单元格居中了。如果图片大小过大,全部选中后更改图片大小即可。然后解析 Xml 文件就不会出现图片与记录对应不到的情况了。</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<h2 id="1、需求"><a href="#1、需求" class="headerlink" title="1、需求"></a>1、需求</h2><p> 有这样一个需求,将采集在 Excel 中的人员信息(包含照片)导入到 Mysql 库。需求很简单,就是读取 Exce
</summary>
<category term="Python" scheme="https://dddreams.github.io/categories/Python/"/>
<category term="Python" scheme="https://dddreams.github.io/tags/Python/"/>
<category term="Excel" scheme="https://dddreams.github.io/tags/Excel/"/>
</entry>
<entry>
<title>Spring RestTemplate的使用</title>
<link href="https://dddreams.github.io/211111-SpringRestTemplate%E7%9A%84%E4%BD%BF%E7%94%A8.html"/>
<id>https://dddreams.github.io/211111-SpringRestTemplate的使用.html</id>
<published>2021-11-11T13:40:58.000Z</published>
<updated>2021-11-11T13:50:51.352Z</updated>
<content type="html"><![CDATA[<p>实际工作中我们经常会遇到利用 java 去访问其他接口获取数据,常用的有 JDK 原生的 HttpURLConnection 无需依赖其他包,提供了很多方法;另外 HttpClient 也是我们的不二选择,在 spring 之前,我们经常会用 HttpClient 来处理;还有一种方式就是利用 Socket,使用起来比较麻烦;用了 spring 之后,RestTemplate 则更加方便,更加灵活。</p><h2 id="一、概述"><a href="#一、概述" class="headerlink" title="一、概述"></a>一、概述</h2><p>spring框架提供的RestTemplate类可用于在应用中调用rest服务,它简化了与http服务的通信方式,统一了RESTful的标准,封装了http链接, 我们只需要传入url及返回值类型即可。相较于之前常用的HttpClient,RestTemplate是一种更优雅的调用RESTful服务的方式。</p><p>在Spring应用程序中访问第三方REST服务与使用Spring RestTemplate类有关。RestTemplate类的设计原则与许多其他Spring 模板类(例如JdbcTemplate、JmsTemplate)相同,为执行复杂任务提供了一种具有默认行为的简化方法。</p><p>RestTemplate默认依赖JDK提供http连接的能力(HttpURLConnection),如果有需要的话也可以通过setRequestFactory方法替换为例如 Apache HttpComponents、Netty或OkHttp等其它HTTP library。</p><p>考虑到RestTemplate类是为调用REST服务而设计的,因此它的主要方法与REST的基础紧密相连就不足为奇了,后者是HTTP协议的方法:HEAD、GET、POST、PUT、DELETE和OPTIONS。例如,RestTemplate类具有headForHeaders()、getForObject()、postForObject()、put()和delete()等方法。</p><h2 id="二、实现"><a href="#二、实现" class="headerlink" title="二、实现"></a>二、实现</h2><p>最新api地址:<br><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html" target="_blank" rel="noopener">https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html</a></p><p>RestTemplate包含以下几个部分:</p><p>HttpMessageConverter 对象转换器<br>ClientHttpRequestFactory 默认是JDK的HttpURLConnection<br>ResponseErrorHandler 异常处理<br>ClientHttpRequestInterceptor 请求拦截器</p><p>常规配置<br><figure class="highlight x86asm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">public</span> MyRestClientService(RestTemplateBuilder restTemplateBuilder) { </span><br><span class="line"> this<span class="number">.</span>restTemplate = restTemplateBuilder </span><br><span class="line"><span class="meta"> .basicAuthorization</span>(<span class="string">"username"</span>, <span class="string">"password"</span>) </span><br><span class="line"><span class="meta"> .setConnectTimeout</span>(<span class="number">3000</span>) </span><br><span class="line"><span class="meta"> .setReadTimeout</span>(<span class="number">5000</span>) </span><br><span class="line"><span class="meta"> .rootUri</span>(<span class="string">"http://api.example.com/"</span>) </span><br><span class="line"><span class="meta"> .build</span>()<span class="comment">; </span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>ClientHttpRequestInterceptor<br>学习使用带有Spring RestTemplate的ClientHttpRequestInterceptor,以Spring AOP风格记录请求和响应头和主体。</p><p>拦截器记录请求和响应<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.slf4j.Logger;<span class="keyword">import</span> org.slf4j.LoggerFactory;</span><br><span class="line"><span class="keyword">import</span> org.springframework.http.HttpRequest;</span><br><span class="line"><span class="keyword">import</span> org.springframework.http.client.ClientHttpRequestExecution;</span><br><span class="line"><span class="keyword">import</span> org.springframework.http.client.ClientHttpRequestInterceptor;</span><br><span class="line"><span class="keyword">import</span> org.springframework.http.client.ClientHttpResponse;</span><br><span class="line"><span class="keyword">import</span> org.springframework.util.StreamUtils;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.nio.charset.Charset;</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RequestResponseLoggingInterceptor</span> <span class="title">implements</span> <span class="title">ClientHttpRequestInterceptor</span> {</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> Logger <span class="built_in">log</span> = LoggerFactory.getLogger(<span class="keyword">this</span>.getClass());</span><br><span class="line"> </span><br><span class="line"> @Override</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ClientHttpResponse <span class="title">intercept</span><span class="params">(HttpRequest request, <span class="keyword">byte</span>[] body, ClientHttpRequestExecution execution)</span> throws IOException</span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> logRequest(request, body);</span><br><span class="line"> ClientHttpResponse response = execution.execute(request, body);</span><br><span class="line"> logResponse(response);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//Add optional additional headers</span></span><br><span class="line"> response.getHeaders().add(<span class="string">"headerName"</span>, <span class="string">"VALUE"</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">logRequest</span><span class="params">(HttpRequest request, <span class="keyword">byte</span>[] body)</span> throws IOException</span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">log</span>.isDebugEnabled())</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"===========================request begin================================================"</span>);</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"URI : {}"</span>, request.getURI());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Method : {}"</span>, request.getMethod());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Headers : {}"</span>, request.getHeaders());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Request body: {}"</span>, <span class="keyword">new</span> <span class="keyword">String</span>(body, <span class="string">"UTF-8"</span>));</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"==========================request end================================================"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">logResponse</span><span class="params">(ClientHttpResponse response)</span> throws IOException</span></span><br><span class="line"><span class="function"> </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">log</span>.isDebugEnabled())</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"============================response begin=========================================="</span>);</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Status code : {}"</span>, response.getStatusCode());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Status text : {}"</span>, response.getStatusText());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Headers : {}"</span>, response.getHeaders());</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"Response body: {}"</span>, StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));</span><br><span class="line"> <span class="built_in">log</span>.debug(<span class="string">"=======================response end================================================="</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>注册ClientHttpRequestInterceptor<br><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">@Bean</span><br><span class="line"><span class="keyword">public</span> RestTemplate restTemplate(){</span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> <span class="type">RestTemplate</span>();</span><br><span class="line"> </span><br><span class="line"> restTemplate.setRequestFactory(<span class="keyword">new</span><span class="type">BufferingClientHttpRequestFactory</span>(clientHttpRequestFactory()));</span><br><span class="line"> restTemplate.setMessageConverters(Collections.singletonList(mappingJacksonHttpMessageConverter()));</span><br><span class="line"> </span><br><span class="line"> restTemplate.setInterceptors( Collections.singletonList(<span class="keyword">new</span><span class="type">RequestResponseLoggingInterceptor</span>()) );</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> restTemplate;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="三、使用"><a href="#三、使用" class="headerlink" title="三、使用"></a>三、使用</h2><p>GET<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">getEmployees</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees"</span>;</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> <span class="keyword">String</span> result = restTemplate.getForObject(uri, <span class="keyword">String</span>.class);</span><br><span class="line"> </span><br><span class="line"> System.out.<span class="built_in">println</span>(result);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>使用RestTemplate定制HTTP头文件<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">getEmployees</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees"</span>;</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> </span><br><span class="line"> HttpHeaders headers = <span class="keyword">new</span> HttpHeaders();</span><br><span class="line"> headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));</span><br><span class="line"> HttpEntity<<span class="keyword">String</span>> entity = <span class="keyword">new</span> HttpEntity<<span class="keyword">String</span>>(<span class="string">"parameters"</span>, headers);</span><br><span class="line"> </span><br><span class="line"> ResponseEntity<<span class="keyword">String</span>> result = restTemplate.exchange(uri, HttpMethod.GET, entity, <span class="keyword">String</span>.class);</span><br><span class="line"> </span><br><span class="line"> System.out.<span class="built_in">println</span>(result);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>Get请求获取响应为一个对象<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">getEmployees</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees"</span>;</span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> </span><br><span class="line"> EmployeeListVO result = restTemplate.getForObject(uri, EmployeeListVO.class);</span><br><span class="line"> </span><br><span class="line"> System.out.<span class="built_in">println</span>(result);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>URL 参数<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">getEmployeeById</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees/{id}"</span>;</span><br><span class="line"> </span><br><span class="line"> Map<<span class="keyword">String</span>, <span class="keyword">String</span>> params = <span class="keyword">new</span> HashMap<<span class="keyword">String</span>, <span class="keyword">String</span>>();</span><br><span class="line"> params.<span class="built_in">put</span>(<span class="string">"id"</span>, <span class="string">"1"</span>);</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> EmployeeVO result = restTemplate.getForObject(uri, EmployeeVO.class, params);</span><br><span class="line"> </span><br><span class="line"> System.out.<span class="built_in">println</span>(result);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>POST<br><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> void createEmployee(){</span><br><span class="line"> final <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees"</span>;</span><br><span class="line"> </span><br><span class="line"> EmployeeVO <span class="keyword">new</span><span class="type">Employee</span> = <span class="keyword">new</span> <span class="type">EmployeeVO</span>(<span class="number">-1</span>, <span class="string">"Adam"</span>, <span class="string">"Gilly"</span>, <span class="string">"[email protected]"</span>);</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> <span class="type">RestTemplate</span>();</span><br><span class="line"> EmployeeVO result = restTemplate.postForObject( uri, <span class="keyword">new</span><span class="type">Employee</span>, EmployeeVO.class);</span><br><span class="line"> </span><br><span class="line"> System.out.println(result);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>Submit Form Data<br><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">HttpHeaders headers = <span class="keyword">new</span> HttpHeaders();</span><br><span class="line">headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);</span><br><span class="line"> </span><br><span class="line">MultiValueMap<<span class="keyword">String</span>, <span class="keyword">String</span>> <span class="built_in">map</span>= <span class="keyword">new</span> LinkedMultiValueMap<>();</span><br><span class="line"><span class="built_in">map</span>.<span class="built_in">add</span>(<span class="string">"id"</span>, <span class="string">"1"</span>);</span><br><span class="line"> </span><br><span class="line">HttpEntity<MultiValueMap<<span class="keyword">String</span>, <span class="keyword">String</span>>> request = <span class="keyword">new</span> HttpEntity<>(<span class="built_in">map</span>, headers);</span><br><span class="line"> </span><br><span class="line">RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line">EmployeeVO result = restTemplate.postForObject( uri, request, EmployeeVO.class);</span><br><span class="line">System.out.<span class="built_in">println</span>(result);</span><br></pre></td></tr></table></figure></p><p>PUT<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">updateEmployee</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees/{id}"</span>;</span><br><span class="line"> </span><br><span class="line"> Map<<span class="keyword">String</span>, <span class="keyword">String</span>> params = <span class="keyword">new</span> HashMap<<span class="keyword">String</span>, <span class="keyword">String</span>>();</span><br><span class="line"> params.<span class="built_in">put</span>(<span class="string">"id"</span>, <span class="string">"2"</span>);</span><br><span class="line"> </span><br><span class="line"> EmployeeVO updatedEmployee = <span class="keyword">new</span> EmployeeVO(<span class="number">2</span>, <span class="string">"New Name"</span>, <span class="string">"Gilly"</span>, <span class="string">"[email protected]"</span>);</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> restTemplate.<span class="built_in">put</span> ( uri, updatedEmployee, params);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>Simple PUT<br><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Foo updatedInstance = <span class="keyword">new</span> <span class="type">Foo</span>(<span class="string">"newName"</span>);</span><br><span class="line">updatedInstance.setId(createResponse.getBody().getId());</span><br><span class="line"><span class="keyword">String</span> resourceUrl = </span><br><span class="line"> fooResourceUrl + <span class="string">'/'</span> + createResponse.getBody().getId();</span><br><span class="line">HttpEntity<Foo> requestUpdate = <span class="keyword">new</span> <span class="type">HttpEntity</span><>(updatedInstance, headers);</span><br><span class="line">template.exchange(resourceUrl, HttpMethod.PUT, requestUpdate, <span class="keyword">Void</span>.class);</span><br></pre></td></tr></table></figure></p><p>使用.exchange和请求回调<br><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">RequestCallback <span class="title">requestCallback</span>(<span class="params">final Foo updatedInstance</span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> clientHttpRequest -> {</span><br><span class="line"> ObjectMapper mapper = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line"> mapper.writeValue(clientHttpRequest.getBody(), updatedInstance);</span><br><span class="line"> clientHttpRequest.getHeaders().<span class="keyword">add</span>(</span><br><span class="line"> HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);</span><br><span class="line"> clientHttpRequest.getHeaders().<span class="keyword">add</span>(</span><br><span class="line"> HttpHeaders.AUTHORIZATION, <span class="string">"Basic "</span> + getBase64EncodedLogPass());</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>DELETE<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">deleteEmployee</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">final</span> <span class="keyword">String</span> uri = <span class="string">"http://localhost:8080/springrestexample/employees/{id}"</span>;</span><br><span class="line"> </span><br><span class="line"> Map<<span class="keyword">String</span>, <span class="keyword">String</span>> params = <span class="keyword">new</span> HashMap<<span class="keyword">String</span>, <span class="keyword">String</span>>();</span><br><span class="line"> params.<span class="built_in">put</span>(<span class="string">"id"</span>, <span class="string">"2"</span>);</span><br><span class="line"> </span><br><span class="line"> RestTemplate restTemplate = <span class="keyword">new</span> RestTemplate();</span><br><span class="line"> restTemplate.<span class="keyword">delete</span> ( uri, params );</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>以上便是 Spring RestTemplate 在实际开发中使用的方法,请笑纳吧!</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<p>实际工作中我们经常会遇到利用 java 去访问其他接口获取数据,常用的有 JDK 原生的 HttpURLConnection 无需依赖其他包,提供了很多方法;另外 HttpClient 也是我们的不二选择,在 spring 之前,我们经常会用 HttpClient 来处理;
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>github精选-秒杀系统设计与实现</title>
<link href="https://dddreams.github.io/211108-github%E7%B2%BE%E9%80%89-%E7%A7%92%E6%9D%80%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0.html"/>
<id>https://dddreams.github.io/211108-github精选-秒杀系统设计与实现.html</id>
<published>2021-11-08T14:46:17.000Z</published>
<updated>2021-11-08T14:49:23.372Z</updated>
<content type="html"><![CDATA[<p>很长时间没有更新了,最近有个想法,做一个github 开源项目精选的主题,我经常会去逛 githb,发现一些好玩或实用的项目就会 star,后来发现 star 了很多项目,但在实际要使用时还是想不起来自己已经 star 了该项目了。所以就有了这样一个想法,整理记录 github 上的一些项目,知道它是做什么的,它能解决什么问题,当实际使用时直接在博客上搜索,岂不是很方便了;于是就有了这个 github精选的分类。</p><p>今天分享的是一个秒杀系统设计与实现,互联网工程师进阶与分析,创建者对高并发大流量如何进行秒杀架构这部分知识做了一个系统的整理。目前已获得 22.9k 的star,其中包含整体设计,数据库设计和常见的一些问题整理,涉及到的技术有 SpringBoot、MQ、Redis、Dubbo、ZK、Maven、lua 等技术。</p><p>我花了一些时间看了其中的内容,真的是干货,nginx优化,mysql 的执行计划,数据的备份和恢复写的都很实用,程序员的进阶修炼不可缺少的好项目,极力推荐。</p><p>送上开源地址:<a href="https://github.com/qiurunze123/miaosha" target="_blank" rel="noopener">https://github.com/qiurunze123/miaosha</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p>]]></content>
<summary type="html">
<p>很长时间没有更新了,最近有个想法,做一个github 开源项目精选的主题,我经常会去逛 githb,发现一些好玩或实用的项目就会 star,后来发现 star 了很多项目,但在实际要使用时还是想不起来自己已经 star 了该项目了。所以就有了这样一个想法,整理记录 gith
</summary>
<category term="Github" scheme="https://dddreams.github.io/categories/Github/"/>
<category term="Github" scheme="https://dddreams.github.io/tags/Github/"/>
<category term="教程" scheme="https://dddreams.github.io/tags/%E6%95%99%E7%A8%8B/"/>
</entry>
<entry>
<title>Mysql登录失败多次锁定配置</title>
<link href="https://dddreams.github.io/210208-Mysql%E7%99%BB%E5%BD%95%E5%A4%B1%E8%B4%A5%E5%A4%9A%E6%AC%A1%E9%94%81%E5%AE%9A%E9%85%8D%E7%BD%AE.html"/>
<id>https://dddreams.github.io/210208-Mysql登录失败多次锁定配置.html</id>
<published>2021-02-08T00:54:26.000Z</published>
<updated>2021-11-07T14:29:39.803Z</updated>
<content type="html"><![CDATA[<p>大家有没有发现,现在我们用的手机,网站只要需要登录或者认证的,都有这样一项功能,密码或口令输错几次后系统锁定N分钟,并提示N分钟过后重试,有时候会很头疼,万一忘记了密码,只能填一堆信息重新获取,当然这也是对安全的考虑,其实在等保测评中,也有类似的要求,今天我们来学习一下MySQL是如何设置登录多次失败锁定的。</p><blockquote><p>Tips:示例是以 Mysql 5.7 为例</p></blockquote><h2 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h2><p>Mysql 数据库密码数据 3 次,自动锁定15分钟。</p><h2 id="查看配置"><a href="#查看配置" class="headerlink" title="查看配置"></a>查看配置</h2><p>首先使用下面命令查看参数是否设置<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'%connection_control%'</span>;</span><br></pre></td></tr></table></figure></p><p>如果看到<code>Empty set (0.01 sec)</code> 的字样,说明没有设置该参数;<br>再查看是否安装 CONNECTION_CONTROL 和 CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS 插件。</p><ul><li>CONNECTION_CONTROL:用来控制登录失败的次数及延迟响应时间。</li><li>CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS:该表将登录失败的操作记录至IS库中。</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> plugins;</span><br></pre></td></tr></table></figure><p><img src="https://i.loli.net/2021/02/08/ONElGRIMhUKYvny.png" alt="图一"><br>如果插件列表中没有【图一】中的勾出的两项,需要安装插件;</p><h2 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">install</span> <span class="keyword">plugin</span> CONNECTION_CONTROL <span class="keyword">soname</span> <span class="string">'connection_control.so'</span>;</span><br><span class="line"><span class="keyword">install</span> <span class="keyword">plugin</span> CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS <span class="keyword">soname</span> <span class="string">'connection_control.so'</span>;</span><br></pre></td></tr></table></figure><p>执行完之后,再次查看插件列表,会出现【图一】中勾出的两项;现在再次查看<code>connection_control</code>的参数:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'%connection_control%'</span>;</span><br></pre></td></tr></table></figure></p><p><img src="https://i.loli.net/2021/02/08/dJQIyaX7iZcnO1D.png" alt="图二"><br>可以看到已有的默认配置:</p><ul><li><code>connection_control_failed_connections_threshold</code>:失败尝试的次数,默认为3,表示当连接失败3次后启用连接控制,0表示不开启。</li><li><code>connection_control_max_connection_delay</code>:响应延迟的最大时间,默认约25天</li><li><code>connection_control_min_connection_delay</code>:响应延迟的最小时间,默认1000微秒,1秒</li></ul><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>方式一:修改配置文件 my.cnf<br><figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/<span class="keyword">my</span>.cnf</span><br><span class="line"><span class="comment"># 添加下面两项</span></span><br><span class="line">connection-control-failed-connections-threshold=<span class="number">3</span> <span class="comment">#登陆失败次数限制</span></span><br><span class="line">connection-control-min-connection-<span class="built_in">delay</span>=<span class="number">900000</span> <span class="comment">#限制重试时间,此处为毫秒,注意按需求换算</span></span><br></pre></td></tr></table></figure></p><p>重启 MySQL 服务<code>service mysqld restart</code> 或 <code>service mysql restart</code></p><p>方式二:设置全局变量<br><figure class="highlight gams"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> GLOBAL <span class="comment">connection_control_failed_connections_threshold = 3</span>;</span><br><span class="line"><span class="keyword">SET</span> GLOBAL <span class="comment">connection_control_min_connection_delay = 900000</span>;</span><br></pre></td></tr></table></figure></p><p>配置完成,再次使用<code>show variables like '%connection_control%';</code>查看参数:<br><img src="https://i.loli.net/2021/02/08/EjRINTklfFc5m8D.png" alt="图三"><br>可以看到已经是我们配置的值了。</p><h2 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h2><p><img src="https://i.loli.net/2021/02/08/CKpO8iZ2EFglNDb.png" alt="图四"><br>如【图四】输错三次密码之后,第四次程序已经锁住了。</p><h2 id="参考和相关链接"><a href="#参考和相关链接" class="headerlink" title="参考和相关链接"></a>参考和相关链接</h2><p>MySQL数据库限制多次登录失败重试时间:<a href="https://blog.csdn.net/ywd1992/article/details/83865537" target="_blank" rel="noopener">https://blog.csdn.net/ywd1992/article/details/83865537</a></p><p>MySQL 插件之连接控制插件:<a href="https://www.cnblogs.com/zhenxing/p/11050823.html" target="_blank" rel="noopener">https://www.cnblogs.com/zhenxing/p/11050823.html</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: 治恒说说</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>大家有没有发现,现在我们用的手机,网站只要需要登录或者认证的,都有这样一项功能,密码或口令输错几次后系统锁定N分钟,并提示N分钟过后重试,有时候会很头疼,万一忘记了密码,只能填一堆信息重新获取,当然这也是对安全的考虑,其实在等保测评中,也有类似的要求,今天我们来学习一下My
</summary>
<category term="Mysql" scheme="https://dddreams.github.io/categories/Mysql/"/>
<category term="运维" scheme="https://dddreams.github.io/tags/%E8%BF%90%E7%BB%B4/"/>
<category term="Mysql" scheme="https://dddreams.github.io/tags/Mysql/"/>
</entry>
<entry>
<title>SpringBoot-使用JdbcTemplate操作数据库</title>
<link href="https://dddreams.github.io/210206-SpringBoot-%E4%BD%BF%E7%94%A8JdbcTemplate%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93.html"/>
<id>https://dddreams.github.io/210206-SpringBoot-使用JdbcTemplate操作数据库.html</id>
<published>2021-02-06T08:14:25.000Z</published>
<updated>2021-02-06T08:42:47.332Z</updated>
<content type="html"><![CDATA[<p>关于 JDBC 肯定是 java 开发者的入门知识,很显然在 Spring boot 中的使用也是非常简单的,这一节先当与给大家复习了,已熟知的同学请忽略。</p><blockquote><p>Tips:示例中 MySQL 使用5.7版本,spring boot 使用 2.4.2版本</p></blockquote><h2 id="加依赖"><a href="#加依赖" class="headerlink" title="加依赖"></a>加依赖</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-jdbc<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><p>请注意这里使用的包是<code>spring-boot-starter-jdbc</code>,其实在使用中你会发现会有这样一个包<code>spring-boot-starter-data-jdbc</code>,那么他们是什么区别呢?</p><p>区别在于,前者是 Spring支持JDBC数据库的包,使用<code>DataSourceTransactionManager</code>管理事务;后者是 Jpa 对JDBC的支持,使用<code>JpaTransactionManager</code>管理事务。至于 Jpa,后面会有相关的文章介绍。</p><h2 id="写配置"><a href="#写配置" class="headerlink" title="写配置"></a>写配置</h2><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">server</span>:</span><br><span class="line"> <span class="attribute">port</span>: <span class="number">8080</span></span><br><span class="line"><span class="attribute">spring</span>:</span><br><span class="line"> <span class="attribute">application</span>:</span><br><span class="line"> <span class="attribute">name</span>: spring-boot-jdbc</span><br><span class="line"> <span class="attribute">datasource</span>:</span><br><span class="line"> <span class="attribute">driver-class-name</span>: com.mysql.jdbc.Driver</span><br><span class="line"> <span class="attribute">url</span>: <span class="attribute">jdbc</span>:<span class="attribute">mysql</span>:<span class="comment">//localhost:3306/test</span></span><br><span class="line"> <span class="attribute">username</span>: shure</span><br><span class="line"> <span class="attribute">password</span>: shure</span><br></pre></td></tr></table></figure><p>要想访问数据库,那肯定要配置数据库的连接信息了<br><strong>注意:</strong>mysql8.0的驱动包有所变化 <code>com.mysql.cj.jdbc.Driver</code></p><h2 id="加注解"><a href="#加注解" class="headerlink" title="加注解"></a>加注解</h2><p>无需注解</p><h2 id="编码"><a href="#编码" class="headerlink" title="编码"></a>编码</h2><p>首先在mysql数据库中建立 user 的数据表。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> <span class="keyword">IF</span> <span class="keyword">EXISTS</span> <span class="string">`t_user`</span>;</span><br><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> <span class="string">`t_user`</span> (</span><br><span class="line"> <span class="string">`id`</span> <span class="built_in">int</span>(<span class="number">8</span>) <span class="keyword">unsigned</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> <span class="string">`name`</span> <span class="built_in">varchar</span>(<span class="number">100</span>) <span class="keyword">COLLATE</span> utf8mb4_general_ci <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> <span class="string">`age`</span> <span class="built_in">int</span> <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> <span class="string">`gender`</span> <span class="built_in">varchar</span>(<span class="number">8</span>) <span class="keyword">COLLATE</span> utf8mb4_general_ci <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> <span class="string">`address`</span> <span class="built_in">varchar</span>(<span class="number">500</span>) <span class="keyword">COLLATE</span> utf8mb4_general_ci <span class="keyword">NOT</span> <span class="literal">NULL</span>,</span><br><span class="line"> PRIMARY <span class="keyword">KEY</span> (<span class="string">`id`</span>)</span><br><span class="line">) <span class="keyword">ENGINE</span>=<span class="keyword">InnoDB</span> <span class="keyword">DEFAULT</span> <span class="keyword">CHARSET</span>=utf8mb4 <span class="keyword">COLLATE</span>=utf8mb4_general_ci;</span><br></pre></td></tr></table></figure></p><p>然后,建立 user 表对应的实体类,并添加构造函数。<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">@Data</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserEntity</span> {</span></span><br><span class="line"> <span class="keyword">private</span> Integer id;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">String</span> name;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> age;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">boolean</span> gender;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">String</span> address;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">UserEntity</span><span class="params">()</span></span>{}</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">UserEntity</span><span class="params">(<span class="keyword">String</span> name, <span class="keyword">int</span> age, <span class="keyword">boolean</span> gender, <span class="keyword">String</span> address)</span></span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.age = age;</span><br><span class="line"> <span class="keyword">this</span>.gender = gender;</span><br><span class="line"> <span class="keyword">this</span>.address = address;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>再然后,编写访问数据库的操作。<br><figure class="highlight livescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line">@Repository</span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">UserJdbcDao</span> {</span></span><br><span class="line"> @Autowired</span><br><span class="line"> private JdbcTemplate jdbcTemplate;</span><br><span class="line"> public List<UserEntity> findAll(String name){</span><br><span class="line"> List<UserEntity> <span class="keyword">list</span> = jdbcTemplate.query<span class="function"><span class="params">(<span class="string">"SELECT * FROM t_user WHERE name = ?"</span>, (res, i) -> {</span></span></span><br><span class="line"><span class="function"><span class="params"> UserEntity user = <span class="keyword">new</span> UserEntity();</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setId(res.getInt(<span class="string">"ID"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setName(res.getString(<span class="string">"NAME"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setAddress(res.getString(<span class="string">"ADDRESS"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setAge(res.getInt(<span class="string">"AGE"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setGender(res.getBoolean(<span class="string">"GENDER"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">return</span> user;</span></span></span><br><span class="line"><span class="function"><span class="params"> }, name)</span>;</span></span><br><span class="line"><span class="function"> <span class="title">return</span> <span class="title">list</span>;</span></span><br><span class="line"><span class="function"> }</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="title">public</span> <span class="title">UserEntity</span> <span class="title">getUser</span><span class="params">(Integer id)</span>{</span></span><br><span class="line"><span class="function"> <span class="title">List</span><<span class="title">UserEntity</span>> <span class="title">list</span> = <span class="title">jdbcTemplate</span>.<span class="title">query</span><span class="params">(<span class="string">"SELECT * FROM t_user WHERE id = ?"</span>, (res, i) -> {</span></span></span><br><span class="line"><span class="function"><span class="params"> UserEntity user = <span class="keyword">new</span> UserEntity();</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setId(res.getInt(<span class="string">"ID"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setName(res.getString(<span class="string">"NAME"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setAddress(res.getString(<span class="string">"ADDRESS"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setAge(res.getInt(<span class="string">"AGE"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> user.setGender(res.getBoolean(<span class="string">"GENDER"</span>));</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">return</span> user;</span></span></span><br><span class="line"><span class="function"><span class="params"> }, id)</span>;</span></span><br><span class="line"><span class="function"> <span class="title">return</span> <span class="title">list</span>.<span class="title">get</span><span class="params">(<span class="number">0</span>)</span>;</span></span><br><span class="line"><span class="function"> }</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="title">public</span> <span class="title">Integer</span> <span class="title">save</span><span class="params">(UserEntity user)</span>{</span></span><br><span class="line"><span class="function"> <span class="title">return</span> <span class="title">jdbcTemplate</span>.<span class="title">update</span><span class="params">(<span class="string">"INSERT INTO t_user(name, age, gender, address) VALUES(?, ?, ?, ?)"</span>,</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getName(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getAge(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.isGender(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getAddress())</span>;</span></span><br><span class="line"><span class="function"> }</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="title">public</span> <span class="title">void</span> <span class="title">update</span><span class="params">(UserEntity user)</span>{</span></span><br><span class="line"><span class="function"> <span class="title">jdbcTemplate</span>.<span class="title">update</span><span class="params">(<span class="string">"UPDATE t_user SET name = ?, age = ?, gender = ?, address = ? WHERE id = ?"</span>,</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getName(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getAge(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.isGender(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getAddress(),</span></span></span><br><span class="line"><span class="function"><span class="params"> user.getId())</span>;</span></span><br><span class="line"><span class="function"> }</span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"> <span class="title">public</span> <span class="title">void</span> <span class="title">delete</span><span class="params">(Integer id)</span>{</span></span><br><span class="line"><span class="function"> <span class="title">jdbcTemplate</span>.<span class="title">update</span><span class="params">(<span class="string">"DELETE FROM t_user WHERE id = ?"</span>, id)</span>;</span></span><br><span class="line"><span class="function"> }</span></span><br><span class="line"><span class="function">}</span></span><br></pre></td></tr></table></figure></p><p>最后,编写单元测试用例,创建对UserService的单元测试用例,通过创建、删除和查询来验证数据库操作的正确性。</p><p>这样 jdbc 的基本操作就完成了,通过上面这个简单的例子,我们可以看到在Spring Boot下访问数据库的配置依然秉承了框架的初衷:简单。我们只需要在pom.xml中加入数据库依赖,再到application.properties中配置连接信息,不需要像Spring应用中创建JdbcTemplate的Bean,就可以直接在自己的对象中注入使用。</p><h2 id="参考与相关链接"><a href="#参考与相关链接" class="headerlink" title="参考与相关链接"></a>参考与相关链接</h2><p>示例代码:<a href="https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-jdbc" target="_blank" rel="noopener">https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-jdbc</a></p><p>程序员DD的博文:<a href="https://blog.didispace.com/spring-boot-learning-21-3-1/" target="_blank" rel="noopener">https://blog.didispace.com/spring-boot-learning-21-3-1/</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>关于 JDBC 肯定是 java 开发者的入门知识,很显然在 Spring boot 中的使用也是非常简单的,这一节先当与给大家复习了,已熟知的同学请忽略。</p>
<blockquote>
<p>Tips:示例中 MySQL 使用5.7版本,spring boot 使用
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/categories/Spring-Boot/"/>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>Mysql密码安全策略配置</title>
<link href="https://dddreams.github.io/210204-Mysql%E5%AF%86%E7%A0%81%E5%AE%89%E5%85%A8%E7%AD%96%E7%95%A5%E9%85%8D%E7%BD%AE.html"/>
<id>https://dddreams.github.io/210204-Mysql密码安全策略配置.html</id>
<published>2021-02-04T06:49:39.000Z</published>
<updated>2021-02-06T08:43:00.410Z</updated>
<content type="html"><![CDATA[<p>近些年来,网络安全相应的法律法规在逐步完善,网络安全等级保护制度在《网络安全法》的正式实施后,得到大力推进。很多公司、单位,逐渐认识到网络安全等级保护的重要性,为了落实网络安全等级保护工作,我所在的项目最近也做了一次等保的测评,下面是针对 MySQL 数据库相关的一些整改配置。</p><blockquote><p>Tips:示例是以 Mysql 5.7 为例</p></blockquote><h2 id="密码策略"><a href="#密码策略" class="headerlink" title="密码策略"></a>密码策略</h2><p>首先登录 Mysql 查看目前数据库的密码策略<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'validate_password%'</span>;</span><br></pre></td></tr></table></figure></p><p>如果出现 <code>Empty set (0.01 sec)</code> 的字样,说明你的 Mysql 还未安装 <code>validate_password</code> 的插件。查看插件列表:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> plugins;</span><br></pre></td></tr></table></figure></p><p><img src="https://i.loli.net/2021/02/04/5IgGol6d9bTfkFC.png" alt="图一"><br>如果没有这一项,说明确实没有安装<code>validate_password</code>插件。</p><h2 id="安装插件"><a href="#安装插件" class="headerlink" title="安装插件"></a>安装插件</h2><p>要使服务器可用,插件库文件必须位于 MySQL 插件目录中(由plugin_dir系统变量命名的目录)。如果需要,通过在服务器启动时设置 plugin_dir 的值来配置插件目录位置。<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'plugin_dir'</span>;</span><br></pre></td></tr></table></figure></p><p><img src="https://i.loli.net/2021/02/04/kHz2YjrEyGQ6J3P.png" alt="图二"></p><p>插件库的文件名是 validate_password.文件名后缀根据平台的不同而不同(.so 是Unix和类Linux系统,.dll是Windows系统)。要在服务器启动时加载插件,可以在配置文件中使用 <code>plugin-load-add</code> 参数来命名包含它的库文件。修改配置文件<code>my.cnf</code><br><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/my.cnf</span><br><span class="line"></span><br><span class="line">[mysqld]</span><br><span class="line"><span class="attribute">plugin-load-add</span>=validate_password.so</span><br><span class="line"><span class="comment">#服务器在启动时加载插件,并防止在服务器运行时删除插件。</span></span><br><span class="line"><span class="attribute">validate-password</span>=FORCE_PLUS_PERMANENT</span><br></pre></td></tr></table></figure></p><p>修改 my.cnf 之后,重启服务器以使新设置生效。或者,要在运行时注册插件,可以使用以下命令:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">service mysqld restart</span><br><span class="line"><span class="comment">#或</span></span><br><span class="line"><span class="keyword">install</span> <span class="keyword">plugin</span> validate_password <span class="keyword">soname</span> <span class="string">'validate_password.so'</span>;</span><br></pre></td></tr></table></figure></p><p>然后在次查看插件列表,就会出现图一中的那一项 <code>validate_password</code></p><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><p>再次执行查看密码策略的命令:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">show</span> <span class="keyword">variables</span> <span class="keyword">like</span> <span class="string">'validate_password%'</span>;</span><br></pre></td></tr></table></figure></p><p><img src="https://i.loli.net/2021/02/04/Y4I5nLstzM9ClmH.png" alt="image.png"></p><p><code>validate_password_check_user_name</code>:设置为ON的时候表示能将密码设置成当前用户名。<br><code>validate_password_dictionary_file</code>:用于检查密码的字典文件的路径名。<br><code>validate_password_length</code>:所需密码的最小长度。<br><code>validate_password_mixed_case_count</code>:默认值为1,如果密码策略是中等或更强的,限制小写字符和大写字符个数。<br><code>validate_password_number_count</code>:如果密码策略是中等或更强的,要求密码具有数字的个数。<br><code>validate_password_policy</code>: 默认值为1, 密码强度等级 [LOW:0| MEDIUM:1 | STRONG:2]<br><code>validate_password_special_char_count</code> : 默认值为1,限制特殊字符个数<br>其中,validate_password_policy<br>0/LOW:只检查长度。<br>1/MEDIUM:检查长度、数字、大小写、特殊字符。<br>2/STRONG:检查长度、数字、大小写、特殊字符字典文件。</p><p>可以使用一下命令修改上述值:<br><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">set</span> <span class="keyword">global</span> validate_password_policy=<span class="number">1</span>;</span><br><span class="line"><span class="keyword">set</span> <span class="keyword">global</span> validate_password_length=<span class="number">8</span>;</span><br><span class="line"><span class="keyword">flush</span> <span class="keyword">privileges</span>;</span><br></pre></td></tr></table></figure></p><p>也可以在配置文件中直接配置:<br><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">vim /etc/my.cnf</span><br><span class="line"></span><br><span class="line">[mysqld]</span><br><span class="line"><span class="attribute">validate_password_policy</span>=1</span><br><span class="line"><span class="attribute">validate_password_length</span>=8</span><br></pre></td></tr></table></figure></p><h2 id="参考和相关链接"><a href="#参考和相关链接" class="headerlink" title="参考和相关链接"></a>参考和相关链接</h2><p>mysql 5.7安装密码校验插件validate_password:<a href="https://www.cnblogs.com/alonely/p/10927541.html" target="_blank" rel="noopener">https://www.cnblogs.com/alonely/p/10927541.html</a></p><p>MYSQL57密码策略修改:<a href="https://www.cnblogs.com/zhi-leaf/p/5994478.html" target="_blank" rel="noopener">https://www.cnblogs.com/zhi-leaf/p/5994478.html</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>近些年来,网络安全相应的法律法规在逐步完善,网络安全等级保护制度在《网络安全法》的正式实施后,得到大力推进。很多公司、单位,逐渐认识到网络安全等级保护的重要性,为了落实网络安全等级保护工作,我所在的项目最近也做了一次等保的测评,下面是针对 MySQL 数据库相关的一些整改配
</summary>
<category term="Mysql" scheme="https://dddreams.github.io/categories/Mysql/"/>
<category term="运维" scheme="https://dddreams.github.io/tags/%E8%BF%90%E7%BB%B4/"/>
<category term="Mysql" scheme="https://dddreams.github.io/tags/Mysql/"/>
</entry>
<entry>
<title>SpringBoot-集成Swagger2文档生成</title>
<link href="https://dddreams.github.io/210127-SpringBoot-%E9%9B%86%E6%88%90Swagger2%E6%96%87%E6%A1%A3%E7%94%9F%E6%88%90.html"/>
<id>https://dddreams.github.io/210127-SpringBoot-集成Swagger2文档生成.html</id>
<published>2021-01-27T14:26:00.000Z</published>
<updated>2021-02-06T08:42:54.238Z</updated>
<content type="html"><![CDATA[<p>随着前后端分离和微服务架构的流行,甚至有些公司强制使用微服务架构,在提高效率同时,问题也随之而来。通常我们的一个RESTful API,会对应不同的开发者,Android,IOS,Web,小程序等不同团队研发人员,显然沟通成本成倍的增长,一般我们都会通过共享文档来维护这些 API ,但随着项目中 API 越来越多,版本升级越多,就会造成文档维护不及时或者无人维护的问题。如果你所在团队也正在面临这样的问题,那么今天我们一起来学习文档神器 Swagger。</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>Swagger 是一款 RESTful 接口的文档在线自动生成外加调试功能的软件。可以将项目的所有接口在一个UI界面上展示出来,同时表明了这个接口的用途,接口需要的参数是什么类型参数是否必须,输入了参数可以直接测试接口类似postman的功能,会显示接口请求的状态码和返回的数据结构。</p><p>那么后端开发人员只需要在代码中添加几个注解,就会生成一套标准的文档,而且随着代码的变动也随之更新,这样减少了单独维护文档成本,也减少了团队之间的沟通成本,接下来我们来看怎么将 Swagger 集成到 Spring boot 中。</p><h2 id="加依赖"><a href="#加依赖" class="headerlink" title="加依赖"></a>加依赖</h2><p>需要加入 swagger2 和 swagger-ui 的包<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>io.springfox<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>springfox-swagger2<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.9.2<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>io.springfox<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>springfox-swagger-ui<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>2.9.2<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p><h2 id="写配置"><a href="#写配置" class="headerlink" title="写配置"></a>写配置</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">swagger:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p><strong>注意:在生产环境将此配置设置为 false </strong></p><p>因为使用原生的 swagger,需要额外加入swagger 的配置类<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Configuration</span></span><br><span class="line"><span class="variable">@EnableSwagger2</span></span><br><span class="line"><span class="variable">@ConditionalOnProperty</span>(name = <span class="string">"swagger.enable"</span>, havingValue = <span class="string">"true"</span>)</span><br><span class="line">public class SwaggerConfig {</span><br><span class="line"> <span class="variable">@Bean</span></span><br><span class="line"> public Docket createRestApi() {</span><br><span class="line"> <span class="selector-tag">return</span> <span class="selector-tag">new</span> <span class="selector-tag">Docket</span>(DocumentationType.SWAGGER_2)</span><br><span class="line"> <span class="selector-class">.apiInfo</span>(apiInfo())</span><br><span class="line"> <span class="selector-class">.select</span>()</span><br><span class="line"> <span class="selector-class">.apis</span>(RequestHandlerSelectors.basePackage(<span class="string">"com.shure.swagger2"</span>))</span><br><span class="line"> <span class="selector-class">.paths</span>(PathSelectors.any())</span><br><span class="line"> <span class="selector-class">.build</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//配置在线文档的基本信息</span></span><br><span class="line"> <span class="selector-tag">private</span> <span class="selector-tag">ApiInfo</span> <span class="selector-tag">apiInfo</span>() {</span><br><span class="line"> <span class="selector-tag">return</span> <span class="selector-tag">new</span> <span class="selector-tag">ApiInfoBuilder</span>()</span><br><span class="line"> <span class="selector-class">.title</span>(<span class="string">"springboot利用swagger构建api文档"</span>)</span><br><span class="line"> <span class="selector-class">.description</span>(<span class="string">"用户管理接口文档"</span>)</span><br><span class="line"> <span class="selector-class">.termsOfServiceUrl</span>(<span class="string">"https://dddreams.github.io/"</span>)</span><br><span class="line"> <span class="selector-class">.version</span>(<span class="string">"0.1"</span>)</span><br><span class="line"> <span class="selector-class">.build</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="加注解"><a href="#加注解" class="headerlink" title="加注解"></a>加注解</h2><p>在启动类加 <code>@EnableSwagger2</code>注解,或者在上面 <code>SwaggerConfig</code> 类上加入此注解。<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@SpringBootApplication</span></span><br><span class="line"><span class="variable">@EnableSwagger2</span></span><br><span class="line">public class SpringBootSwagger2Application {</span><br><span class="line"> <span class="selector-tag">public</span> <span class="selector-tag">static</span> <span class="selector-tag">void</span> <span class="selector-tag">main</span>(String[] args) {</span><br><span class="line"> <span class="selector-tag">SpringApplication</span><span class="selector-class">.run</span>(SpringBootSwagger2Application.class, args);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="如何使用"><a href="#如何使用" class="headerlink" title="如何使用"></a>如何使用</h2><p>OK,完成上面三步,已经可以使用 swagger 了,启动后访问,<code>http://localhost:8080/swagger-ui.html</code></p><p><img src="https://i.loli.net/2021/01/27/GDUCxPmvXIkWhoa.png" alt="swagger-ui"></p><h3 id="编写业务接口"><a href="#编写业务接口" class="headerlink" title="编写业务接口"></a>编写业务接口</h3><p>新建用户的实体类 UserEntity,为了简化代码我们会引入 lombok 的插件。<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Data</span></span><br><span class="line"><span class="variable">@ApiModel</span>(description=<span class="string">"用户实体"</span>)</span><br><span class="line">public class UserEntity {</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(<span class="string">"用户编号"</span>)</span><br><span class="line"> private Integer id;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(<span class="string">"用户姓名"</span>)</span><br><span class="line"> private String name;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(<span class="string">"用户年龄"</span>)</span><br><span class="line"> private int age;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(<span class="string">"用户性别"</span>)</span><br><span class="line"> private boolean gender;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(<span class="string">"住址"</span>)</span><br><span class="line"> private String address;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>新建用户相关的 API 接口<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Api(tags = <span class="meta-string">"1-用户管理"</span>)</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(<span class="meta-string">"/user"</span>)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UserController</span> </span>{</span><br><span class="line"> <span class="comment">// 模拟users信息的存储</span></span><br><span class="line"> static Map<Integer, UserEntity> users = Collections.synchronizedMap(new HashMap<>());</span><br><span class="line"></span><br><span class="line"> <span class="meta">@GetMapping(<span class="meta-string">"/list"</span>)</span></span><br><span class="line"> <span class="meta">@ApiOperation(value = <span class="meta-string">"获取用户列表"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> List<UserEntity> list(){</span><br><span class="line"> List<UserEntity> us = new ArrayList<>(users.values());</span><br><span class="line"> <span class="keyword">return</span> us;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@GetMapping(<span class="meta-string">"/get/{id}"</span>)</span></span><br><span class="line"> <span class="meta">@ApiOperation(value = <span class="meta-string">"获取用户详细信息"</span>, notes = <span class="meta-string">"根据用户id来获取用户详细信息"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> UserEntity getUser(<span class="meta">@PathVariable</span> Integer id) {</span><br><span class="line"> <span class="keyword">return</span> users.<span class="keyword">get</span>(id);</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@PostMapping(<span class="meta-string">"/saveOrUpdate"</span>)</span></span><br><span class="line"> <span class="meta">@ApiOperation(value = <span class="meta-string">"创建用户"</span>, notes = <span class="meta-string">"根据User对象创建用户"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> String postUser(<span class="meta">@RequestBody</span> UserEntity user) {</span><br><span class="line"> users.put(user.getId(), user);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"success"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@DeleteMapping(<span class="meta-string">"/del/{id}"</span>)</span></span><br><span class="line"> <span class="meta">@ApiOperation(value = <span class="meta-string">"删除用户"</span>, notes = <span class="meta-string">"根据用户的id来指定删除对象"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> String deleteUser(<span class="meta">@PathVariable</span> Integer id) {</span><br><span class="line"> users.remove(id);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"success"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="查看接口文档"><a href="#查看接口文档" class="headerlink" title="查看接口文档"></a>查看接口文档</h3><p>再来看看 swagger 的页面会发生什么变化。</p><p><img src="https://i.loli.net/2021/01/27/HdwOWS7IRvzpZAi.png" alt="swagger-ui"></p><p>展开标签后,会看到每个接口的详细信息,并且可以在线调试,返回结果的数据结构也一目了然。还包括实体的信息也会在文档中展现。</p><p><img src="https://i.loli.net/2021/01/27/dNXwRZj8AramJtl.png" alt="swagger-ui"></p><h2 id="接口及方法的分类排序"><a href="#接口及方法的分类排序" class="headerlink" title="接口及方法的分类排序"></a>接口及方法的分类排序</h2><p>随着业务的增长,API 接口数量的增长,实际使用中我们会发现找一个接口会非常困难,这就考虑为接口和方法进行分类排序,方便查找。</p><h3 id="接口的分类"><a href="#接口的分类" class="headerlink" title="接口的分类"></a>接口的分类</h3><p>对于接口的分类,swagger 提供了 tags 的参数,只需在 Controller 的 <code>@Api</code> 注解上加参数 tags 即可。<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Api</span>(tags = <span class="string">"用户管理"</span>)</span><br><span class="line"><span class="variable">@RestController</span></span><br><span class="line"><span class="variable">@RequestMapping</span>(<span class="string">"/user"</span>)</span><br><span class="line">public class UserController {</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable">@Api</span>(tags = {<span class="string">"商品管理"</span>, <span class="string">"..."</span>})</span><br><span class="line"><span class="variable">@RestController</span></span><br><span class="line"><span class="variable">@RequestMapping</span>(<span class="string">"/goods"</span>)</span><br><span class="line">public class GoodsController {</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>可以将同类的接口,指定在 tags 中,文档中就会出现一个分类中。<br><img src="https://i.loli.net/2021/01/27/qOiw9pfHyjoJcYE.png" alt="swagger-ui"></p><h3 id="接口的排序"><a href="#接口的排序" class="headerlink" title="接口的排序"></a>接口的排序</h3><p>对于接口的排序,swagger 默认是根据字母顺序排的,并且只提供了这一种排序方式,我们可以投机取巧在 tags 参数的前面添加数字让其按顺序排列。<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Api</span>(tags = <span class="string">"1-用户管理"</span>)</span><br><span class="line"><span class="variable">@RestController</span></span><br><span class="line"><span class="variable">@RequestMapping</span>(<span class="string">"/user"</span>)</span><br><span class="line">public class UserController {</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable">@Api</span>(tags = {<span class="string">"2-商品管理"</span>})</span><br><span class="line"><span class="variable">@RestController</span></span><br><span class="line"><span class="variable">@RequestMapping</span>(<span class="string">"/goods"</span>)</span><br><span class="line">public class GoodsController {</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><img src="https://i.loli.net/2021/01/27/ZPpGE2DKBl5J6FR.png" alt="swagger-ui"></p><h3 id="方法的排序"><a href="#方法的排序" class="headerlink" title="方法的排序"></a>方法的排序</h3><p>方式一,swagger 默认是根据字母顺序排的,可以通过以下方式:<br><img src="http://img.didispace.com/Fj283ftc5WXw7yCH6UFl7VvCshrC" alt="图片来源:程序猿DD"><br>图片来源于 程序猿DD 的博客:<a href="https://blog.didispace.com/spring-boot-learning-21-2-4/" target="_blank" rel="noopener">https://blog.didispace.com/spring-boot-learning-21-2-4/</a></p><p>方式二,引入 spring4all 社区开源的 <code>swagger-spring-boot-starter</code><br>github地址:<a href="https://github.com/SpringForAll/spring-boot-starter-swagger" target="_blank" rel="noopener">https://github.com/SpringForAll/spring-boot-starter-swagger</a><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.spring4all<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>swagger-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.9.1.RELEASE<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p><p>添加配置:<br><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">swagger.ui-config.operations-sorter</span>=alpha</span><br></pre></td></tr></table></figure></p><p>方法的排序 swagger 提供了两个配置,<code>alpha</code>和<code>method</code>,默认为<code>alpha</code>。</p><p>方式三,引入 knife4j 开源的,<code>knife4j-spring-ui</code>基于bootstrap的ui框架。</p><p>github地址:<a href="https://github.com/xiaoymin/swagger-bootstrap-ui" target="_blank" rel="noopener">https://github.com/xiaoymin/swagger-bootstrap-ui</a></p><p>gitee地址:<a href="https://gitee.com/xiaoym/knife4j/tree/master/swagger-bootstrap-ui" target="_blank" rel="noopener">https://gitee.com/xiaoym/knife4j/tree/master/swagger-bootstrap-ui</a><br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag"><<span class="name">dependency</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">groupId</span>></span>com.github.xiaoymin<span class="tag"></<span class="name">groupId</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">artifactId</span>></span>swagger-bootstrap-ui<span class="tag"></<span class="name">artifactId</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">version</span>></span>$</span><span class="template-variable">{lastVersion}</span><span class="xml"><span class="tag"></<span class="name">version</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">dependency</span>></span></span></span><br></pre></td></tr></table></figure></p><p>使用该方式需要在 <code>SwaggerConfig</code> 配置类上添加 <code>@EnableSwaggerBootstrapUI</code>注解,然后在方法上添加<code>@ApiOperationSupport(order = 1)</code>注解,并指定 order 的值。<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@GetMapping(<span class="meta-string">"/list"</span>)</span></span><br><span class="line"><span class="meta">@ApiOperation(value = <span class="meta-string">"获取用户列表"</span>)</span></span><br><span class="line"><span class="meta">@ApiOperationSupport(order = 1)</span></span><br><span class="line"><span class="keyword">public</span> List<UserEntity> list(){</span><br><span class="line"> List<UserEntity> us = new ArrayList<>(users.values());</span><br><span class="line"> <span class="keyword">return</span> us;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@GetMapping(<span class="meta-string">"/get/{id}"</span>)</span></span><br><span class="line"><span class="meta">@ApiOperation(value = <span class="meta-string">"获取用户详细信息"</span>, notes = <span class="meta-string">"根据用户id来获取用户详细信息"</span>)</span></span><br><span class="line"><span class="meta">@ApiOperationSupport(order = 2)</span></span><br><span class="line"><span class="keyword">public</span> UserEntity getUser(<span class="meta">@PathVariable</span> Integer id) {</span><br><span class="line"> <span class="keyword">return</span> users.<span class="keyword">get</span>(id);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>启动程序,访问:<code>http://localhost:8080/doc.html</code>,界面长这个样子。<br><img src="https://i.loli.net/2021/01/27/fEq8aKducMCW7gh.png" alt="QQ截图20210127164051.png"></p><p><strong>注意:需要在个性化设置中开启 <code>启用SwaggerBootstrapUi提供的增强功能</code></strong><br><img src="https://i.loli.net/2021/01/27/gUmf87BbEtMoHa4.png" alt="QQ截图20210127164429.png"></p><p>这样你就会发现,方法的排序按照我们设置的 <code>order</code> 排序了。</p><h3 id="参数的排序"><a href="#参数的排序" class="headerlink" title="参数的排序"></a>参数的排序</h3><p>参数的排序比较简单,只需在实体字段的注解上加 <code>position</code> 属性,参数就可以根据设置的<code>position</code>值进行排序了。<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Data</span></span><br><span class="line"><span class="variable">@ApiModel</span>(description=<span class="string">"商品实体"</span>)</span><br><span class="line">public class GoodsEntity {</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(value = <span class="string">"商品编号"</span>, position = <span class="number">1</span>)</span><br><span class="line"> private Long id;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(value = <span class="string">"商品名称"</span>, position = <span class="number">2</span>)</span><br><span class="line"> private String name;</span><br><span class="line"> <span class="variable">@ApiModelProperty</span>(value = <span class="string">"商品描述"</span>, position = <span class="number">3</span>)</span><br><span class="line"> private String desc;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="参考和相关链接"><a href="#参考和相关链接" class="headerlink" title="参考和相关链接"></a>参考和相关链接</h2><p>示例代码:<a href="https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-swagger2" target="_blank" rel="noopener">https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-swagger2</a></p><p>Swagger官网:<a href="https://swagger.io/" target="_blank" rel="noopener">https://swagger.io/</a></p><p>Swagger接口分类与各元素排序问题详解:<a href="https://blog.didispace.com/spring-boot-learning-21-2-4/" target="_blank" rel="noopener">https://blog.didispace.com/spring-boot-learning-21-2-4/</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>随着前后端分离和微服务架构的流行,甚至有些公司强制使用微服务架构,在提高效率同时,问题也随之而来。通常我们的一个RESTful API,会对应不同的开发者,Android,IOS,Web,小程序等不同团队研发人员,显然沟通成本成倍的增长,一般我们都会通过共享文档来维护这些
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/categories/Spring-Boot/"/>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>SpringBoot-集成Quartz作业调度</title>
<link href="https://dddreams.github.io/210125-SpringBoot-%E9%9B%86%E6%88%90Quartz%E4%BD%9C%E4%B8%9A%E8%B0%83%E5%BA%A6.html"/>
<id>https://dddreams.github.io/210125-SpringBoot-集成Quartz作业调度.html</id>
<published>2021-01-25T08:41:46.000Z</published>
<updated>2021-01-26T06:30:22.058Z</updated>
<content type="html"><![CDATA[<p>在说 quartz 之前,我们先回顾一下 spring 的定时任务,使用相当简单,默认集成在 spring boot 中,所以在 spring boot 项目中无需额外添加依赖,无需配置,只需要加个注解就可以了,当然也可以实现动态添加删除定时任务,详情前往上一篇博文<a href="https://dddreams.github.io/210122-SpringBoot-%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1.html">SpringBoot-定时任务</a>,那为什么要使用 quartz 呢,主要还是考虑分布式的应用,下面我们就来看一下 spring boot 是怎么集成 quartz 的。</p><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><p>简单介绍下 quartz,Quartz是OpenSymphony开源组织在任务调度领域的一个开源项目,完全基于Java实现。作为一个优秀的开源调度框架,Quartz具有以下特点:</p><ul><li>(1)强大的调度功能,例如支持丰富多样的调度方法,可以满足各种常规及特殊需求;</li><li>(2)灵活的应用方式,例如支持任务和调度的多种组合方式,支持调度数据的多种存储方式;</li><li>(3)分布式和集群能力。</li></ul><blockquote><p>Tips<br>还记得 spring boot 的三板斧吗?加依赖,写配置,添注解</p></blockquote><h2 id="加依赖"><a href="#加依赖" class="headerlink" title="加依赖"></a>加依赖</h2><p>引入 spring-boot-starter-quartz 的依赖<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"><!--quartz定时任务依赖--></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-quartz<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p><h2 id="写配置"><a href="#写配置" class="headerlink" title="写配置"></a>写配置</h2><p>无需配置</p><h2 id="加注解"><a href="#加注解" class="headerlink" title="加注解"></a>加注解</h2><p>无需注解</p><h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><p>我们需要新建一个 QuartzService 的类,由于代码较长,在这里就不贴了,想看具体代码,请前往<a href="https://github.com/dddreams/learn-spring-boot/blob/master/spring-boot-quartz/src/main/java/com/shure/quartz/QuartzService.java" target="_blank" rel="noopener">QuartzService.java</a>。quartz 的 Job 实现方式很多,不一定要用这种方式,只是笔者认为这是一种比较简单的实现。</p><p>新加一个测试类 QuartzTest<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">@SpringBootTest</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">QuartzTest</span> {</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Logger logger = LoggerFactory.getLogger(QuartzTest.class);</span><br><span class="line"> @Autowired</span><br><span class="line"> <span class="keyword">private</span> QuartzService quartzService;</span><br><span class="line"> @Test</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">quartzTest</span><span class="params">()</span> </span>{</span><br><span class="line"> logger.info(<span class="string">"添加定时任务"</span>);</span><br><span class="line"> <span class="keyword">String</span> jobName = <span class="string">"test-1"</span>;</span><br><span class="line"> Map<<span class="keyword">String</span>, Object> <span class="built_in">map</span> = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> <span class="built_in">map</span>.<span class="built_in">put</span>(<span class="string">"test"</span>, <span class="string">"测试任务执行"</span>);</span><br><span class="line"> <span class="built_in">map</span>.<span class="built_in">put</span>(<span class="string">"name"</span>, jobName);</span><br><span class="line"> quartzService.deleteJob(jobName, <span class="string">"test"</span>);</span><br><span class="line"> quartzService.addJob(TestQuartz.class, jobName, <span class="string">"test"</span>, <span class="string">"0 */2 * * * ?"</span>, <span class="built_in">map</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>新建 Job 类,需要继承 QuartzJobBean<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">TestQuartz</span> <span class="keyword">extends</span> <span class="title">QuartzJobBean</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> static <span class="keyword">final</span> <span class="type">Logger</span> logger = <span class="type">LoggerFactory</span>.getLogger(<span class="type">TestQuartz</span><span class="class">.<span class="keyword">class</span>)</span>;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">protected</span> void executeInternal(<span class="type">JobExecutionContext</span> jobExecutionContext) <span class="keyword">throws</span> <span class="type">JobExecutionException</span> {</span><br><span class="line"> <span class="type">SimpleDateFormat</span> formatter = <span class="keyword">new</span> <span class="type">SimpleDateFormat</span>(<span class="string">"yyyy-MM-dd HH:mm:ss"</span>);</span><br><span class="line"> logger.info(<span class="string">"任务开始执行:"</span> + formatter.format(<span class="type">System</span>.currentTimeMillis()));</span><br><span class="line"> <span class="type">JobDataMap</span> jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();</span><br><span class="line"> <span class="type">String</span> test = jobDataMap.get(<span class="string">"test"</span>).toString();</span><br><span class="line"> <span class="type">String</span> jobName = jobDataMap.get(<span class="string">"name"</span>).toString();</span><br><span class="line"> logger.info(test + <span class="string">":"</span> + jobName);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>启动,运行 Test 类,便可添加一个任务,创建 Job 时需要的参数可以通过 <code>JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();</code>来获取,这样就完成了一个定时任务功能。</p><h2 id="Quartz-集群使用"><a href="#Quartz-集群使用" class="headerlink" title="Quartz 集群使用"></a>Quartz 集群使用</h2><p>一个 Quartz 集群中的每个节点是一个独立的 Quartz 应用,它又管理着其他的节点。这就意味着你必须对每个节点分别启动或停止。Quartz 集群中,独立的 Quartz 节点并不与另一其的节点或是管理节点通信,而是通过相同的数据库表来感知到另一 Quartz 应用的。</p><p>因为 Quartz 集群依赖于数据库,所以必须首先创建 Quartz 数据库表,Quartz 发布包中包括了所有被支持的数据库平台的SQL脚本。这些SQL脚本存放于 <code><quartz_home>/docs/dbTables</code> 目录下,总共12张表,不同版本,表个数可能不同。下面是具体表的说明:</p><ul><li>qrtz_blob_triggers : 以Blob 类型存储的触发器。</li><li>qrtz_calendars:存放日历信息, quartz可配置一个日历来指定一个时间范围。</li><li>qrtz_cron_triggers:存放cron类型的触发器。</li><li>qrtz_fired_triggers:存放已触发的触发器。</li><li>qrtz_job_details:存放一个jobDetail信息。</li><li>qrtz_job_listeners:job监听器。</li><li>qrtz_locks: 存储程序的悲观锁的信息(假如使用了悲观锁)。</li><li>qrtz_paused_trigger_graps:存放暂停掉的触发器。</li><li>qrtz_scheduler_state:调度器状态。</li><li>qrtz_simple_triggers:简单触发器的信息。</li><li>qrtz_trigger_listeners:触发器监听器。</li><li>qrtz_triggers:触发器的基本信息。</li></ul><p>接下来,新建 quartz.yml 的配置文件,来覆盖默认的配置。<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">org:</span></span><br><span class="line"> <span class="attr">quartz:</span></span><br><span class="line"> <span class="attr">jobStore:</span></span><br><span class="line"> <span class="attr">useProperties:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">tablePrefix:</span> <span class="string">qrtz_</span></span><br><span class="line"> <span class="comment"># 开启集群模式</span></span><br><span class="line"> <span class="attr">isClustered:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># 集群实例检测时间间隔 ms</span></span><br><span class="line"> <span class="attr">clusterCheckinInterval:</span> <span class="number">5000</span></span><br><span class="line"> <span class="comment"># misfire 任务的超时阈值 ms</span></span><br><span class="line"> <span class="attr">misfireThreshold:</span> <span class="number">60000</span></span><br><span class="line"> <span class="attr">txIsolationLevelReadCommitted:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">class:</span> <span class="string">org.quartz.impl.jdbcjobstore.JobStoreTX</span></span><br><span class="line"> <span class="attr">driverDelegateClass:</span> <span class="string">org.quartz.impl.jdbcjobstore.StdJDBCDelegate</span></span><br><span class="line"></span><br><span class="line"> <span class="attr">scheduler:</span></span><br><span class="line"> <span class="attr">instanceId:</span> <span class="string">AUTO</span></span><br><span class="line"> <span class="attr">rmi.export:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">rmi.proxy:</span> <span class="literal">false</span></span><br><span class="line"> <span class="attr">wrapJobExecutionInUserTransaction:</span> <span class="literal">false</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 工作线程的线程池设置</span></span><br><span class="line"> <span class="attr">threadPool:</span></span><br><span class="line"> <span class="attr">class:</span> <span class="string">org.quartz.simpl.SimpleThreadPool</span></span><br><span class="line"> <span class="attr">threadCount:</span> <span class="number">25</span></span><br><span class="line"> <span class="attr">threadPriority:</span> <span class="number">5</span></span><br><span class="line"> <span class="attr">threadsInheritContextClassLoaderOfInitializingThread:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure></p><p>这样 Quartz 集群就可以实现了。</p><h2 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h2><h3 id="1、Quartz-触发时间配置的三种方式"><a href="#1、Quartz-触发时间配置的三种方式" class="headerlink" title="1、Quartz 触发时间配置的三种方式"></a>1、Quartz 触发时间配置的三种方式</h3><ul><li>cron 方式:采用cronExpression表达式配置时间。</li><li>simple 方式:和JavaTimer差不多,可以指定一个开始时间和结束时间外加一个循环时间。</li><li>calendars 方式:可以和cron配合使用,用cron表达式指定一个触发时间规律,用calendar指定一个范围。</li></ul><p><strong>注意:cron方式需要用到的4张数据表: qrtz_triggers,qrtz_cron_triggers,qrtz_fired_triggers,qrtz_job_details</strong></p><h3 id="2、使用-quartz-遇到的问题"><a href="#2、使用-quartz-遇到的问题" class="headerlink" title="2、使用 quartz 遇到的问题"></a>2、使用 quartz 遇到的问题</h3><h4 id="2-1、在定时任务执行中-service-Autowired-注解不进来"><a href="#2-1、在定时任务执行中-service-Autowired-注解不进来" class="headerlink" title="2.1、在定时任务执行中 service @Autowired 注解不进来"></a>2.1、在定时任务执行中 service @Autowired 注解不进来</h4><p>创建 JobFactory 的 Bean,并在 SchedulerConfig 中添加到 SchedulerFactoryBean<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// JobFactory</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line">public <span class="class"><span class="keyword">class</span> <span class="title">JobFactory</span> <span class="keyword">extends</span> <span class="title">SpringBeanJobFactory</span> <span class="title">implements</span> <span class="title">ApplicationContextAware</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> transient <span class="type">AutowireCapableBeanFactory</span> beanFactory;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> public void setApplicationContext(<span class="keyword">final</span> <span class="type">ApplicationContext</span> context) {</span><br><span class="line"> beanFactory = context.getAutowireCapableBeanFactory();</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="keyword">protected</span> <span class="type">Object</span> createJobInstance(<span class="keyword">final</span> <span class="type">TriggerFiredBundle</span> bundle) <span class="keyword">throws</span> <span class="type">Exception</span> {</span><br><span class="line"> <span class="keyword">final</span> <span class="type">Object</span> job = <span class="keyword">super</span>.createJobInstance(bundle);</span><br><span class="line"> beanFactory.autowireBean(job);</span><br><span class="line"> <span class="keyword">return</span> job;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>SchedulerConfig.java<br><figure class="highlight aspectj"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Configuration</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SchedulerConfig</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> DataSource dataSource;</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> JobFactory jobFactory;</span><br><span class="line"> <span class="meta">@Bean</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function">Properties <span class="title">quartzProperties</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> PropertiesFactoryBean propertiesFactoryBean = <span class="keyword">new</span> PropertiesFactoryBean();</span><br><span class="line"> propertiesFactoryBean.setLocation(<span class="keyword">new</span> ClassPathResource(<span class="string">"/quartz.yml"</span>));</span><br><span class="line"> propertiesFactoryBean.afterPropertiesSet();</span><br><span class="line"> <span class="function"><span class="keyword">return</span> propertiesFactoryBean.<span class="title">getObject</span><span class="params">()</span></span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@Bean</span></span><br><span class="line"> <span class="keyword">public</span> <span class="function">SchedulerFactoryBean <span class="title">schedulerFactoryBean</span><span class="params">()</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> SchedulerFactoryBean factory = <span class="keyword">new</span> SchedulerFactoryBean();</span><br><span class="line"> factory.setSchedulerName(<span class="string">"Cluster_Scheduler"</span>);</span><br><span class="line"> factory.setDataSource(dataSource);</span><br><span class="line"> factory.setApplicationContextSchedulerContextKey(<span class="string">"applicationContext"</span>);</span><br><span class="line"> factory.setQuartzProperties(quartzProperties());</span><br><span class="line"> factory.setJobFactory(jobFactory);</span><br><span class="line"> <span class="keyword">return</span> factory;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h4 id="2-2、quartz-任务激活失败"><a href="#2-2、quartz-任务激活失败" class="headerlink" title="2.2、quartz 任务激活失败"></a>2.2、quartz 任务激活失败</h4><p>在Quartz中,当一个持久化的触发器会因为:</p><ol><li>调度器被关闭;</li><li>线程池没有可用线程;</li><li>项目重启;</li><li>任务的串行执行;</li></ol><p>而错过激活时间,就会发生激活失败(misfire)。</p><p>可以设置 quartz中CornTrigger使用的策略<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//所有的misfile任务马上执行</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY = <span class="number">-1</span>;</span><br><span class="line"><span class="comment">//在Trigger中默认选择MISFIRE_INSTRUCTION_FIRE_ONCE_NOW 策略</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MISFIRE_INSTRUCTION_SMART_POLICY = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// CornTrigger默认策略,合并部分misfire,正常执行下一个周期的任务。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MISFIRE_INSTRUCTION_FIRE_ONCE_NOW = <span class="number">1</span>;</span><br><span class="line"><span class="comment">//所有的misFire都不管,执行下一个周期的任务。</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MISFIRE_INSTRUCTION_DO_NOTHING = <span class="number">2</span>;</span><br></pre></td></tr></table></figure></p><p>1、 通过setMisfireInstruction方法设置misfire策略。<br><figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">CronTriggerFactoryBean triggerFactoryBean = new CronTriggerFactoryBean<span class="params">()</span>;</span><br><span class="line">triggerFactoryBean.<span class="keyword">set</span>Name<span class="params">("corn_" + clazzName)</span>;</span><br><span class="line">triggerFactoryBean.<span class="keyword">set</span>JobDetail<span class="params">(jobFactory.getObject()</span>);</span><br><span class="line">triggerFactoryBean.<span class="keyword">set</span>CronExpression<span class="params">(quartzCorn)</span>;</span><br><span class="line">triggerFactoryBean.<span class="keyword">set</span>Group<span class="params">(QUARTZ_TRIGGER_GROUP)</span>;</span><br><span class="line"><span class="string">//</span>设置misfire策略</span><br><span class="line">triggerFactoryBean.<span class="keyword">set</span>MisfireInstruction<span class="params">(CronTrigger.MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY)</span>;</span><br><span class="line">triggerFactoryBean.afterPropertiesSet<span class="params">()</span>;</span><br></pre></td></tr></table></figure></p><p>2、 也可以通过CronScheduleBuilder设置misfire策略。<br><figure class="highlight protobuf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">CronScheduleBuilder csb = CronScheduleBuilder.cronSchedule(<span class="string">"0/5 * * * * ?"</span>);</span><br><span class="line"><span class="comment">//MISFIRE_INSTRUCTION_DO_NOTHING </span></span><br><span class="line">csb.withMisfireHandlingInstructionDoNothing();</span><br><span class="line"><span class="comment">//MISFIRE_INSTRUCTION_FIRE_ONCE_NOW</span></span><br><span class="line">csb.withMisfireHandlingInstructionFireAndProceed();<span class="comment">//(默认)</span></span><br><span class="line"><span class="comment">//MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY</span></span><br><span class="line">csb.withMisfireHandlingInstructionIgnoreMisfires();</span><br></pre></td></tr></table></figure></p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>任务调度在实际项目中会经常用到,Quartz 也是我们的不二选择,但是在高可用的系统中也存在不少问题,具体问题小伙伴们可以在留言区留言,我们一起共同探讨。</p><h2 id="参考与相关链接"><a href="#参考与相关链接" class="headerlink" title="参考与相关链接"></a>参考与相关链接</h2><p>示例代码:<a href="https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-quartz" target="_blank" rel="noopener">https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-quartz</a></p><p>Quartz 官网:<a href="http://www.quartz-scheduler.org/" target="_blank" rel="noopener">http://www.quartz-scheduler.org/</a></p><p>Quartz集群原理及配置应用:<a href="https://www.cnblogs.com/xiang--liu/p/10120105.html" target="_blank" rel="noopener">https://www.cnblogs.com/xiang–liu/p/10120105.html</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>在说 quartz 之前,我们先回顾一下 spring 的定时任务,使用相当简单,默认集成在 spring boot 中,所以在 spring boot 项目中无需额外添加依赖,无需配置,只需要加个注解就可以了,当然也可以实现动态添加删除定时任务,详情前往上一篇博文<a h
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/categories/Spring-Boot/"/>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>SpringBoot-定时任务</title>
<link href="https://dddreams.github.io/210122-SpringBoot-%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1.html"/>
<id>https://dddreams.github.io/210122-SpringBoot-定时任务.html</id>
<published>2021-01-22T03:20:51.000Z</published>
<updated>2021-01-26T03:15:40.671Z</updated>
<content type="html"><![CDATA[<p>在我们开发项目中,定时任务是经常用到的一种技术,来处理一些业务,SpringBoot 默认支持定时任务,怎么样是不是感觉 Spring Boot 太人性化了,那么下面我们看一下怎么实现一个定时器吧。</p><blockquote><p>Tips<br>开发 Spring Boot 项目有个口诀或者说是 Spring Boot 的三板斧:加依赖、写配置、添注解<br>示例代码详见:<code>https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule</code></p></blockquote><h2 id="加依赖"><a href="#加依赖" class="headerlink" title="加依赖"></a>加依赖</h2><p>Spring Boot 默认支持定时任务,所以只要加入 Spring Boot 的依赖即可<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p><h2 id="写配置"><a href="#写配置" class="headerlink" title="写配置"></a>写配置</h2><p>实际开发中一般将经常变化的值配置到配置文件中,定时任务中我们可以自定义配置项 cron 来控制定时任务执行的时间<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">server:</span></span><br><span class="line"> <span class="attr">port:</span> <span class="number">8080</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line"> <span class="attr">application:</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">schedule</span></span><br><span class="line"><span class="attr">schedule:</span></span><br><span class="line"> <span class="attr">cron:</span> <span class="number">0</span> <span class="string">*/1</span> <span class="string">*</span> <span class="string">*</span> <span class="string">*</span> <span class="string">?</span> <span class="comment"># 表达式表示 1 分钟执行一次</span></span><br></pre></td></tr></table></figure></p><p>关于 cron 表达式,如果不清楚请先百度。</p><h2 id="添注解"><a href="#添注解" class="headerlink" title="添注解"></a>添注解</h2><p>我们需要在启动类上加 <code>@EnableScheduling</code> 注解即可开启定时任务<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@SpringBootApplication</span></span><br><span class="line"><span class="variable">@EnableScheduling</span></span><br><span class="line">public class DemoApplication {</span><br><span class="line"> <span class="selector-tag">public</span> <span class="selector-tag">static</span> <span class="selector-tag">void</span> <span class="selector-tag">main</span>(String[] args) {</span><br><span class="line"> <span class="selector-tag">SpringApplication</span><span class="selector-class">.run</span>(DemoApplication.class, args);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="编写定时任务的代码"><a href="#编写定时任务的代码" class="headerlink" title="编写定时任务的代码"></a>编写定时任务的代码</h2><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Slf4j</span></span><br><span class="line"><span class="variable">@Component</span></span><br><span class="line">public class TestTasks {</span><br><span class="line"> <span class="comment">//@Scheduled(cron = "0 */1 * * * ?")</span></span><br><span class="line"> <span class="variable">@Scheduled</span>(cron = <span class="string">"${schedule.cron}"</span>)</span><br><span class="line"> public void testTask() {</span><br><span class="line"> <span class="selector-tag">log</span><span class="selector-class">.info</span>(<span class="string">"哈哈,我执行了!"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>启动项目,便会在控制台隔1分钟打印<br><figure class="highlight erlang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">哈哈,我执行了!</span><br><span class="line">哈哈,我执行了!</span><br><span class="line">哈哈,我执行了!</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><p><strong>注意:</strong> <code>@Component</code> 注解是启动后立即执行,<code>${schedule.cron}</code> 便是从配置文件读取配置的执行时间。</p><p>前面说了如果你对 cron 不了解,不用担心,Spring boot 提供了另外的方式 <code>fixedRate</code>,详解如下:<br><figure class="highlight less"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">@Scheduled</span>(fixedRate = <span class="number">60000</span>) :上一次开始执行时间点之后<span class="number">1</span>分再执行</span><br><span class="line"><span class="variable">@Scheduled</span>(fixedDelay = <span class="number">60000</span>) :上一次执行完毕时间点之后<span class="number">1</span>分再执行</span><br><span class="line"><span class="variable">@Scheduled</span>(initialDelay=<span class="number">1000</span>, fixedRate=<span class="number">60000</span>) :第一次延迟<span class="number">1</span>秒后执行,之后按 fixedRate 的规则每<span class="number">1</span>分执行一次</span><br></pre></td></tr></table></figure></p><p>代码:<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">@Scheduled(fixedRate = <span class="number">60000</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testTask1</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="built_in">log</span>.info(<span class="string">"哈哈,我又执行了!"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>结果:<br><figure class="highlight erlang"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">哈哈,我执行了!</span><br><span class="line">哈哈,我又执行了!</span><br><span class="line">哈哈,我执行了!</span><br><span class="line">哈哈,我又执行了!</span><br><span class="line">...</span><br></pre></td></tr></table></figure></p><h2 id="动态添加或删除定时任务"><a href="#动态添加或删除定时任务" class="headerlink" title="动态添加或删除定时任务"></a>动态添加或删除定时任务</h2><p>在实际项目中,往往会遇到动态的添加或停止定时任务,来我们看看怎么实现,首先添加一个配置类<br><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line">@Configuration</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">DefaultSchedulingConfigurer</span> <span class="title">implements</span> <span class="title">SchedulingConfigurer</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> ScheduledTaskRegistrar taskRegistrar;</span><br><span class="line"> <span class="keyword">private</span> Set<ScheduledFuture<?>> scheduledFutures = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">private</span> Map<String, ScheduledFuture<?>> taskFutures = <span class="keyword">new</span> ConcurrentHashMap<>();</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">configureTasks</span>(<span class="params">ScheduledTaskRegistrar taskRegistrar</span>)</span> {</span><br><span class="line"> <span class="keyword">this</span>.taskRegistrar = taskRegistrar;</span><br><span class="line"> <span class="comment">//System.out.println(inited());</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> @SuppressWarnings(<span class="string">"unchecked"</span>)</span><br><span class="line"> <span class="keyword">private</span> Set<ScheduledFuture<?>> getScheduledFutures() {</span><br><span class="line"> <span class="keyword">if</span> (scheduledFutures == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// spring版本不同选用不同字段scheduledFutures</span></span><br><span class="line"> scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar, <span class="string">"scheduledTasks"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (NoSuchFieldException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> SchedulingException(<span class="string">"not found scheduledFutures field."</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> scheduledFutures;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 添加任务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">addTriggerTask</span>(<span class="params">String taskId, TriggerTask triggerTask</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span> (taskFutures.containsKey(taskId)) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> SchedulingException(<span class="string">"the taskId["</span> + taskId + <span class="string">"] was added."</span>);</span><br><span class="line"> }</span><br><span class="line"> TaskScheduler scheduler = taskRegistrar.getScheduler();</span><br><span class="line"> ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());</span><br><span class="line"> getScheduledFutures().<span class="keyword">add</span>(future);</span><br><span class="line"> taskFutures.put(taskId, future);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 取消任务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">cancelTriggerTask</span>(<span class="params">String taskId</span>)</span> {</span><br><span class="line"> ScheduledFuture<?> future = taskFutures.<span class="keyword">get</span>(taskId);</span><br><span class="line"> <span class="keyword">if</span> (future != <span class="literal">null</span>) {</span><br><span class="line"> future.cancel(<span class="literal">true</span>);</span><br><span class="line"> }</span><br><span class="line"> taskFutures.<span class="keyword">remove</span>(taskId);</span><br><span class="line"> getScheduledFutures().<span class="keyword">remove</span>(future);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 重置任务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">resetTriggerTask</span>(<span class="params">String taskId, TriggerTask triggerTask</span>)</span> {</span><br><span class="line"> cancelTriggerTask(taskId);</span><br><span class="line"> addTriggerTask(taskId, triggerTask);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 任务编号</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Set<String> <span class="title">taskIds</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> taskFutures.keySet();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 是否存在任务</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> boolean <span class="title">hasTask</span>(<span class="params">String taskId</span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.taskFutures.containsKey(taskId);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 任务调度是否已经初始化完成</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> boolean <span class="title">inited</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.taskRegistrar != <span class="literal">null</span> && <span class="keyword">this</span>.taskRegistrar.getScheduler() != <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>然后新建一个 controller<br><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping(<span class="meta-string">"/task"</span>)</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestTaskController</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> DefaultSchedulingConfigurer defaultSchedulingConfigurer;</span><br><span class="line"> <span class="meta">@GetMapping(<span class="meta-string">"/add"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> String add(<span class="meta">@RequestParam(name = <span class="meta-string">"name"</span>)</span> String name) {</span><br><span class="line"> defaultSchedulingConfigurer.addTriggerTask(name,</span><br><span class="line"> new TriggerTask(</span><br><span class="line"> () -> System.<span class="keyword">out</span>.println(<span class="string">"hello world!"</span>),</span><br><span class="line"> new CronTrigger(<span class="string">"0/5 * * * * ? "</span>)));<span class="comment">//5秒执行一次</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务开启成功"</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@GetMapping(<span class="meta-string">"/del"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> String del(<span class="meta">@RequestParam(name = <span class="meta-string">"name"</span>)</span> String name) {</span><br><span class="line"> defaultSchedulingConfigurer.cancelTriggerTask(name);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"任务删除成功"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>请求 <code>http://localhost:8080/task/add</code>,定时任务即可开启<br>请求 <code>http://localhost:8080/task/del</code>,定时任务就删除成功了</p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>其实 Spring boot 的定时任务相对比较简单,如果在高并发集群环境下,我们尽量使用框架来支撑我们的业务,下一节我会介绍定时任务的框架 quartz ,大家敬请期待吧。</p><h2 id="参考与相关链接"><a href="#参考与相关链接" class="headerlink" title="参考与相关链接"></a>参考与相关链接</h2><p>示例代码:<a href="https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule" target="_blank" rel="noopener">https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-schedule</a><br>Spring boot 定时任务:<a href="http://www.ityouknow.com/springboot/2016/12/02/spring-boot-scheduler.html" target="_blank" rel="noopener">http://www.ityouknow.com/springboot/2016/12/02/spring-boot-scheduler.html</a><br>SpringBoot+schedule+可以动态添加或删除定时任务:<a href="https://blog.csdn.net/nicky_lc/article/details/106961779" target="_blank" rel="noopener">https://blog.csdn.net/nicky_lc/article/details/106961779</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>在我们开发项目中,定时任务是经常用到的一种技术,来处理一些业务,SpringBoot 默认支持定时任务,怎么样是不是感觉 Spring Boot 太人性化了,那么下面我们看一下怎么实现一个定时器吧。</p>
<blockquote>
<p>Tips<br>开发 Spring
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/categories/Spring-Boot/"/>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>是年味淡了,还是我们老了</title>
<link href="https://dddreams.github.io/200121-%E6%98%AF%E5%B9%B4%E5%91%B3%E6%B7%A1%E4%BA%86%EF%BC%8C%E8%BF%98%E6%98%AF%E6%88%91%E4%BB%AC%E8%80%81%E4%BA%86.html"/>
<id>https://dddreams.github.io/200121-是年味淡了,还是我们老了.html</id>
<published>2020-01-21T06:29:11.000Z</published>
<updated>2020-01-21T10:53:28.056Z</updated>
<content type="html"><![CDATA[<blockquote><p>对幸福说个早安,给吉祥送个花篮,给祝福送份快餐,祝大家春节快乐!</p></blockquote><p>叮咚…微信收到一条消息。</p><p>「敲响的是钟声,走过的是岁月,留下的是故事,带来的是希望,盼望的是美好,送来的是祝福,愿朋友新年快乐。」是一条新年祝福,顺着华丽的符号,伴随着满屏的笑脸与礼花的表情雨,往上滑,发现和对方的上一次对话是在去年过年,我不禁陷入了沉思…「这人是谁啊!」。</p><p>“诶,有敬业福吗?来,我粘一张”,支付宝上集福集的热火朝天,每天一睁眼便浇水,一看到福字就扫,除夕夜里陪伴自己和家人最多的还是手机,终于等到开启五福了,结果,分到2.88元。</p><p>工作群里忽然热闹起来,各种祝福语,各种表情,令人紧张的是偶尔会有红包闪现,你紧盯屏幕,活动手指,生怕错过。终于来了,你庆幸自己眼疾手快抢到了,心情无比激动,感觉登上了人生巅峰,发现红包面额只有0.8元。</p><p>“狗娃啊,今年怎么没带媳妇回来啊!”,小时候盼望发压岁钱的七大姑八大姨变成了避而远之的催婚对象。你只能极其尴尬的,笑一声,“等着阿姨给介绍呢”。</p><p>突然感觉这个年,不像是小时候过的那个年了,感觉年味淡了,一切过年的活动都成了大公司散钱争用户的套路,朋友发来的新年祝福也显得那么苍白无力,只是一句虚话而已。那到底是年味淡了,还是我们老了?</p><p>“三啊,你什么时候回来”,电话那头愣了半天,“哦,我明天就回来。”原本打算不回家的小儿子听到母亲的声音,决定回家过年,他急忙的打开12306网上购票,没想到票还有,没有往年那么紧张了。</p><p>“爷爷,我们已经在路上了,爸爸开的车”,老父亲开心嘱咐慢点开车,不用着急。挂断电话又急切的让母亲准备杀鱼杀鸡。</p><p>小两口刚生了二胎,今年不准备回家,而是把父母接过来,在城里过年。火车站上有的人迫切的回家,有的人焦急的等待。每出来一批人,他们都在人群中张望着,寻找着,终于看到了父母的身影,跑过去接住父亲手里提着的东西,开心的拥抱在一起。这几年来越来越多的人选择了“反向春运”,把老人孩子接到城里过年。</p><p>是的,“团圆”成了人们过年最大的愿望。小时候最开心的是吃到包着硬币的饺子,长大了,吃什么不重要,重要的是过年能跟家人在一起。</p><p>最后,提前祝大家春节快乐!年味未淡,我们不老!</p><p>ps:这几天被一个叫做“新型肺炎”的病毒搞的人心惶惶,在这里也提醒读者朋友们注意防范,尽量避开人多的地方,加强自身卫生防止病毒传染。同时也保持镇定,不必恐慌,不要过于相信部分媒体扩大化的报道。</p><p>pps:估计大家已经看到了,我公众号改名字了,改名字的原因有二,第一是字母拼音不利于搜索;第二是博客感觉有点老气,所以改名为:「<strong>治恒说说</strong>」</p><hr><p>本文同步更新至<a href="https://dddreams.github.io/">zhiheng’s blog</a>、微信公众号。</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/wechat-sys.png" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">微信搜一搜:治恒说说</p><p style="text-align: center;margin:20 0;">记得点关注哦</p>]]></content>
<summary type="html">
<blockquote>
<p>对幸福说个早安,给吉祥送个花篮,给祝福送份快餐,祝大家春节快乐!</p>
</blockquote>
<p>叮咚…微信收到一条消息。</p>
<p>「敲响的是钟声,走过的是岁月,留下的是故事,带来的是希望,盼望的是美好,送来的是祝福,愿朋友新年快乐
</summary>
<category term="杂谈" scheme="https://dddreams.github.io/tags/%E6%9D%82%E8%B0%88/"/>
</entry>
<entry>
<title>Java1.8你还不知道的新特性</title>
<link href="https://dddreams.github.io/200110-Java1-8%E4%BD%A0%E8%BF%98%E4%B8%8D%E7%9F%A5%E9%81%93%E7%9A%84%E6%96%B0%E7%89%B9%E6%80%A7.html"/>
<id>https://dddreams.github.io/200110-Java1-8你还不知道的新特性.html</id>
<published>2020-01-10T10:35:26.000Z</published>
<updated>2020-01-10T10:37:04.855Z</updated>
<content type="html"><![CDATA[<h2 id="Lambda-表达式"><a href="#Lambda-表达式" class="headerlink" title="Lambda 表达式"></a>Lambda 表达式</h2><p>Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。</p><p>Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。</p><p>使用 Lambda 表达式可以使代码变的更加简洁紧凑。</p><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">String</span>[] arr = {<span class="string">"Google"</span>, <span class="string">"Baidu"</span>, <span class="string">"Taobao"</span>, <span class="string">"Sina"</span>, <span class="string">"Runoob"</span>};</span><br><span class="line">List<<span class="keyword">String</span>> <span class="built_in">list</span> = Arrays.asList(arr);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 java 7 排序</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sortUsingJava7</span><span class="params">(List<<span class="keyword">String</span>> names)</span> </span>{</span><br><span class="line"> Collections.sort(names, <span class="keyword">new</span> Comparator<<span class="keyword">String</span>>() {</span><br><span class="line"> @Override</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> compare(<span class="keyword">String</span> s1, <span class="keyword">String</span> s2) {</span><br><span class="line"> <span class="keyword">return</span> s1.compareTo(s2);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 java 8 排序</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">sortUsingJava8</span><span class="params">(List<<span class="keyword">String</span>> names)</span> </span>{</span><br><span class="line"> Collections.sort(names, (s1, s2) -> s1.compareTo(s2));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">使用 Java <span class="number">7</span> 语法: </span><br><span class="line">[Baidu , Google , Runoob , Sina , Taobao ]</span><br><span class="line">使用 Java <span class="number">8</span> 语法: </span><br><span class="line">[Baidu , Google , Runoob , Sina , Taobao ]</span><br></pre></td></tr></table></figure><h2 id="Stream流式操作"><a href="#Stream流式操作" class="headerlink" title="Stream流式操作"></a>Stream流式操作</h2><p>问题:<br>给定一个字符串元素列表,如下所示:<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="string">"1"</span>, <span class="string">"2"</span>, <span class="string">"bilibili"</span>, <span class="string">"of"</span>, <span class="string">"codesheep"</span>, <span class="string">"5"</span>, <span class="string">"at"</span>, <span class="string">"BILIBILI"</span>, <span class="string">"codesheep"</span>, <span class="string">"23"</span>, <span class="string">"CHEERS"</span>, <span class="string">"6"</span>]</span><br></pre></td></tr></table></figure></p><p>找出所有长度 >=5 的字符串,并且忽略大小写、去除重复字符串,然后按字母排序,最后用“-”连接成一个字符串输出!</p><p>来,小伙伴们,5分钟,搞定它。。。什么搞定不了,该去补习补习了。<br>那么使用Java8怎么搞定呢,开启装逼模式:<br><figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">String result = list.stream() <span class="comment">// 首先将列表转化为Stream流 </span></span><br><span class="line"> .<span class="function"><span class="title">filter</span>( i -></span> !isNum(i) ) <span class="comment">// 筛选出字母型字符串 </span></span><br><span class="line"> .<span class="function"><span class="title">filter</span>( i -></span> i.length() >= <span class="number">5</span> ) <span class="comment">// 其次筛选出长度>=5的字符串 </span></span><br><span class="line"> .<span class="function"><span class="title">map</span>( i -></span> i.toLowerCase() ) <span class="comment">// 字符串统一转小写 </span></span><br><span class="line"> .distinct() <span class="comment">// 去重操作来一下 </span></span><br><span class="line"> .sorted( Comparator.naturalOrder() ) <span class="comment">// 字符串排序来一下 </span></span><br><span class="line"> .collect( Collectors.joining(<span class="string">"-"</span>) ); <span class="comment">// 连词成句来一下,完美!</span></span><br><span class="line">System.out.println(result);</span><br><span class="line"></span><br><span class="line">输出:bilibili-cheers-codesheep</span><br></pre></td></tr></table></figure></p><p>这操作够sao了吧!</p><h2 id="Consumer接口"><a href="#Consumer接口" class="headerlink" title="Consumer接口"></a>Consumer接口</h2><p>Consumer的语义是消费的意思,了解一些消息队列的同学,肯定对这个单词,有一定的理解。<br>看个简单的例子:打印3次 <code>Hello World!</code><br><figure class="highlight swift"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原来的写法</span></span><br><span class="line"><span class="type">System</span>.out.<span class="built_in">println</span>(<span class="string">"Hello World!"</span>);</span><br><span class="line"><span class="type">System</span>.out.<span class="built_in">println</span>(<span class="string">"Hello World!"</span>);</span><br><span class="line"><span class="type">System</span>.out.<span class="built_in">println</span>(<span class="string">"Hello World!"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//使用Consumer接口</span></span><br><span class="line"><span class="type">Consumer</span> <span class="built_in">c</span> = <span class="type">System</span>.out::<span class="built_in">println</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">c</span>.andThen(<span class="built_in">c</span>).andThen(<span class="built_in">c</span>).accept(<span class="string">"Hello World!"</span>);</span><br></pre></td></tr></table></figure></p><p>搞定。</p><p>一个简单的平方计算:<br><figure class="highlight llvm"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">Consumer<Integer> square = <span class="keyword">x</span> -> System.out.println(<span class="string">"print square : "</span> + <span class="keyword">x</span> * <span class="keyword">x</span>)<span class="comment">;</span></span><br><span class="line"><span class="comment">square.accept(2);</span></span><br></pre></td></tr></table></figure></p><h2 id="Optional接口"><a href="#Optional接口" class="headerlink" title="Optional接口"></a>Optional接口</h2><p>Optional本质是个容器,你可以将你的变量交由它进行封装,这样我们就不用显式对原变量进行 null值检测,防止出现各种空指针异常。举例:<br><figure class="highlight perl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">public Integer getScore( Student student ){</span><br><span class="line"> <span class="keyword">if</span>(student != null){</span><br><span class="line"> Subject <span class="function"><span class="keyword">sub</span> = <span class="title">student</span>.<span class="title">getSubject</span></span>();</span><br><span class="line"> <span class="keyword">if</span>(<span class="function"><span class="keyword">sub</span> != <span class="title">null</span>)</span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">sub</span>.<span class="title">getScore</span></span>();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> null;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>多嵌套的 if 判空,这么处理当然可以,然而有更好的解决办法哦。<br><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="built_in">Integer</span> getScore(Student student){</span><br><span class="line"> <span class="keyword">return</span> Optional.ofNullable(student)</span><br><span class="line"> .<span class="built_in">map</span>(Student<span class="type">::getSubject</span>)</span><br><span class="line"> .<span class="built_in">map</span>(Subject<span class="type">::getScore</span>)</span><br><span class="line"> .orElse(<span class="built_in">null</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>完美实现。</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<h2 id="Lambda-表达式"><a href="#Lambda-表达式" class="headerlink" title="Lambda 表达式"></a>Lambda 表达式</h2><p>Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特
</summary>
<category term="Java" scheme="https://dddreams.github.io/tags/Java/"/>
</entry>
<entry>
<title>java代码优化建议</title>
<link href="https://dddreams.github.io/200103-java%E4%BB%A3%E7%A0%81%E4%BC%98%E5%8C%96%E5%BB%BA%E8%AE%AE.html"/>
<id>https://dddreams.github.io/200103-java代码优化建议.html</id>
<published>2020-01-03T04:07:26.000Z</published>
<updated>2020-01-03T05:08:50.624Z</updated>
<content type="html"><![CDATA[<blockquote><p>帮助开发人员夯实基础,从规范的编码操作开始,培养良好的编码习惯,助力未来技术成长。</p></blockquote><p>通过java代码规范来优化程序,优化内存使用情况,防止内存泄露</p><p>可供程序利用的资源(内存、CPU时间、网络带宽等)是有限的,优化的目的就是让程序用尽可能少的资源完成预定的任务。优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率。本文讨论的主要是如何提高代码的效率。<br>在Java程序中,性能问题的大部分原因并不在于Java语言,而是在于程序本身。养成好的代码编写习惯非常重要,比如正确地、巧妙地运用java.lang.String类和java.util.Vector类,它能够显著地提高程序的性能。下面我们就来具体地分析一下这方面的问题。</p><p><strong>1、 尽量指定类的final修饰符 带有final修饰符的类是不可派生的。</strong></p><p>在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联(inline)所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50% 。</p><p><strong>2、 尽量重用对象。</strong></p><p>特别是String 对象的使用中,出现字符串连接情况时应用StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。</p><p><strong>3、 尽量使用局部变量,调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。</strong></p><p>其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。另外,依赖于具体的编译器/JVM,局部变量还可能得到进一步优化。请参见《尽可能使用堆栈变量》。</p><p><strong>4、 不要重复初始化变量</strong></p><p>默认情况下,调用类的构造函数时, Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。</p><p><strong>5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。</strong></p><p><strong>6、 Java 编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,即使关闭以释放资源。</strong></p><p>因为对这些大对象的操作会造成系统大的开销,稍有不慎,会导致严重的后果。</p><p><strong>7、 由于JVM的有其自身的GC机制,不需要程序开发者的过多考虑,从一定程度上减轻了开发者负担,但同时也遗漏了隐患,过分的创建对象会消耗系统的大量内存,严重时会导致内存泄露,因此,保证过期对象的及时回收具有重要意义。</strong></p><p>JVM回收垃圾的条件是:对象不在被引用;然而,JVM的GC并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成null。</p><p><strong>8、 在使用同步机制时,应尽量使用方法同步代替代码块同步。</strong></p><p><strong>9、 尽量减少对变量的重复计算</strong><br><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(int <span class="built_in">i</span> = <span class="number">0</span>;<span class="built_in">i</span> < list.<span class="built_in">size</span>; <span class="built_in">i</span> ++) {</span><br><span class="line">…</span><br><span class="line">}</span><br><span class="line">应替换为:</span><br><span class="line"><span class="keyword">for</span>(int <span class="built_in">i</span> = <span class="number">0</span>,int len = list.<span class="built_in">size</span>();<span class="built_in">i</span> < len; <span class="built_in">i</span> ++){</span><br><span class="line">…</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>10、尽量采用lazy loading 的策略,即在需要的时候才开始创建。</strong></p><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">例如: </span><br><span class="line"><span class="keyword">String</span> <span class="built_in">str</span> = “aaa”;</span><br><span class="line"><span class="keyword">if</span>(i == <span class="number">1</span>) {</span><br><span class="line"> list.<span class="built_in">add</span>(<span class="built_in">str</span>);</span><br><span class="line">}</span><br><span class="line">应替换为:</span><br><span class="line"><span class="keyword">if</span>(i == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">String</span> <span class="built_in">str</span> = “aaa”;</span><br><span class="line"> list.<span class="built_in">add</span>(<span class="built_in">str</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>11、慎用异常</strong></p><p>异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。 异常只能用于错误处理,不应该用来控制程序流程。</p><p><strong>12、不要在循环中使用:</strong></p><figure class="highlight cos"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Try</span> {</span><br><span class="line"></span><br><span class="line">} <span class="keyword">catch</span>() {</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>应把其放置在最外层。</p><p><strong>13、StringBuffer 的使用:</strong></p><p><code>StringBuffer</code>表示了可变的、可写的字符串。<br>有三个构造方法 :<br><code>StringBuffer ();</code> //默认分配16个字符的空间<br><code>StringBuffer (int size);</code> //分配size个字符的空间<br><code>StringBuffer (String str);</code> //分配16个字符+str.length()个字符空间<br>你可以通过StringBuffer的构造函数来设定它的初始化容量,这样可以明显地提升性能。</p><p>这里提到的构造函数是<code>StringBuffer(int length)</code>,<code>length</code>参数表示当前的<code>StringBuffer</code>能保持的字符数量。你也可以使用<code>ensureCapacity(int minimumcapacity)</code>方法在StringBuffer对象创建之后设置它的容量。首先我们看看<code>StringBuffer</code>的缺省行为,然后再找出一条更好的提升性能的途径。<br><code>StringBuffer</code>在内部维护一个字符数组,当你使用缺省的构造函数来创建<code>StringBuffer</code>对象的时候,因为没有设置初始化字符长度,<code>StringBuffer</code>的容量被初始化为16个字符,也就是说缺省容量就是16个字符。当<code>StringBuffer</code>达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,也就是(2<em>旧值+2)。如果你使用缺省值,初始化之后接着往里面追加字符,在你追加到第16个字符的时候它会将容量增加到34(2</em>16+2),当追加到34个字符的时候就会将容量增加到70(2*34+2)。无论何事只要StringBuffer到达它的最大容量它就不得不创建一个新的字符数组然后重新将旧字符和新字符都拷贝一遍――这也太昂贵了点。所以总是给StringBuffer设置一个合理的初始化容量值是错不了的,这样会带来立竿见影的性能增益。StringBuffer初始化过程的调整的作用由此可见一斑。所以,使用一个合适的容量值来初始化StringBuffer永远都是一个最佳的建议。</p><p><strong>14、合理的使用Java类 java.util.Vector。</strong></p><p>简单地说,一个Vector就是一个java.lang.Object实例的数组。Vector与数组相似,它的元素可以通过整数形式的索引访问。但是,Vector类型的对象在创建之后,对象的大小能够根据元素的增加或者删除而扩展、缩小。请考虑下面这个向Vector加入元素的例子:<br><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Object</span> bj = <span class="keyword">new</span> <span class="keyword">Object</span>();</span><br><span class="line">Vector v = <span class="keyword">new</span> Vector(<span class="number">100000</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="built_in">int</span> i = <span class="number">0</span>; i < <span class="number">100000</span>; i++) { </span><br><span class="line"> v.<span class="built_in">add</span>(<span class="number">0</span>,obj); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>除非有绝对充足的理由要求每次都把新元素插入到Vector的前面,否则上面的代码对性能不利。在默认构造函数中,Vector的初始存储能力是10个元素,如果新元素加入时存储能力不足,则以后存储能力每次加倍。Vector类就对象StringBuffer类一样,每次扩展存储能力时,所有现有的元素都要复制到新的存储空间之中。下面的代码片段要比前面的例子快几个数量级:<br><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Object</span> bj = <span class="keyword">new</span> <span class="keyword">Object</span>();</span><br><span class="line">Vector v = <span class="keyword">new</span> Vector(<span class="number">100000</span>);</span><br><span class="line"><span class="keyword">for</span>(<span class="built_in">int</span> i = <span class="number">0</span>; i < <span class="number">100000</span>; i++) { </span><br><span class="line"> v.<span class="built_in">add</span>(obj); </span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>同样的规则也适用于Vector类的remove()方法。由于Vector中各个元素之间不能含有“空隙”,删除除最后一个元素之外的任意其他元素都导致被删除元素之后的元素向前移动。也就是说,从Vector删除最后一个元素要比删除第一个元素“开销”低好几倍。</p><p>假设要从前面的Vector删除所有元素,我们可以使用这种代码:<br><figure class="highlight lsl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(int i = <span class="number">0</span>; i < <span class="number">100000</span>; i++) {</span><br><span class="line"> v.remove(<span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>但是,与下面的代码相比,前面的代码要慢几个数量级:<br><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">for(<span class="name">int</span> i = <span class="number">0</span><span class="comment">; i < 100000; i++) {</span></span><br><span class="line"> v.remove(<span class="name">v</span>.size()<span class="number">-1</span>)<span class="comment">;</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>从Vector类型的对象v删除所有元素的最好方法是:<br><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">v.removeAllElements()<span class="comment">;</span></span><br></pre></td></tr></table></figure></p><p>假设Vector类型的对象v包含字符串“Hello”。考虑下面的代码,它要从这个Vector中删除“Hello”字符串:<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">String</span> s = <span class="string">"Hello"</span>;</span><br><span class="line"><span class="keyword">int</span> i = v.indexOf(s);</span><br><span class="line"><span class="keyword">if</span>(I != <span class="number">-1</span>) v.<span class="built_in">remove</span>(s);</span><br></pre></td></tr></table></figure></p><p>这些代码看起来没什么错误,但它同样对性能不利。在这段代码中,indexOf()方法对v进行顺序搜索寻找字符串“Hello”,remove(s)方法也要进行同样的顺序搜索。改进之后的版本是:<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">String</span> s = <span class="string">"Hello"</span>;</span><br><span class="line"><span class="keyword">int</span> i = v.indexOf(s);</span><br><span class="line"><span class="keyword">if</span>(I != <span class="number">-1</span>) v.<span class="built_in">remove</span>(i);</span><br></pre></td></tr></table></figure></p><p>这个版本中我们直接在remove()方法中给出待删除元素的精确索引位置,从而避免了第二次搜索。一个更好的版本是:<br><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">String s = <span class="string">"Hello"</span><span class="comment">; </span></span><br><span class="line">v.remove(s)<span class="comment">;</span></span><br></pre></td></tr></table></figure></p><p>最后,我们再来看一个有关Vector类的代码片段:<br><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(int <span class="built_in">i</span> = <span class="number">0</span>; <span class="built_in">i</span>++;<span class="built_in">i</span> < v.<span class="built_in">length</span>)</span><br></pre></td></tr></table></figure></p><p>如果v包含100,000个元素,这个代码片段将调用v.size()方法100,000次。虽然size方法是一个简单的方法,但它仍旧需要一次方法调用的开销,至少JVM需要为它配置以及清除堆栈环境。在这里,for循环内部的代码不会以任何方式修改Vector类型对象v的大小,因此上面的代码最好改写成下面这种形式:<br><figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span> <span class="built_in">size</span> = v.<span class="built_in">size</span>(); </span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">int</span> I=<span class="number">0</span>; I++;I<<span class="built_in">size</span>)</span><br></pre></td></tr></table></figure></p><p>虽然这是一个简单的改动,但它仍旧赢得了性能。毕竟,每一个CPU周期都是宝贵的。</p><p><strong>15、当复制大量数据时,使用System.arraycopy()命令。</strong><br><figure class="highlight groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">int</span>[] src={<span class="number">1</span>,<span class="number">3</span>,<span class="number">5</span>,<span class="number">6</span>,<span class="number">7</span>,<span class="number">8</span>};</span><br><span class="line"><span class="keyword">int</span>[] dest = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">6</span>];</span><br><span class="line">System.arraycopy(src, <span class="number">0</span>, dest, <span class="number">0</span>, <span class="number">6</span>);</span><br><span class="line"><span class="string">src:</span>源数组; <span class="string">srcPos:</span>源数组要复制的起始位置;</span><br><span class="line"><span class="string">dest:</span>目的数组; <span class="string">destPos:</span>目的数组放置的起始位置;</span><br><span class="line"><span class="string">length:</span>复制的长度.</span><br><span class="line"></span><br><span class="line">注意:src and dest都必须是同类型或者可以进行转换类型的数组.</span><br></pre></td></tr></table></figure></p><p><strong>16、代码重构:增强代码的可读性。</strong><br><figure class="highlight cs"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ShopCart</span> {</span><br><span class="line"> <span class="keyword">private</span> List carts ;</span><br><span class="line"> …</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span> (<span class="params">Object item</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span>(carts == <span class="literal">null</span>) {</span><br><span class="line"> carts = <span class="keyword">new</span> ArrayList();</span><br><span class="line"> }</span><br><span class="line"> crts.<span class="keyword">add</span>(item);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">remove</span>(<span class="params">Object item</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span>(carts. contains(item)) {</span><br><span class="line"> carts.<span class="keyword">remove</span>(item);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> List <span class="title">getCarts</span>(<span class="params"></span>)</span> {</span><br><span class="line"> <span class="comment">//返回只读列表</span></span><br><span class="line"> <span class="keyword">return</span> Collections.unmodifiableList(carts);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//不推荐这种方式</span></span><br><span class="line"> <span class="comment">//this.getCarts().add(item);</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>17、不用new关键词创建类的实例</strong></p><p>用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。<br>在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Credit <span class="title">getNewCredit</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Credit();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>改进后的代码使用clone()方法,如下所示:<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> Credit BaseCredit = <span class="keyword">new</span> Credit();</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Credit <span class="title">getNewCredit</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (Credit) BaseCredit.clone();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>上面的思路对于数组处理同样很有用。</p><p><strong>18、乘法和除法</strong></p><p>考虑下面的代码:<br><figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">val</span> = <span class="number">0</span>; <span class="keyword">val</span> < <span class="number">100000</span>; <span class="keyword">val</span> +=<span class="number">5</span>) {</span><br><span class="line"> alterX = <span class="keyword">val</span> * <span class="number">8</span>; myResult = <span class="keyword">val</span> * <span class="number">2</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:<br><figure class="highlight fsharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">val</span> = <span class="number">0</span>; <span class="keyword">val</span> < <span class="number">100000</span>; <span class="keyword">val</span> += <span class="number">5</span>) {</span><br><span class="line"> alterX = <span class="keyword">val</span> << <span class="number">3</span>; myResult = <span class="keyword">val</span> << <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。</p><p><strong>19、在JSP页面中关闭无用的会话。</strong></p><p>一个常见的误解是以为session在有客户端访问时就被创建,然而事实是直到某server端程序调用HttpServletRequest.getSession(true)这样的语句时才被创建,注意如果JSP没有显示的使用 <> 关闭session,则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session = HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。由于session会消耗内存资源,因此,如果不打算使用session,应该在所有的JSP中关闭它。<br>对于那些无需跟踪会话状态的页面,关闭自动创建的会话可以节省一些资源。使用如下page指令:<br><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><%@<span class="built_in"> page </span><span class="attribute">session</span>=<span class="string">"false"</span>%></span><br></pre></td></tr></table></figure></p><p><strong>20、JDBC与I/O</strong></p><p>如果应用程序需要访问一个规模很大的数据集,则应当考虑使用块提取方式。默认情况下,JDBC每次提取32行数据。举例来说,假设我们要遍历一个5000行的记录集,JDBC必须调用数据库157次才能提取到全部数据。如果把块大小改成512,则调用数据库的次数将减少到10次。</p><p><strong>21、Servlet与内存使用</strong></p><p>许多开发者随意地把大量信息保存到用户会话之中。一些时候,保存在会话中的对象没有及时地被垃圾回收机制回收。从性能上看,典型的症状是用户感到系统周期性地变慢,却又不能把原因归于任何一个具体的组件。如果监视JVM的堆空间,它的表现是内存占用不正常地大起大落。<br>解决这类内存问题主要有二种办法。第一种办法是,在所有作用范围为会话的Bean中实现HttpSessionBindingListener接口。这样,只要实现valueUnbound()方法,就可以显式地释放Bean使用的资源。</p><p>另外一种办法就是尽快地把会话作废。大多数应用服务器都有设置会话作废间隔时间的选项。另外,也可以用编程的方式调用会话的setMaxInactiveInterval()方法,该方法用来设定在作废会话之前,Servlet容器允许的客户请求的最大间隔时间,以秒计。</p><p><strong>22、使用缓冲标记</strong></p><p>一些应用服务器加入了面向JSP的缓冲标记功能。例如,BEA的WebLogic Server从6.0版本开始支持这个功能,Open Symphony工程也同样支持这个功能。JSP缓冲标记既能够缓冲页面片断,也能够缓冲整个页面。当JSP页面执行时,如果目标片断已经在缓冲之中,则生成该片断的代码就不用再执行。页面级缓冲捕获对指定URL的请求,并缓冲整个结果页面。对于购物篮、目录以及门户网站的主页来说,这个功能极其有用。对于这类应用,页面级缓冲能够保存页面执行的结果,供后继请求使用。</p><p><strong>23、选择合适的引用机制</strong></p><p>在典型的JSP应用系统中,页头、页脚部分往往被抽取出来,然后根据需要引入页头、页脚。当前,在JSP页面中引入外部资源的方法主要有两种:include指令,以及include动作。<br>include指令:例如<%@ include file=”copyright.html” %>。该指令在编译时引入指定的资源。在编译之前,带有include指令的页面和指定的资源被合并成一个文件。被引用的外部资源在编译时就确定,比运行时才确定资源更高效。<br>include动作:例如<br><code><jsp:include page="copyright.jsp" /></code>。该动作引入指定页面执行后生成的结果。由于它在运行时完成,因此对输出结果的控制更加灵活。但时,只有当被引用的内容频繁地改变时,或者在对主页面的请求没有出现之前,被引用的页面无法确定时,使用include动作才合算。</p><p><strong>24、及时清除不再需要的会话</strong></p><p>为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多会话时,如果内存容量不足,操作系统会把部分内存数据转移到磁盘,应用服务器也可能根据“最近最频繁使用”(Most Recently Used)算法把部分不活跃的会话转储到磁盘,甚至可能抛出“内存不足”异常。在大规模系统中,串行化会话的代价是很昂贵的。当会话不再需要时,应当及时调用HttpSession.invalidate()方法清除会话。HttpSession.invalidate()方法通常可以在应用的退出页面调用。</p><p><strong>25、不要将数组声明为:public static final 。</strong></p><p><strong>26、HashMap的遍历效率讨论</strong></p><p>经常遇到对HashMap中的key和value值对的遍历操作,有如下两种方法:<br><figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Map</span><<span class="built_in">String</span>, <span class="built_in">String</span>[]> paraMap = <span class="keyword">new</span> HashMap<<span class="built_in">String</span>, <span class="built_in">String</span>[]>();</span><br><span class="line"><span class="comment">//第一个循环</span></span><br><span class="line"><span class="built_in">Set</span><<span class="built_in">String</span>> appFieldDefIds = paraMap.keySet();</span><br><span class="line"><span class="keyword">for</span> (<span class="built_in">String</span> appFieldDefId : appFieldDefIds) {</span><br><span class="line"> <span class="built_in">String</span>[] values = paraMap.<span class="keyword">get</span>(appFieldDefId);</span><br><span class="line"> ......</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//第二个循环</span></span><br><span class="line"><span class="keyword">for</span>(Entry<<span class="built_in">String</span>, <span class="built_in">String</span>[]> entry : paraMap.entrySet()){</span><br><span class="line"> <span class="built_in">String</span> appFieldDefId = entry.getKey();</span><br><span class="line"> <span class="built_in">String</span>[] values = entry.getValue();</span><br><span class="line"> .......</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>第一种实现明显的效率不如第二种实现。<br>分析如下 <code>Set<String> appFieldDefIds = paraMap.keySet();</code> 是先从HashMap中取得keySet<br>代码如下:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Set<K> <span class="title">keySet</span><span class="params">()</span> </span>{</span><br><span class="line"> Set<K> ks = keySet;</span><br><span class="line"> <span class="keyword">return</span> (ks != <span class="keyword">null</span> ? ks : (keySet = <span class="keyword">new</span> KeySet()));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">KeySet</span> <span class="keyword">extends</span> <span class="title">AbstractSet</span><<span class="title">K</span>> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> Iterator<K> <span class="title">iterator</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> newKeyIterator();</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> size;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">contains</span><span class="params">(Object o)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> containsKey(o);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> HashMap.<span class="keyword">this</span>.removeEntryForKey(o) != <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">clear</span><span class="params">()</span> </span>{</span><br><span class="line"> HashMap.<span class="keyword">this</span>.clear();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>其实就是返回一个私有类KeySet, 它是从AbstractSet继承而来,实现了Set接口。</p><p>再来看看for/in循环的语法<br><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="title">for</span><span class="params">(declaration : expression)</span></span></span><br><span class="line">statement</span><br></pre></td></tr></table></figure></p><p>在执行阶段被翻译成如下各式<br><figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">for(<span class="name">Iterator<E></span> #i = (<span class="name">expression</span>).iterator()<span class="comment">; #i.hashNext();){</span></span><br><span class="line"> declaration = #i.next()<span class="comment">;</span></span><br><span class="line"> statement</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>因此在第一个for语句<code>for (String appFieldDefId : appFieldDefIds)</code>中调用了<code>HashMap.keySet().iterator()</code></p><p>而这个方法调用了<code>newKeyIterator()</code><br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Iterator</span><<span class="type">K</span>> newKeyIterator() {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="type">KeyIterator</span>();</span><br><span class="line">}</span><br><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">KeyIterator</span> <span class="keyword">extends</span> <span class="title">HashIterator<K></span> </span>{</span><br><span class="line"> public <span class="type">K</span> next() {</span><br><span class="line"> <span class="keyword">return</span> nextEntry().getKey();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>所以在for中还是调用了<br>在第二个循环for(Entry<String, String[]> entry : paraMap.entrySet())中使用的Iterator是如下的一个内部类<br><figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="class"><span class="keyword">class</span> <span class="title">EntryIterator</span> <span class="keyword">extends</span> <span class="title">HashIterator<Map</span>.<span class="title">Entry<K</span>,<span class="title">V>></span> </span>{</span><br><span class="line"> public <span class="type">Map</span>.<span class="type">Entry</span><<span class="type">K</span>,<span class="type">V</span>> next() {</span><br><span class="line"> <span class="keyword">return</span> nextEntry();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>此时第一个循环得到key,第二个循环得到HashMap的Entry效率就是从循环里面体现出来的第二个循环此致可以直接取key和value值而第一个循环还是得再利用HashMap的get(Object key)来取value值现在看看HashMap的get(Object key)方法<br><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="built_in">get</span>(<span class="keyword">Object</span> <span class="built_in">key</span>) {</span><br><span class="line"> <span class="keyword">Object</span> k = maskNull(<span class="built_in">key</span>);</span><br><span class="line"> <span class="built_in">int</span> hash = hash(k);</span><br><span class="line"> <span class="built_in">int</span> i = indexFor(hash, table.length); <span class="comment">//Entry[] table</span></span><br><span class="line"> Entry<K,V> e = table;</span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">true</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (e.hash == hash && eq(k, e.<span class="built_in">key</span>))</span><br><span class="line"> <span class="keyword">return</span> e.value;</span><br><span class="line"> e = e.next;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>其实就是再次利用Hash值取出相应的Entry做比较得到结果,所以使用第一中循环相当于两次进入HashMap的Entry</p><p>中而第二个循环取得Entry的值之后直接取key和value,效率比第一个循环高。其实按照Map的概念来看也应该是用第二个循环好一点,它本来就是key和value的值对,将key和value分开操作在这里不是个好选择。</p><p><strong>27、array(数组) 和 ArryList的使用</strong></p><p>array([]):最高效;但是其容量固定且无法动态改变;<br>ArrayList:容量可动态增长;但牺牲效率;<br>基于效率和类型检验,应尽可能使用array,无法确定数组大小时才使用ArrayList!<br>ArrayList是Array的复杂版本<br>ArrayList内部封装了一个Object类型的数组,从一般的意义来说,它和数组没有本质的差别,甚至于ArrayList的许多方法,如Index、IndexOf、Contains、Sort等都是在内部数组的基础上直接调用Array的对应方法。<br>ArrayList存入对象时,抛弃类型信息,所有对象屏蔽为Object,编译时不检查类型,但是运行时会报错。<br>注:jdk5中加入了对泛型的支持,已经可以在使用ArrayList时进行类型检查。<br>从这一点上看来,ArrayList与数组的区别主要就是由于动态增容的效率问题了</p><p><strong>28、尽量使用HashMap 和ArrayList ,除非必要,否则不推荐使用HashTable和Vector ,后者由于使用同步机制,而导致了性能的开销。</strong></p><p><strong>29、StringBuffer 和StringBuilder的区别:</strong></p><p>java.lang.StringBuffer线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。</p><p>StringBuilder。与该类相比,通常应该优先使用 java.lang.StringBuilder类,因为它支持所有相同的操作,但由于它不执行同步,所以速度更快。为了获得更好的性能,在构造 StirngBuffer 或 StirngBuilder 时应尽可能指定它的容量。当然,如果你操作的字符串长度不超过 16 个字符就不用了。 相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%-15% 左右的性能提升,但却要冒多线程不安全的风险。而在现实的模块化编程中,负责某一模块的程序员不一定能清晰地判断该模块是否会放入多线程的环境中运行,因此:除非你能确定你的系统的瓶颈是在 StringBuffer 上,并且确定你的模块不会运行在多线程模式下,否则还是用 StringBuffer 吧。</p><p><strong>30、尽量避免使用split</strong></p><p>除非是必须的,否则应该避免使用split,split由于支持正则表达式,所以效率比较低,如果是频繁的几十,几百万的调用将会耗费大量资源,如果确实需要频繁的调用split,可以考虑使用apache的 StringUtils.split(string,char),频繁split的可以缓存结果。</p><p>其他补充:</p><p>1、及时清除不再使用的对象,设为null<br>2、尽可能使用final,static等关键字<br>3、尽可能使用buffered对象</p><p>如何优化代码使JAVA源文件及编译后CLASS文件更小</p><p>1 尽量使用继承,继承的方法越多,你要写的代码量也就越少</p><p>2 打开JAVA编译器的优化选项: javac -O 这个选项将删除掉CLASS文件中的行号,并能把<br>一些private, static,final的小段方法申明为inline方法调用</p><p>3 把公用的代码提取出来</p><p>4 不要初始化很大的数组,尽管初始化一个数组在JAVA代码中只是一行的代码量,但<br>编译后的代码是一行代码插入一个数组的元素,所以如果你有大量的数据需要存在数组<br>中的话,可以先把这些数据放在String中,然后在运行期把字符串解析到数组中</p><p>5 日期类型的对象会占用很大的空间,如果你要存储大量的日期对象,可以考虑把它存储为<br>long型,然后在使用的时候转换为Date类型</p><p>6 类名,方法名和变量名尽量使用简短的名字,可以考虑使用Hashjava, Jobe, Obfuscate and Jshrink等工具自动完成这个工作</p><p>7 将static final类型的变量定义到Interface中去</p><p>8 算术运算 能用左移/右移的运算就不要用*和/运算,相同的运算不要运算多次</p><p>2、不要两次初始化变量<br>Java通过调用独特的类构造器默认地初始化变量为一个已知的值。所有的对象被设置成null,integers (byte, short, int, long)被设置成0,float和double设置成0.0,Boolean变量设置成false。这对那些扩展自其它类的类尤其重要,这跟使用一个新的关键词创建一个对象时所有一连串的构造器被自动调用一样。</p><p>3、在任何可能的地方让类为Final<br>标记为final的类不能被扩展。在《核心Java API》中有大量这个技术的例子,诸如java.lang.String。将String类标记为final阻止了开发者创建他们自己实现的长度方法。<br>更深入点说,如果类是final的,所有类的方法也是final的。Java编译器可能会内联所有的方法(这依赖于编译器的实现)。在我的测试里,我已经看到性能平均增加了50%。</p><p>3、异常在需要抛出的地方抛出,try catch能整合就整合<br><figure class="highlight actionscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> some.method1(); <span class="comment">// Difficult for javac</span></span><br><span class="line">} <span class="keyword">catch</span>( method1Exception e ) { <span class="comment">// and the JVM runtime</span></span><br><span class="line"> <span class="comment">// Handle exception 1 // to optimize this</span></span><br><span class="line">} <span class="comment">// code</span></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> some.method2();</span><br><span class="line">} <span class="keyword">catch</span>( method2Exception e ) {</span><br><span class="line"> <span class="comment">// Handle exception 2</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> some.method3();</span><br><span class="line">} <span class="keyword">catch</span>( method3Exception e ) {</span><br><span class="line"> <span class="comment">// Handle exception 3</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>已下代码 更容易被编译器优化<br><figure class="highlight gcode"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">try {</span><br><span class="line"> some.method<span class="number">1</span><span class="comment">()</span>; <span class="comment">// Easier to optimize</span></span><br><span class="line"> some.method<span class="number">2</span><span class="comment">()</span>;</span><br><span class="line"> some.method<span class="number">3</span><span class="comment">()</span>;</span><br><span class="line">} catch<span class="comment">( method1Exception e )</span> {</span><br><span class="line"> <span class="comment">// Handle exception 1</span></span><br><span class="line">} catch<span class="comment">( method2Exception e )</span> {</span><br><span class="line"> <span class="comment">// Handle exception 2</span></span><br><span class="line">} catch<span class="comment">( method3Exception e )</span> {</span><br><span class="line"> <span class="comment">// Handle exception 3</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>4、For循环的优化</p><figure class="highlight matlab"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">Replace…</span><br><span class="line"><span class="keyword">for</span>( int <span class="built_in">i</span> = <span class="number">0</span>; <span class="built_in">i</span> < collection.<span class="built_in">size</span>(); <span class="built_in">i</span>++ ) {</span><br><span class="line">...</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">with…</span><br><span class="line"><span class="keyword">for</span>( int <span class="built_in">i</span> = <span class="number">0</span>, n = collection.<span class="built_in">size</span>(); <span class="built_in">i</span> < n; <span class="built_in">i</span>++ ) {</span><br><span class="line">...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>5、 在JAVA + ORACLE 的应用系统开发中,java中内嵌的SQL语句尽量使用大写的形式,以减轻ORACLE解析器的解析负担。</p><p>6、尽量采用lazy loading 的策略,即在需要的时候才开始创建。<br><figure class="highlight processing"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">例如: <span class="keyword">String</span> <span class="built_in">str</span> = “aaa”;</span><br><span class="line"><span class="keyword">if</span>(i == <span class="number">1</span>) {</span><br><span class="line"> list.<span class="built_in">add</span>(<span class="built_in">str</span>);</span><br><span class="line">}</span><br><span class="line">应替换为:</span><br><span class="line"><span class="keyword">if</span>(i == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">String</span> <span class="built_in">str</span> = “aaa”;</span><br><span class="line"> list.<span class="built_in">add</span>(<span class="built_in">str</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>7、不要在循环中使用:<br><figure class="highlight cos"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">Try</span> {</span><br><span class="line"></span><br><span class="line">} <span class="keyword">catch</span>() {</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>应把其放置在最外层</p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<blockquote>
<p>帮助开发人员夯实基础,从规范的编码操作开始,培养良好的编码习惯,助力未来技术成长。</p>
</blockquote>
<p>通过java代码规范来优化程序,优化内存使用情况,防止内存泄露</p>
<p>可供程序利用的资源(内存、CPU时间、网络带
</summary>
<category term="Java" scheme="https://dddreams.github.io/tags/Java/"/>
</entry>
<entry>
<title>SpringBoot-Thymeleaf模版引擎</title>
<link href="https://dddreams.github.io/180718-Thymeleaf%E6%A8%A1%E7%89%88%E5%BC%95%E6%93%8E.html"/>
<id>https://dddreams.github.io/180718-Thymeleaf模版引擎.html</id>
<published>2019-01-04T10:07:00.000Z</published>
<updated>2020-01-03T05:09:15.938Z</updated>
<content type="html"><![CDATA[<p>上一节我们简单的认识了 Thymeleaf,并简单介绍了它与 Spring Boot 的结合,这一节来具体的看一下,Thymeleaf 是的语法和应用。</p><h2 id="创建模版文件"><a href="#创建模版文件" class="headerlink" title="创建模版文件"></a>创建模版文件</h2><p>创建文件其实在上节中已经说过,这边在重复一遍,新建 <code>HTML</code> 文件,在头文件中添加 <code>xmlns:th="http://www.thymeleaf.org"</code><br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">html</span>></span><span class="tag"><<span class="name">html</span> <span class="attr">xmlns:th</span>=<span class="string">"http://www.thymeleaf.org"</span>></span><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure></p><p><strong>注意:</strong>html 中的标签必须严格规范,标签必须闭合,即<code><div /></code>技术或者<code></div></code>类似结束。</p><h2 id="表达式语法"><a href="#表达式语法" class="headerlink" title="表达式语法"></a>表达式语法</h2><p>它们分为四类:</p><ul><li>1.变量表达式</li><li>2.选择或星号表达式</li><li>3.通用 Message 表达式</li><li>4.URL表达式</li></ul><h3 id="变量表达式"><a href="#变量表达式" class="headerlink" title="变量表达式"></a>变量表达式</h3><p>ongl标准语法或者 Spring EL 表达式 <code>${user.userName}</code>,它们将以 html 标签的一个属性来表示</p><h3 id="选择或星号表达式"><a href="#选择或星号表达式" class="headerlink" title="选择或星号表达式"></a>选择或星号表达式</h3><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag"><<span class="name">div</span> <span class="attr">th:object</span>=<span class="string">"$</span></span></span><span class="template-variable">{user}</span><span class="xml"><span class="tag"><span class="string">"</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Name: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*</span></span></span><span class="template-variable">{firstName}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Sebastian<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Surname: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*</span></span></span><span class="template-variable">{lastName}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Pepper<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Nationality: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">*</span></span></span><span class="template-variable">{nationality}</span><span class="xml"><span class="tag">"></span>Saturn<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">div</span>></span> </span></span><br><span class="line"><span class="xml">等价于</span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Name: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$</span></span></span><span class="template-variable">{user.firstName}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Sebastian<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Surname: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$</span></span></span><span class="template-variable">{user.lastName}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Pepper<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Nationality: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"$</span></span></span><span class="template-variable">{user.nationality}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Saturn<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">div</span>></span></span></span><br><span class="line"><span class="xml">当然了,这两者可以混合使用</span></span><br><span class="line"><span class="xml">还有一种方式</span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">div</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Name: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*</span></span></span><span class="template-variable">{user.name}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Sebastian<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Surname: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*</span></span></span><span class="template-variable">{user.surname}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Pepper<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">p</span>></span>Nationality: <span class="tag"><<span class="name">span</span> <span class="attr">th:text</span>=<span class="string">"*</span></span></span><span class="template-variable">{user.nationality}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>Saturn<span class="tag"></<span class="name">span</span>></span>.<span class="tag"></<span class="name">p</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">div</span>></span></span></span><br></pre></td></tr></table></figure><h3 id="通用-Message-表达式"><a href="#通用-Message-表达式" class="headerlink" title="通用 Message 表达式"></a>通用 Message 表达式</h3><p>通用表达式允许我们从外部的配置文件(properties)中取值,用 Key,Value的形式。<br><figure class="highlight vala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#{main.title} </span></span><br><span class="line"><span class="meta">#{message.entrycreated(${entryId})}</span></span><br></pre></td></tr></table></figure></p><p>可以在模板文件中找到这样的表达式代码:<br><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag"><<span class="name">table</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">th</span> <span class="attr">th:text</span>=<span class="string">"#</span></span></span><span class="template-variable">{header.address.city}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>...<span class="tag"></<span class="name">th</span>></span> </span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">th</span> <span class="attr">th:text</span>=<span class="string">"#</span></span></span><span class="template-variable">{header.address.country}</span><span class="xml"><span class="tag"><span class="string">"</span>></span>...<span class="tag"></<span class="name">th</span>></span> </span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">table</span>></span></span></span><br></pre></td></tr></table></figure></p><h3 id="URL表达式"><a href="#URL表达式" class="headerlink" title="URL表达式"></a>URL表达式</h3><p>URL表达式指的是把一个有用的上下文或回话信息添加到URL,这个过程经常被叫做URL重写。<br> <code>@{/order/list}</code><br>URL还可以设置参数:<br> <code>@{/order/details(id=${orderId})}</code><br>相对路径:<br> <code>@{../documents/report}</code></p><h2 id="表达式支持的语法"><a href="#表达式支持的语法" class="headerlink" title="表达式支持的语法"></a>表达式支持的语法</h2><p><strong>字面(Literals)</strong></p><ul><li>文本文字(Text literals): ‘one text’, ‘Another one!’,…</li><li>数字文本(Number literals): 0, 34, 3.0, 12.3,…</li><li>布尔文本(Boolean literals): true, false</li><li>空(Null literal): null</li><li>文字标记(Literal tokens): one, sometext, main,…</li></ul><p><strong>文本操作(Text operations)</strong></p><ul><li>字符串连接(String concatenation): +</li><li>文本替换(Literal substitutions): |The name is ${name}|</li></ul><p><strong>算术运算(Arithmetic operations)</strong></p><ul><li>二元运算符(Binary operators): +, -, *, /, %</li><li>减号(单目运算符)Minus sign (unary operator): -</li></ul><p><strong>布尔操作(Boolean operations)</strong></p><ul><li>二元运算符(Binary operators):and, or</li><li>布尔否定(一元运算符)Boolean negation (unary operator):!, not</li></ul><p><strong>比较和等价(Comparisons and equality)</strong></p><ul><li>比较(Comparators): >, <, >=, <= (gt, lt, ge, le)</li><li>等值运算符(Equality operators):==, != (eq, ne)</li></ul><p><strong>条件运算符(Conditional operators)</strong></p><ul><li>If-then: (if) ? (then)</li><li>If-then-else: (if) ? (then) : (else)</li><li>Default: (value) ?: (defaultvalue)</li></ul><h2 id="th-标签"><a href="#th-标签" class="headerlink" title="th 标签"></a>th 标签</h2><p>参考 thymeleaf 官方文档:<a href="https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf" target="_blank" rel="noopener">https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.pdf</a></p><h2 id="常用的使用方法"><a href="#常用的使用方法" class="headerlink" title="常用的使用方法"></a>常用的使用方法</h2><h2 id="相关参考和链接"><a href="#相关参考和链接" class="headerlink" title="相关参考和链接"></a>相关参考和链接</h2><p>Thymeleaf 模板的使用:<a href="http://www.cnblogs.com/lazio10000/p/5603955.html" target="_blank" rel="noopener">http://www.cnblogs.com/lazio10000/p/5603955.html</a></p><p>新一代Java模板引擎Thymeleaf:<a href="https://www.tianmaying.com/tutorial/using-thymeleaf" target="_blank" rel="noopener">https://www.tianmaying.com/tutorial/using-thymeleaf</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>上一节我们简单的认识了 Thymeleaf,并简单介绍了它与 Spring Boot 的结合,这一节来具体的看一下,Thymeleaf 是的语法和应用。</p>
<h2 id="创建模版文件"><a href="#创建模版文件" class="headerlink" tit
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>SpringBoot-单元测试及Thymeleaf模版</title>
<link href="https://dddreams.github.io/180716-SpringBoot-%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95%E5%8F%8AThymeleaf%E6%A8%A1%E7%89%88.html"/>
<id>https://dddreams.github.io/180716-SpringBoot-单元测试及Thymeleaf模版.html</id>
<published>2018-08-13T03:20:51.000Z</published>
<updated>2021-01-26T03:16:00.702Z</updated>
<content type="html"><![CDATA[<p>上一节中我们已经做了一个简单的 Hello world!应用,这一节我们来学习 Spring Boot 的 web 开发。以前做过 web 的童鞋都知道,一个 web 应用最常用的就是 MVC 的模式 jsp(视图层),Servlet(控制层),Dao(数据持久层),另外还会用到 单元测试,Json,Filte,Property,Log,数据库操作,热部署等一些相关的技术。这节我们就来看看在 Spring Boot 中是如何使用这些技术的。</p><h2 id="单元测试"><a href="#单元测试" class="headerlink" title="单元测试"></a>单元测试</h2><p>我们来为上节中的 hello world!应用进行单元测试。Spring Boot 的单元测试推荐使用 <code>mockmvc</code> 来进行,好处是不用启动服务即可进行测试。首先添加 test 的 maven 依赖, test 的依赖默认是存在的。</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-test<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>test<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure><p>在 <code>src/test/</code> 下面新建包 controller ,然后新建类 HelloTest.java。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@RunWith</span>(SpringRunner<span class="class">.<span class="keyword">class</span>)</span></span><br><span class="line"><span class="class">@<span class="title">SpringBootTest</span></span></span><br><span class="line"><span class="class"><span class="title">public</span> <span class="title">class</span> <span class="title">HelloTest</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> MockMvc mvc;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Before</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setUp</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> mvc = MockMvcBuilders.standaloneSetup(<span class="keyword">new</span> HelloController()).build();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">getHello</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> mvc.perform(MockMvcRequestBuilders.get(<span class="string">"/"</span>).accept(MediaType.APPLICATION_JSON))</span><br><span class="line"> .andExpect(status().isOk())</span><br><span class="line"> .andExpect(content().string(equalTo(<span class="string">"hello world!"</span>)));</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>运行测试方法。</p><h2 id="Jsp-Or-Thymeleaf"><a href="#Jsp-Or-Thymeleaf" class="headerlink" title="Jsp Or Thymeleaf"></a>Jsp Or Thymeleaf</h2><p>Spring Boot 推荐使用 Thymeleaf 来代替 Jsp,那么我们先来认识一下 Thymeleaf 是个什么东东</p><h3 id="Thymeleaf-是个什么?"><a href="#Thymeleaf-是个什么?" class="headerlink" title="Thymeleaf 是个什么?"></a>Thymeleaf 是个什么?</h3><p>Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:</p><ul><li>Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。</li><li>Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。</li><li>Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。</li></ul><h3 id="如何应用"><a href="#如何应用" class="headerlink" title="如何应用"></a>如何应用</h3><p> 同样的首先添加 Thymeleaf 的 maven 组件依赖,这就是 Spring Boot 的优点,把所有优秀的组件都集成进来,使用时仅仅添加依赖就可以了。<br> <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-thymeleaf<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></p><p> <code>spring-boot-starter-thymeleaf</code> 组件中已经包含了 web 的组件,也可以将 <code>spring-boot-starter-web</code> 依赖删除。</p><p> 然后在 <code>resources/templates/</code> 下新建 <code>hello.html</code> <strong>注意</strong>添加头文件 <code>xmlns:th="http://www.thymeleaf.org"</code><br> <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="meta-keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span> <span class="attr">xmlns:th</span>=<span class="string">"http://www.thymeleaf.org"</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span> ... <span class="tag"></<span class="name">head</span>></span></span><br></pre></td></tr></table></figure></p><p>修改 <code>HelloController</code> 与 <code>hello.html</code></p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloController</span> </span>{</span><br><span class="line"> <span class="meta">@RequestMapping(<span class="meta-string">"/hello"</span>)</span></span><br><span class="line"> <span class="keyword">public</span> String hello(Model model,</span><br><span class="line"> <span class="meta">@RequestParam(value=<span class="meta-string">"name"</span>, required=false, defaultValue=<span class="meta-string">"World"</span>)</span> String name){</span><br><span class="line"> model.addAttribute(<span class="string">"name"</span>, name);</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"hello"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight dust"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="meta"><!DOCTYPE <span class="meta-keyword">html</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">html</span> <span class="attr">lang</span>=<span class="string">"en"</span> <span class="attr">xmlns:th</span>=<span class="string">"http://www.thymeleaf.org"</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">head</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">meta</span> <span class="attr">charset</span>=<span class="string">"UTF-8"</span>></span></span></span><br><span class="line"><span class="xml"> <span class="tag"><<span class="name">title</span>></span>Spring Boot<span class="tag"></<span class="name">title</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">head</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">body</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"><<span class="name">p</span> <span class="attr">th:text</span>=<span class="string">"'Hello, ' + $</span></span></span><span class="template-variable">{name}</span><span class="xml"><span class="tag"><span class="string"> + '!'"</span> /></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">body</span>></span></span></span><br><span class="line"><span class="xml"><span class="tag"></<span class="name">html</span>></span></span></span><br></pre></td></tr></table></figure><p>启动服务,访问 <code>http://localhost:8080/hello?name=Shure</code><br><img src="/images/20180716/20180716184202.png" alt="hello shure"></p><h2 id="参考与相关链接"><a href="#参考与相关链接" class="headerlink" title="参考与相关链接"></a>参考与相关链接</h2><p>纯洁的微笑博客:<a href="http://www.ityouknow.com/springboot/2016/01/06/spring-boot-quick-start.html" target="_blank" rel="noopener">http://www.ityouknow.com/springboot/2016/01/06/spring-boot-quick-start.html</a></p><p>thymeleaf参考手册 by CSDN:<a href="https://blog.csdn.net/zrk1000/article/details/72667478" target="_blank" rel="noopener">https://blog.csdn.net/zrk1000/article/details/72667478</a></p><p>示例代码:<a href="https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-helloWorld" target="_blank" rel="noopener">https://github.com/dddreams/learn-spring-boot/tree/master/spring-boot-helloWorld</a></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>上一节中我们已经做了一个简单的 Hello world!应用,这一节我们来学习 Spring Boot 的 web 开发。以前做过 web 的童鞋都知道,一个 web 应用最常用的就是 MVC 的模式 jsp(视图层),Servlet(控制层),Dao(数据持久层),另外还
</summary>
<category term="Spring Boot" scheme="https://dddreams.github.io/categories/Spring-Boot/"/>
<category term="Spring Boot" scheme="https://dddreams.github.io/tags/Spring-Boot/"/>
</entry>
<entry>
<title>给“抽奖助手”的一点点建议</title>
<link href="https://dddreams.github.io/180717-%E7%BB%99%E6%8A%BD%E5%A5%96%E5%8A%A9%E6%89%8B%E7%9A%84%E4%B8%80%E7%82%B9%E7%82%B9%E5%BB%BA%E8%AE%AE.html"/>
<id>https://dddreams.github.io/180717-给抽奖助手的一点点建议.html</id>
<published>2018-07-17T10:02:32.000Z</published>
<updated>2018-07-17T10:14:34.907Z</updated>
<content type="html"><![CDATA[<p>“抽奖助手”是无码科技做的一款小程序,如果你不知道无码科技,冯大辉你一定听说过吧,“抽奖助手”就是他家的产品,当然如果你听说过冯大辉那么你一定知道他的言论是有多么的犀利,尤其是对互联网产品,吐槽起来一针见血见解独到,好多公司都不敢让他来测评自家的产品,怕被爆出很多问题来,今天我也要给他家的产品“抽奖助手”提一点建议。</p><p>之前在冯大的公众号上看过一篇关于推荐系统和用户画像的文章,标题是什么我忘了,我觉得那篇文章写的很好,也是收益与那篇文章的启发,提出这点建议。</p><p>首先要感谢“抽奖助手”昨天我中奖了,很激动,我参与抽奖460多次,这是第一次中奖,然而我仔细一看中奖的内容却是“婴儿屁屁湿巾”,我激动的心情一下就没了,我一个还没结婚的人,让我中这么个奖,你说让我领呢还是不领呢?</p><p>所以我的建议是,通过用户画像分析出参与抽奖用户的基本情况,筛选出一些真正有需要该奖品的用户,让其中奖,这样让中奖者不再有心理上的落差。比如说,我是一个程序员,肯定对电子产品有浓厚的兴趣,如果奖品是个机械键盘什么的,可以优先让像我这样有兴趣的一类用户中奖,而不是在家带孩子的宝妈;再比如,奖品是婴儿用品,通过对参与抽奖的用户的分析,优先让拥有宝妈、已婚、刚结婚等属性的用户中奖,而不是像我这样还没结婚的用户,这样是不是更接近完美了呢。</p><p>可能有人会反驳了,你看到婴儿用品可以不参与啊,为什么你不想要,还要参与抽奖呢?其实,在“抽奖助手”里有很多人是不看奖品是什么的,直接划到最后点击“参与抽奖”,要知道一个奖项参与的用户好几十万,如果设定只有一人中奖,那么中奖的概率几十万分之一,很多人已经不报有中奖的希望,只是重在参与而已。所以这种反驳个人认为已经可以排除在外了。</p><p>也许这就是作为一个程序员对产品的一点点思考吧,下面推荐一个抽奖,扫码参与,或许会中奖哦!</p><p><img src="/images/20180717/20180717154849.jpg" alt="抽奖啦"></p><p><div style="text-align:center;margin:0;" markdown="1"><img src="../img/ddAnswer.jpg" alt="ddAnswer" style="margin:0 auto;"></div></p><p style="text-align: center;margin:0;">更多文章请关注微信公众号: zhiheng博客</p><p style="text-align: center;margin:20 0;">如果文章对你有用,转发分享、点赞赞赏才是真爱 [斜眼笑]</p>]]></content>
<summary type="html">
<p>“抽奖助手”是无码科技做的一款小程序,如果你不知道无码科技,冯大辉你一定听说过吧,“抽奖助手”就是他家的产品,当然如果你听说过冯大辉那么你一定知道他的言论是有多么的犀利,尤其是对互联网产品,吐槽起来一针见血见解独到,好多公司都不敢让他来测评自家的产品,怕被爆出很多问题来,今
</summary>
<category term="杂谈" scheme="https://dddreams.github.io/tags/%E6%9D%82%E8%B0%88/"/>
</entry>
</feed>