-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathatom.xml
More file actions
449 lines (238 loc) · 396 KB
/
atom.xml
File metadata and controls
449 lines (238 loc) · 396 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>七月流火 - 四零九六</title>
<subtitle>imrui</subtitle>
<link href="http://www.imrui.net/atom.xml" rel="self"/>
<link href="http://www.imrui.net/"/>
<updated>2021-11-25T15:16:24.018Z</updated>
<id>http://www.imrui.net/</id>
<author>
<name>Rui</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Windows Terminal 美化与包管理工具</title>
<link href="http://www.imrui.net/2021/11/24/windows-terminal/"/>
<id>http://www.imrui.net/2021/11/24/windows-terminal/</id>
<published>2021-11-24T08:46:18.000Z</published>
<updated>2021-11-25T15:16:24.018Z</updated>
<content type="html"><![CDATA[<h2 id="Windows-Terminal-安装"><a href="#Windows-Terminal-安装" class="headerlink" title="Windows Terminal 安装"></a>Windows Terminal 安装</h2><p><code>请参阅</code> <a href="https://docs.microsoft.com/zh-cn/windows/terminal/get-started">安装和设置 Windows 终端</a></p><figure class="highlight plain"><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><br><span class="line"> "actions": [</span><br><span class="line"> // 自定义快捷键</span><br><span class="line"> { "command": "closeWindow", "keys": "alt+F4" },</span><br><span class="line"> { "command": "nextTab", "keys": "alt+shift+]" },</span><br><span class="line"> { "command": "prevTab", "keys": "alt+shift+[" },</span><br><span class="line"> { "command": "nextTab", "keys": "ctrl+alt+right" },</span><br><span class="line"> { "command": "prevTab", "keys": "ctrl+alt+left" },</span><br><span class="line"> { "command": "closeTab", "keys": "alt+w" },</span><br><span class="line"> { "command": "closeTab", "keys": "ctrl+w" },</span><br><span class="line"> { "command": "newTab", "keys": "alt+t" },</span><br><span class="line"> { "command": "newTab", "keys": "ctrl+t" },</span><br><span class="line"> { "command": "newTab", "keys": "ctrl+shift+t" },</span><br><span class="line"> { "command": "duplicateTab", "keys": "ctrl+shift+d" },</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Windows-Terminal-美化"><a href="#Windows-Terminal-美化" class="headerlink" title="Windows Terminal 美化"></a>Windows Terminal 美化</h2><p><code>请参阅</code> <a href="https://docs.microsoft.com/zh-cn/windows/terminal/tutorials/powerline-setup">教程:在 Windows 终端中设置 Powerline</a></p><h3 id="Nerd-Fonts"><a href="#Nerd-Fonts" class="headerlink" title="Nerd Fonts"></a>Nerd Fonts</h3><p>Oh My Posh was designed to use <a href="https://www.nerdfonts.com/">Nerd Fonts</a>. Nerd Fonts are popular fonts that are patched to include icons. We recommend <a href="https://github.com/ryanoasis/nerd-fonts/releases/download/v2.1.0/Meslo.zip">Meslo LGM NF</a>, but any Nerd Font should be compatible with the standard <a href="https://github.com/JanDeDobbeleer/oh-my-posh/tree/main/themes">themes</a>.</p><h3 id="安装-Powerline-字体"><a href="#安装-Powerline-字体" class="headerlink" title="安装 Powerline 字体"></a>安装 Powerline 字体</h3><p>可以使用scoop安装,也可以从 <a href="https://github.com/microsoft/cascadia-code/releases">Cascadia Code GitHub发布页</a> 安装这些字体。</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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"><span class="meta">#</span><span class="bash"> 搜索字体相关信息</span></span><br><span class="line">scoop search CascadiaCode</span><br><span class="line"><span class="meta">#</span><span class="bash"> 搜索结果如下</span></span><br><span class="line">'nerd-fonts' bucket:</span><br><span class="line"> CascadiaCode-NF-Mono (2.1.0)</span><br><span class="line"> CascadiaCode-NF (2.1.0)</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 添加字体库的bucket</span></span><br><span class="line">scoop bucket add nerd-fonts</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 安装字体,需要申请管理员权限,若无sudo请先安装 scoop install sudo</span></span><br><span class="line">sudo scoop install CascadiaCode-NF</span><br></pre></td></tr></table></figure><p><strong>修改字体配置</strong></p><p>Windows PowerShell 配置文件 settings.json 文件现在应如下所示:</p><figure class="highlight plain"><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><br><span class="line">// If enabled, selections are automatically copied to your clipboard.</span><br><span class="line">"copyOnSelect": true,</span><br><span class="line">"profiles":</span><br><span class="line"> {</span><br><span class="line"> "defaults":</span><br><span class="line"> {</span><br><span class="line"> // Put settings here that you want to apply to all profiles.</span><br><span class="line"> "fontFace": "Cascadia Code PL"</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><h3 id="设置-Powerline"><a href="#设置-Powerline" class="headerlink" title="设置 Powerline"></a>设置 Powerline</h3><p>如果尚未安装,请 <a href="https://git-scm.com/downloads">安装适用于 Windows 的 Git</a> 。</p><p>使用 PowerShell,安装 Posh-Git 和 Oh-My-Posh:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">Install-Module posh-git -Scope CurrentUser</span><br><span class="line">Install-Module oh-my-posh -Scope CurrentUser</span><br><span class="line"><span class="meta">#</span><span class="bash"> 安装 Get-ChildItemColor 为 PowerShell 的输出添加颜色(比如为 ls 的输出上色)</span></span><br><span class="line">Install-Module -AllowClobber Get-ChildItemColor -Scope CurrentUser</span><br></pre></td></tr></table></figure><p><strong>自定义 PowerShell 提示符</strong></p><p>使用 notepad $PROFILE 或所选的文本编辑器打开 PowerShell 配置文件。 这不是你的 Windows 终端配置文件。 你的 PowerShell 配置文件是一个脚本,该脚本在每次启动 PowerShell 时运行。 <a href="https://docs.microsoft.com/zh-cn/powershell/module/microsoft.powershell.core/about/about_profiles?view=powershell-7">详细了解 PowerShell 配置文件</a> 。<br>在 PowerShell 配置文件中,将以下内容添加到文件的末尾:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">Import-Module posh-git</span><br><span class="line">Import-Module oh-my-posh</span><br><span class="line">Import-Module Get-ChildItemColor</span><br><span class="line">Set-Alias ll Get-ChildItem # 设置ll别名</span><br><span class="line">Set-Theme Robbyrussell</span><br></pre></td></tr></table></figure><p>现在,每个新实例启动时都会导入 Posh-Git 和 Oh-My-Posh,然后从 Oh-My-Posh 设置 Paradox 主题。 Oh-My-Posh 附带了若干<a href="https://github.com/JanDeDobbeleer/oh-my-posh#themes">内置主题</a> 。</p><h2 id="Windows-包管理工具安装"><a href="#Windows-包管理工具安装" class="headerlink" title="Windows 包管理工具安装"></a>Windows 包管理工具安装</h2><h3 id="winget"><a href="#winget" class="headerlink" title="winget"></a>winget</h3><p><strong>可使用多种方法安装 winget 工具:</strong></p><ul><li><a href="https://www.microsoft.com/p/app-installer/9nblggh4nns1?ocid=9nblggh4nns1_ORSEARCH_Bing&rtc=1&activetab=pivot:overviewtab">Windows 应用安装程序</a> 的外部测试版或预览版中包含 winget 工具。 必须安装 应用安装程序 的预览版本才能使用 winget 。 若要获取提前访问权限,请将你的请求提交到 <a href="https://aka.ms/AppInstaller_InsiderProgram">Windows 程序包管理器预览体验计划</a> 。 参与外部测试版 Ring 将保证你可以看到最新的预览版更新。</li><li>参与 <a href="https://insider.windows.com/">Windows 外部测试版 Ring</a> 。</li><li>安装位于 <a href="https://github.com/microsoft/winget-cli">winget 存储库</a> 的 release 文件夹中的 Windows 桌面应用安装程序包。</li></ul><p><code>请参阅</code> <a href="https://docs.microsoft.com/zh-cn/windows/package-manager/winget/">使用 winget 工具安装和管理应用程序</a><br><em>winget 工具需要 Windows 10 版本 1709 (10.0.16299) 或更高版本的 Windows 10。</em></p><p>Usage Example:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">winget -v</span><br><span class="line"><span class="meta">#</span><span class="bash"> or</span></span><br><span class="line">winget install postman --rainbow</span><br></pre></td></tr></table></figure><h3 id="scoop"><a href="#scoop" class="headerlink" title="scoop"></a>scoop</h3><p><code>请参阅</code> <a href="https://scoop.sh/">scoop.sh</a></p><p>Scoop installs the tools you know and love</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scoop install curl</span><br></pre></td></tr></table></figure><p>Make sure PowerShell 5 (or later, include PowerShell Core) and .NET Framework 4.5 (or later) are installed. Then run:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">Invoke-Expression (New-Object System.Net.WebClient).DownloadString('https://get.scoop.sh')</span><br><span class="line"><span class="meta">#</span><span class="bash"> or shorter</span></span><br><span class="line">iwr -useb get.scoop.sh | iex</span><br></pre></td></tr></table></figure><p>Note: if you get an error you might need to change the execution policy (i.e. enable Powershell) with</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ExecutionPolicy RemoteSigned -scope CurrentUser</span><br></pre></td></tr></table></figure><p><strong>推荐安装软件</strong></p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">scoop install git # scoop依赖git</span><br><span class="line">scoop install 7zip # scoop依赖7zip解压</span><br><span class="line">scoop install aria2 # 可以考虑开启aria2下载</span><br><span class="line">scoop install sudo # 申请管理员权限,和linux下sudo命令相似,全局安装必备</span><br></pre></td></tr></table></figure><p><strong>List</strong></p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">scoop list # 列出已安装软件</span><br><span class="line">scoop bucket list # 列出bucket()</span><br></pre></td></tr></table></figure><p><strong>Bucket</strong></p><p>每个bucket中维护了许多json文件,描述了软件的相关信息,安装方式,更新方式,可以理解为软件源(虽然并不相同),默认只有一个main,几乎全是命令行工具,可以添加其他的:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">scoop bucket known # 列出已知的bucket</span><br><span class="line">scoop bucket list # 列出已添加的bucket</span><br><span class="line">scoop bucket add extras # 添加bucket</span><br><span class="line">scoop bucket rm games # 移除bucket</span><br></pre></td></tr></table></figure><p><strong>推荐的bucket</strong></p><ul><li>main</li><li>extras</li></ul><p><em>因不符合main收录标准,但常用的一些库</em></p><ul><li>versions</li></ul><p><em>安装特定版本</em></p><ul><li>nerd-fonts</li></ul><h3 id="chocolatey"><a href="#chocolatey" class="headerlink" title="chocolatey"></a>chocolatey</h3><p><code>请参阅</code> <a href="https://chocolatey.org/install#individual">Chocolatey.org</a></p><p>PowerShell 安装 Chocolatey 非常简单,管理员运行,然后输入如下命令</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))</span><br></pre></td></tr></table></figure><p>Usage Example:</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">choco</span><br><span class="line"><span class="meta">#</span><span class="bash"> or</span></span><br><span class="line">choco -?</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">Windows Terminal 美化配置,Windows 包管理工具 winget / scoop / chocolatey 的安装使用。</summary>
<category term="System" scheme="http://www.imrui.net/tags/System/"/>
<category term="开发工具" scheme="http://www.imrui.net/tags/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
</entry>
<entry>
<title>基于 OpenResty + GitHub WebHook 的代码自动更新</title>
<link href="http://www.imrui.net/2020/12/02/nginx-lua-webhook/"/>
<id>http://www.imrui.net/2020/12/02/nginx-lua-webhook/</id>
<published>2020-12-02T09:09:34.000Z</published>
<updated>2021-11-25T16:03:25.202Z</updated>
<content type="html"><![CDATA[<h2 id="OpenResty"><a href="#OpenResty" class="headerlink" title="OpenResty"></a>OpenResty</h2><p>OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。</p><p>安装及使用请参阅<a href="https://openresty.org/">OpenResty官网</a></p><h2 id="Nginx配置"><a href="#Nginx配置" class="headerlink" title="Nginx配置"></a>Nginx配置</h2><figure class="highlight nginx"><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="section">server</span> {</span><br><span class="line"> <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"> <span class="attribute">server_name</span> yourdomain;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> /webhook {</span><br><span class="line"> <span class="attribute">default_type</span> <span class="string">'text/plain'</span>;</span><br><span class="line"> <span class="attribute">content_by_lua_file</span> /your/lua/path/webhook.lua;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="webhook-lua-源码"><a href="#webhook-lua-源码" class="headerlink" title="webhook.lua 源码"></a>webhook.lua 源码</h2><figure class="highlight lua"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> GITHUB_WEBHOOK_SECRET = <span class="string">"your github webhook secret"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> request_method = ngx.var.request_method</span><br><span class="line"><span class="keyword">if</span> <span class="string">"POST"</span> ~= request_method <span class="keyword">then</span></span><br><span class="line"> ngx.<span class="built_in">exit</span>(<span class="number">404</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> signature = ngx.req.get_headers()[<span class="string">"X-Hub-Signature"</span>]</span><br><span class="line"><span class="keyword">if</span> signature == <span class="literal">nil</span> <span class="keyword">then</span></span><br><span class="line"> <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">404</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line">ngx.req.read_body()</span><br><span class="line"><span class="keyword">local</span> req_body = ngx.req.get_body_data()</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> req_body <span class="keyword">then</span></span><br><span class="line"> <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">404</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> dt = {}</span><br><span class="line"><span class="keyword">for</span> k, v <span class="keyword">in</span> <span class="built_in">string</span>.<span class="built_in">gmatch</span>(signature, <span class="string">"(%w+)=(%w+)"</span>) <span class="keyword">do</span></span><br><span class="line"> dt[k] = v</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> str = <span class="built_in">require</span> <span class="string">"resty.string"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">local</span> digest = ngx.hmac_sha1(GITHUB_WEBHOOK_SECRET, req_body)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> str.to_hex(digest) == dt[<span class="string">"sha1"</span>] <span class="keyword">then</span></span><br><span class="line"> ngx.<span class="built_in">log</span>(ngx.ERR, <span class="string">"signature error"</span>)</span><br><span class="line"> <span class="keyword">return</span> ngx.<span class="built_in">exit</span>(<span class="number">404</span>)</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">os</span>.<span class="built_in">execute</span>(<span class="string">"cd /your/blog/repositorie/path/ && git pull"</span>);</span><br><span class="line">ngx.say(<span class="string">"OK"</span>)</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">基于 OpenResty + GitHub WebHook 的代码自动更新</summary>
<category term="Nginx" scheme="http://www.imrui.net/categories/Nginx/"/>
<category term="Nginx" scheme="http://www.imrui.net/tags/Nginx/"/>
<category term="OpenResty" scheme="http://www.imrui.net/tags/OpenResty/"/>
</entry>
<entry>
<title>OAuth2 + JWT 授权认证服务</title>
<link href="http://www.imrui.net/2017/10/20/oauth2-jwt/"/>
<id>http://www.imrui.net/2017/10/20/oauth2-jwt/</id>
<published>2017-10-19T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.840Z</updated>
<content type="html"><![CDATA[<p>OAuth2 + JWT 授权认证服务</p><h2 id="参考阅读"><a href="#参考阅读" class="headerlink" title="参考阅读"></a>参考阅读</h2><ul><li><a href="http://www.jianshu.com/p/ec9b7bc47de9">Spring Security整合JSON Web Token(JWT)提升REST安全性</a></li><li><a href="http://geek.csdn.net/news/detail/236321">Spring Cloud下微服务权限方案</a></li><li><a href="http://www.jianshu.com/p/6307c89fe3fa">使用JWT和Spring Security保护REST API</a></li><li><a href="http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html">理解OAuth 2.0</a></li></ul><h2 id="JWT-简单介绍"><a href="#JWT-简单介绍" class="headerlink" title="JWT 简单介绍"></a>JWT 简单介绍</h2><h3 id="JWT长什么样"><a href="#JWT长什么样" class="headerlink" title="JWT长什么样"></a>JWT长什么样</h3><p>JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0OTA1NjMsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI3YjNiNTBlMC1hMjhjLTQ4NTQtYWNhMi0zMmQwZTIyZTY1ZmUiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhbGwiXX0.hPhvl1XbBMjoPER7BggWD1UO83o8SNtgg15rlUBndDo</span><br></pre></td></tr></table></figure><h3 id="JWT的构成"><a href="#JWT的构成" class="headerlink" title="JWT的构成"></a>JWT的构成</h3><p>第一部分我们称它为头部(header),第二部分我们称其为载荷(payload, 类似于飞机上承载的物品),第三部分是签证(signature).</p><h4 id="header"><a href="#header" class="headerlink" title="header"></a>header</h4><h5 id="jwt的头部承载两部分信息"><a href="#jwt的头部承载两部分信息" class="headerlink" title="jwt的头部承载两部分信息"></a>jwt的头部承载两部分信息</h5><ul><li>声明类型,这里是jwt</li><li>声明加密的算法 通常直接使用 HMAC SHA256</li></ul><p>完整的头部就像下面这样的JSON:</p><figure class="highlight json"><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 class="attr">"alg"</span>: <span class="string">"HS256"</span>,</span><br><span class="line"> <span class="attr">"typ"</span>: <span class="string">"JWT"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后将头部进行base64编码,构成了第一部分</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9</span><br></pre></td></tr></table></figure><h4 id="playload"><a href="#playload" class="headerlink" title="playload"></a>playload</h4><p>载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分</p><ul><li>标准中注册的声明</li><li>公共的声明</li><li>私有的声明</li></ul><h5 id="标准中注册的声明-建议但不强制使用"><a href="#标准中注册的声明-建议但不强制使用" class="headerlink" title="标准中注册的声明 (建议但不强制使用)"></a>标准中注册的声明 (建议但不强制使用)</h5><ul><li>iss: jwt签发者</li><li>sub: jwt所面向的用户</li><li>aud: 接收jwt的一方</li><li>exp: jwt的过期时间,这个过期时间必须要大于签发时间</li><li>nbf: 定义在什么时间之前,该jwt都是不可用的.</li><li>iat: jwt的签发时间</li><li>jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。</li></ul><h5 id="公共的声明"><a href="#公共的声明" class="headerlink" title="公共的声明"></a>公共的声明</h5><p>公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息.但不建议添加敏感信息,因为该部分在客户端可解密.</p><h5 id="私有的声明"><a href="#私有的声明" class="headerlink" title="私有的声明"></a>私有的声明</h5><p>私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64编码是可逆的,意味着该部分信息可以归类为明文信息。</p><p>定义一个payload:</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"exp"</span>: <span class="number">1508490563</span>,</span><br><span class="line"> <span class="attr">"user_name"</span>: <span class="string">"admin"</span>,</span><br><span class="line"> <span class="attr">"authorities"</span>: [</span><br><span class="line"> <span class="string">"ROLE_ADMIN"</span>,</span><br><span class="line"> <span class="string">"ROLE_USER"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"jti"</span>: <span class="string">"7b3b50e0-a28c-4854-aca2-32d0e22e65fe"</span>,</span><br><span class="line"> <span class="attr">"client_id"</span>: <span class="string">"abc"</span>,</span><br><span class="line"> <span class="attr">"scope"</span>: [</span><br><span class="line"> <span class="string">"all"</span></span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>然后将其进行base64编码,得到Jwt的第二部分</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJleHAiOjE1MDg0OTA1NjMsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI3YjNiNTBlMC1hMjhjLTQ4NTQtYWNhMi0zMmQwZTIyZTY1ZmUiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhbGwiXX0</span><br></pre></td></tr></table></figure><h4 id="signature"><a href="#signature" class="headerlink" title="signature"></a>signature</h4><p>jwt的第三部分是一个签证信息,这个签证信息由三部分组成:</p><ul><li>header (base64后的)</li><li>payload (base64后的)</li><li>secret</li></ul><p>这个部分需要base64编码后的header和base64编码后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。</p><figure class="highlight plain"><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">// javascript</span><br><span class="line">var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);</span><br><span class="line">var signature = HMACSHA256(encodedString, 'secret');</span><br></pre></td></tr></table></figure><p>将这三部分用.连接成一个完整的字符串,构成了最终的jwt</p><p><strong>注意</strong></p><p>secret是保存在服务器端的,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。</p><h2 id="OAuth2-介绍及使用"><a href="#OAuth2-介绍及使用" class="headerlink" title="OAuth2 介绍及使用"></a>OAuth2 介绍及使用</h2><h3 id="名词定义"><a href="#名词定义" class="headerlink" title="名词定义"></a>名词定义</h3><ul><li>Third-party application:第三方应用程序,本文中又称”客户端”(client),即上一节例子中的”云冲印”。</li><li>HTTP service:HTTP服务提供商,本文中简称”服务提供商”,即上一节例子中的Google。</li><li>Resource Owner:资源所有者,本文中又称”用户”(user)。</li><li>User Agent:用户代理,本文中就是指浏览器。</li><li>Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。</li><li>Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。</li></ul><h3 id="OAuth的思路"><a href="#OAuth的思路" class="headerlink" title="OAuth的思路"></a>OAuth的思路</h3><p>OAuth在”客户端”与”服务提供商”之间,设置了一个授权层(authorization layer)。”客户端”不能直接登录”服务提供商”,只能登录授权层,以此将用户与客户端区分开来。”客户端”登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。<br>“客户端”登录授权层以后,”服务提供商”根据令牌的权限范围和有效期,向”客户端”开放用户储存的资料。</p><h3 id="运行流程"><a href="#运行流程" class="headerlink" title="运行流程"></a>运行流程</h3><ul><li>A 用户打开客户端以后,客户端要求用户给予授权。</li><li>B 用户同意给予客户端授权。</li><li>C 客户端使用上一步获得的授权,向认证服务器申请令牌。</li><li>D 认证服务器对客户端进行认证以后,确认无误,同意发放令牌。</li><li>E 客户端使用令牌,向资源服务器申请获取资源。</li><li>F 资源服务器确认令牌无误,同意向客户端开放资源。</li></ul><h3 id="客户端的授权模式"><a href="#客户端的授权模式" class="headerlink" title="客户端的授权模式"></a>客户端的授权模式</h3><p>客户端必须得到用户的授权(authorization grant), 才能获得令牌(access token)</p><p>OAuth 2.0定义了四种授权方式:</p><ol><li>授权码模式 (authorization code)</li><li>简化模式 (implicit)</li><li>密码模式 (resource owner password credentials)</li><li>客户端模式 (client credentials)</li></ol><h3 id="验证token接口-oauth-check-token"><a href="#验证token接口-oauth-check-token" class="headerlink" title="验证token接口 /oauth/check_token"></a>验证token接口 /oauth/check_token</h3><p>参数 token: 访问令牌</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">http://localhost:8080/oauth/check_token?token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0NzcyNjgsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiJmZTk1MTNiZS1kZjEwLTQ2MDEtYWM0Yy0zOTc4Njc2NzQ1M2IiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhcHAiXX0.AOWIDfMfqzvMhV1T6eYDVpnvU03vST8XpNY35gNrNnyy_DQcLtFB0n_KIYieG4CYaVGlKnMGGXL7L592Njx6xy3bCdmSHw5_4MZZxxWN9_hj96ZTvi4H5Yc3vVzlXNHIvTTJxUsst15EbmN5s5ZBlZptv_3T70bG6x1iq6sbmMQ8zjHRgSKsZM1hvZ5pAO6BwYmPgIKFDPFtxB36I0LegLypUmzjgUFU5dAfgY-w00yyOf9aNooisM22CmbR0QXg3NAlzeufe_Jjf5W0jBTRzdhnVFreRUeOMMwlyKUb_rXUf53Yx0AXhcEhZYtYaYQpDyGxazcoO_VWSgM89S3nQuf23r20gp-jxgElNvRUnPhbO_jIuOcn2FQlvm-L37FfzlO6cK7BGqegE4ItVERfchznWFPq4Jm98IEKV5mQgAcutrE393uIL2137cNzfdg-DZ5HjFoPeEpiJ_ZL7IydxXgOsVTgYsLZ8Ccl0mO51kOCRC4tRBQxXVS9vtRQtp0TVjGTpdXK7SfLyohK1Ga0TXOA76HZv_nAoKy1BRg-i2bejV900g7-nkQGgthPdZbFk4rylXZe6t8grxCIDgtdYFGNLXiMjmnUYU9MJ35AFc1yGhqjwMX8lzdGsTNPLwNeKq9rt82rZxZuuKpiK3ph4LZgnQX5th6XiNBZDfnRz1M</span><br></pre></td></tr></table></figure><p>响应内容 JSON</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"exp"</span>:<span class="number">1508477268</span>,</span><br><span class="line"> <span class="attr">"user_name"</span>:<span class="string">"admin"</span>,</span><br><span class="line"> <span class="attr">"authorities"</span>:[</span><br><span class="line"> <span class="string">"ROLE_ADMIN"</span>,</span><br><span class="line"> <span class="string">"ROLE_USER"</span></span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">"jti"</span>:<span class="string">"fe9513be-df10-4601-ac4c-39786767453b"</span>,</span><br><span class="line"> <span class="attr">"client_id"</span>:<span class="string">"abc"</span>,</span><br><span class="line"> <span class="attr">"scope"</span>:[</span><br><span class="line"> <span class="string">"all"</span></span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="刷新token接口-oauth-token"><a href="#刷新token接口-oauth-token" class="headerlink" title="刷新token接口 /oauth/token"></a>刷新token接口 /oauth/token</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -d 'grant_type=refresh_token&refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiI4MzRmY2FmOS1hOGVhLTQwYWItYjkxMi1jNjU3NDFmOWJkYmMiLCJleHAiOjE1MDg0ODY2MDMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiNWJkYmEzNTctZTRjOS00MzdjLWE2Y2EtODdhM2FiNGVlZTUzIiwiY2xpZW50X2lkIjoiYWJjIn0.JEttthKb3Laj6iNeMZFvnj_2CHLW5WjzQF0RA5RMyGs&client_id=abc&client_secret=aaaabbbb' "http://localhost:8080/oauth/token"</span><br></pre></td></tr></table></figure><p>响应内容 JSON</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"access_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0ODY5NzAsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI2ZTQ2Yzg4Yi01YzczLTQxNTktYWY1ZC01YjFlOTVlMGQ2MjIiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhbGwiXX0.3OAl_Tjz-btYjQz5PxrYqc5I1T2iNYPcPrzXeog9eqE"</span>,</span><br><span class="line"> <span class="attr">"token_type"</span>: <span class="string">"bearer"</span>,</span><br><span class="line"> <span class="attr">"refresh_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiI2ZTQ2Yzg4Yi01YzczLTQxNTktYWY1ZC01YjFlOTVlMGQ2MjIiLCJleHAiOjE1MDg0ODY2MDMsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiNWJkYmEzNTctZTRjOS00MzdjLWE2Y2EtODdhM2FiNGVlZTUzIiwiY2xpZW50X2lkIjoiYWJjIn0.IvO62nDVawLiVv3oqkjS_P2vlsSntviVltULCcOXQzw"</span>,</span><br><span class="line"> <span class="attr">"expires_in"</span>: <span class="number">3599</span>,</span><br><span class="line"> <span class="attr">"scope"</span>: <span class="string">"all"</span>,</span><br><span class="line"> <span class="attr">"jti"</span>: <span class="string">"6e46c88b-5c73-4159-af5d-5b1e95e0d622"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="授权码模式-authorization-code"><a href="#授权码模式-authorization-code" class="headerlink" title="授权码模式 (authorization code)"></a>授权码模式 (authorization code)</h2><p>授权码模式(authorization code)是功能最完整、流程最严密的授权模式。它的特点就是通过客户端的后台服务器,与”服务提供商”的认证服务器进行互动。</p><ul><li>A 用户访问客户端,后者将前者导向认证服务器。</li><li>B 用户选择是否给予客户端授权。</li><li>C 假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码。</li><li>D 客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。</li><li>E 认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。</li></ul><h3 id="获取code接口-oauth-authorize"><a href="#获取code接口-oauth-authorize" class="headerlink" title="获取code接口 /oauth/authorize"></a>获取code接口 /oauth/authorize</h3><p>客户端申请认证的URI,包含以下参数:</p><ul><li>response_type:表示授权类型,必选项,此处的值固定为”code”</li><li>client_id:表示客户端的ID,必选项</li><li>redirect_uri:表示重定向URI,可选项</li><li>scope:表示申请的权限范围,可选项</li><li>state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。</li></ul><p>服务器回应客户端的URI,包含以下参数:</p><ul><li>code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。</li><li>state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。</li></ul><figure class="highlight plain"><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">GET http://localhost:8080/oauth/authorize?client_id=abc&response_type=code&redirect_uri=http://localhost:8080/resources/roles</span><br><span class="line">授权之后 302 http://localhost:8080/resources/roles?code=zmpbg2</span><br></pre></td></tr></table></figure><h3 id="获取token接口-oauth-token"><a href="#获取token接口-oauth-token" class="headerlink" title="获取token接口 /oauth/token"></a>获取token接口 /oauth/token</h3><p>D 步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:</p><p>_ grant_type:表示使用的授权模式,必选项,此处的值固定为”authorization_code”。<br>_ code:表示上一步获得的授权码,必选项。<br>_ redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。<br>_ client_id:表示客户端ID,必选项。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -d 'grant_type=authorization_code&redirect_uri=http://localhost:8080/resources/roles&client_id=abc&client_secret=aaaabbbb&code=zmpbg2' "http://localhost:8080/oauth/token"</span><br></pre></td></tr></table></figure><p>Response Header content-type: application/json;charset=UTF-8</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"access_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0ODEzOTAsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIwMDE3NGQyNC1jNWVhLTRhN2ItOTYxZS1jNDdkN2Y4YTNhYmQiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhbGwiXX0.cCAJ-r1qARUQCYjwLswWhui7g48lD_MPaarkPyIZMkM"</span>,</span><br><span class="line"> <span class="attr">"token_type"</span>: <span class="string">"bearer"</span>,</span><br><span class="line"> <span class="attr">"refresh_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiIwMDE3NGQyNC1jNWVhLTRhN2ItOTYxZS1jNDdkN2Y4YTNhYmQiLCJleHAiOjE1MDg0ODEzOTAsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiMTZjMmNiNzktNDE5My00Y2ZiLWJhODgtZGMyMDU5MzRiNGEzIiwiY2xpZW50X2lkIjoiYWJjIn0.mAZSTghSNFEaoV22jwOQjeD35KVa6Sb-zhxYM_ppimE"</span>,</span><br><span class="line"> <span class="attr">"expires_in"</span>: <span class="number">3599</span>,</span><br><span class="line"> <span class="attr">"scope"</span>: <span class="string">"all"</span>,</span><br><span class="line"> <span class="attr">"jti"</span>: <span class="string">"00174d24-c5ea-4a7b-961e-c47d7f8a3abd"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="简化模式-implicit"><a href="#简化模式-implicit" class="headerlink" title="简化模式 (implicit)"></a>简化模式 (implicit)</h2><p>简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了”授权码”这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。</p><ul><li>A 客户端将用户导向认证服务器。</li><li>B 用户决定是否给于客户端授权。</li><li>C 假设用户给予授权,认证服务器将用户导向客户端指定的”重定向URI”,并在URI的Hash部分包含了访问令牌。</li><li>D 浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。</li><li>E 资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。</li><li>F 浏览器执行上一步获得的脚本,提取出令牌。</li><li>G 浏览器将令牌发给客户端。</li></ul><h3 id="获取token接口-oauth-token-1"><a href="#获取token接口-oauth-token-1" class="headerlink" title="获取token接口 /oauth/token"></a>获取token接口 /oauth/token</h3><p>A步骤中,客户端发出的HTTP请求,包含以下参数:</p><ul><li>response_type:表示授权类型,此处的值固定为”token”,必选项。</li><li>client_id:表示客户端的ID,必选项。</li><li>redirect_uri:表示重定向的URI,可选项。</li><li>scope:表示权限范围,可选项。</li><li>state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。</li></ul><p>C步骤中,认证服务器回应客户端的URI,包含以下参数:</p><ul><li>access_token:表示访问令牌,必选项。</li><li>token_type:表示令牌类型,该值大小写不敏感,必选项。</li><li>expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。</li><li>scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。</li><li>state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。</li></ul><figure class="highlight plain"><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">http://localhost:8080/oauth/authorize?client_id=abc&response_type=token&redirect_uri=http://localhost:8080/resources/roles</span><br><span class="line">授权之后 302</span><br><span class="line">http://localhost:8080/resources/roles#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0Nzc3MDIsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiI4ZTA2ZGFlZi0xMGVkLTQ3ZmYtOWY3Ni04ZjM2YmFmNDNiNjciLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhcHAiXX0.7ygi5Z64CvZnjThSf2IlZ1PdLkOOTPQbiqXYrbhg8wM&token_type=bearer&expires_in=3599&scope=app&jti=8e06daef-10ed-47ff-9f76-8f36baf43b67</span><br></pre></td></tr></table></figure><h2 id="密码模式-resource-owner-password-credentials"><a href="#密码模式-resource-owner-password-credentials" class="headerlink" title="密码模式 (resource owner password credentials)"></a>密码模式 (resource owner password credentials)</h2><p>密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向”服务商提供商”索要授权。<br>在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。</p><ul><li>A 用户向客户端提供用户名和密码。</li><li>B 客户端将用户名和密码发给认证服务器,向后者请求令牌。</li><li>C 认证服务器确认无误后,向客户端提供访问令牌。</li></ul><h3 id="获取token接口-oauth-token-2"><a href="#获取token接口-oauth-token-2" class="headerlink" title="获取token接口 /oauth/token"></a>获取token接口 /oauth/token</h3><p>B步骤中,客户端发出的HTTP请求,包含以下参数:</p><ul><li>grant_type:表示授权类型,此处的值固定为”password”,必选项。</li><li>username:表示用户名,必选项。</li><li>password:表示用户的密码,必选项。</li><li>scope:表示权限范围,可选项。</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -d 'grant_type=password&username=admin&password=admin&client_id=abc&client_secret=aaaabbbb' "http://localhost:8080/oauth/token"</span><br></pre></td></tr></table></figure><p>响应内容 JSON</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"access_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MDg0OTAyNTgsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiIsIlJPTEVfVVNFUiJdLCJqdGkiOiIyY2NjYzUyZC0xMTg2LTQ1NzMtOWEyZC1mNzQ3NzM4MTIyZWIiLCJjbGllbnRfaWQiOiJhYmMiLCJzY29wZSI6WyJhbGwiXX0.4ql81lMcnBVUarwIVqcJBGTEQdCMrl1iSdoUsehuH8Y"</span>,</span><br><span class="line"> <span class="attr">"token_type"</span>: <span class="string">"bearer"</span>,</span><br><span class="line"> <span class="attr">"refresh_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiIyY2NjYzUyZC0xMTg2LTQ1NzMtOWEyZC1mNzQ3NzM4MTIyZWIiLCJleHAiOjE1MDg0OTAyNTgsImF1dGhvcml0aWVzIjpbIlJPTEVfQURNSU4iLCJST0xFX1VTRVIiXSwianRpIjoiNjE2N2I3MjEtZTYyYS00ZjViLWI2YmQtMWQyZDY3OTIyYWYwIiwiY2xpZW50X2lkIjoiYWJjIn0.y-UBydaYeAbw0ctD3OseHfpT5WrHvpw1bZkVihPGLrY"</span>,</span><br><span class="line"> <span class="attr">"expires_in"</span>: <span class="number">3599</span>,</span><br><span class="line"> <span class="attr">"scope"</span>: <span class="string">"all"</span>,</span><br><span class="line"> <span class="attr">"jti"</span>: <span class="string">"2cccc52d-1186-4573-9a2d-f747738122eb"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="客户端模式-client-credentials"><a href="#客户端模式-client-credentials" class="headerlink" title="客户端模式 (client credentials)"></a>客户端模式 (client credentials)</h2><p>客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向”服务提供商”进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求”服务提供商”提供服务,其实不存在授权问题。</p><ul><li>A 客户端向认证服务器进行身份认证,并要求一个访问令牌。</li><li>B 认证服务器确认无误后,向客户端提供访问令牌。</li></ul><h3 id="获取token接口-oauth-token-3"><a href="#获取token接口-oauth-token-3" class="headerlink" title="获取token接口 /oauth/token"></a>获取token接口 /oauth/token</h3><p>A步骤中,客户端发出的HTTP请求,包含以下参数:</p><ul><li>granttype:表示授权类型,此处的值固定为”clientcredentials”,必选项。</li><li>scope:表示权限范围,可选项。</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">curl -d 'grant_type=client_credentials&client_id=abc&client_secret=aaaabbbb' "http://localhost:8080/oauth/token"</span><br></pre></td></tr></table></figure><p>响应内容 JSON</p><figure class="highlight json"><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><br><span class="line"> <span class="attr">"access_token"</span>: <span class="string">"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJhbGwiXSwiZXhwIjoxNTA4NDg3MzAzLCJqdGkiOiJiYTBmMDVmOS1iYTY1LTQ5MTItOGMzOC1lMWRmYjU1NWZkZTAiLCJjbGllbnRfaWQiOiJhYmMifQ.Shxb0filgBLtQEfspWpmTYfrzFbkC48DX4lSrMxRvX4"</span>,</span><br><span class="line"> <span class="attr">"token_type"</span>: <span class="string">"bearer"</span>,</span><br><span class="line"> <span class="attr">"expires_in"</span>: <span class="number">3599</span>,</span><br><span class="line"> <span class="attr">"scope"</span>: <span class="string">"all"</span>,</span><br><span class="line"> <span class="attr">"jti"</span>: <span class="string">"ba0f05f9-ba65-4912-8c38-e1dfb555fde0"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">基于Spring的OAuth2+JWT授权认证服务实现</summary>
<category term="Web" scheme="http://www.imrui.net/categories/Web/"/>
<category term="Spring" scheme="http://www.imrui.net/tags/Spring/"/>
<category term="OAuth2" scheme="http://www.imrui.net/tags/OAuth2/"/>
</entry>
<entry>
<title>RESTful风格鉴权设计</title>
<link href="http://www.imrui.net/2017/09/30/spring-restful-auth/"/>
<id>http://www.imrui.net/2017/09/30/spring-restful-auth/</id>
<published>2017-09-29T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.840Z</updated>
<content type="html"><![CDATA[<p>基于Spring及Redis的Token鉴权设计</p><h2 id="REST简介"><a href="#REST简介" class="headerlink" title="REST简介"></a>REST简介</h2><p>REST (Representational State Transfer) 是一种软件架构风格。它将服务端的信息和功能等所有事物统称为资源,客户端的请求实际就是对资源进行操作,它的主要特点有: 每一个资源都会对应一个独一无二的 url,客户端通过<br> HTTP 的 GET、POST、PUT、DELETE 请求方法对资源进行查询、创建、修改、删除操作。 客户端与服务端的交互必须是无状态的。</p><h2 id="Token身份鉴权"><a href="#Token身份鉴权" class="headerlink" title="Token身份鉴权"></a>Token身份鉴权</h2><p>网站应用一般使用 Session 进行登录用户信息的存储及验证,而在移动端使用 Token 则更加普遍。它们之间并没有太大区别,Token 比较像是一个更加精简的自定义的 Session。Session 的主要功能是保持会话信息,而 Token 则>只用于登录用户的身份鉴权。所以在移动端使用 Token 会比使用 Session 更加简易并且有更高的安全性,同时也更加符合 RESTful 中无状态的定义。</p><h2 id="交互流程"><a href="#交互流程" class="headerlink" title="交互流程"></a>交互流程</h2><ol><li>客户端通过登录请求提交用户名和密码,服务端验证通过后生成一个 Token 与该用户进行关联,并将 Token 返回给客户端。</li><li>客户端在接下来的请求中都会携带 Token,服务端通过解析 Token 检查登录状态。</li><li>当用户退出登录、其他终端登录同一账号(被顶号)、长时间未进行操作时 Token 会失效,这时用户需要重新登录。</li></ol><h2 id="程序示例"><a href="#程序示例" class="headerlink" title="程序示例"></a>程序示例</h2><p>服务端生成的 Token 一般为随机的非重复字符串,根据应用对安全性的不同要求,会将其添加时间戳(通过时间判断 Token 是否被盗用)或 url 签名(通过请求地址判断 Token 是否被盗用)后加密进行传输。在本文中为了演示方便,仅是将 User Id 与 Token 以”_”进行拼接。</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Token 的 Model 类,可以增加字段提高安全性,例如时间戳、url 签名</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TokenModel</span> </span>{</span><br><span class="line"> <span class="comment">// 用户 id</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">long</span> userId;</span><br><span class="line"> <span class="comment">// 随机生成的 uuid</span></span><br><span class="line"> <span class="keyword">private</span> String token;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">TokenModel</span> <span class="params">(<span class="keyword">long</span> userId, String token)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.userId = userId;</span><br><span class="line"> <span class="keyword">this</span>.token = token;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">getUserId</span> <span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> userId;</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">setUserId</span> <span class="params">(<span class="keyword">long</span> userId)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.userId = userId;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getToken</span> <span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> token;</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">setToken</span> <span class="params">(String token)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.token = token;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Redis 是一个 Key-Value 结构的内存数据库,用它维护 User Id 和 Token 的映射表会比传统数据库速度更快,这里使用 Spring-Data-Redis 封装的 TokenManager 对 Token 进行基础操作:</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><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 对 token 进行操作的接口</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">TokenManager</span> </span>{</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建一个 token 关联上指定用户</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> userId 指定用户的 id</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 生成的 token</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> TokenModel <span class="title">createToken</span> <span class="params">(<span class="keyword">long</span> userId)</span></span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 检查 token 是否有效</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> model token</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span> 是否有效</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">boolean</span> <span class="title">checkToken</span> <span class="params">(TokenModel model)</span></span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 从字符串中解析 token</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> authentication 加密后的字符串</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@return</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> TokenModel <span class="title">getToken</span> <span class="params">(String authentication)</span></span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 清除 token</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> userId 登录用户的 id</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">deleteToken</span> <span class="params">(<span class="keyword">long</span> userId)</span></span>;</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"> * 通过 Redis 存储和验证 token 的实现类</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RedisTokenManager</span> <span class="keyword">implements</span> <span class="title">TokenManager</span> </span>{</span><br><span class="line"> <span class="keyword">private</span> RedisTemplate redis;</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setRedis</span> <span class="params">(RedisTemplate redis)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.redis = redis;</span><br><span class="line"> <span class="comment">// 泛型设置成 Long 后必须更改对应的序列化方案</span></span><br><span class="line"> redis.setKeySerializer (<span class="keyword">new</span> JdkSerializationRedisSerializer ());</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> TokenModel <span class="title">createToken</span> <span class="params">(<span class="keyword">long</span> userId)</span> </span>{</span><br><span class="line"> <span class="comment">// 使用 uuid 作为源 token</span></span><br><span class="line"> String token = UUID.randomUUID ().toString ().replace (<span class="string">"-"</span>, <span class="string">""</span>);</span><br><span class="line"> TokenModel model = <span class="keyword">new</span> TokenModel (userId, token);</span><br><span class="line"> <span class="comment">// 存储到 redis 并设置过期时间</span></span><br><span class="line"> redis.boundValueOps (userId).set (token, Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);</span><br><span class="line"> <span class="keyword">return</span> model;</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> TokenModel <span class="title">getToken</span> <span class="params">(String authentication)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (authentication == <span class="keyword">null</span> || authentication.length () == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> String [] param = authentication.split (<span class="string">"_"</span>);</span><br><span class="line"> <span class="keyword">if</span> (param.length != <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 使用 userId 和源 token 简单拼接成的 token,可以增加加密措施</span></span><br><span class="line"> <span class="keyword">long</span> userId = Long.parseLong (param [<span class="number">0</span>]);</span><br><span class="line"> String token = param [<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> TokenModel (userId, token);</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">checkToken</span> <span class="params">(TokenModel model)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (model == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> String token = redis.boundValueOps (model.getUserId ()).get ();</span><br><span class="line"> <span class="keyword">if</span> (token == <span class="keyword">null</span> || !token.equals (model.getToken ())) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果验证成功,说明此用户进行了一次有效操作,延长 token 的过期时间</span></span><br><span class="line"> redis.boundValueOps (model.getUserId ()).expire (Constants.TOKEN_EXPIRES_HOUR, TimeUnit.HOURS);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</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">deleteToken</span> <span class="params">(<span class="keyword">long</span> userId)</span> </span>{</span><br><span class="line"> redis.delete (userId);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>RESTful 中所有请求的本质都是对资源进行 CRUD 操作,所以登录和退出登录也可以抽象为对一个 Token 资源的创建和删除,根据该想法创建 Controller:</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><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="comment">/**</span></span><br><span class="line"><span class="comment"> * 获取和删除 token 的请求地址,在 Restful 设计中其实就对应着登录和退出登录的资源映射</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@RestController</span></span><br><span class="line"><span class="meta">@RequestMapping</span> (<span class="string">"/tokens"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TokenController</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TokenManager tokenManager;</span><br><span class="line"> <span class="meta">@RequestMapping</span> (method = RequestMethod.POST)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ResponseEntity <span class="title">login</span> <span class="params">(<span class="meta">@RequestParam</span> String username, <span class="meta">@RequestParam</span> String password)</span> </span>{</span><br><span class="line"> Assert.notNull (username, <span class="string">"username can not be empty"</span>);</span><br><span class="line"> Assert.notNull (password, <span class="string">"password can not be empty"</span>);</span><br><span class="line"> User user = userRepository.findByUsername (username);</span><br><span class="line"> <span class="keyword">if</span> (user == <span class="keyword">null</span> || <span class="comment">// 未注册</span></span><br><span class="line"> !user.getPassword ().equals (password)) { <span class="comment">// 密码错误</span></span><br><span class="line"> <span class="comment">// 提示用户名或密码错误</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity (ResultModel.error (ResultStatus.USERNAME_OR_PASSWORD_ERROR), HttpStatus.NOT_FOUND);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 生成一个 token,保存用户登录状态</span></span><br><span class="line"> TokenModel model = tokenManager.createToken (user.getId ());</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity (ResultModel.ok (model), HttpStatus.OK);</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@RequestMapping</span> (method = RequestMethod.DELETE)</span><br><span class="line"> <span class="meta">@Authorization</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> ResponseEntity <span class="title">logout</span> <span class="params">(<span class="meta">@CurrentUser</span> User user)</span> </span>{</span><br><span class="line"> tokenManager.deleteToken (user.getId ());</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> ResponseEntity (ResultModel.ok (), HttpStatus.OK);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这个 Controller 中有两个自定义的注解分别是@Authorization和@CurrentUser,其中@Authorization用于表示该操作需要登录后才能进行:</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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在 Controller 的方法上使用此注解,该方法在映射时会检查用户是否登录,未登录返回 401 错误</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Target</span> (ElementType.METHOD)</span><br><span class="line"><span class="meta">@Retention</span> (RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> Authorization {</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里使用 Spring 的拦截器完成这个功能,该拦截器会检查每一个请求映射的方法是否有@Authorization注解,并使用 TokenManager 验证 Token,如果验证失败直接返回 401 状态码(未授权):</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><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="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="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AuthorizationInterceptor</span> <span class="keyword">extends</span> <span class="title">HandlerInterceptorAdapter</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> TokenManager manager;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">preHandle</span> <span class="params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">// 如果不是映射到方法直接通过</span></span><br><span class="line"> <span class="keyword">if</span> (!(handler <span class="keyword">instanceof</span> HandlerMethod)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> HandlerMethod handlerMethod = (HandlerMethod) handler;</span><br><span class="line"> Method method = handlerMethod.getMethod ();</span><br><span class="line"> <span class="comment">// 从 header 中得到 token</span></span><br><span class="line"> String authorization = request.getHeader (Constants.AUTHORIZATION);</span><br><span class="line"> <span class="comment">// 验证 token</span></span><br><span class="line"> TokenModel model = manager.getToken (authorization);</span><br><span class="line"> <span class="keyword">if</span> (manager.checkToken (model)) {</span><br><span class="line"> <span class="comment">// 如果 token 验证成功,将 token 对应的用户 id 存在 request 中,便于之后注入</span></span><br><span class="line"> request.setAttribute (Constants.CURRENT_USER_ID, model.getUserId ());</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果验证 token 失败,并且方法注明了 Authorization,返回 401 错误</span></span><br><span class="line"> <span class="keyword">if</span> (method.getAnnotation (Authorization.class) != <span class="keyword">null</span>) {</span><br><span class="line"> response.setStatus (HttpServletResponse.SC_UNAUTHORIZED);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>@CurrentUser</code>注解定义在方法的参数中,表示该参数是登录用户对象。这里同样使用了 Spring 的解析器完成参数注入:</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 在 Controller 的方法参数中使用此注解,该方法在映射时会注入当前登录的 User 对象</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Target</span> (ElementType.PARAMETER)</span><br><span class="line"><span class="meta">@Retention</span> (RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> CurrentUser {</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"> * 增加方法注入,将含有 CurrentUser 注解的方法参数注入当前登录用户</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CurrentUserMethodArgumentResolver</span> <span class="keyword">implements</span> <span class="title">HandlerMethodArgumentResolver</span> </span>{</span><br><span class="line"> <span class="meta">@Autowired</span></span><br><span class="line"> <span class="keyword">private</span> UserRepository userRepository;</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">supportsParameter</span> <span class="params">(MethodParameter parameter)</span> </span>{</span><br><span class="line"> <span class="comment">// 如果参数类型是 User 并且有 CurrentUser 注解则支持</span></span><br><span class="line"> <span class="keyword">if</span> (parameter.getParameterType ().isAssignableFrom (User.class) &&</span><br><span class="line"> parameter.hasParameterAnnotation (CurrentUser.class)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Object <span class="title">resolveArgument</span> <span class="params">(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="comment">// 取出鉴权时存入的登录用户 Id</span></span><br><span class="line"> Long currentUserId = (Long) webRequest.getAttribute (Constants.CURRENT_USER_ID, RequestAttributes.SCOPE_REQUEST);</span><br><span class="line"> <span class="keyword">if</span> (currentUserId != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">// 从数据库中查询并返回</span></span><br><span class="line"> <span class="keyword">return</span> userRepository.findOne (currentUserId);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> MissingServletRequestPartException (Constants.CURRENT_USER_ID);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="一些细节"><a href="#一些细节" class="headerlink" title="一些细节"></a>一些细节</h2><p>登录请求一定要使用 HTTPS,否则无论 Token 做的安全性多好密码泄露了也是白搭<br>Token 的生成方式有很多种,例如比较热门的有 JWT(JSON Web Tokens)、OAuth 等。</p>]]></content>
<summary type="html">基于Spring及Redis的Token鉴权设计与实现</summary>
<category term="Spring" scheme="http://www.imrui.net/categories/Spring/"/>
<category term="Spring" scheme="http://www.imrui.net/tags/Spring/"/>
<category term="Redis" scheme="http://www.imrui.net/tags/Redis/"/>
</entry>
<entry>
<title>Spring RedisTemplate的使用</title>
<link href="http://www.imrui.net/2017/09/29/spring-redis-template/"/>
<id>http://www.imrui.net/2017/09/29/spring-redis-template/</id>
<published>2017-09-28T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.840Z</updated>
<content type="html"><![CDATA[<h2 id="Redis-数据结构简介"><a href="#Redis-数据结构简介" class="headerlink" title="Redis 数据结构简介"></a>Redis 数据结构简介</h2><p>Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字符串)、List(列表)、Set(集合)、Hash(散列)和 Zset(有序集合)。<br>了解更多内容,请参阅官方网站: <a href="https://redis.io/">官网</a>、<a href="http://www.redis.cn/">中文网站</a></p><p>下面来对这5种数据结构类型作简单的介绍:</p><table><thead><tr><th>结构类型</th><th>结构存储的值</th><th>结构的读写能力</th></tr></thead><tbody><tr><td>String</td><td>可以是字符串、整数或者浮点数</td><td>对整个字符串或者字符串的其中一部分执行操作;对象和浮点数执行自增(increment)或者自减(decrement)</td></tr><tr><td>List</td><td>一个链表,链表上的每个节点都包含了一个字符串</td><td>从链表的两端推入或者弹出元素;根据偏移量对链表进行修剪(trim);读取单个或者多个元素;根据值来查找或者移除元素</td></tr><tr><td>Set</td><td>包含字符串的无序收集器(unorderedcollection),并且被包含的每个字符串都是独一无二的、各不相同</td><td>添加、获取、移除单个元素;检查一个元素是否存在于某个集合中;计算交集、并集、差集;从集合里卖弄随机获取元素</td></tr><tr><td>Hash</td><td>包含键值对的无序散列表</td><td>添加、获取、移除单个键值对;获取所有键值对</td></tr><tr><td>Zset</td><td>字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列顺序由分值的大小决定</td><td>添加、获取、删除单个元素;根据分值范围(range)或者成员来获取元素</td></tr></tbody></table><p>Redis 5种数据结构的概念大致介绍到这边,下面将结合Spring封装的RedisTemplate来对这5种数据结构的运用进行演示。</p><h2 id="RedisTemplate介绍"><a href="#RedisTemplate介绍" class="headerlink" title="RedisTemplate介绍"></a>RedisTemplate介绍</h2><p>Spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 API 。</p><h3 id="RedisTemplate在spring代码中的结构如下:"><a href="#RedisTemplate在spring代码中的结构如下:" class="headerlink" title="RedisTemplate在spring代码中的结构如下:"></a>RedisTemplate在spring代码中的结构如下:</h3><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></pre></td><td class="code"><pre><span class="line">org.springframework.data.redis.core</span><br><span class="line">Class RedisTemplate<K,V></span><br><span class="line">java.lang.Object</span><br><span class="line"> org.springframework.data.redis.core.RedisAccessor</span><br><span class="line"> org.springframework.data.redis.core.RedisTemplate<K,V></span><br></pre></td></tr></table></figure><p>Type Parameters:</p><ul><li>K: the Redis key type against which the template works (usually a String) 模板中的Redis key的类型(通常为String)如:RedisTemplate<String, Object> 注意:<code>如果没特殊情况,切勿定义成RedisTemplate<Object, Object></code>,否则根据里氏替换原则,使用的时候会造成类型错误 。</li><li>V: the Redis value type against which the template works 模板中的Redis value的类型</li></ul><h3 id="RedisTemplate中定义了对5种数据结构操作"><a href="#RedisTemplate中定义了对5种数据结构操作" class="headerlink" title="RedisTemplate中定义了对5种数据结构操作"></a>RedisTemplate中定义了对5种数据结构操作</h3><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></pre></td><td class="code"><pre><span class="line">redisTemplate.opsForValue();<span class="comment">//操作字符串</span></span><br><span class="line">redisTemplate.opsForHash();<span class="comment">//操作hash</span></span><br><span class="line">redisTemplate.opsForList();<span class="comment">//操作list</span></span><br><span class="line">redisTemplate.opsForSet();<span class="comment">//操作set</span></span><br><span class="line">redisTemplate.opsForZSet();<span class="comment">//操作有序set</span></span><br><span class="line">StringRedisTemplate与RedisTemplate</span><br></pre></td></tr></table></figure><ul><li>两者的关系是StringRedisTemplate继承RedisTemplate。</li><li>两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。</li><li>SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。</li><li>StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。</li><li>RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。</li></ul><h3 id="RedisTemplate配置"><a href="#RedisTemplate配置" class="headerlink" title="RedisTemplate配置"></a>RedisTemplate配置</h3><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">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> RedisTemplate<String, Object> <span class="title">redisTemplate</span><span class="params">(RedisConnectionFactory redisConnectionFactory)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = <span class="keyword">new</span> Jackson2JsonRedisSerializer<Object>(Object.class);</span><br><span class="line"> ObjectMapper om = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line"> om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line"> om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);</span><br><span class="line"> jackson2JsonRedisSerializer.setObjectMapper(om);</span><br><span class="line"> RedisTemplate<String, Object> template = <span class="keyword">new</span> RedisTemplate<String, Object>();</span><br><span class="line"> template.setConnectionFactory(redisConnectionFactory);</span><br><span class="line"> template.setKeySerializer(jackson2JsonRedisSerializer);</span><br><span class="line"> template.setValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line"> template.setHashKeySerializer(jackson2JsonRedisSerializer);</span><br><span class="line"> template.setHashValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line"> template.afterPropertiesSet();</span><br><span class="line"> <span class="keyword">return</span> template;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Redis的String数据结构"><a href="#Redis的String数据结构" class="headerlink" title="Redis的String数据结构"></a>Redis的String数据结构</h2><p>** 推荐使用StringRedisTemplate ** 如果使用RedisTemplate需要更改序列化方式</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></pre></td><td class="code"><pre><span class="line">RedisSerializer<String> stringSerializer = <span class="keyword">new</span> StringRedisSerializer();</span><br><span class="line">template.setKeySerializer(stringSerializer );</span><br><span class="line">template.setValueSerializer(stringSerializer );</span><br><span class="line">template.setHashKeySerializer(stringSerializer );</span><br><span class="line">template.setHashValueSerializer(stringSerializer );</span><br></pre></td></tr></table></figure><h3 id="set-void-set-K-key-V-value"><a href="#set-void-set-K-key-V-value" class="headerlink" title="set void set(K key, V value)"></a>set void set(K key, V value)</h3><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></pre></td><td class="code"><pre><span class="line">redisTemplate.opsForValue().set(<span class="string">"name"</span>,<span class="string">"tom"</span>);</span><br><span class="line">redisTemplate.opsForValue().get(<span class="string">"name"</span>);</span><br><span class="line">输出结果为tom</span><br></pre></td></tr></table></figure><h3 id="set-void-set-K-key-V-value-long-timeout-TimeUnit-unit"><a href="#set-void-set-K-key-V-value-long-timeout-TimeUnit-unit" class="headerlink" title="set void set(K key, V value, long timeout, TimeUnit unit)"></a>set void set(K key, V value, long timeout, TimeUnit unit)</h3><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></pre></td><td class="code"><pre><span class="line">redisTemplate.opsForValue().set(<span class="string">"name"</span>,<span class="string">"tom"</span>,<span class="number">10</span>, TimeUnit.SECONDS);</span><br><span class="line">redisTemplate.opsForValue().get(<span class="string">"name"</span>);</span><br><span class="line">由于设置的是<span class="number">10</span>秒失效,十秒之内查询有结果,十秒之后返回为<span class="keyword">null</span></span><br></pre></td></tr></table></figure><h3 id="set-void-set-K-key-V-value-long-offset"><a href="#set-void-set-K-key-V-value-long-offset" class="headerlink" title="set void set(K key, V value, long offset)"></a>set void set(K key, V value, long offset)</h3><p>该方法是用 value 参数覆写(overwrite)给定 key 所储存的字符串值,从偏移量 offset 开始</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().set(<span class="string">"key"</span>,<span class="string">"hello world"</span>);</span><br><span class="line">template.opsForValue().set(<span class="string">"key"</span>,<span class="string">"redis"</span>, <span class="number">6</span>);</span><br><span class="line">System.out.println(<span class="string">"***************"</span>+template.opsForValue().get(<span class="string">"key"</span>));</span><br><span class="line">结果:***************hello redis</span><br></pre></td></tr></table></figure><h3 id="setIfAbsent-Boolean-setIfAbsent-K-key-V-value"><a href="#setIfAbsent-Boolean-setIfAbsent-K-key-V-value" class="headerlink" title="setIfAbsent Boolean setIfAbsent(K key, V value)"></a>setIfAbsent Boolean setIfAbsent(K key, V value)</h3><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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForValue().setIfAbsent(<span class="string">"multi1"</span>,<span class="string">"multi1"</span>));<span class="comment">//false multi1之前已经存在</span></span><br><span class="line">System.out.println(template.opsForValue().setIfAbsent(<span class="string">"multi111"</span>,<span class="string">"multi111"</span>));<span class="comment">//true multi111之前不存在</span></span><br><span class="line">结果:<span class="keyword">false</span></span><br><span class="line"><span class="keyword">true</span></span><br></pre></td></tr></table></figure><h3 id="multiSet-void-multiSet-Map-lt-extends-K-extends-V-gt-m"><a href="#multiSet-void-multiSet-Map-lt-extends-K-extends-V-gt-m" class="headerlink" title="multiSet void multiSet(Map<? extends K, ? extends V> m)"></a>multiSet void multiSet(Map<? extends K, ? extends V> m)</h3><p>为多个键分别设置它们的值</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></pre></td><td class="code"><pre><span class="line">Map<String,String> maps = <span class="keyword">new</span> HashMap<String, String>();</span><br><span class="line">maps.put(<span class="string">"multi1"</span>,<span class="string">"multi1"</span>);</span><br><span class="line">maps.put(<span class="string">"multi2"</span>,<span class="string">"multi2"</span>);</span><br><span class="line">maps.put(<span class="string">"multi3"</span>,<span class="string">"multi3"</span>);</span><br><span class="line">template.opsForValue().multiSet(maps);</span><br><span class="line">List<String> keys = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">keys.add(<span class="string">"multi1"</span>);</span><br><span class="line">keys.add(<span class="string">"multi2"</span>);</span><br><span class="line">keys.add(<span class="string">"multi3"</span>);</span><br><span class="line">System.out.println(template.opsForValue().multiGet(keys));</span><br><span class="line">结果:[multi1, multi2, multi3]</span><br></pre></td></tr></table></figure><h3 id="multiSetIfAbsent-Boolean-multiSetIfAbsent-Map-lt-extends-K-extends-V-gt-m"><a href="#multiSetIfAbsent-Boolean-multiSetIfAbsent-Map-lt-extends-K-extends-V-gt-m" class="headerlink" title="multiSetIfAbsent Boolean multiSetIfAbsent(Map<? extends K, ? extends V> m)"></a>multiSetIfAbsent Boolean multiSetIfAbsent(Map<? extends K, ? extends V> m)</h3><p>为多个键分别设置它们的值,如果存在则返回false,不存在返回true</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></pre></td><td class="code"><pre><span class="line">Map<String,String> maps = <span class="keyword">new</span> HashMap<String, String>();</span><br><span class="line">maps.put(<span class="string">"multi11"</span>,<span class="string">"multi11"</span>);</span><br><span class="line">maps.put(<span class="string">"multi22"</span>,<span class="string">"multi22"</span>);</span><br><span class="line">maps.put(<span class="string">"multi33"</span>,<span class="string">"multi33"</span>);</span><br><span class="line">Map<String,String> maps2 = <span class="keyword">new</span> HashMap<String, String>();</span><br><span class="line">maps2.put(<span class="string">"multi1"</span>,<span class="string">"multi1"</span>);</span><br><span class="line">maps2.put(<span class="string">"multi2"</span>,<span class="string">"multi2"</span>);</span><br><span class="line">maps2.put(<span class="string">"multi3"</span>,<span class="string">"multi3"</span>);</span><br><span class="line">System.out.println(template.opsForValue().multiSetIfAbsent(maps));</span><br><span class="line">System.out.println(template.opsForValue().multiSetIfAbsent(maps2));</span><br><span class="line">结果:<span class="keyword">true</span></span><br><span class="line"><span class="keyword">false</span></span><br></pre></td></tr></table></figure><h3 id="get-V-get-Object-key"><a href="#get-V-get-Object-key" class="headerlink" title="get V get(Object key)"></a>get V get(Object key)</h3><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></pre></td><td class="code"><pre><span class="line">template.opsForValue().set(<span class="string">"key"</span>,<span class="string">"hello world"</span>);</span><br><span class="line">System.out.println(<span class="string">"***************"</span>+template.opsForValue().get(<span class="string">"key"</span>));</span><br><span class="line">结果:***************hello world</span><br></pre></td></tr></table></figure><h3 id="getAndSet-V-getAndSet-K-key-V-value"><a href="#getAndSet-V-getAndSet-K-key-V-value" class="headerlink" title="getAndSet V getAndSet(K key, V value)"></a>getAndSet V getAndSet(K key, V value)</h3><p>设置键的字符串值并返回其旧值</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().set(<span class="string">"getSetTest"</span>,<span class="string">"test"</span>);</span><br><span class="line">System.out.println(template.opsForValue().getAndSet(<span class="string">"getSetTest"</span>,<span class="string">"test2"</span>));</span><br><span class="line">结果:test</span><br></pre></td></tr></table></figure><h3 id="multiGet-List-multiGet-Collection-keys"><a href="#multiGet-List-multiGet-Collection-keys" class="headerlink" title="multiGet List multiGet(Collection keys)"></a>multiGet List<V> multiGet(Collection<K> keys)</h3><p>为多个键分别取出它们的值</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></pre></td><td class="code"><pre><span class="line">Map<String,String> maps = <span class="keyword">new</span> HashMap<String, String>();</span><br><span class="line">maps.put(<span class="string">"multi1"</span>,<span class="string">"multi1"</span>);</span><br><span class="line">maps.put(<span class="string">"multi2"</span>,<span class="string">"multi2"</span>);</span><br><span class="line">maps.put(<span class="string">"multi3"</span>,<span class="string">"multi3"</span>);</span><br><span class="line">template.opsForValue().multiSet(maps);</span><br><span class="line">List<String> keys = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">keys.add(<span class="string">"multi1"</span>);</span><br><span class="line">keys.add(<span class="string">"multi2"</span>);</span><br><span class="line">keys.add(<span class="string">"multi3"</span>);</span><br><span class="line">System.out.println(template.opsForValue().multiGet(keys));</span><br><span class="line">结果:[multi1, multi2, multi3]</span><br></pre></td></tr></table></figure><h3 id="increment-Long-increment-K-key-long-delta"><a href="#increment-Long-increment-K-key-long-delta" class="headerlink" title="increment Long increment(K key, long delta)"></a>increment Long increment(K key, long delta)</h3><p>支持整数</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().increment(<span class="string">"increlong"</span>,<span class="number">1</span>);</span><br><span class="line">System.out.println(<span class="string">"***************"</span>+template.opsForValue().get(<span class="string">"increlong"</span>));</span><br><span class="line">结果:***************<span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="increment-Double-increment-K-key-double-delta"><a href="#increment-Double-increment-K-key-double-delta" class="headerlink" title="increment Double increment(K key, double delta)"></a>increment Double increment(K key, double delta)</h3><p>也支持浮点数</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().increment(<span class="string">"increlong"</span>,<span class="number">1.2</span>);</span><br><span class="line">System.out.println(<span class="string">"***************"</span>+template.opsForValue().get(<span class="string">"increlong"</span>));</span><br><span class="line">***************<span class="number">2.2</span></span><br></pre></td></tr></table></figure><h3 id="append-Integer-append-K-key-String-value"><a href="#append-Integer-append-K-key-String-value" class="headerlink" title="append Integer append(K key, String value)"></a>append Integer append(K key, String value)</h3><p>如果key已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此APPEND在这种特殊情况下将类似于SET。</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().append(<span class="string">"appendTest"</span>,<span class="string">"Hello"</span>);</span><br><span class="line">System.out.println(template.opsForValue().get(<span class="string">"appendTest"</span>));</span><br><span class="line">template.opsForValue().append(<span class="string">"appendTest"</span>,<span class="string">"world"</span>);</span><br><span class="line">System.out.println(template.opsForValue().get(<span class="string">"appendTest"</span>));</span><br><span class="line">结果:Hello</span><br><span class="line">Helloworld</span><br></pre></td></tr></table></figure><h3 id="get-String-get-K-key-long-start-long-end"><a href="#get-String-get-K-key-long-start-long-end" class="headerlink" title="get String get(K key, long start, long end)"></a>get String get(K key, long start, long end)</h3><p>截取key所对应的value字符串</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></pre></td><td class="code"><pre><span class="line">appendTest对应的value为Helloworld</span><br><span class="line">System.out.println(<span class="string">"*********"</span>+template.opsForValue().get(<span class="string">"appendTest"</span>,<span class="number">0</span>,<span class="number">5</span>));</span><br><span class="line">结果:*********Hellow</span><br><span class="line">System.out.println(<span class="string">"*********"</span>+template.opsForValue().get(<span class="string">"appendTest"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:*********Helloworld</span><br><span class="line">System.out.println(<span class="string">"*********"</span>+template.opsForValue().get(<span class="string">"appendTest"</span>,-<span class="number">3</span>,-<span class="number">1</span>));</span><br><span class="line">结果:*********rld</span><br></pre></td></tr></table></figure><h3 id="size-Long-size-K-key"><a href="#size-Long-size-K-key" class="headerlink" title="size Long size(K key)"></a>size Long size(K key)</h3><p>返回key所对应的value值得长度</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().set(<span class="string">"key"</span>,<span class="string">"hello world"</span>);</span><br><span class="line">System.out.println(<span class="string">"***************"</span>+template.opsForValue().size(<span class="string">"key"</span>));</span><br><span class="line">结果:***************<span class="number">11</span></span><br></pre></td></tr></table></figure><h3 id="setBit-Boolean-setBit-K-key-long-offset-boolean-value"><a href="#setBit-Boolean-setBit-K-key-long-offset-boolean-value" class="headerlink" title="setBit Boolean setBit(K key, long offset, boolean value)"></a>setBit Boolean setBit(K key, long offset, boolean value)</h3><p>对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit) key键对应的值value对应的ascii码,在offset的位置(从左向右数)变为value</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></pre></td><td class="code"><pre><span class="line">template.opsForValue().set(<span class="string">"bitTest"</span>,<span class="string">"a"</span>);</span><br><span class="line"><span class="comment">// 'a' 的ASCII码是 97。转换为二进制是:01100001</span></span><br><span class="line"><span class="comment">// 'b' 的ASCII码是 98 转换为二进制是:01100010</span></span><br><span class="line"><span class="comment">// 'c' 的ASCII码是 99 转换为二进制是:01100011</span></span><br><span class="line"><span class="comment">//因为二进制只有0和1,在setbit中true为1,false为0,因此我要变为'b'的话第六位设置为1,第七位设置为0</span></span><br><span class="line">template.opsForValue().setBit(<span class="string">"bitTest"</span>,<span class="number">6</span>, <span class="keyword">true</span>);</span><br><span class="line">template.opsForValue().setBit(<span class="string">"bitTest"</span>,<span class="number">7</span>, <span class="keyword">false</span>);</span><br><span class="line">System.out.println(template.opsForValue().get(<span class="string">"bitTest"</span>));</span><br><span class="line">结果:b</span><br></pre></td></tr></table></figure><h3 id="K-key-long-offset"><a href="#K-key-long-offset" class="headerlink" title="(K key, long offset)"></a>(K key, long offset)</h3><p>获取键对应值的ascii码的在offset处位值</p><figure class="highlight java"><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">System.out.println(template.opsForValue().getBit(<span class="string">"bitTest"</span>,<span class="number">7</span>));</span><br><span class="line">结果:<span class="keyword">false</span></span><br></pre></td></tr></table></figure><h2 id="Redis的List数据结构"><a href="#Redis的List数据结构" class="headerlink" title="Redis的List数据结构"></a>Redis的List数据结构</h2><p>这边我们把RedisTemplate序列化方式改回之前的</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></pre></td><td class="code"><pre><span class="line">Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = <span class="keyword">new</span> Jackson2JsonRedisSerializer<Object>(Object.class);</span><br><span class="line">ObjectMapper om = <span class="keyword">new</span> ObjectMapper();</span><br><span class="line">om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);</span><br><span class="line">om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);</span><br><span class="line">jackson2JsonRedisSerializer.setObjectMapper(om);</span><br><span class="line"></span><br><span class="line">RedisTemplate<String, Object> template = <span class="keyword">new</span> RedisTemplate<String, Object>();</span><br><span class="line">template.setKeySerializer(jackson2JsonRedisSerializer);</span><br><span class="line">template.setValueSerializer(jackson2JsonRedisSerializer);</span><br><span class="line">template.setHashKeySerializer(jackson2JsonRedisSerializer);</span><br><span class="line">template.setHashValueSerializer(jackson2JsonRedisSerializer);</span><br></pre></td></tr></table></figure><p>public interface ListOperations<K,V><br>Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)<br>ListOperations专门操作list列表</p><h3 id="List-range-K-key-long-start-long-end"><a href="#List-range-K-key-long-start-long-end" class="headerlink" title="List range(K key, long start, long end)"></a>List<V> range(K key, long start, long end)</h3><p>返回存储在键中的列表的指定元素。偏移开始和停止是基于零的索引,其中0是列表的第一个元素(列表的头部),1是下一个元素</p><figure class="highlight"><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">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[c#, c++, python, java, c#, c#]</span><br></pre></td></tr></table></figure><h3 id="void-trim-K-key-long-start-long-end"><a href="#void-trim-K-key-long-start-long-end" class="headerlink" title="void trim(K key, long start, long end)"></a>void trim(K key, long start, long end)</h3><p>修剪现有列表,使其只包含指定的指定范围的元素,起始和停止都是基于0的索引</p><figure class="highlight"><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">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">template.opsForList().trim(<span class="string">"list"</span>,<span class="number">1</span>,-<span class="number">1</span>);<span class="comment">//裁剪第一个元素</span></span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[c#, c++, python, java, c#, c#]</span><br><span class="line">[c++, python, java, c#, c#]</span><br></pre></td></tr></table></figure><h3 id="Long-size-K-key"><a href="#Long-size-K-key" class="headerlink" title="Long size(K key)"></a>Long size(K key)</h3><p>返回存储在键中的列表的长度。如果键不存在,则将其解释为空列表,并返回0。当key存储的值不是列表时返回错误。</p><figure class="highlight java"><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">System.out.println(template.opsForList().size(<span class="string">"list"</span>));</span><br><span class="line">结果:<span class="number">6</span></span><br></pre></td></tr></table></figure><h3 id="Long-leftPush-K-key-V-value"><a href="#Long-leftPush-K-key-V-value" class="headerlink" title="Long leftPush(K key, V value)"></a>Long leftPush(K key, V value)</h3><p>将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)</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></pre></td><td class="code"><pre><span class="line">template.opsForList().leftPush(<span class="string">"list"</span>,<span class="string">"java"</span>);</span><br><span class="line">template.opsForList().leftPush(<span class="string">"list"</span>,<span class="string">"python"</span>);</span><br><span class="line">template.opsForList().leftPush(<span class="string">"list"</span>,<span class="string">"c++"</span>);</span><br><span class="line">结果:返回的结果为推送操作后的列表的长度</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><h3 id="Long-leftPushAll-K-key-V…-values"><a href="#Long-leftPushAll-K-key-V…-values" class="headerlink" title="Long leftPushAll(K key, V… values)"></a>Long leftPushAll(K key, V… values)</h3><p>批量把一个数组插入到列表中</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></pre></td><td class="code"><pre><span class="line">String[] stringarrays = <span class="keyword">new</span> String[]{<span class="string">"1"</span>,<span class="string">"2"</span>,<span class="string">"3"</span>};</span><br><span class="line">template.opsForList().leftPushAll(<span class="string">"listarray"</span>,stringarrays);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listarray"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[<span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-leftPushAll-K-key-Collection-values"><a href="#Long-leftPushAll-K-key-Collection-values" class="headerlink" title="Long leftPushAll(K key, Collection values)"></a>Long leftPushAll(K key, Collection<V> values)</h3><p>批量把一个集合插入到列表中</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></pre></td><td class="code"><pre><span class="line">List<Object> strings = <span class="keyword">new</span> ArrayList<Object>();</span><br><span class="line">strings.add(<span class="string">"1"</span>);</span><br><span class="line">strings.add(<span class="string">"2"</span>);</span><br><span class="line">strings.add(<span class="string">"3"</span>);</span><br><span class="line">template.opsForList().leftPushAll(<span class="string">"listcollection4"</span>, strings);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listcollection4"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[<span class="number">3</span>, <span class="number">2</span>, <span class="number">1</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-leftPushIfPresent-K-key-V-value"><a href="#Long-leftPushIfPresent-K-key-V-value" class="headerlink" title="Long leftPushIfPresent(K key, V value)"></a>Long leftPushIfPresent(K key, V value)</h3><p>只有存在key对应的列表才能将这个value值插入到key所对应的列表中</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().leftPushIfPresent(<span class="string">"leftPushIfPresent"</span>,<span class="string">"aa"</span>));</span><br><span class="line">System.out.println(template.opsForList().leftPushIfPresent(<span class="string">"leftPushIfPresent"</span>,<span class="string">"bb"</span>));</span><br><span class="line">==========分割线===========</span><br><span class="line">System.out.println(template.opsForList().leftPush(<span class="string">"leftPushIfPresent"</span>,<span class="string">"aa"</span>));</span><br><span class="line">System.out.println(template.opsForList().leftPushIfPresent(<span class="string">"leftPushIfPresent"</span>,<span class="string">"bb"</span>));</span><br><span class="line">结果:</span><br><span class="line"><span class="number">0</span></span><br><span class="line"><span class="number">0</span></span><br><span class="line">==========分割线===========</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br></pre></td></tr></table></figure><h3 id="Long-leftPush-K-key-V-pivot-V-value"><a href="#Long-leftPush-K-key-V-pivot-V-value" class="headerlink" title="Long leftPush(K key, V pivot, V value)"></a>Long leftPush(K key, V pivot, V value)</h3><p>把value值放到key对应列表中pivot值的左面,如果pivot值存在的话</p><figure class="highlight"><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">template.opsForList().leftPush(<span class="string">"list"</span>,<span class="string">"java"</span>,<span class="string">"oc"</span>);</span><br><span class="line">System.out.print(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[c++, python, oc, java, c#, c#]</span><br></pre></td></tr></table></figure><h3 id="Long-rightPush-K-key-V-value"><a href="#Long-rightPush-K-key-V-value" class="headerlink" title="Long rightPush(K key, V value)"></a>Long rightPush(K key, V value)</h3><p>将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从右边插入)</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></pre></td><td class="code"><pre><span class="line">template.opsForList().rightPush(<span class="string">"listRight"</span>,<span class="string">"java"</span>);</span><br><span class="line">template.opsForList().rightPush(<span class="string">"listRight"</span>,<span class="string">"python"</span>);</span><br><span class="line">template.opsForList().rightPush(<span class="string">"listRight"</span>,<span class="string">"c++"</span>);</span><br><span class="line">结果:</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><h3 id="Long-rightPushAll-K-key-V…-values"><a href="#Long-rightPushAll-K-key-V…-values" class="headerlink" title="Long rightPushAll(K key, V… values)"></a>Long rightPushAll(K key, V… values)</h3><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></pre></td><td class="code"><pre><span class="line">String[] stringarrays = <span class="keyword">new</span> String[]{<span class="string">"1"</span>,<span class="string">"2"</span>,<span class="string">"3"</span>};</span><br><span class="line">template.opsForList().rightPushAll(<span class="string">"listarrayright"</span>,stringarrays);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listarrayright"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-rightPushAll-K-key-Collection-values"><a href="#Long-rightPushAll-K-key-Collection-values" class="headerlink" title="Long rightPushAll(K key, Collection values)"></a>Long rightPushAll(K key, Collection<V> values)</h3><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></pre></td><td class="code"><pre><span class="line">List<Object> strings = <span class="keyword">new</span> ArrayList<Object>();</span><br><span class="line">strings.add(<span class="string">"1"</span>);</span><br><span class="line">strings.add(<span class="string">"2"</span>);</span><br><span class="line">strings.add(<span class="string">"3"</span>);</span><br><span class="line">template.opsForList().rightPushAll(<span class="string">"listcollectionright"</span>, strings);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listcollectionright"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-rightPushIfPresent-K-key-V-value"><a href="#Long-rightPushIfPresent-K-key-V-value" class="headerlink" title="Long rightPushIfPresent(K key, V value)"></a>Long rightPushIfPresent(K key, V value)</h3><p>只有存在key对应的列表才能将这个value值插入到key所对应的列表中</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().rightPushIfPresent(<span class="string">"rightPushIfPresent"</span>,<span class="string">"aa"</span>));</span><br><span class="line">System.out.println(template.opsForList().rightPushIfPresent(<span class="string">"rightPushIfPresent"</span>,<span class="string">"bb"</span>));</span><br><span class="line">System.out.println(<span class="string">"==========分割线==========="</span>);</span><br><span class="line">System.out.println(template.opsForList().rightPush(<span class="string">"rightPushIfPresent"</span>,<span class="string">"aa"</span>));</span><br><span class="line">System.out.println(template.opsForList().rightPushIfPresent(<span class="string">"rightPushIfPresent"</span>,<span class="string">"bb"</span>));</span><br><span class="line">结果:<span class="number">0</span></span><br><span class="line"><span class="number">0</span></span><br><span class="line">==========分割线===========</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="number">2</span></span><br></pre></td></tr></table></figure><h3 id="Long-rightPush-K-key-V-pivot-V-value"><a href="#Long-rightPush-K-key-V-pivot-V-value" class="headerlink" title="Long rightPush(K key, V pivot, V value)"></a>Long rightPush(K key, V pivot, V value)</h3><p>把value值放到key对应列表中pivot值的右面,如果pivot值存在的话</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">template.opsForList().rightPush(<span class="string">"listRight"</span>,<span class="string">"python"</span>,<span class="string">"oc"</span>);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[java, python, c++]</span><br><span class="line">[java, python, oc, c++]</span><br></pre></td></tr></table></figure><h3 id="void-set-K-key-long-index-V-value"><a href="#void-set-K-key-long-index-V-value" class="headerlink" title="void set(K key, long index, V value)"></a>void set(K key, long index, V value)</h3><p>在列表中index的位置设置value值</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">template.opsForList().set(<span class="string">"listRight"</span>,<span class="number">1</span>,<span class="string">"setValue"</span>);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[java, python, oc, c++]</span><br><span class="line">[java, setValue, oc, c++]</span><br></pre></td></tr></table></figure><h3 id="Long-remove-K-key-long-count-Object-value"><a href="#Long-remove-K-key-long-count-Object-value" class="headerlink" title="Long remove(K key, long count, Object value)"></a>Long remove(K key, long count, Object value)</h3><p>从存储在键中的列表中删除等于值的元素的第一个计数事件。<br>计数参数以下列方式影响操作:<br>count> 0:删除等于从头到尾移动的值的元素。<br>count <0:删除等于从尾到头移动的值的元素。<br>count = 0:删除等于value的所有元素。</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">template.opsForList().remove(<span class="string">"listRight"</span>,<span class="number">1</span>,<span class="string">"setValue"</span>);<span class="comment">//将删除列表中存储的列表中第一次次出现的“setValue”。</span></span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[java, setValue, oc, c++]</span><br><span class="line">[java, oc, c++]</span><br></pre></td></tr></table></figure><h3 id="V-index-K-key-long-index"><a href="#V-index-K-key-long-index" class="headerlink" title="V index(K key, long index)"></a>V index(K key, long index)</h3><p>根据下表获取列表中的值,下标是从0开始的</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForList().range(<span class="string">"listRight"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForList().index(<span class="string">"listRight"</span>,<span class="number">2</span>));</span><br><span class="line">结果:[java, oc, c++]</span><br><span class="line">c++</span><br></pre></td></tr></table></figure><h3 id="V-leftPop-K-key"><a href="#V-leftPop-K-key" class="headerlink" title="V leftPop(K key)"></a>V leftPop(K key)</h3><p>弹出最左边的元素,弹出之后该值在列表中将不复存在</p><figure class="highlight"><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">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForList().leftPop(<span class="string">"list"</span>));</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:</span><br><span class="line">[c++, python, oc, java, c#, c#]</span><br><span class="line">c++</span><br><span class="line">[python, oc, java, c#, c#]</span><br></pre></td></tr></table></figure><h3 id="V-leftPop-K-key-long-timeout-TimeUnit-unit"><a href="#V-leftPop-K-key-long-timeout-TimeUnit-unit" class="headerlink" title="V leftPop(K key, long timeout, TimeUnit unit)"></a>V leftPop(K key, long timeout, TimeUnit unit)</h3><p>移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。<br>用法与 leftPop(K key);一样</p><h3 id="V-rightPop-K-key"><a href="#V-rightPop-K-key" class="headerlink" title="V rightPop(K key);"></a>V rightPop(K key);</h3><p>弹出最右边的元素,弹出之后该值在列表中将不复存在</p><figure class="highlight"><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">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForList().rightPop(<span class="string">"list"</span>));</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[python, oc, java, c#, c#]</span><br><span class="line">c#</span><br><span class="line">[python, oc, java, c#]</span><br></pre></td></tr></table></figure><h3 id="V-rightPop-K-key-long-timeout-TimeUnit-unit"><a href="#V-rightPop-K-key-long-timeout-TimeUnit-unit" class="headerlink" title="V rightPop(K key, long timeout, TimeUnit unit)"></a>V rightPop(K key, long timeout, TimeUnit unit)</h3><p>移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。<br>用法与 rightPop(K key);一样</p><h3 id="V-rightPopAndLeftPush-K-sourceKey-K-destinationKey"><a href="#V-rightPopAndLeftPush-K-sourceKey-K-destinationKey" class="headerlink" title="V rightPopAndLeftPush(K sourceKey, K destinationKey)"></a>V rightPopAndLeftPush(K sourceKey, K destinationKey)</h3><p>用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回。</p><figure class="highlight"><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">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">template.opsForList().rightPopAndLeftPush(<span class="string">"list"</span>,<span class="string">"rightPopAndLeftPush"</span>);</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"list"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForList().range(<span class="string">"rightPopAndLeftPush"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[oc, java,c#]</span><br><span class="line">[oc, java]</span><br><span class="line">[c#]</span><br></pre></td></tr></table></figure><h3 id="V-rightPopAndLeftPush-K-sourceKey-K-destinationKey-long-timeout-TimeUnit-unit"><a href="#V-rightPopAndLeftPush-K-sourceKey-K-destinationKey-long-timeout-TimeUnit-unit" class="headerlink" title="V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit)"></a>V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit)</h3><p>用于移除列表的最后一个元素,并将该元素添加到另一个列表并返回,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。<br>用法与rightPopAndLeftPush(K sourceKey, K destinationKey)一样</p><h2 id="Redis的Hash数据机构"><a href="#Redis的Hash数据机构" class="headerlink" title="Redis的Hash数据机构"></a>Redis的Hash数据机构</h2><p>Redis的散列可以让用户将多个键值对存储到一个Redis键里面。<br>public interface HashOperations<H,HK,HV><br>HashOperations提供一系列方法操作hash</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></pre></td><td class="code"><pre><span class="line">初始数据:</span><br><span class="line"><span class="comment">//template.opsForHash().put("redisHash","name","tom");</span></span><br><span class="line"><span class="comment">//template.opsForHash().put("redisHash","age",26);</span></span><br><span class="line"><span class="comment">//template.opsForHash().put("redisHash","class","6");</span></span><br><span class="line"></span><br><span class="line"><span class="comment">//Map<String,Object> testMap = new HashMap();</span></span><br><span class="line"><span class="comment">//testMap.put("name","jack");</span></span><br><span class="line"><span class="comment">//testMap.put("age",27);</span></span><br><span class="line"><span class="comment">//testMap.put("class","1");</span></span><br><span class="line"><span class="comment">//template.opsForHash().putAll("redisHash1",testMap);</span></span><br></pre></td></tr></table></figure><h3 id="Long-delete-H-key-Object…-hashKeys"><a href="#Long-delete-H-key-Object…-hashKeys" class="headerlink" title="Long delete(H key, Object… hashKeys)"></a>Long delete(H key, Object… hashKeys)</h3><p>删除给定的哈希hashKeys</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().delete(<span class="string">"redisHash"</span>,<span class="string">"name"</span>));</span><br><span class="line">System.out.println(template.opsForHash().entries(<span class="string">"redisHash"</span>));</span><br><span class="line">结果:<span class="number">1</span></span><br><span class="line">{<span class="class"><span class="keyword">class</span></span>=<span class="number">6</span>, age=<span class="number">28.1</span>}</span><br></pre></td></tr></table></figure><h3 id="Boolean-hasKey-H-key-Object-hashKey"><a href="#Boolean-hasKey-H-key-Object-hashKey" class="headerlink" title="Boolean hasKey(H key, Object hashKey)"></a>Boolean hasKey(H key, Object hashKey)</h3><p>确定哈希hashKey是否存在</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().hasKey(<span class="string">"redisHash"</span>,<span class="string">"age"</span>));</span><br><span class="line">System.out.println(template.opsForHash().hasKey(<span class="string">"redisHash"</span>,<span class="string">"ttt"</span>));</span><br><span class="line">结果:<span class="keyword">true</span></span><br><span class="line"><span class="keyword">false</span></span><br></pre></td></tr></table></figure><h3 id="HV-get-H-key-Object-hashKey"><a href="#HV-get-H-key-Object-hashKey" class="headerlink" title="HV get(H key, Object hashKey)"></a>HV get(H key, Object hashKey)</h3><p>从键中的哈希获取给定hashKey的值</p><figure class="highlight java"><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">System.out.println(template.opsForHash().get(<span class="string">"redisHash"</span>,<span class="string">"age"</span>));</span><br><span class="line">结果:<span class="number">26</span></span><br></pre></td></tr></table></figure><h3 id="List-multiGet-H-key-Collection-hashKeys"><a href="#List-multiGet-H-key-Collection-hashKeys" class="headerlink" title="List multiGet(H key, Collection hashKeys)"></a>List<HV> multiGet(H key, Collection<HK> hashKeys)</h3><p>从哈希中获取给定hashKey的值</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></pre></td><td class="code"><pre><span class="line">List<Object> kes = <span class="keyword">new</span> ArrayList<Object>();</span><br><span class="line">kes.add(<span class="string">"name"</span>);</span><br><span class="line">kes.add(<span class="string">"age"</span>);</span><br><span class="line">System.out.println(template.opsForHash().multiGet(<span class="string">"redisHash"</span>,kes));</span><br><span class="line">结果:[jack, <span class="number">28.1</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-increment-H-key-HK-hashKey-long-delta"><a href="#Long-increment-H-key-HK-hashKey-long-delta" class="headerlink" title="Long increment(H key, HK hashKey, long delta)"></a>Long increment(H key, HK hashKey, long delta)</h3><p>通过给定的delta增加散列hashKey的值(整型)</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().get(<span class="string">"redisHash"</span>,<span class="string">"age"</span>));</span><br><span class="line">System.out.println(template.opsForHash().increment(<span class="string">"redisHash"</span>,<span class="string">"age"</span>,<span class="number">1</span>));</span><br><span class="line">结果:<span class="number">26</span></span><br><span class="line"><span class="number">27</span></span><br></pre></td></tr></table></figure><h3 id="Double-increment-H-key-HK-hashKey-double-delta"><a href="#Double-increment-H-key-HK-hashKey-double-delta" class="headerlink" title="Double increment(H key, HK hashKey, double delta)"></a>Double increment(H key, HK hashKey, double delta)</h3><p>通过给定的delta增加散列hashKey的值(浮点数)</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().get(<span class="string">"redisHash"</span>,<span class="string">"age"</span>));</span><br><span class="line">System.out.println(template.opsForHash().increment(<span class="string">"redisHash"</span>,<span class="string">"age"</span>,<span class="number">1.1</span>));</span><br><span class="line">结果:<span class="number">27</span></span><br><span class="line"><span class="number">28.1</span></span><br></pre></td></tr></table></figure><h3 id="Set-keys-H-key"><a href="#Set-keys-H-key" class="headerlink" title="Set keys(H key)"></a>Set<HK> keys(H key)</h3><p>获取key所对应的散列表的key</p><figure class="highlight"><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">System.out.println(template.opsForHash().keys(<span class="string">"redisHash1"</span>));</span><br><span class="line"><span class="comment">//redisHash1所对应的散列表为{class=1, name=jack, age=27}</span></span><br><span class="line">结果:[name, class, age]</span><br></pre></td></tr></table></figure><h3 id="Long-size-H-key"><a href="#Long-size-H-key" class="headerlink" title="Long size(H key)"></a>Long size(H key)</h3><p>获取key所对应的散列表的大小个数</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().size(<span class="string">"redisHash1"</span>));</span><br><span class="line"><span class="comment">//redisHash1所对应的散列表为{class=1, name=jack, age=27}</span></span><br><span class="line">结果:<span class="number">3</span></span><br></pre></td></tr></table></figure><h3 id="void-putAll-H-key-Map-lt-extends-HK-extends-HV-gt-m"><a href="#void-putAll-H-key-Map-lt-extends-HK-extends-HV-gt-m" class="headerlink" title="void putAll(H key, Map<? extends HK, ? extends HV> m)"></a>void putAll(H key, Map<? extends HK, ? extends HV> m)</h3><p>使用m中提供的多个散列字段设置到key对应的散列表中</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></pre></td><td class="code"><pre><span class="line">Map<String,Object> testMap = <span class="keyword">new</span> HashMap();</span><br><span class="line">testMap.put(<span class="string">"name"</span>,<span class="string">"jack"</span>);</span><br><span class="line">testMap.put(<span class="string">"age"</span>,<span class="number">27</span>);</span><br><span class="line">testMap.put(<span class="string">"class"</span>,<span class="string">"1"</span>);</span><br><span class="line">template.opsForHash().putAll(<span class="string">"redisHash1"</span>,testMap);</span><br><span class="line">System.out.println(template.opsForHash().entries(<span class="string">"redisHash1"</span>));</span><br><span class="line">结果:{<span class="class"><span class="keyword">class</span></span>=<span class="number">1</span>, name=jack, age=<span class="number">27</span>}</span><br></pre></td></tr></table></figure><h3 id="void-put-H-key-HK-hashKey-HV-value"><a href="#void-put-H-key-HK-hashKey-HV-value" class="headerlink" title="void put(H key, HK hashKey, HV value)"></a>void put(H key, HK hashKey, HV value)</h3><p>设置散列hashKey的值</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></pre></td><td class="code"><pre><span class="line">template.opsForHash().put(<span class="string">"redisHash"</span>,<span class="string">"name"</span>,<span class="string">"tom"</span>);</span><br><span class="line">template.opsForHash().put(<span class="string">"redisHash"</span>,<span class="string">"age"</span>,<span class="number">26</span>);</span><br><span class="line">template.opsForHash().put(<span class="string">"redisHash"</span>,<span class="string">"class"</span>,<span class="string">"6"</span>);</span><br><span class="line">System.out.println(template.opsForHash().entries(<span class="string">"redisHash"</span>));</span><br><span class="line">结果:{age=<span class="number">26</span>, <span class="class"><span class="keyword">class</span></span>=<span class="number">6</span>, name=tom}</span><br></pre></td></tr></table></figure><h3 id="Boolean-putIfAbsent-H-key-HK-hashKey-HV-value"><a href="#Boolean-putIfAbsent-H-key-HK-hashKey-HV-value" class="headerlink" title="Boolean putIfAbsent(H key, HK hashKey, HV value)"></a>Boolean putIfAbsent(H key, HK hashKey, HV value)</h3><p>仅当hashKey不存在时才设置散列hashKey的值。</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForHash().putIfAbsent(<span class="string">"redisHash"</span>,<span class="string">"age"</span>,<span class="number">30</span>));</span><br><span class="line">System.out.println(template.opsForHash().putIfAbsent(<span class="string">"redisHash"</span>,<span class="string">"kkk"</span>,<span class="string">"kkk"</span>));</span><br><span class="line">结果:<span class="keyword">false</span></span><br><span class="line"><span class="keyword">true</span></span><br></pre></td></tr></table></figure><h3 id="List-values-H-key"><a href="#List-values-H-key" class="headerlink" title="List values(H key)"></a>List<HV> values(H key)</h3><p>获取整个哈希存储的值根据密钥</p><figure class="highlight java"><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">System.out.println(template.opsForHash().values(<span class="string">"redisHash"</span>));</span><br><span class="line">结果:[tom, <span class="number">26</span>, <span class="number">6</span>]</span><br></pre></td></tr></table></figure><h3 id="Map-lt-HK-HV-gt-entries-H-key"><a href="#Map-lt-HK-HV-gt-entries-H-key" class="headerlink" title="Map<HK, HV> entries(H key)"></a>Map<HK, HV> entries(H key)</h3><p>获取整个哈希存储根据密钥</p><figure class="highlight java"><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">System.out.println(template.opsForHash().entries(<span class="string">"redisHash"</span>));</span><br><span class="line">结果:{age=<span class="number">26</span>, <span class="class"><span class="keyword">class</span></span>=<span class="number">6</span>, name=tom}</span><br></pre></td></tr></table></figure><h3 id="Cursor-lt-Map-Entry-lt-HK-HV-gt-gt-scan-H-key-ScanOptions-options"><a href="#Cursor-lt-Map-Entry-lt-HK-HV-gt-gt-scan-H-key-ScanOptions-options" class="headerlink" title="Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options)"></a>Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options)</h3><p>使用Cursor在key的hash中迭代,相当于迭代器。</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></pre></td><td class="code"><pre><span class="line">Cursor<Map.Entry<Object, Object>> curosr = template.opsForHash().scan(<span class="string">"redisHash"</span>, ScanOptions.ScanOptions.NONE);</span><br><span class="line"><span class="keyword">while</span>(curosr.hasNext()){</span><br><span class="line"> Map.Entry<Object, Object> entry = curosr.next();</span><br><span class="line"> System.out.println(entry.getKey()+<span class="string">":"</span>+entry.getValue());</span><br><span class="line">}</span><br><span class="line">结果:age:<span class="number">28.1</span></span><br><span class="line">class:6</span><br><span class="line">kkk:kkk</span><br></pre></td></tr></table></figure><h2 id="Redis的Set数据结构"><a href="#Redis的Set数据结构" class="headerlink" title="Redis的Set数据结构"></a>Redis的Set数据结构</h2><p>Redis的Set是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。<br>Redis 中 集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。<br>public interface SetOperations<K,V><br>SetOperations提供了对无序集合的一系列操作</p><h3 id="Long-add-K-key-V…-values"><a href="#Long-add-K-key-V…-values" class="headerlink" title="Long add(K key, V… values)"></a>Long add(K key, V… values)</h3><p>无序集合中添加元素,返回添加个数<br>也可以直接在add里面添加多个值 如:template.opsForSet().add(“setTest”,”aaa”,”bbb”)</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></pre></td><td class="code"><pre><span class="line">String[] strarrays = <span class="keyword">new</span> String[]{<span class="string">"strarr1"</span>,<span class="string">"sgtarr2"</span>};</span><br><span class="line">System.out.println(template.opsForSet().add(<span class="string">"setTest"</span>, strarrays));</span><br><span class="line">结果:<span class="number">2</span></span><br></pre></td></tr></table></figure><h3 id="Long-remove-K-key-Object…-values"><a href="#Long-remove-K-key-Object…-values" class="headerlink" title="Long remove(K key, Object… values)"></a>Long remove(K key, Object… values)</h3><p>移除集合中一个或多个成员</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></pre></td><td class="code"><pre><span class="line">String[] strarrays = <span class="keyword">new</span> String[]{<span class="string">"strarr1"</span>,<span class="string">"sgtarr2"</span>};</span><br><span class="line">System.out.println(template.opsForSet().remove(<span class="string">"setTest"</span>,strarrays));</span><br><span class="line">结果:<span class="number">2</span></span><br></pre></td></tr></table></figure><h3 id="V-pop-K-key"><a href="#V-pop-K-key" class="headerlink" title="V pop(K key)"></a>V pop(K key)</h3><p>移除并返回集合中的一个随机元素</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForSet().pop(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">结果:bbb</span><br><span class="line">[aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="Boolean-move-K-key-V-value-K-destKey"><a href="#Boolean-move-K-key-V-value-K-destKey" class="headerlink" title="Boolean move(K key, V value, K destKey)"></a>Boolean move(K key, V value, K destKey)</h3><p>将 member 元素从 source 集合移动到 destination 集合</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></pre></td><td class="code"><pre><span class="line">template.opsForSet().move(<span class="string">"setTest"</span>,<span class="string">"aaa"</span>,<span class="string">"setTest2"</span>);</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">结果:[ccc]</span><br><span class="line">[aaa]</span><br></pre></td></tr></table></figure><h3 id="Long-size-K-key-1"><a href="#Long-size-K-key-1" class="headerlink" title="Long size(K key)"></a>Long size(K key)</h3><p>无序集合的大小长度</p><figure class="highlight java"><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">System.out.println(template.opsForSet().size(<span class="string">"setTest"</span>));</span><br><span class="line">结果:<span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="Boolean-isMember-K-key-Object-o"><a href="#Boolean-isMember-K-key-Object-o" class="headerlink" title="Boolean isMember(K key, Object o)"></a>Boolean isMember(K key, Object o)</h3><p>判断 member 元素是否是集合 key 的成员</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForSet().isMember(<span class="string">"setTest"</span>,<span class="string">"ccc"</span>));</span><br><span class="line">System.out.println(template.opsForSet().isMember(<span class="string">"setTest"</span>,<span class="string">"asd"</span>));</span><br><span class="line">结果:<span class="keyword">true</span></span><br><span class="line"><span class="keyword">false</span></span><br></pre></td></tr></table></figure><h3 id="Set-intersect-K-key-K-otherKey"><a href="#Set-intersect-K-key-K-otherKey" class="headerlink" title="Set intersect(K key, K otherKey)"></a>Set<V> intersect(K key, K otherKey)</h3><p>key对应的无序集合与otherKey对应的无序集合求交集</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().intersect(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>));</span><br><span class="line">结果:[aaa, ccc]</span><br><span class="line">[aaa]</span><br><span class="line">[aaa]</span><br></pre></td></tr></table></figure><h3 id="Set-intersect-K-key-Collection-otherKeys"><a href="#Set-intersect-K-key-Collection-otherKeys" class="headerlink" title="Set intersect(K key, Collection otherKeys)"></a>Set<V> intersect(K key, Collection<K> otherKeys)</h3><p>key对应的无序集合与多个otherKey对应的无序集合求交集</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().intersect(<span class="string">"setTest"</span>,strlist));</span><br><span class="line">结果:[aaa, ccc]</span><br><span class="line">[aaa]</span><br><span class="line">[ccc, aaa]</span><br><span class="line">[aaa]</span><br></pre></td></tr></table></figure><h3 id="Long-intersectAndStore-K-key-K-otherKey-K-destKey"><a href="#Long-intersectAndStore-K-key-K-otherKey-K-destKey" class="headerlink" title="Long intersectAndStore(K key, K otherKey, K destKey)"></a>Long intersectAndStore(K key, K otherKey, K destKey)</h3><p>key无序集合与otherkey无序集合的交集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().intersectAndStore(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>,<span class="string">"destKey1"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"destKey1"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line"><span class="number">2</span></span><br><span class="line">[aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="Long-intersectAndStore-K-key-Collection-otherKeys-K-destKey"><a href="#Long-intersectAndStore-K-key-Collection-otherKeys-K-destKey" class="headerlink" title="Long intersectAndStore(K key, Collection otherKeys, K destKey)"></a>Long intersectAndStore(K key, Collection<K> otherKeys, K destKey)</h3><p>key对应的无序集合与多个otherKey对应的无序集合求交集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest3:"</span> + template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().intersectAndStore(<span class="string">"setTest"</span>,strlist,<span class="string">"destKey2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().members(<span class="string">"destKey2"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">setTest3:[ccc, aaa]</span><br><span class="line"><span class="number">2</span></span><br><span class="line">[aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="Set-union-K-key-K-otherKey"><a href="#Set-union-K-key-K-otherKey" class="headerlink" title="Set union(K key, K otherKey)"></a>Set<V> union(K key, K otherKey)</h3><p>key无序集合与otherKey无序集合的并集</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().union(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">[ccc, aaa, ddd, bbb]</span><br></pre></td></tr></table></figure><h3 id="Set-union-K-key-Collection-otherKeys"><a href="#Set-union-K-key-Collection-otherKeys" class="headerlink" title="Set union(K key, Collection otherKeys)"></a>Set<V> union(K key, Collection<K> otherKeys)</h3><p>key无序集合与多个otherKey无序集合的并集</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest3:"</span> + template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().union(<span class="string">"setTest"</span>,strlist));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">setTest3:[xxx, ccc, aaa]</span><br><span class="line">[ddd, xxx, bbb, aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="Long-unionAndStore-K-key-K-otherKey-K-destKey"><a href="#Long-unionAndStore-K-key-K-otherKey-K-destKey" class="headerlink" title="Long unionAndStore(K key, K otherKey, K destKey)"></a>Long unionAndStore(K key, K otherKey, K destKey)</h3><p>key无序集合与otherkey无序集合的并集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().unionAndStore(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>,<span class="string">"unionAndStoreTest1"</span>));</span><br><span class="line">System.out.println(<span class="string">"unionAndStoreTest1:"</span> + template.opsForSet().members(<span class="string">"unionAndStoreTest1"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">unionAndStoreTest1:[ccc, aaa, ddd, bbb]</span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure><h3 id="Long-unionAndStore-K-key-Collection-otherKeys-K-destKey"><a href="#Long-unionAndStore-K-key-Collection-otherKeys-K-destKey" class="headerlink" title="Long unionAndStore(K key, Collection otherKeys, K destKey)"></a>Long unionAndStore(K key, Collection<K> otherKeys, K destKey)</h3><p>key无序集合与多个otherkey无序集合的并集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest3:"</span> + template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().unionAndStore(<span class="string">"setTest"</span>,strlist,<span class="string">"unionAndStoreTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"unionAndStoreTest2:"</span> + template.opsForSet().members(<span class="string">"unionAndStoreTest2"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">setTest3:[xxx, ccc, aaa]</span><br><span class="line"><span class="number">5</span></span><br><span class="line">unionAndStoreTest2:[ddd, xxx, bbb, aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="Set-difference-K-key-K-otherKey"><a href="#Set-difference-K-key-K-otherKey" class="headerlink" title="Set difference(K key, K otherKey)"></a>Set<V> difference(K key, K otherKey)</h3><p>key无序集合与otherKey无序集合的差集</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().difference(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">[bbb, ddd]</span><br></pre></td></tr></table></figure><h3 id="Set-difference-K-key-Collection-otherKeys"><a href="#Set-difference-K-key-Collection-otherKeys" class="headerlink" title="Set difference(K key, Collection otherKeys)"></a>Set<V> difference(K key, Collection<K> otherKeys)</h3><p>key无序集合与多个otherKey无序集合的差集</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest3:"</span> + template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().difference(<span class="string">"setTest"</span>,strlist));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">setTest3:[xxx, ccc, aaa]</span><br><span class="line">[bbb, ddd]</span><br></pre></td></tr></table></figure><h3 id="Long-differenceAndStore-K-key-K-otherKey-K-destKey"><a href="#Long-differenceAndStore-K-key-K-otherKey-K-destKey" class="headerlink" title="Long differenceAndStore(K key, K otherKey, K destKey)"></a>Long differenceAndStore(K key, K otherKey, K destKey)</h3><p>key无序集合与otherkey无序集合的差集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(template.opsForSet().differenceAndStore(<span class="string">"setTest"</span>,<span class="string">"setTest2"</span>,<span class="string">"differenceAndStore1"</span>));</span><br><span class="line">System.out.println(<span class="string">"differenceAndStore1:"</span> + template.opsForSet().members(<span class="string">"differenceAndStore1"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line"><span class="number">2</span></span><br><span class="line">differenceAndStore1:[bbb, ddd]</span><br></pre></td></tr></table></figure><h3 id="Long-differenceAndStore-K-key-Collection-otherKeys-K-destKey"><a href="#Long-differenceAndStore-K-key-Collection-otherKeys-K-destKey" class="headerlink" title="Long differenceAndStore(K key, Collection otherKeys, K destKey)"></a>Long differenceAndStore(K key, Collection<K> otherKeys, K destKey)</h3><p>key无序集合与多个otherkey无序集合的差集存储到destKey无序集合中</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest2:"</span> + template.opsForSet().members(<span class="string">"setTest2"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTest3:"</span> + template.opsForSet().members(<span class="string">"setTest3"</span>));</span><br><span class="line">List<String> strlist = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">strlist.add(<span class="string">"setTest2"</span>);</span><br><span class="line">strlist.add(<span class="string">"setTest3"</span>);</span><br><span class="line">System.out.println(template.opsForSet().differenceAndStore(<span class="string">"setTest"</span>,strlist,<span class="string">"differenceAndStore2"</span>));</span><br><span class="line">System.out.println(<span class="string">"differenceAndStore2:"</span> + template.opsForSet().members(<span class="string">"differenceAndStore2"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTest2:[ccc, aaa]</span><br><span class="line">setTest3:[xxx, ccc, aaa]</span><br><span class="line"><span class="number">2</span></span><br><span class="line">differenceAndStore2:[bbb, ddd]</span><br></pre></td></tr></table></figure><p>Set<V> members(K key);<br>返回集合中的所有成员</p><figure class="highlight java"><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">System.out.println(template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">结果:[ddd, bbb, aaa, ccc]</span><br></pre></td></tr></table></figure><h3 id="V-randomMember-K-key"><a href="#V-randomMember-K-key" class="headerlink" title="V randomMember(K key)"></a>V randomMember(K key)</h3><p>随机获取key无序集合中的一个元素</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></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="string">"setTest:"</span> + template.opsForSet().members(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTestrandomMember:"</span> + template.opsForSet().randomMember(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTestrandomMember:"</span> + template.opsForSet().randomMember(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTestrandomMember:"</span> + template.opsForSet().randomMember(<span class="string">"setTest"</span>));</span><br><span class="line">System.out.println(<span class="string">"setTestrandomMember:"</span> + template.opsForSet().randomMember(<span class="string">"setTest"</span>));</span><br><span class="line">结果:setTest:[ddd, bbb, aaa, ccc]</span><br><span class="line">setTestrandomMember:aaa</span><br><span class="line">setTestrandomMember:bbb</span><br><span class="line">setTestrandomMember:aaa</span><br><span class="line">setTestrandomMember:ddd</span><br></pre></td></tr></table></figure><h3 id="Set-distinctRandomMembers-K-key-long-count"><a href="#Set-distinctRandomMembers-K-key-long-count" class="headerlink" title="Set distinctRandomMembers(K key, long count)"></a>Set<V> distinctRandomMembers(K key, long count)</h3><p>获取多个key无序集合中的元素(去重),count表示个数</p><figure class="highlight java"><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">System.out.println(<span class="string">"randomMembers:"</span> + template.opsForSet().distinctRandomMembers(<span class="string">"setTest"</span>,<span class="number">5</span>));</span><br><span class="line">结果:randomMembers:[aaa, bbb, ddd, ccc]</span><br></pre></td></tr></table></figure><h3 id="List-randomMembers-K-key-long-count"><a href="#List-randomMembers-K-key-long-count" class="headerlink" title="List randomMembers(K key, long count)"></a>List<V> randomMembers(K key, long count)</h3><p>获取多个key无序集合中的元素,count表示个数</p><figure class="highlight java"><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">System.out.println(<span class="string">"randomMembers:"</span> + template.opsForSet().randomMembers(<span class="string">"setTest"</span>,<span class="number">5</span>));</span><br><span class="line">结果:randomMembers:[ccc, ddd, ddd, ddd, aaa]</span><br></pre></td></tr></table></figure><h3 id="Cursor-scan-K-key-ScanOptions-options"><a href="#Cursor-scan-K-key-ScanOptions-options" class="headerlink" title="Cursor scan(K key, ScanOptions options)"></a>Cursor<V> scan(K key, ScanOptions options)</h3><p>遍历set</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></pre></td><td class="code"><pre><span class="line">Cursor<Object> curosr = template.opsForSet().scan(<span class="string">"setTest"</span>, ScanOptions.NONE);</span><br><span class="line"><span class="keyword">while</span>(curosr.hasNext()){</span><br><span class="line"> System.out.println(curosr.next());</span><br><span class="line">}</span><br><span class="line">结果:ddd</span><br><span class="line">bbb</span><br><span class="line">aaa</span><br><span class="line">ccc</span><br></pre></td></tr></table></figure><h2 id="Redis的ZSet数据结构"><a href="#Redis的ZSet数据结构" class="headerlink" title="Redis的ZSet数据结构"></a>Redis的ZSet数据结构</h2><p>Redis 有序集合和无序集合一样也是string类型元素的集合,且不允许重复的成员。<br>不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。<br>有序集合的成员是唯一的,但分数(score)却可以重复。<br>public interface ZSetOperations<K,V><br>ZSetOperations提供了一系列方法对有序集合进行操作</p><h3 id="Boolean-add-K-key-V-value-double-score"><a href="#Boolean-add-K-key-V-value-double-score" class="headerlink" title="Boolean add(K key, V value, double score)"></a>Boolean add(K key, V value, double score)</h3><p>新增一个有序集合,存在的话为false,不存在的话为true</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().add(<span class="string">"zset1"</span>,<span class="string">"zset-1"</span>,<span class="number">1.0</span>));</span><br><span class="line">结果:<span class="keyword">true</span></span><br></pre></td></tr></table></figure><h3 id="Long-add-K-key-Set-lt-TypedTuple-gt-tuples"><a href="#Long-add-K-key-Set-lt-TypedTuple-gt-tuples" class="headerlink" title="Long add(K key, Set<TypedTuple> tuples)"></a>Long add(K key, Set<TypedTuple<V>> tuples)</h3><p>新增一个有序集合</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></pre></td><td class="code"><pre><span class="line">ZSetOperations.TypedTuple<Object> objectTypedTuple1 = <span class="keyword">new</span> DefaultTypedTuple<Object>(<span class="string">"zset-5"</span>,<span class="number">9.6</span>);</span><br><span class="line">ZSetOperations.TypedTuple<Object> objectTypedTuple2 = <span class="keyword">new</span> DefaultTypedTuple<Object>(<span class="string">"zset-6"</span>,<span class="number">9.9</span>);</span><br><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = <span class="keyword">new</span> HashSet<ZSetOperations.TypedTuple<Object>>();</span><br><span class="line">tuples.add(objectTypedTuple1);</span><br><span class="line">tuples.add(objectTypedTuple2);</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zset1"</span>,tuples));</span><br><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>, zset-<span class="number">6</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-remove-K-key-Object…-values-1"><a href="#Long-remove-K-key-Object…-values-1" class="headerlink" title="Long remove(K key, Object… values)"></a>Long remove(K key, Object… values)</h3><p>从有序集合中移除一个或者多个元素</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForZSet().remove(<span class="string">"zset1"</span>,<span class="string">"zset-6"</span>));</span><br><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>, zset-<span class="number">6</span>]</span><br><span class="line"><span class="number">1</span></span><br><span class="line">[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>]</span><br></pre></td></tr></table></figure><h3 id="Double-incrementScore-K-key-V-value-double-delta"><a href="#Double-incrementScore-K-key-V-value-double-delta" class="headerlink" title="Double incrementScore(K key, V value, double delta)"></a>Double incrementScore(K key, V value, double delta)</h3><p>增加元素的score值,并返回增加后的值</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().incrementScore(<span class="string">"zset1"</span>,<span class="string">"zset-1"</span>,<span class="number">1.1</span>)); <span class="comment">//原为1.1</span></span><br><span class="line">结果:<span class="number">2.2</span></span><br></pre></td></tr></table></figure><h3 id="Long-rank-K-key-Object-o"><a href="#Long-rank-K-key-Object-o" class="headerlink" title="Long rank(K key, Object o)"></a>Long rank(K key, Object o)</h3><p>返回有序集中指定成员的排名,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForZSet().rank(<span class="string">"zset1"</span>,<span class="string">"zset-2"</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>]</span><br><span class="line"><span class="number">0</span> <span class="comment">//表明排名第一</span></span><br></pre></td></tr></table></figure><h3 id="Long-reverseRank-K-key-Object-o"><a href="#Long-reverseRank-K-key-Object-o" class="headerlink" title="Long reverseRank(K key, Object o)"></a>Long reverseRank(K key, Object o)</h3><p>返回有序集中指定成员的排名,其中有序集成员按分数值递减(从大到小)顺序排列</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForZSet().reverseRank(<span class="string">"zset1"</span>,<span class="string">"zset-2"</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>]</span><br><span class="line"><span class="number">4</span> <span class="comment">//递减之后排到第五位去了</span></span><br></pre></td></tr></table></figure><h3 id="Set-range-K-key-long-start-long-end"><a href="#Set-range-K-key-long-start-long-end" class="headerlink" title="Set range(K key, long start, long end)"></a>Set<V> range(K key, long start, long end)</h3><p>通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().range(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>, zset-<span class="number">5</span>]</span><br></pre></td></tr></table></figure><h3 id="Set-lt-TypedTuple-gt-rangeWithScores-K-key-long-start-long-end"><a href="#Set-lt-TypedTuple-gt-rangeWithScores-K-key-long-start-long-end" class="headerlink" title="Set<TypedTuple> rangeWithScores(K key, long start, long end)"></a>Set<TypedTuple<V>> rangeWithScores(K key, long start, long end)</h3><p>通过索引区间返回有序集合成指定区间内的成员对象,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeWithScores(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-2score:<span class="number">1.2</span></span><br><span class="line">value:zset-1score:<span class="number">2.2</span></span><br><span class="line">value:zset-3score:<span class="number">2.3</span></span><br><span class="line">value:zset-4score:<span class="number">6.6</span></span><br><span class="line">value:zset-5score:<span class="number">9.6</span></span><br></pre></td></tr></table></figure><h3 id="Set-rangeByScore-K-key-double-min-double-max"><a href="#Set-rangeByScore-K-key-double-min-double-max" class="headerlink" title="Set rangeByScore(K key, double min, double max)"></a>Set<V> rangeByScore(K key, double min, double max)</h3><p>通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().rangeByScore(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>]</span><br></pre></td></tr></table></figure><h3 id="Set-lt-TypedTuple-gt-rangeByScoreWithScores-K-key-double-min-double-max"><a href="#Set-lt-TypedTuple-gt-rangeByScoreWithScores-K-key-double-min-double-max" class="headerlink" title="Set<TypedTuple> rangeByScoreWithScores(K key, double min, double max)"></a>Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max)</h3><p>通过分数返回有序集合指定区间内的成员对象,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeByScoreWithScores(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-2score:<span class="number">1.2</span></span><br><span class="line">value:zset-1score:<span class="number">2.2</span></span><br><span class="line">value:zset-3score:<span class="number">2.3</span></span><br></pre></td></tr></table></figure><h3 id="Set-rangeByScore-K-key-double-min-double-max-long-offset-long-count"><a href="#Set-rangeByScore-K-key-double-min-double-max-long-offset-long-count" class="headerlink" title="Set rangeByScore(K key, double min, double max, long offset, long count)"></a>Set<V> rangeByScore(K key, double min, double max, long offset, long count)</h3><p>通过分数返回有序集合指定区间内的成员,并在索引范围内,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().rangeByScore(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>));</span><br><span class="line">System.out.println(template.opsForZSet().rangeByScore(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>]</span><br><span class="line">[zset-<span class="number">1</span>, zset-<span class="number">3</span>]</span><br></pre></td></tr></table></figure><h3 id="Set-lt-TypedTuple-gt-rangeByScoreWithScores-K-key-double-min-double-max-long-offset-long-count"><a href="#Set-lt-TypedTuple-gt-rangeByScoreWithScores-K-key-double-min-double-max-long-offset-long-count" class="headerlink" title="Set<TypedTuple> rangeByScoreWithScores(K key, double min, double max, long offset, long count)"></a>Set<TypedTuple<V>> rangeByScoreWithScores(K key, double min, double max, long offset, long count)</h3><p>通过分数返回有序集合指定区间内的成员对象,并在索引范围内,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeByScoreWithScores(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>,<span class="number">1</span>,<span class="number">2</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-1score:<span class="number">2.2</span></span><br><span class="line">value:zset-3score:<span class="number">2.3</span></span><br></pre></td></tr></table></figure><h3 id="Set-reverseRange-K-key-long-start-long-end"><a href="#Set-reverseRange-K-key-long-start-long-end" class="headerlink" title="Set reverseRange(K key, long start, long end)"></a>Set<V> reverseRange(K key, long start, long end)</h3><p>通过索引区间返回有序集合成指定区间内的成员,其中有序集成员按分数值递减(从大到小)顺序排列</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().reverseRange(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">5</span>, zset-<span class="number">4</span>, zset-<span class="number">3</span>, zset-<span class="number">1</span>, zset-<span class="number">2</span>]</span><br></pre></td></tr></table></figure><h3 id="Set-lt-TypedTuple-gt-reverseRangeWithScores-K-key-long-start-long-end"><a href="#Set-lt-TypedTuple-gt-reverseRangeWithScores-K-key-long-start-long-end" class="headerlink" title="Set<TypedTuple> reverseRangeWithScores(K key, long start, long end)"></a>Set<TypedTuple<V>> reverseRangeWithScores(K key, long start, long end)</h3><p>通过索引区间返回有序集合成指定区间内的成员对象,其中有序集成员按分数值递减(从大到小)顺序排列</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></pre></td><td class="code"><pre><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().reverseRangeWithScores(<span class="string">"zset1"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-5score:<span class="number">9.6</span></span><br><span class="line">value:zset-4score:<span class="number">6.6</span></span><br><span class="line">value:zset-3score:<span class="number">2.3</span></span><br><span class="line">value:zset-1score:<span class="number">2.2</span></span><br><span class="line">value:zset-2score:<span class="number">1.2</span></span><br></pre></td></tr></table></figure><h3 id="Set-reverseRangeByScore-K-key-double-min-double-max"><a href="#Set-reverseRangeByScore-K-key-double-min-double-max" class="headerlink" title="Set reverseRangeByScore(K key, double min, double max)"></a>Set<V> reverseRangeByScore(K key, double min, double max)</h3><p>与rangeByScore调用方法一样,其中有序集成员按分数值递减(从大到小)顺序排列</p><h3 id="Set-lt-TypedTuple-gt-reverseRangeByScoreWithScores-K-key-double-min-double-max"><a href="#Set-lt-TypedTuple-gt-reverseRangeByScoreWithScores-K-key-double-min-double-max" class="headerlink" title="Set<TypedTuple> reverseRangeByScoreWithScores(K key, double min, double max)"></a>Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, double min, double max)</h3><p>与rangeByScoreWithScores调用方法一样,其中有序集成员按分数值递减(从大到小)顺序排列</p><h3 id="Set-reverseRangeByScore-K-key-double-min-double-max-long-offset-long-count"><a href="#Set-reverseRangeByScore-K-key-double-min-double-max-long-offset-long-count" class="headerlink" title="Set reverseRangeByScore(K key, double min, double max, long offset, long count)"></a>Set<V> reverseRangeByScore(K key, double min, double max, long offset, long count)</h3><p>与rangeByScore调用方法一样,其中有序集成员按分数值递减(从大到小)顺序排列</p><h3 id="Set-lt-TypedTuple-gt-reverseRangeByScoreWithScores-K-key-double-min-double-max-long-offset-long-count"><a href="#Set-lt-TypedTuple-gt-reverseRangeByScoreWithScores-K-key-double-min-double-max-long-offset-long-count" class="headerlink" title="Set<TypedTuple> reverseRangeByScoreWithScores(K key, double min, double max, long offset, long count)"></a>Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, double min, double max, long offset, long count)</h3><p>与rangeByScoreWithScores调用方法一样,其中有序集成员按分数值递减(从大到小)顺序排列</p><h3 id="Long-count-K-key-double-min-double-max"><a href="#Long-count-K-key-double-min-double-max" class="headerlink" title="Long count(K key, double min, double max)"></a>Long count(K key, double min, double max)</h3><p>通过分数返回有序集合指定区间内的成员个数</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().rangeByScore(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>));</span><br><span class="line">System.out.println(template.opsForZSet().count(<span class="string">"zset1"</span>,<span class="number">0</span>,<span class="number">5</span>));</span><br><span class="line">结果:[zset-<span class="number">2</span>, zset-<span class="number">1</span>, zset-<span class="number">3</span>]</span><br><span class="line"><span class="number">3</span></span><br></pre></td></tr></table></figure><h3 id="Long-size-K-key-2"><a href="#Long-size-K-key-2" class="headerlink" title="Long size(K key)"></a>Long size(K key)</h3><p>获取有序集合的成员数,内部调用的就是zCard方法</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().size(<span class="string">"zset1"</span>));</span><br><span class="line">结果:<span class="number">6</span></span><br></pre></td></tr></table></figure><h3 id="Long-zCard-K-key"><a href="#Long-zCard-K-key" class="headerlink" title="Long zCard(K key)"></a>Long zCard(K key)</h3><p>获取有序集合的成员数</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().zCard(<span class="string">"zset1"</span>));</span><br><span class="line">结果:<span class="number">6</span></span><br></pre></td></tr></table></figure><h3 id="Double-score-K-key-Object-o"><a href="#Double-score-K-key-Object-o" class="headerlink" title="Double score(K key, Object o)"></a>Double score(K key, Object o)</h3><p>获取指定成员的score值</p><figure class="highlight java"><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">System.out.println(template.opsForZSet().score(<span class="string">"zset1"</span>,<span class="string">"zset-1"</span>));</span><br><span class="line">结果:<span class="number">2.2</span></span><br></pre></td></tr></table></figure><h3 id="Long-removeRange-K-key-long-start-long-end"><a href="#Long-removeRange-K-key-long-start-long-end" class="headerlink" title="Long removeRange(K key, long start, long end)"></a>Long removeRange(K key, long start, long end)</h3><p>移除指定索引位置的成员,其中有序集成员按分数值递增(从小到大)顺序排列</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset2"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForZSet().removeRange(<span class="string">"zset2"</span>,<span class="number">1</span>,<span class="number">2</span>));</span><br><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset2"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">3</span>, zset-<span class="number">4</span>]</span><br><span class="line"><span class="number">2</span></span><br><span class="line">[zset-<span class="number">1</span>, zset-<span class="number">4</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-removeRangeByScore-K-key-double-min-double-max"><a href="#Long-removeRangeByScore-K-key-double-min-double-max" class="headerlink" title="Long removeRangeByScore(K key, double min, double max)"></a>Long removeRangeByScore(K key, double min, double max)</h3><p>根据指定的score值得范围来移除成员</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zset2","zset-1",1.1));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zset2","zset-2",1.2));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zset2","zset-3",2.3));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zset2","zset-4",6.6));</span></span><br><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset2"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">System.out.println(template.opsForZSet().removeRangeByScore(<span class="string">"zset2"</span>,<span class="number">2</span>,<span class="number">3</span>));</span><br><span class="line">System.out.println(template.opsForZSet().range(<span class="string">"zset2"</span>,<span class="number">0</span>,-<span class="number">1</span>));</span><br><span class="line">结果:[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">3</span>,zset-<span class="number">4</span>]</span><br><span class="line"><span class="number">1</span></span><br><span class="line">[zset-<span class="number">1</span>, zset-<span class="number">2</span>, zset-<span class="number">4</span>]</span><br></pre></td></tr></table></figure><h3 id="Long-unionAndStore-K-key-K-otherKey-K-destKey-1"><a href="#Long-unionAndStore-K-key-K-otherKey-K-destKey-1" class="headerlink" title="Long unionAndStore(K key, K otherKey, K destKey)"></a>Long unionAndStore(K key, K otherKey, K destKey)</h3><p>计算给定的一个有序集的并集,并存储在新的 destKey中,key相同的话会把score值相加</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><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">System.out.println(template.opsForZSet().add(<span class="string">"zzset1"</span>,<span class="string">"zset-1"</span>,<span class="number">1.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset1"</span>,<span class="string">"zset-2"</span>,<span class="number">2.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset1"</span>,<span class="string">"zset-3"</span>,<span class="number">3.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset1"</span>,<span class="string">"zset-4"</span>,<span class="number">6.0</span>));</span><br><span class="line"></span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset2"</span>,<span class="string">"zset-1"</span>,<span class="number">1.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset2"</span>,<span class="string">"zset-2"</span>,<span class="number">2.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset2"</span>,<span class="string">"zset-3"</span>,<span class="number">3.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset2"</span>,<span class="string">"zset-4"</span>,<span class="number">6.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset2"</span>,<span class="string">"zset-5"</span>,<span class="number">7.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().unionAndStore(<span class="string">"zzset1"</span>,<span class="string">"zzset2"</span>,<span class="string">"destZset11"</span>));</span><br><span class="line"></span><br><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeWithScores(<span class="string">"destZset11"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-1score:<span class="number">2.0</span></span><br><span class="line">value:zset-2score:<span class="number">4.0</span></span><br><span class="line">value:zset-3score:<span class="number">6.0</span></span><br><span class="line">value:zset-5score:<span class="number">7.0</span></span><br><span class="line">value:zset-4score:<span class="number">12.0</span></span><br></pre></td></tr></table></figure><h3 id="Long-unionAndStore-K-key-Collection-otherKeys-K-destKey-1"><a href="#Long-unionAndStore-K-key-Collection-otherKeys-K-destKey-1" class="headerlink" title="Long unionAndStore(K key, Collection otherKeys, K destKey)"></a>Long unionAndStore(K key, Collection<K> otherKeys, K destKey)</h3><p>计算给定的多个有序集的并集,并存储在新的 destKey中</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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset1","zset-1",1.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset1","zset-2",2.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset1","zset-3",3.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset1","zset-4",6.0));</span></span><br><span class="line"><span class="comment">//</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset2","zset-1",1.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset2","zset-2",2.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset2","zset-3",3.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset2","zset-4",6.0));</span></span><br><span class="line"><span class="comment">//System.out.println(template.opsForZSet().add("zzset2","zset-5",7.0));</span></span><br><span class="line"></span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset3"</span>,<span class="string">"zset-1"</span>,<span class="number">1.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset3"</span>,<span class="string">"zset-2"</span>,<span class="number">2.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset3"</span>,<span class="string">"zset-3"</span>,<span class="number">3.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset3"</span>,<span class="string">"zset-4"</span>,<span class="number">6.0</span>));</span><br><span class="line">System.out.println(template.opsForZSet().add(<span class="string">"zzset3"</span>,<span class="string">"zset-5"</span>,<span class="number">7.0</span>));</span><br><span class="line"></span><br><span class="line">List<String> stringList = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">stringList.add(<span class="string">"zzset2"</span>);</span><br><span class="line">stringList.add(<span class="string">"zzset3"</span>);</span><br><span class="line">System.out.println(template.opsForZSet().unionAndStore(<span class="string">"zzset1"</span>,stringList,<span class="string">"destZset22"</span>));</span><br><span class="line"></span><br><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeWithScores(<span class="string">"destZset22"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-1score:<span class="number">3.0</span></span><br><span class="line">value:zset-2score:<span class="number">6.0</span></span><br><span class="line">value:zset-3score:<span class="number">9.0</span></span><br><span class="line">value:zset-5score:<span class="number">14.0</span></span><br><span class="line">value:zset-4score:<span class="number">18.0</span></span><br></pre></td></tr></table></figure><h3 id="Long-intersectAndStore-K-key-K-otherKey-K-destKey-1"><a href="#Long-intersectAndStore-K-key-K-otherKey-K-destKey-1" class="headerlink" title="Long intersectAndStore(K key, K otherKey, K destKey)"></a>Long intersectAndStore(K key, K otherKey, K destKey)</h3><p>计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中</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></pre></td><td class="code"><pre><span class="line">System.out.println(template.opsForZSet().intersectAndStore(<span class="string">"zzset1"</span>,<span class="string">"zzset2"</span>,<span class="string">"destZset33"</span>));</span><br><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeWithScores(<span class="string">"destZset33"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-1score:<span class="number">2.0</span></span><br><span class="line">value:zset-2score:<span class="number">4.0</span></span><br><span class="line">value:zset-3score:<span class="number">6.0</span></span><br><span class="line">value:zset-4score:<span class="number">12.0</span></span><br></pre></td></tr></table></figure><h3 id="Long-intersectAndStore-K-key-Collection-otherKeys-K-destKey-1"><a href="#Long-intersectAndStore-K-key-Collection-otherKeys-K-destKey-1" class="headerlink" title="Long intersectAndStore(K key, Collection otherKeys, K destKey)"></a>Long intersectAndStore(K key, Collection<K> otherKeys, K destKey)</h3><p>计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中</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></pre></td><td class="code"><pre><span class="line">List<String> stringList = <span class="keyword">new</span> ArrayList<String>();</span><br><span class="line">stringList.add(<span class="string">"zzset2"</span>);</span><br><span class="line">stringList.add(<span class="string">"zzset3"</span>);</span><br><span class="line">System.out.println(template.opsForZSet().intersectAndStore(<span class="string">"zzset1"</span>,stringList,<span class="string">"destZset44"</span>));</span><br><span class="line"></span><br><span class="line">Set<ZSetOperations.TypedTuple<Object>> tuples = template.opsForZSet().rangeWithScores(<span class="string">"destZset44"</span>,<span class="number">0</span>,-<span class="number">1</span>);</span><br><span class="line">Iterator<ZSetOperations.TypedTuple<Object>> iterator = tuples.iterator();</span><br><span class="line"><span class="keyword">while</span> (iterator.hasNext())</span><br><span class="line">{</span><br><span class="line"> ZSetOperations.TypedTuple<Object> typedTuple = iterator.next();</span><br><span class="line"> System.out.println(<span class="string">"value:"</span> + typedTuple.getValue() + <span class="string">"score:"</span> + typedTuple.getScore());</span><br><span class="line">}</span><br><span class="line">结果:value:zset-1score:<span class="number">3.0</span></span><br><span class="line">value:zset-2score:<span class="number">6.0</span></span><br><span class="line">value:zset-3score:<span class="number">9.0</span></span><br><span class="line">value:zset-4score:<span class="number">18.0</span></span><br></pre></td></tr></table></figure><h3 id="Cursor-lt-TypedTuple-gt-scan-K-key-ScanOptions-options"><a href="#Cursor-lt-TypedTuple-gt-scan-K-key-ScanOptions-options" class="headerlink" title="Cursor<TypedTuple> scan(K key, ScanOptions options)"></a>Cursor<TypedTuple<V>> scan(K key, ScanOptions options)</h3><p>遍历zset</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></pre></td><td class="code"><pre><span class="line">Cursor<ZSetOperations.TypedTuple<Object>> cursor = template.opsForZSet().scan(<span class="string">"zzset1"</span>, ScanOptions.NONE);</span><br><span class="line"><span class="keyword">while</span> (cursor.hasNext()){</span><br><span class="line"> ZSetOperations.TypedTuple<Object> item = cursor.next();</span><br><span class="line"> System.out.println(item.getValue() + <span class="string">":"</span> + item.getScore());</span><br><span class="line">}</span><br><span class="line">结果:zset-<span class="number">1</span>:<span class="number">1.0</span></span><br><span class="line">zset-<span class="number">2</span>:<span class="number">2.0</span></span><br><span class="line">zset-<span class="number">3</span>:<span class="number">3.0</span></span><br><span class="line">zset-<span class="number">4</span>:<span class="number">6.0</span></span><br></pre></td></tr></table></figure><p>注:TimeUnit是java.util.concurrent包下面的一个类,表示给定单元粒度的时间段</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></pre></td><td class="code"><pre><span class="line"><span class="comment">//常用的颗粒度</span></span><br><span class="line">TimeUnit.DAYS <span class="comment">//天</span></span><br><span class="line">TimeUnit.HOURS <span class="comment">//小时</span></span><br><span class="line">TimeUnit.MINUTES <span class="comment">//分钟</span></span><br><span class="line">TimeUnit.SECONDS <span class="comment">//秒</span></span><br><span class="line">TimeUnit.MILLISECONDS <span class="comment">//毫秒</span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">介绍Spring RedisTemplate对Redis中5种数据结构的运用</summary>
<category term="Spring" scheme="http://www.imrui.net/categories/Spring/"/>
<category term="Spring" scheme="http://www.imrui.net/tags/Spring/"/>
<category term="Redis" scheme="http://www.imrui.net/tags/Redis/"/>
</entry>
<entry>
<title>Nginx配置Google Fonts反向代理</title>
<link href="http://www.imrui.net/2016/09/19/google-fonts-proxy/"/>
<id>http://www.imrui.net/2016/09/19/google-fonts-proxy/</id>
<published>2016-09-18T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.840Z</updated>
<content type="html"><![CDATA[<p>使用Nginx反向代理谷歌字体库,可缓存加快访问速度。</p><a id="more"></a><h2 id="谷歌字体库"><a href="#谷歌字体库" class="headerlink" title="谷歌字体库"></a>谷歌字体库</h2><p>比如以下地址,虽然不再被墙,但访问有时不太稳定,加载速度慢。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic,700italic</span><br></pre></td></tr></table></figure><h2 id="国内镜像"><a href="#国内镜像" class="headerlink" title="国内镜像"></a>国内镜像</h2><figure class="highlight plain"><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">http://fonts.useso.com/css?family=Source+Sans+Pro:300,400,600,300italic,400italic,600italic</span><br><span class="line">360的,目前已不再维护</span><br><span class="line"></span><br><span class="line">http://fonts.gmirror.org/css?family=Lato:400,700|Roboto+Slab:400,700|Inconsolata:400,700</span><br></pre></td></tr></table></figure><h3 id="使用过程的一些问题"><a href="#使用过程的一些问题" class="headerlink" title="使用过程的一些问题"></a>使用过程的一些问题</h3><ol><li>大部分镜像不支持https</li><li>打开页面的速度寄托在别人的服务器</li><li>特殊网络环境可能无法访问</li></ol><h2 id="搭建反向代理服务"><a href="#搭建反向代理服务" class="headerlink" title="搭建反向代理服务"></a>搭建反向代理服务</h2><p>准备以下应用环境,局域网/公网环境都可以,根据自己需要。</p><ol><li>可访问的IP或域名(例如<code>fonts.yourdomain.com</code>)</li><li>机器环境 linux + nginx</li></ol><p>安装nginx及第三方模块等就忽略了,直接贴上配置文件。</p><h3 id="修改nginx缓存"><a href="#修改nginx缓存" class="headerlink" title="修改nginx缓存"></a>修改nginx缓存</h3><figure class="highlight nginx"><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="comment"># Nginx Cache Settings</span></span><br><span class="line"><span class="attribute">proxy_temp_file_write_size</span> <span class="number">128k</span>;</span><br><span class="line"><span class="attribute">proxy_temp_path</span> /data/nginx_cache/proxy_temp;</span><br><span class="line"><span class="attribute">proxy_cache_path</span> /data/nginx_cache/proxy_cache levels=<span class="number">1</span>:<span class="number">2</span> keys_zone=cache_one:<span class="number">200m</span> inactive=<span class="number">30d</span> max_size=<span class="number">30g</span>;</span><br></pre></td></tr></table></figure><p>其中,<code>/data/nginx_cache/proxy_temp</code>和<code>/data/nginx_cache/proxy_cache</code>是自定义的目录,如不存在请创建目录。</p><figure class="highlight shell"><figcaption><span>script</span></figcaption><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">mkdir -p /data/nginx_cache/proxy_temp</span><br><span class="line">mkdir -p /data/nginx_cache/proxy_cache</span><br></pre></td></tr></table></figure><p>缓存时间为30天,里面各参数根据自己需要进行修改。</p><h3 id="反向代理配置"><a href="#反向代理配置" class="headerlink" title="反向代理配置"></a>反向代理配置</h3><p>fonts.yourdomain.com.conf</p><figure class="highlight nginx"><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><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">upstream</span> google {</span><br><span class="line"> <span class="attribute">server</span> fonts.googleapis.com:<span class="number">443</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="attribute">upstream</span> gstatic {</span><br><span class="line"> <span class="attribute">server</span> fonts.gstatic.com:<span class="number">443</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">server</span> {</span><br><span class="line"> <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">server_name</span> fonts.yourdomain.com;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">resolver</span> <span class="number">8.8.8.8</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">access_log</span> /data/logs/nginx/fonts.yourdomain.com.access.log main;</span><br><span class="line"> <span class="attribute">error_log</span> /data/logs/nginx/fonts.yourdomain.com.<span class="literal">error</span>.log <span class="literal">info</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> /css {</span><br><span class="line"> <span class="attribute">sub_filter</span> <span class="string">'fonts.gstatic.com'</span> <span class="string">'fonts.yourdomain.com'</span>;</span><br><span class="line"> <span class="attribute">sub_filter_once</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">sub_filter_types</span> text/css;</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.googleapis.com;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Accept-Encoding <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://google;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> /icon {</span><br><span class="line"> <span class="attribute">sub_filter</span> <span class="string">'fonts.gstatic.com'</span> <span class="string">'fonts.yourdomain.com'</span>;</span><br><span class="line"> <span class="attribute">sub_filter_once</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">sub_filter_types</span> text/css;</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.googleapis.com;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Accept-Encoding <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://google;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> / {</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.gstatic.com;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://gstatic;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">server</span> {</span><br><span class="line"> <span class="attribute">listen</span> <span class="number">443</span> ssl;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">ssl</span> <span class="literal">on</span>;</span><br><span class="line"> <span class="attribute">ssl_certificate</span> /root/ssl/css.crt;</span><br><span class="line"> <span class="attribute">ssl_certificate_key</span> /root/ssl/css.key;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">ssl_prefer_server_ciphers</span> <span class="literal">on</span>;</span><br><span class="line"> <span class="attribute">ssl_dhparam</span> /etc/ssl/certs/dhparam.pem;</span><br><span class="line"> <span class="attribute">ssl_protocols</span> TLSv1 TLSv1.<span class="number">1</span> TLSv1.<span class="number">2</span>;</span><br><span class="line"> <span class="attribute">ssl_ciphers</span> <span class="string">"EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH+aRSA+RC4 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"</span>;</span><br><span class="line"> <span class="attribute">keepalive_timeout</span> <span class="number">70</span>;</span><br><span class="line"> <span class="attribute">ssl_session_cache</span> shared:SSL:<span class="number">10m</span>;</span><br><span class="line"> <span class="attribute">ssl_session_timeout</span> <span class="number">10m</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">server_name</span> fonts.yourdomain.com;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">resolver</span> <span class="number">8.8.8.8</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> /css {</span><br><span class="line"> <span class="attribute">sub_filter</span> <span class="string">'fonts.gstatic.com'</span> <span class="string">'fonts.yourdomain.com'</span>;</span><br><span class="line"> <span class="attribute">sub_filter_once</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">sub_filter_types</span> text/css;</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.googleapis.com;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Accept-Encoding <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://google;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> /icon {</span><br><span class="line"> <span class="attribute">sub_filter</span> <span class="string">'fonts.gstatic.com'</span> <span class="string">'fonts.yourdomain.com'</span>;</span><br><span class="line"> <span class="attribute">sub_filter_once</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">sub_filter_types</span> text/css;</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.googleapis.com;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Accept-Encoding <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://google;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="attribute">location</span> / {</span><br><span class="line"> <span class="attribute">proxy_pass_header</span> Server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host fonts.gstatic.com;</span><br><span class="line"> <span class="attribute">proxy_redirect</span> <span class="literal">off</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP $remote_addr;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Scheme $scheme;</span><br><span class="line"> <span class="attribute">proxy_pass</span> https://gstatic;</span><br><span class="line"> <span class="attribute">proxy_cache</span> cache_one;</span><br><span class="line"> <span class="attribute">proxy_cache_valid</span> <span class="number">200</span> <span class="number">304</span> <span class="number">365d</span>;</span><br><span class="line"> <span class="attribute">proxy_cache_key</span> $host$uri$is_args$args;</span><br><span class="line"> <span class="attribute">expires</span> max;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>使用Nginx反向代理谷歌字体库,可缓存加快访问速度。</p></summary>
<category term="Nginx" scheme="http://www.imrui.net/categories/Nginx/"/>
<category term="Nginx" scheme="http://www.imrui.net/tags/Nginx/"/>
</entry>
<entry>
<title>《三体》书评</title>
<link href="http://www.imrui.net/2016/07/06/%E4%B8%89%E4%BD%93%E4%B9%A6%E8%AF%84/"/>
<id>http://www.imrui.net/2016/07/06/%E4%B8%89%E4%BD%93%E4%B9%A6%E8%AF%84/</id>
<published>2016-07-05T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.840Z</updated>
<content type="html"><![CDATA[<p>转载一篇《三体》书评</p><p>以下是我生活中的朋友,花了6个多小时(当然是熬夜)为我而赶制的《三体》评介,让我很感动。不管我内心上多么抵触类型小说,单凭他的这份真诚,我也要(在今年内)将《三体》读完。不赘言,将其精彩分享: 我读书一直是一目十行,速战速决,看完后基本上记不住主角的名字,故事情节也模模糊糊。特别是《三体》这样,从地球写到三体星系再写到宇宙的尽头、从文革写到地球毁灭再写到时间的终结,从三维空间写到十维空间再写到高维粒子的低维展开,从化学飞船写到曲率引擎再写到光墓安全声明,混沌迷乱,支离破碎。但它带给我的冲击却是实实在在的,就如同高维物体在低维空间的投影一样真实,一本书能做到这一点,足够了!</p><p>下面几个关键词是我一目十行之后印象最深的地方,姑且可作为书评。</p><h2 id="【科技】"><a href="#【科技】" class="headerlink" title="【科技】"></a>【科技】</h2><p>作为一个硬科幻的长篇小说,对科技的描述是核心所在,对于一个科幻迷或伪科幻迷来说(例如我),硬科幻总是能让人读起来酣畅淋漓。《三体》中对人类、三体人和诸神(能够改变物理规则的超级智慧生物)的科技都有过细致的描写或者想象,特别是对人类的科技发展(或者说是绝望中的科学探索)的过程着墨甚多,重点是光速和维度。我觉得《三体》写科技有两个特点:一是写了一个科学发展的过程。非科幻迷根本不用为文中各种没有听说过的专业术语发愁,你不用理解它到底是什么原理,只管随着故事往前走,你就能明白它的作用。所以这个很硬很硬的硬科幻小说,其实读起来一点都不费劲,科技描写是情节设计的需要,当然也是满足读者好奇心和探索欲的需要。(后来我找了不少书,恶补了不少关于引力波、中子波、曲率、维度、宇宙噪声、微波背景辐射、反物质等等知识,最后当然是没看懂)。二是将科技的发展推到了极致。什么是极致,就是想象力之外。呵呵,你能想象维度攻击吗?你能想象改变物理规则吗?你能想象多维空间吗?你能想象控制光速吗?你能想象游离在时间之外吗?你能想象纯能体吗?最后,你能想象宇宙中的生物能够重新规划设计宇宙吗(归零行动)?</p><p>什么是科技?是汽车、计算机和宇宙飞船?不是,科技是人类仰望星空时的梦想,科技是人类掌握自身命运的钥匙,科技是宇宙中最伟大的美,科技是人类对自身的终极关怀。从这个意义上讲,《三体》对科技的描述和设定,超越了作为一个科幻小说的所需要的范畴,甚至直指人类探索宇宙和生命终极意义的不懈努力。 </p><h2 id="【爱情】"><a href="#【爱情】" class="headerlink" title="【爱情】"></a>【爱情】</h2><p>《三体》对爱情的描写不多,在一个如此庞大叙事的小说里,是一个容易被忽略的地方,但云天明和程心的爱情(我觉得更应该是云天明一个人的爱情),却被我扎扎实实的记住了。云天明从小孤僻受冷落(貌似还有反人类倾向),只是因为程心让他觉得温暖和平等,就义无反顾的爱上了她(程心不爱他也不知道他爱她)。我不想讲这种爱有没有意义,我只想说后来云天明为程心的付出:他为她买了一颗星星(浪漫),他为她将自己的大脑送给了三体舰队(无畏),他为她冒死讲了那三个童话(执着),他为她留下了一个小宇宙(付出)。云天明靠自己一个人的力量,在这个宇宙黑暗森林严酷的法则下,给程心、给人类留下了最后也是唯一的一个生存的希望,尽管人类没有能够把握住。但是他依然默默的坚持、执着的付出,生命对他来说早已没有了意义,只有爱才是他活下来的唯一力量。</p><p>爱情是什么?可能也必须有千万种的答案,我不知道。云天明的爱情又是什么?是义无反顾?是默默付出?不是,他的爱是:因为爱一个人而爱整个人类。我始终不认为云天明拯救人类的行动是出于对人类的热爱和慈悲,他对人类并没有什么感情,但是因为程心他能做的都做到了。我其实也不理解大刘(作者)这么写想表达什么?是单纯的让我们感动?还是探讨个体与族群的归属与背离?还是一如既往的想把爱情这个千古命题推到极致,放在人类生死存亡的特殊时刻来进行考量?你们自己看吧。</p><h2 id="【绝境】"><a href="#【绝境】" class="headerlink" title="【绝境】"></a>【绝境】</h2><p>《三体》里人类不得不一次又一次的面对绝境,三体人一样,诸神(解释同上)也一样,谁都逃不开黑暗森林法则的束缚,甚至宇宙本身,也从十维降到了三维,不可避免的向绝境一步一步滑去。尽管有一次又一次的绝处逢生,《三体》最终还是指向了一个悲剧的结尾,就像不管多美丽的焰火,最终都会归于沉寂。你别说什么毕竟绽放过啊,历史会记住我们的,呵呵,大刘(作者)不给你这个机会,宇宙归零(没有时间之外的小宇宙了),什么都不会存在,包括时间和历史,新生的宇宙对过去一无所知。</p><p>我始终认为“面壁者计划”是小说中最美妙的构思(自己看吧,剧透就没意思了),也是最打动人心的绝境逢生的故事;而三体舰队飞向地球,然后迫不得已调转方向离开地球,驶向茫茫太空深处,是最悲壮最无奈的绝处求生的故事(特别喜欢这一点,大刘没有让三体人成为类似星际争霸虫族那样的掠夺者,他们有自己的痛苦和挣扎,有自己的梦想和奋斗,所以这不是一部星际战争小说,请不要这么侮辱它)。</p><p>是不是只有写孤注一掷的徒劳挣扎才能有如此入人心魄的力量?还是在黑暗森林法则的设定下,这,本身就是宿命!说到这里,我忽然想起当年看今何在的《悟空传》的时候,封面上有这么几句话:“我要这天,再遮不住我眼;我要这地,再埋不了我心;我要众生,皆明白我意;我要诸佛,都烟消云散!”这么多年了,我一直认为这是悟空向命运的挑战,现在看来,这何尝不是他绝望的怒吼!</p><h2 id="【图景】"><a href="#【图景】" class="headerlink" title="【图景】"></a>【图景】</h2><p>基于小说的主旨,前面的内容的确有点悲哀,是不是觉得心情沉重?那现在来点轻松的。王小波说:生活一定要有趣,同理可证,一本有趣的书才会有读者。《三体》是一本有趣的书,当然我不是指它的内容好笑,而是说,它能给你一些不一样的体验。《三体》讲述的是星球、星系和星云,讲述的是二维、四维和十维,讲述的是宇宙边缘、时间尽头和生命终点,总之一句话,讲述的是你做梦都不会梦到的东西。如何去想象?大刘给你放电影,带我们窥视他的宇宙。他用最细致的笔墨为你描绘出他脑海中的想象图景,例如:从四维空间看三维、空间坟墓、三体星系,智子的蚀刻,最经典的当然是二维化中的太阳系。那是一种在清冷星光下的凄美,那是一种一望无际的悲壮,那是一种穿过星际尘埃看到宇宙尽头的寂寞,呵呵,想象有多美,你就能看到多美。</p><p>《三体》中像这样细致的景物描写,比比皆是。就像当初看《独立日》,一个镜头记得特别清楚,就是外星飞船在火烧云的挟裹中,到达了全球各大城市上空,电影里给出了一组连续不断的闪现,华盛顿–纽约–东京–莫斯科……,就像我读完《三体》一样,脑子里总有好多的画面在不停的闪现,三体星系–空间坟墓–智子蚀刻–水滴……,等等,我有点晕了。</p><h2 id="【尺度-】"><a href="#【尺度-】" class="headerlink" title="【尺度 】"></a>【尺度 】</h2><p>这几年我有两种东西看的最多,一个是网上的各种小段子,另一种就是科幻类的东西。段子吗,都是调侃现实的,网上高人多呢,从段子里不仅能看到诡辩、狡辩,甚至还有点思辨的味道;科幻吗,都是超越现实的(不喜欢看玄幻,那是逃避现实的),有时候遇到些困难和迷茫,把它放在光年的尺度上去想一想,一切就豁然开朗。(顺便说一句什么是现实,现实就是这么晚了我他妈的还在写什么狗屁书评!)</p><p>智子来到地球以后,人类的微观物理研究就停滞了,因此《三体》里的叙事大部分是宏观尺度,是站在整个时空的高度来俯视众生,最小的单位都是恒星级的。例如叶文洁是通过太阳才发出信号,二向箔毁灭的是整个太阳系,诸神的战争引爆恒星是最低级的攻击方式,改变宇宙常数所需的能量是毁灭一个或者几个星系得来的等等。几万年也叫沧海桑田?那不过是一瞬;几光年也叫星际旅行?那不过是串门。在读《三体》的时候,总会不自觉的用诸神的视角来看世界,那种掌控宇宙纤毫、独立时间之外的俯视感,你以前没有体会过吧?什么时间旅行啊,什么银河系联盟啊,什么变形金刚啊,什么大怪兽哥斯拉啊,统统弱爆了,因为你就是宇宙与时间本身。</p><h2 id="【人性-】"><a href="#【人性-】" class="headerlink" title="【人性 】"></a>【人性 】</h2><p>最后不能不来讲讲人性,《三体》里很着力刻画表现人物冲突,例如叶文洁和地球拯救组织,面壁者与破壁者,历任执剑人,蓝色空间号,戴维,还有艾AA等,每个人物的个性鲜明、矛盾突出、冲突剧烈,很多是在人类生死存亡的极端条件下对人性的拷问和道德标准的再思考。看的出大刘在这方面是很认真的,但网上还是有人说大刘写人性差把火候,我觉得,不是写的不好,而是在如此庞大的叙事结构下,对比起来显得单薄了一些,除非再多些500万字。</p><p>还是要说一下程心(网上好像叫她圣母,很多人不喜欢她),她作为执剑人时,因为爱心泛滥(优柔寡断?)一手导致了三体人摧毁了地球的引力波发射装置,威慑失效,差点使人类落入万劫不复的深渊(虽然打击终究到来,地球被毁灭)。按下按钮,是毁灭地球和三体两个文明,只是毁灭的时间不确定,但打击终会到来;不按按钮,是三体人奴役地球,人类作为小白鼠继续生存。她选择了后者!</p><p>如果从宇宙的角度上看,三体和地球都是一样的智慧生物,种族的生生灭灭对久远的时间来说,都是过眼云烟,就如同恒星的毁灭与重生,选择谁不重要,与其都要灭亡,那为人类留下一粒种子到底对不对?如果从人类的角度来看, 与其苟延残喘的活着,不如轰轰烈烈的死去,人之所以为人,是因为不仅仅只是活着,还有尊严!(大刘这点有些太虐心的,总是搞这么难的选择题怎么做啊?)</p><p>我又想起了一个故事,小时看过的郑渊洁的童话《保卫叛逆者》(让我学学云天明,也来讲个童话),故事讲的是M星(比地球科技先进N倍)在地球上投放了四个生命的种子,时间到了就接走进行研究,可是这四个生命(一个男孩、一个女孩、一个老人和一只羊)与地球产生了千丝万缕不可割舍的感情,一致决定做一名地球人。可是M星不同意,坚决要接走这四名叛逆者,同时宣布:如果人类敢阻拦,就毁灭地球。人类进行了全球公投,一致同意保卫叛逆者。结果呢?我把最后一段(部分)抄下来:</p><p>地球是太空中最美丽的星球。</p><p>人类是宇宙中最美妙的生命。</p><p>当人类决定保卫叛逆者时, 以上两条就成了终极真理。</p><p>尊严、人格、精神、爱。</p><p>使人类充实的东西。</p><p>11点整,人们走出房屋,面对天空,等待M星球,地球静极了。在这个时刻,在这个人类为了保卫M星球四个叛逆者而可能献出50亿条生命的时刻,人类获得了永生。</p><p>12点整,数百个笼罩在浅蓝色光环中的飞碟列队出现在天空中,它们缓缓逼近地球。</p><p>最后,以上一切全凭记忆,有与《三体》冲突的地方,那是我没记清楚,呵呵。</p>]]></content>
<summary type="html">地球是太空中最美丽的星球,人类是宇宙中最美妙的生命,当人类决定保卫叛逆者时,以上两条就成了终极真理。尊严、人格、精神、爱,使人类充实的东西。</summary>
<category term="书评影评" scheme="http://www.imrui.net/categories/%E4%B9%A6%E8%AF%84%E5%BD%B1%E8%AF%84/"/>
<category term="书评影评" scheme="http://www.imrui.net/tags/%E4%B9%A6%E8%AF%84%E5%BD%B1%E8%AF%84/"/>
</entry>
<entry>
<title>AngularJS Form</title>
<link href="http://www.imrui.net/2016/07/06/AngularJS-Form/"/>
<id>http://www.imrui.net/2016/07/06/AngularJS-Form/</id>
<published>2016-07-05T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.839Z</updated>
<content type="html"><![CDATA[<p>AngularJS provides a small and well-defined set of constructs that make standard form-based operations easier. For a form, we should consider three points:</p><ol><li>Allowing user input</li><li>Validating those inputs against business rules</li><li>Submitting the data to the backend server</li></ol><p><code>form</code> in AngularJS is extend from html <code>form</code>. Now it’s a directive. When Angular encounters the <code>form</code> tag, it executes the <code>form</code> directive. This directive creates an instance of a special Angular class <code>FormController</code> that is made available to us on the current scope. It’s this controller which provides an API to check and manipulate the state of the form. The same as <code>ng-model</code> which creates an instance of <code>NgModelController</code>.</p><h2 id="Validation"><a href="#Validation" class="headerlink" title="Validation"></a>Validation</h2><p>It already has <strong>built-in support for validating</strong>. Validations are automatically setup by AngularJS according to:</p><ul><li>Input type - text, numbers, e-mails, URLs, radios, checkboxes, and a few others. (such as <code><intput type='email'/></code>).</li><li>Validation attributes - <code>required</code>, <code>min</code>, <code>max</code>, and custom attributes such as <code>ng-pattern</code>, <code>ng-minlength</code>, and <code>ng-maxlength</code>.</li></ul><figure class="highlight html"><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="comment"><!-- novalidate is used to disable browser's native form validation --></span></span><br><span class="line"><span class="tag"><<span class="name">form</span> <span class="attr">name</span>=<span class="string">"form"</span> <span class="attr">novalidate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"form-name"</span>></span>Name:<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">name</span>=<span class="string">"formName"</span> <span class="attr">id</span>=<span class="string">"form-name"</span> <span class="attr">class</span>=<span class="string">"form-control"</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">ng-model</span>=<span class="string">"user.name"</span> <span class="attr">required</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.formName.$error.required && form.formName.$dirty"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Name is required<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><p>So AngularJS will check <code>required</code> attribute. When input is empty, the error label will be shown.</p><p>More details, <code>from</code> directive creates an instace of <code>FormController</code> and published into the scope using the name attribute. Then the inside <code>ng-model</code> create <code>NgModelController</code> and published as a property of the form instance using the name attribute. <code>$error</code> and $dirty is of <code>NgModelController</code>.</p><h3 id="error"><a href="#error" class="headerlink" title="$error"></a>$error</h3><p>It contains a list of all errors for the specific ng-model directive. From above example, <code>required</code> is validation attribute. So to check the validation, we need to follow this format:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">formName.inputName.$error.validation</span><br></pre></td></tr></table></figure><p>For example:</p><figure class="highlight javascript"><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">formName.inputName.$error.required <span class="comment">// For required</span></span><br><span class="line">formName.inputName.$error.number <span class="comment">// For type="number"</span></span><br><span class="line">formName.inputName.$error.pattern <span class="comment">// For ng-pattern</span></span><br></pre></td></tr></table></figure><p><strong>When validation is failing, <code>$error.validation</code> will be true</strong>. We could use it to decide whether to show error messages.</p><h3 id="States-by-ng-model"><a href="#States-by-ng-model" class="headerlink" title="States by ng-model"></a>States by ng-model</h3><p>Besides <code>$error</code>, every element that uses ng-model — including input, textarea, and select — has states defined on the associated model controller:</p><ul><li>$pristine: True if user does not interact with the input. Any updates to the input field and $pristine is set to false. <strong>Once false, it never flips, unless we call the <code>$setPristine()</code> function on the model controller</strong>.</li><li>$dirty: Reverse of $pristine. This is true when the input data has been updated. <strong>This gets reset to false if <code>$setPristine()</code> is called.</strong></li><li>$touched: True if the control ever had focus.</li><li>$untouched: True if the control has never lost focus.</li><li>$valid: True if there are validations defined on the input element and none of them are failing.</li><li>$invalid: True if any of the validations defined on the element are failing.</li></ul><p>So from above example:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.formName.$error.required && form.formName.$dirty"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Name is required<span class="tag"></<span class="name">label</span>></span></span><br></pre></td></tr></table></figure><p>If there is no <code>form.formName.$dirty</code>, the validation message is shown as soon as we load the form. When user never interacts with the input, <code>$pristine</code> is true and <code>$pristine</code> is false. So here, the error message will never be shown until user begins to interact with the input.</p><h3 id="CSS-to-an-input-element"><a href="#CSS-to-an-input-element" class="headerlink" title="CSS to an input element"></a>CSS to an input element</h3><p>Based on the model state, Angular also adds some CSS classes automatically to an input element.</p><ul><li><code>ng-valid/ng-invalid</code>: This is used if the model is valid or not</li><li><code>ng-pristine/ng-dirty</code>: This is used if the model is pristine or ng-dirty.</li><li><code>ng-untouched/ng-touched</code>: This is used when the input is never visited or not.</li><li><code>ng-invalid-<errorkey>/ng-valid-<errorkey></code>: This is used for a specific failed/sucessed validation.</li><li><code>ng-empty/ng-not-empty</code>: This is used if the model is empty or not</li></ul><p>For example, when we check the page by Inspect of browser (not your own code), we could find a list of class already added in input:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"test"</span> <span class="attr">class</span>=<span class="string">"ng-pristine ng-untouched ng-invalid ng-invalid-required"</span> <span class="attr">...</span>></span></span><br></pre></td></tr></table></figure><p>When we type something, it could change to:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"test"</span> <span class="attr">class</span>=<span class="string">"ng-dirty ng-touched ng-valid ng-valid-required"</span> <span class="attr">...</span>></span></span><br></pre></td></tr></table></figure><p>So we could customize input element by these classes according to different state. For example:</p><figure class="highlight css"><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="selector-tag">input</span><span class="selector-class">.ng-invalid</span> {</span><br><span class="line"><span class="attribute">border</span>:<span class="number">1px</span> solid blue;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Points-to-be-careful"><a href="#Points-to-be-careful" class="headerlink" title="Points to be careful"></a>Points to be careful</h3><p><strong>If data in the model is invalid, it does not show up in the view and the view element is empty.</strong></p><p>For example, we set age value as “2” in controller in init method. But on the view, we set a “min=’5’” validation. So the input is empty when we load page.</p><h3 id="Error-messages-by-ng-messages"><a href="#Error-messages-by-ng-messages" class="headerlink" title="Error messages by ng-messages"></a>Error messages by ng-messages</h3><p><code>ng-messages</code> and <code>ng-message</code> that allow us to show/hide error messages with a less verbose syntax. It’s better to use them to show validation errors instead of <code>ng-show/ng-hide</code>.</p><blockquote><p>To use them, we need to add angular-messages.js and add ngMessages module.</p></blockquote><p>Think about a lot of validation in one input:</p><figure class="highlight html"><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">form</span> <span class="attr">name</span>=<span class="string">"form"</span> <span class="attr">novalidate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"test"</span>></span>Demo:<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"number"</span> <span class="attr">name</span>=<span class="string">"test"</span> <span class="attr">id</span>=<span class="string">"test"</span> <span class="attr">ng-model</span>=<span class="string">"user.demo"</span> <span class="attr">min</span>=<span class="string">"1"</span> <span class="attr">ng-pattern</span>=<span class="string">"/^-?\d+$/"</span> <span class="attr">required</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.test.$dirty && form.test.$error.required"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Required<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.test.$dirty && form.test.$error.number"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Must be number<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.test.$dirty && form.test.$error.min"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Min 1<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.test.$dirty && form.test.$error.pattern"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Must in right format<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><p>We could change it to:</p><figure class="highlight html"><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">form</span> <span class="attr">name</span>=<span class="string">"form"</span> <span class="attr">novalidate</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">for</span>=<span class="string">"test"</span>></span>Demo:<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"number"</span> <span class="attr">name</span>=<span class="string">"test"</span> <span class="attr">id</span>=<span class="string">"test"</span> <span class="attr">ng-model</span>=<span class="string">"user.demo"</span> <span class="attr">min</span>=<span class="string">"1"</span> <span class="attr">ng-pattern</span>=<span class="string">"/^-?\d+$/"</span> <span class="attr">required</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-messages</span>=<span class="string">"form.test.$error"</span> <span class="attr">ng-if</span>=<span class="string">"form.test.$dirty"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-message</span>=<span class="string">"required"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Required<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-message</span>=<span class="string">"number"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Must be number<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-message</span>=<span class="string">"min"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Min 1<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">label</span> <span class="attr">ng-message</span>=<span class="string">"pattern"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Must in right format<span class="tag"></<span class="name">label</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><h3 id="ng-messages-multiple"><a href="#ng-messages-multiple" class="headerlink" title="ng-messages-multiple"></a>ng-messages-multiple</h3><p>With above example, each time, only one message will be shown. (Always the upper one has higher priority). When user typed “-1”, both <code>$error.min</code> and <code>$error.pattern</code> will be true. But only error message for <code>min</code> will be shown. To avoid this problem:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-messages</span>=<span class="string">"..."</span> <span class="attr">ng-messages-multiple</span>></span></span><br></pre></td></tr></table></figure><h3 id="Message-reuse-and-override"><a href="#Message-reuse-and-override" class="headerlink" title="Message reuse and override"></a>Message reuse and override</h3><h4 id="Reuse"><a href="#Reuse" class="headerlink" title="Reuse"></a>Reuse</h4><p>Because many messages are the same in a big and complex form, we could reuse them by defining all messages in a sepereted file:</p><figure class="highlight html"><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">div</span> <span class="attr">ng-message</span>=<span class="string">"required"</span>></span>This field is required<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-message</span>=<span class="string">"minlength"</span>></span>This field is too short<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-message</span>=<span class="string">"maxlength"</span>></span>This field is too long<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-message</span>=<span class="string">"required"</span>></span>This field is required<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">ng-message</span>=<span class="string">"email"</span>></span>This needs to be a valid email<span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><p>And include it by <code>ng-messages-include</code>:</p><figure class="highlight html"><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="tag"><<span class="name">div</span> <span class="attr">ng-messages</span>=<span class="string">"form.test.$error"</span> <span class="attr">ng-if</span>=<span class="string">"form.test.$dirty"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">ng-messages-include</span>=<span class="string">"fileName.html"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><h4 id="Override"><a href="#Override" class="headerlink" title="Override"></a>Override</h4><p>If generic messages are not enough to match all input fields, we could override messages defined in the remote template by redefining them within the directive container.</p><figure class="highlight html"><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="tag"><<span class="name">div</span> <span class="attr">ng-messages</span>=<span class="string">"form.test.$error"</span> <span class="attr">ng-if</span>=<span class="string">"form.test.$dirty"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">ng-message</span>=<span class="string">"required"</span>></span>Override the required message<span class="tag"></<span class="name">div</span>></span></span><br><span class="line"> <span class="comment"><!-- Must put override ones above template --></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">ng-messages-include</span>=<span class="string">"fileName.html"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure><h3 id="Custom-validation"><a href="#Custom-validation" class="headerlink" title="Custom validation"></a>Custom validation</h3><p>There two ways to create a custom validation:</p><ol><li>By <a href="https://htmlpreview.github.io/?https://github.com/angular-ui/ui-validate/master/demo/index.html">AngularJS-UI</a></li><li>By our own directive</li></ol><h4 id="By-AngularJS-UI"><a href="#By-AngularJS-UI" class="headerlink" title="By AngularJS-UI"></a>By AngularJS-UI</h4><p>Check <a href="https://htmlpreview.github.io/?https://github.com/angular-ui/ui-validate/master/demo/index.html">AngularJS-UI</a></p><h4 id="By-our-onw-directive"><a href="#By-our-onw-directive" class="headerlink" title="By our onw directive"></a>By our onw directive</h4><p>Define a directive:</p><figure class="highlight javascript"><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="comment">// A validation works like a blacklist, user input can not be in this list</span></span><br><span class="line">app.directive(<span class="string">'blacklist'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{ </span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="built_in">require</span>: <span class="string">'ngModel'</span>,</span><br><span class="line"> link: <span class="function"><span class="keyword">function</span>(<span class="params">scope, elem, attr, ngModel</span>) </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> blacklist = attr.blacklist.split(<span class="string">','</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">//For DOM -> model validation</span></span><br><span class="line"> ngModel.$parsers.unshift(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> valid = blacklist.indexOf(value) === -<span class="number">1</span>;</span><br><span class="line"> ngModel.$setValidity(<span class="string">'blacklist'</span>, valid);</span><br><span class="line"> <span class="keyword">return</span> valid ? value : <span class="literal">undefined</span>;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">//For model -> DOM validation</span></span><br><span class="line"> ngModel.$formatters.unshift(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>) </span>{</span><br><span class="line"> ngModel.$setValidity(<span class="string">'blacklist'</span>, blacklist.indexOf(value) === -<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> value;</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>Add it in input element:</p><figure class="highlight html"><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">form</span> <span class="attr">name</span>=<span class="string">"form"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">name</span>=<span class="string">"type"</span> <span class="attr">ng-model</span>=<span class="string">"data.fruitName"</span> <span class="attr">blacklist</span>=<span class="string">"coconuts,bananas,pears"</span> <span class="attr">required</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">ng-show</span>=<span class="string">"myForm.fruitName.$error.blacklist"</span>></span> The phrase "{{data.fruitName}}" is blacklisted<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">span</span> <span class="attr">ng-show</span>=<span class="string">"myForm.fruitName.$error.required"</span>></span>required<span class="tag"></<span class="name">span</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><h2 id="Submit"><a href="#Submit" class="headerlink" title="Submit"></a>Submit</h2><p>Form in Angular has a different role to play as compared to traditional html form that posts data to the server. We could not find <code>action</code> attribute. So how to submit data?!</p><p>The standard form behavior of posting data to the server using full-page post-back does not make sense with a SPA framework such as AngularJS. In Angular, <strong>all server requests are made through AJAX</strong> invocations originating from controllers, directives, or services. While the traditional one will refresh the whole page.</p><p>So two ways to do it:</p><ol><li>By <code>ng-submit</code></li></ol><figure class="highlight html"><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="tag"><<span class="name">form</span> <span class="attr">name</span>=<span class="string">"form"</span> <span class="attr">ng-submit</span>=<span class="string">"submit()"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">ng-disabled</span>=<span class="string">"form.$invalid"</span>></span>Submit<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><ol start="2"><li>By binding function to the button directly</li></ol><figure class="highlight html"><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="tag"><<span class="name">form</span> <span class="attr">name</span>=<span class="string">"form"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> <span class="attr">ng-click</span>=<span class="string">"submit()"</span> <span class="attr">ng-disabled</span>=<span class="string">"form.$invalid"</span>></span>Submit<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">form</span>></span></span><br></pre></td></tr></table></figure><p>Then, we need to know that from controller also has some APIs and properties as model controller:</p><ul><li><code>$setValidity(validationKey, status, childController)</code>: This is similar to the <code>$setValidity</code> API of <code>NgModelController</code> but is used to set the validation state of the model controller inside form controller.</li><li><code>$setDirty()</code>: This is used to mark the form dirty.</li><li><code>$setPristine()</code>: This is used to make the form pristine. This is often used to mark the form pristine after persisting the data to server. The $setPristine call propagates to all model controllers registered with the form, so <strong>all child inputs are also set back to the pristine state</strong>.</li><li><code>$setUntouched()</code>: This is used to mark the form untouched. This is mostly called in sync with <code>$setPristine</code>, after data is submitted.</li></ul><p>Other than the state manipulation API, there are some handy properties:</p><ul><li>$pristine</li><li>$dirty</li><li>$valid</li><li>$invalid</li><li>$error</li></ul><p>They are similar to model controller properties except for the $error property. It’s in fact a bit more complex. <strong>It aggregates all failures across all contained inputs</strong>. The <code>$error</code>‘s property corresponds to the failing error condition and the value is an array of controllers that are invalid. For example, If there are three <code>required</code> errors, <code>$error.required</code> should be an array with three controllers with this kind of error.</p><p>Any way, we <strong>use the $invalid property of the form controller to verify if there are validation errors before we perform a submit</strong>.</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">button</span> <span class="attr">ng-class</span>=<span class="string">"{'btn-default':formName.$valid,'btn-warning':formName.$invalid}"</span> <span class="attr">ng-click</span>=<span class="string">"submit()"</span> <span class="attr">ng-disabled</span>=<span class="string">"formName.$invalid"</span>></span>Submit<span class="tag"></<span class="name">button</span>></span></span><br></pre></td></tr></table></figure><p>So submit button will only be available when no validation errors of its elements. We also use <code>$valid/$invalid</code> to set css :D.</p><h3 id="Points-to-be-careful-1"><a href="#Points-to-be-careful-1" class="headerlink" title="Points to be careful"></a>Points to be careful</h3><p>In general, instead of disabling a submit button, we prefer to inform user all validation errors when user clicks submit button.<br>If we remove <code>ng-disabled</code>, when user loads the page and clicks submit button directly without touching others. Nothing is submitted as the form is invalid, but validation errors on its child elements like input do not show up at all. Look at one label:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.formName.$error.required && form.formName.$dirty"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Name is required<span class="tag"></<span class="name">label</span>></span></span><br></pre></td></tr></table></figure><p><code>form.formName.$dirty</code> disables validation messages until user has touched the element. It’s why here.</p><p>So in <code>submit()</code>, we could set a flag to fix this problem.</p><figure class="highlight javascript"><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">$scope.submit = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"></span><br><span class="line"> $scope.submitted = <span class="literal">true</span>; <span class="comment">// Will force validations</span></span><br><span class="line"> <span class="keyword">if</span> ($scope.form.$invalid) <span class="keyword">return</span>; <span class="comment">// Nothing will be submitted if invalid!</span></span><br><span class="line"></span><br><span class="line"> $scope.form.$setPristine();</span><br><span class="line"> $scope.submitted = <span class="literal">false</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Other operations</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>So now update label:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"form.formName.$error.required && (submitted || form.formName.$dirty)"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Name is required<span class="tag"></<span class="name">label</span>></span></span><br></pre></td></tr></table></figure><p>When user clicks submit button, we force validation to be true!. But still a problem here, we need to repeat this fix on all labels and a little complex to read. So why not create a function because all validations need to check <code>submitted</code></p><figure class="highlight javascript"><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">$scope.hasError = <span class="function"><span class="keyword">function</span> (<span class="params">modelController, error</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> (modelController.$dirty || $scope.submitted) && error;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>So now update label again:</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">label</span> <span class="attr">ng-show</span>=<span class="string">"hasError(formName.inputName, formName.inputName.$error.required)"</span> <span class="attr">class</span>=<span class="string">"text-danger"</span>></span>Name is required<span class="tag"></<span class="name">label</span>></span></span><br></pre></td></tr></table></figure><p>For the moment, we only need to pass model controller and its validation without caring the conditions!</p><h2 id="Reset"><a href="#Reset" class="headerlink" title="Reset"></a>Reset</h2><p>The standard way to reset a form is to call the reset method on the form object such as <code>document.forms["formName"].reset()</code> or to use <code>type="reset"</code> for a button.</p><p>But we could also define a function by ourselves to add on the reset button</p><figure class="highlight javascript"><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">$scope.reset = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line">$scope.formName.$setPristine();</span><br><span class="line"><span class="comment">// Restore other parameters</span></span><br><span class="line"><span class="comment">// Other things to do</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="Ref"><a href="#Ref" class="headerlink" title="Ref"></a>Ref</h2><ul><li><a href="https://docs.angularjs.org/guide/forms">AngularJS Form</a></li><li><a href="https://www.packtpub.com/web-development/angularjs-example">AngularJS by Example</a></li><li><a href="https://docs.angularjs.org/api/ngMessages">AngularJS ngMessage</a></li><li><a href="http://stackoverflow.com/questions/12581439/how-to-add-custom-validation-to-an-angularjs-form">Stackover Custom validation</a></li></ul>]]></content>
<summary type="html">AngularJS provides a small and well-defined set of constructs that make standard form-based operations easier.</summary>
<category term="AngularJS" scheme="http://www.imrui.net/categories/AngularJS/"/>
<category term="AngularJS" scheme="http://www.imrui.net/tags/AngularJS/"/>
</entry>
<entry>
<title>《来自星星的你》观剧笔记</title>
<link href="http://www.imrui.net/2016/07/05/%E6%9D%A5%E8%87%AA%E6%98%9F%E6%98%9F%E7%9A%84%E4%BD%A0/"/>
<id>http://www.imrui.net/2016/07/05/%E6%9D%A5%E8%87%AA%E6%98%9F%E6%98%9F%E7%9A%84%E4%BD%A0/</id>
<published>2016-07-04T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.839Z</updated>
<content type="html"><![CDATA[<p>转载一篇《来自星星的你》观剧笔记</p><h2 id="第一集:我不是阳光不是雨露-我没有你见到的如此美丽"><a href="#第一集:我不是阳光不是雨露-我没有你见到的如此美丽" class="headerlink" title="第一集:我不是阳光不是雨露,我没有你见到的如此美丽"></a>第一集:我不是阳光不是雨露,我没有你见到的如此美丽</h2><p>朝鲜光海1年,原州牧附近,不明圆形飞行物降落地球。 </p><p>江陵,女主人公的前世,命运多桀的“望门寡”少女归行途中,遭遇不明飞行物,命垂一线之际,被跟随不明飞行物来到地球的外星人都敏俊所救。 </p><p>时间静止一切凝固,一身黑袍的都敏俊从远方走来,轻轻拨开浮动空中的落叶这一幕,会让我不由想起拉菲尔的宗教油画(腹诽:自动忽略泡菜国不忍直视的特效,张导别哭,我们知道你尽力了) </p><p>仿佛看到了一位天使,他从这个世界遥远的尽头走来,纤长的身影联系了天上和人间。把悬落在悬崖边上的轿子拉回,大地回复了生机,当都敏俊拉开郊门,犹带泪痕的少女楞楞地看向他,温和的大手缓缓伸向她。。。。。故事从这里开始了,数百年的命运纠缠也从这里开始了。 </p><p>请原谅我用穷摇的语句来描述这一段,因为它真的太美了,轻缓低沉的钢琴声里,他们四目相对,她的孤独、倔强和悲哀,都在他温和的笑容里淡去,这一眼,却纠缠四百年。 </p><p>四百年漫长的岁月,不同的时代不同的角色不同的生活,在都敏俊平淡的旁白里就这么流淌了过来,比人类灵敏7倍的听力和视力,“会看到不想看到的东西,会听到不想听到的东西”。编剧在这里留下了第一个梗,“不能与地球人的血液和唾液相互融合”,(在这里听见无数狼女惊天地泣鬼神的哀号“卧槽,吻戏怎么办船戏怎么办激情撕咬戏怎么办千演员来大姨妈怎么办”= =)“不管愿意或还是不愿意,该发生的总会发生,地球人称之为,命运”这一段的处理很有趣,都敏俊就像是局外人,他淡笑地描述着,说着别人的故事。是啊,这个时候的他,的确已经把自己看成是一个旁观者,“三个月后,彗星接近地球,我就能回到我所生活的行星了”。 </p><p>倒计时,开始。 </p><p>千颂伊,这个国家的顶级花瓶演员。美貌,骄傲,自私,然后,脑袋似乎不那么灵光,常常因为无知引发公共事件,却常常又因为美貌和人气被轻易原谅。(虽然我也没明白摩卡和棉花这个包袱的笑点在哪里)。 </p><p>自恋的花瓶演员和古板的小(老)学究,在电梯里偶遇了。高大上文化精英被错当成跟踪变态小年轻,千演员一大段自恋自傲外带嫌弃吐槽的独角戏,旁人眼里的女神瞬间成了教授眼里的女神经,无奈的摇头,忍耐的闭眼,嫌弃的摆手,表情欠奉台词几无。四百年后的初遇,似乎并不那么愉快。“在大韩民国还有人不认识千颂尹吗,从北韩来的吗,外星人吗”左拉伟隐右点题,女神预言帝,编剧细节控,观众?反正我的嘴角一直没拉平过。这一段女神的台词、微表情功底点赞。 </p><p>也许很多人羡慕千颂尹,或者嫉妒、鄙夷,不过就是长的漂亮的花瓶,就算是她的母亲也是这样认为。编剧好功力,一个电话就揭开了女神靓丽的光环。势利的母亲,因为钱抛弃父亲,像吸血虫一样吸附明星女儿生活,这样家庭成长起来的她,任何人都不值得爱,都不值得付出感情。 </p><p>“女神经”半夜发泄的大吼高唱,又引来了小(老)学究的上门控诉。文化精英吵架实在不在行,刚说上几句就被一轮机关枪式抢白给噎住了,只能呆呆地看着她,扔下一句“你不用道歉了”,然后对着被关紧的大门憋屈叹气。秀才遇到兵,有理说不清,教授教授,果真受! </p><p>千演员窝回被窝的哭诉,继续扰人清梦,睡不着怎么办,起床写日记吧。华丽就如大英博物馆的地下书房,首尔国家图书馆的文物馆藏也不一定比这丰富吧,活了四百年的老学究品位不同凡响,“最后三个月,地球上的记录”再次倒计时。 </p><p>心机深沉的女二世美,痴情向日葵的男二辉京,利欲熏心的男二父兄陆续登场。 </p><p>在大众舆论压力的逼迫下,千演员回到阔别已久的学校,老实地做回学生。巧了,教授正是那个“变态跟踪小年轻”。在课堂上关于苍蝇交配的性本能理论解释,我想编剧应该不仅仅是为了展现教授的职业本色,还另有深意。EROS(爱欲),弗洛伊德最著名的性心理学中苍蝇交配这个故事用来解释,所有的爱情不过是性行为的副产品,爱情受控于原始性欲望的支配,用这个梗来表现教授对于人类爱情的嘲讽,以及,暗示这个剧后面有大段的sex桥段(咳~);或者,用这个故事来预言,教授将为这段爱情付出生命?(= =不许打脸,我只是分析,可能性!)因为苍蝇交配过程,以雄性被雌性吞下付出生命作为结束,再结合第一个梗,“不能与地球人的唾液、血液融合”,恩~~~(卧槽,还打脸!) </p><p>课后,高傲的千颂伊为学分折腰,向教授谄媚讨好。情势果然比人强,气势高下立马倒转,教授一步又一步紧迫,“人生在世可不是这样的,只要帮上一次,就会两次,三次,继续求人帮忙,这才是人生在世”美男在前,见惯大阵仗的国民花瓶好大咽口水的声音,“人生啊,可没有足够让人类懂事的足够的时间”。(赶紧趁机会就拽吧你个傲娇货,以后别说学分,连论文都得你做= =!) </p><p>来了,第一集最让我有感触的一段对手戏,教授和律师基友的对弈。 </p><p>(导演和编剧都是细节控,棋室的群演都是老头子) </p><p>在一窝老头子中间,教授和律师基友关于“裴湖”“小姐”“旅途”的回忆和交声赞叹,配合金演员的嫩颜,本来很富有喜感,楞是被演技给拗回来了。这个外表年轻的小学究里住着一位饱经沧桑的老人啊。 </p><p>“三个月后,我要回到我住的地方,这段时间,谢谢你了”。 </p><p>“三十年前,我和你一起年轻过,你还这么年轻呢” </p><p>看着手中三十年前的老照片,“我害怕交朋友的原因就是因为这个,因为人类容颜易老生命易逝,徒留我一人,帅气的模样”自嘲惨淡地一笑。 </p><p>我说小金进步了,就是因为这个镜头,拥月里也有过这样的情境,这样的一笑,李暄坐在木偶戏前对着月,诉说思念烟雨时的一笑,同样的惨淡,同样的自嘲,同样的落寞,但那时的笑容和情绪明显过了。而这里,语气、语调、微表情,嘴角的幅度,一切恰到好处,淡淡地,就这样淡淡地就好,命运的寥落与无奈,撕裂的哭吼远不如这么惨淡的一笑更具有触动人心的力量。还有什么,比无可奈何到接受,更让人疼痛。 </p><p>律师基友问起四百年前的往事,“为什么没有回到故乡?”“有个孩子,本来会死的。。第一个送我礼物的孩子”月上柳梢头,人约黄昏后,月夜,树下,情窦初开的素衣少女,蓝衣翩翩的俊雅儒生,一幅亲手画就的诗画卷轴,他和她的最初回忆。(请允许我在这里先尖叫三声,意境人物情感,无一不美!张导,为毛不是你拍拥月,泡菜古装剧大师) </p><p>“真的有件奇怪的事情呢” </p><p>教授和千演员在不同的场景,同时回忆起十二年前的相遇。负气狂奔的少女,四百年后再次被超能力感召而来的都敏俊从卡车里救了下来,“叔叔,你是黑白无常吗”,也许那一刻,都敏俊看着怀里的少女,有一种莫名的熟悉感觉,四百年前的那个月夜下,她也这样问起过“叔叔,你是阴曹使者吗”。“世界上,真的有长相如此相像的一个人吗,很想在离开之前,再,再见一面。” </p><p>她是400年前的初恋,他是十二年前的初恋,他们又重逢了。 </p><p>看完第一集,忍不住想起哈代的那句诗:我不是阳光不是雨露,我没有你见到的如此美丽。教授有世人羡慕的长生不死,学识地位,帅气外表,千演员有世人羡慕的美貌,财富,人气,地位,可是,他们都没有我们见到的如此美丽。 </p><p>人物基本登场,关系网基本交代,性格基本清晰,好戏之人坐看一场牵连400年的好戏。 </p><p>作为金秀贤的演技控,第一集看得很满意,有些人曾说金演员演戏有些用力了,有些人也曾说他擅于表现强势外放的角色,可是我一直以为圣雪里的车康镇,那样的表演方式,才是金演员作为演员最具有灵气光彩的一面。一个深沉的角色,不温不火,不露不白,恰到好处,合乎自然合乎生活,就这样自然地展现出来,这才是高级的表演。就像敏俊和律师在棋室对弈那段戏,他们面对面地坐着,对过去生活的回忆,微妙,难以言表的内心感情都在金演员的眼神里,“我害怕交朋友的原因就是因为这个,因为人类容颜易老生命易逝,徒留我一人,帅气的模样”觉得应该笑又笑不出来,想哭也抑制不能哭,嘴角微微地扭动,眼睛垂下,抬起,脸上强露出的笑容……,这一切都是在一个近景镜头里表现出来的,我的心被紧紧地揪住了。 </p><h2 id="第二集:当男神爱上女神经,当神性遭遇人性"><a href="#第二集:当男神爱上女神经,当神性遭遇人性" class="headerlink" title="第二集:当男神爱上女神经,当神性遭遇人性"></a>第二集:当男神爱上女神经,当神性遭遇人性</h2><p>第二集结束,关上视频软件的那一刻,长长地吁出一口气。心里产生一个小小的疑问,编剧是基督徒吗? </p><p>开篇,都敏俊和外星小伙伴们采集地球植物花卉的那一幕,色调慵懒而温暖,整个画面沉浸在梦幻而暧昧的柔粉色之中,有一种特别纯洁、安宁的抚慰。都敏俊轻轻触摸花朵,眼神里如此专注而温暖,甚至带有一抹先验者圣洁的底色。我想,大概这时真正理解,为什么张导说,都敏俊非金秀贤不可。初来地球的外星人,清澈杳远的眼神,游走尘世的古代儒生,高洁淡然的气质,沉沦四百年后,洞若观火的悲悯,能够集于一身的,大概也只有他了。 </p><p>古代朝鲜的集市,热闹的市井一角突然时间凝固,乔装蓝衣儒生的都敏俊初入这个世界,有着幼童一般的性情与好奇心,拿起凝固在空中的毽子,左右一视调皮地踢了一下,然后再左右一视,放回原地。眼睛一闭,大地回生。这一段,并不仅仅为了表现他的超能力,更像是这个人物身上被赋予的超脱于现实而弥漫着的神秘色彩。如果让我去形容它,也许我会把它称为“神性”——纯净无暇、单纯大爱的先验本性,使我们真实地感到一种内心的温暖。因为他身上有着“神性”,所以他对这片土地、以及生活于这片土地上的人,有着本能的关怀和好奇。 </p><p>就像市井赌局中的赌徒,压上全副身家时,他会动用超能力去帮助他,就像他会一次又一次拯救来到这个世界第一眼看到的素衣少女。这都是这个人物身上“神性”的闪现。 </p><p>但是,堕落尘世,他身上的“神性”,最后也可能被人贪婪污浊的原罪磨砺得几不可见,他叹息地回答律师基友那句话,“帮一下忙,并不会改善什么,最终发生的事情还是会发生,糟糕的事情,变得更加糟糕。”人性之贪婪、贪欲,并不是因为他的介入而根除,反而会变本加厉,400年前那个赌徒因为他的一时出手而最终家破人亡,转身后那个落寞、悲凉的神情,或许也是昭示着“神性”的退散吧。所以在漫长的400年里,他不再介入任何人的命运,不再给予帮助,在岁月的流光中越站越高,高到硬冷如雕塑。 </p><p>但是,在那个女人面前,他从来都是不同的。当满身血污哦哦哦的“徐宜花”满怀期待地问他,你是谁,你叫什么名字?他只是思考了一会,在她面前漂浮了起来,半空中都敏俊面对少女惊诧眼神的那个表情很值得玩味,他抿了下嘴,移开了眼神,然后又顿了一下,缓缓回到地上。或许在那个时候的都敏俊心目中,他隐约了解自己和这个地方的人是不同的,但到底有什么不同,他无法用语言来具体表达,所以在这个他本能信任的少女面前,他用真实的动作来告诉她,他是谁。这是他们缘之初,之所以牵连400年的缘分,不过因为都敏俊,在这个女人面前(无论哪一世),情感从来不稳定的,都是可以随时爆发的,都,不再是他自己。 </p><p>千颂尹,包括前世的徐宜花,她的身上,有着最真实最袒露的“人性”,自私又善良,嚣张又脆弱,爱慕虚荣,伪装作假,神经质,会有意无意地伤害别人,也会被别人所伤害,会将别人的感情和尊严视若无睹,千演员身上,就像是我们自己的投现。特别喜欢她和竞争女演员在洗手间的那段对话,编剧太特么敢说了,在这个称王败寇的时代,成功的唯一标志就是实绩,电视剧成功与否只有一个衡量标志:收视率,再多的所谓赞美和追捧在惨淡的收视率都是虚假,因为前者可以修饰,而后者赤裸裸。对,这就是赤裸裸的现实,在其他电视剧里或许编剧会借角色之口为自己作各种辩解,但是在这里,无论是为了表现千演员对物欲的坦率,或是编剧自己,这种直接都是极其大胆和狂妄的。编剧,请容许我向你表个白,无论星剧最后是否称王,为这种坦率当浮一大白! </p><p>当男神遭遇女神经,当神性遭遇人性,都敏俊和千颂伊,碰撞在一起,都是火花四射。电梯口关于“枯围堰”的争论,课堂上关于论文抄袭的指责,酒醉后的登堂入室,古板保守的老学究and率真莽撞的女演员,“滋滋滋”,再理智和冷硬的雕塑,在她面前也有了人气,叹气,叹气,还是叹气,摇头,摇头,还是摇头,无奈吗,都敏俊,两人的战争中,你已经输在了起跑线上。所以他会不自觉地去维护她,一次,两次,两次,把想发恶帖的手机给摔了,允许她进入自己最不为外人踏足的家(虽然表面上看起来不那么请愿,别装了!),看到酒醉的她被男人搀扶在怀里动用超能力敛起脱落肩头的衣襟,不自觉地,他已经把这个女人,这个讨厌的女人,划进了自己绝对禁止外人接近的范围,无论物理距离还是心理距离。 </p><p>随着剧情的进一步推进,理智的都敏俊,看你如何不理智;率性自私的千演员,看你如何忸怩温暖起来。到底400年前曾经发生过什么,编剧还没有揭开谜团,唯一可能肯定的就是,都敏俊曾经介入过前世徐宜花的命运,也许他的介入最终导致了这个女孩的悲剧。都敏俊对这个女孩的感情,从怜惜开始,如果没有生死离别的催化,想来都敏俊不会有牵连400年的挂念、以后的挚爱,时间可以沉淀很多东西,也可能酝酿很多东西,也许曾经失败过,都敏俊,你还敢吗,皮夹里的那张照片已经向你揭开了她的身份,三个月后将要离开地球的你,还会有决然而不惜一切的勇气吗? </p><p>而对千颂尹来说,无论是400年的都敏俊,还是12年前的无名叔叔,就像是她贫瘠生命中陡然出现的保护神,她在心底一直默默经营着这样的高大形象,完全依照她自己的构想,以至于后来三番两次遇见都敏俊其人,却不能相认。当他真实地出现在你面前,率真鲁莽的你,到底会先跨出这一步吗? </p><p>第二集,这个故事里的所有局中人也慢慢都揭开了面具,一直被颂尹无意无心伤害着的世美,在暗处窥视着她伺机报复,但是我不讨厌这个人物,被伤害后想要报复,想要给予伤害者疼痛,这是人的天性,无论我们多么伪装,这不是单纯的善或恶可以说清楚的;向一朵向日葵一样追逐着太阳颂尹的辉京,他的生命里只有一个名字:千颂尹,这个单纯热情的大男孩,当他的真情空付时,他又会如何,是释然或是。。。。。;心理变态、恶毒的辉京哥哥,自私到令人厌恶的颂尹母亲,他们都是这个故事里的参与者,推动着这个故事的发展,推动着男女主人公的情感迁移,或者推动着他们的矛盾与贴合,《来自星星你》,是一个丰富多彩大千世界的缩影,每个人物都有自己的悲哀和弱点,各种可能性都在其中存在,就像这出浪漫喜剧的语调中总是夹杂了悲喜的双重回音。 </p><p>“如果有必要相遇,就终究会遇到的,如果没有遇到,就说明我们之间没必要相遇,这是我在这里生活漫漫岁月悟出来的道理”麻将馆里的这一出戏真是神来一笔;看到皮夹里颂尹童年照片的都敏俊,第一次大惊失色,第一次失去了理智和冷静,第一次肆意在别人面前表现了自己的超能力,也第一次在这一世作为保护神的姿态出现在颂尹面前,眼睛一闭,满室灯碎,听见那清脆的声音了吗?分明是这座冷硬的雕塑剥落“神”外壳的声音,都敏俊,欢迎来到人间! </p><h2 id="第三集:我不相信爱情,我只用行动证明它的存在"><a href="#第三集:我不相信爱情,我只用行动证明它的存在" class="headerlink" title="第三集:我不相信爱情,我只用行动证明它的存在"></a>第三集:我不相信爱情,我只用行动证明它的存在</h2><p>张太侑,泥垢了!古代戏拍得这么朦胧唯美,什么意思?!</p><p>深秋,晚霞,枫叶金黄,小溪潺潺,斯人相对,情意绵长。美得叫人透心的镜头,呈现出一种极致的华丽和悠远的意境。偏黄的滤镜,淡淡的感伤和唯美烘托到了极致,一个擅长用光线来参与叙事的古典主义导演,再次认证!(拥月导演一定坐在电视前做小人死命戳你= =)</p><p>都敏俊为何对前世的徐宜花有着这么深的挂牵?答案,在这里揭开了一角。</p><p>她说:“跟我。。。一起走吧,我来帮你”</p><p>他说:“跟我。。。一起走吧,我来保护你”</p><p>这个陌生的星球,全新的世界,那个小女孩是他伸手搭救的第一人,也是引他真正成“人”的导师,教他说话,给他温暖,她让他看到了人性的善,也让他看见了人性的恶。</p><p>古代朝鲜社会,一个死了的寡妇,大概比一个活着的寡妇,更容易立起一座贞节牌坊。一劳永逸,以防后患,徐宜花必须要“死”。</p><p>墙角边,他低头看着被恶毒婆宣判了“死刑”的小女孩,眼里抑制不住的怜惜。所以他出手了,一个旋身,一个拥抱,带她走出那个阴险污浊的世界,改变了她的命运,也改变了自己。</p><p>“念来去,如水流,徘徊久,叹息浓,旧心情,情依旧,愁自去,去更愁”。缘起,惟有一声叹息。</p><p>“你问我是谁,我是千颂尹!”</p><p>“对,你是千颂尹。。。。”喜欢这里的处理,眼睛从千颂尹身上移开,眼神里有失望,有怀念,还有那么一点庆幸。“。。。。。不可能是她。”你怎么可能是她呢?你怎么不是她?</p><p>徐宜花是他的缘,千颂尹是他的劫。是缘想要再续,是劫却想避开。至少在这一刻,都敏俊退缩了,他还没有爱上,他还不够勇敢。</p><p>可是敏俊啊,既然是劫,命运又岂能容你避开,该你历的劫,你逃不掉。</p><p>梦中预示,和现实中的千颂尹联系在了一起。律师基友的那句问话,是我们的疑问,或许也是都敏俊自己的疑问。“万一像12年前提前预知了要发生的事,那双鞋子的主人也有可能会死了,你想介入此事吗,还是一直就像你做的那样,袖手旁观呢?”</p><p>从这里开始,都敏俊身上才开始真正有了人的味道,不再是那个冷眼旁观的局外人,不再是那个云淡风清的叙事者,或者说,他真正走下了神坛。因为他开始有了犹豫、彷徨这些专属于人的情绪,有了弱点,才是人啊。</p><p>到此,这个故事的视角也发生了变化。都敏俊从舞台下,走上了舞台,真正开始参与这个故事。</p><p>千颂尹,真是一个可爱的女人。编剧用两集时间刻画了一个神经大条,自私虚荣,但不失坦率爽朗的女明星。又有一个细节,让她成为一个可爱善良的女人。这个现实的世界,人都被贴上了一张又一张的标签,因为不同的标签得到不同的待遇。比如千颂尹,她是国民女神,是有人气的前辈,所以后辈争相讨好好。比如世美,她只是一个不那么红的前辈,所以可以视而不见。“因人而异地问好,到底是哪来的臭毛病。我会关注你们的,如果再让我看到你们当着世美的面给她难堪,我真的不会轻饶你们。”大明星瞬间有了高大上的人性光辉。</p><p>全姐的采访中说,千颂尹和我很像。她在扮演千颂尹吗,不,她在扮演她自己,只不过在这里,她叫千颂尹。</p><p>半夜肚痛,却叫天天不应,叫地地不灵,小跟班靠不住,厌恶的母亲不想靠,四顾无人,那就只有靠自己。一个公众人物不允许有形象上的瑕疵,挣扎着起来,给自己抹粉上妆,对着化妆镜,颤抖着描眉口红,“你是明星,你是亚洲之星,你是国民女神”,“医院时尚,我也得成第一人才行。”不知道全姐演这一段有没有心有戚戚,光鲜靓丽的背后,从来都是别人看不到的心酸与痛苦,你得到多少,就要付出多少,这个世界多公平,这个世界多特么地公平。</p><p>“关我什么事?”低低自语的话音刚过,都敏俊已经假模假样地推开门,假装偶遇。</p><p>“不好意思,你去哪”</p><p>“我?去附近的成民大学医院”</p><p>“现在可是凌晨2点啊”</p><p>“我有事要去”= =傲娇闷骚这种病,无药可医。</p><p>医院这段戏,太特么逗了。</p><p>弗洛伊德曾说,潜意识暗中支配着人的言谈举止和心理活动。虽然人们不能感觉到这一层想法,但他们所作的就是潜意识是的体现,而且往往,潜意识还具有着冲突矛盾性,在人的前意识层面(潜意识的上一层,人能够模模糊糊地感觉到如果精神集中的话)是被严防死守的。</p><p>这一段的都敏俊,简直是弗大神这个理论的视觉演示教材。</p><p>“我怎么是你的经纪人”“我为什么是你的经纪人”“那也不能随便把我说成是你的经纪人吧”“我什么时候答应你了?!”</p><p>小护士一声喊,“千颂尹经纪人?”自然回头应声,“是”。</p><p>不要听一个人说了什么,而要看一个人做了什么。懊恼摇头叹气的都敏俊,让人啼笑皆非。</p><p>400年前的怜惜,在这里渐渐开始发酵了。</p><p>手术室外,躺在病床上的颂尹,紧紧抓住他的衣服,“不能走,我醒来的时候,你一定要在身边”这个时候都敏俊的眼神格外值得玩味。目光闪烁,惊诧?犹豫?</p><p>走开的步子渐渐停下,回头一看,返身坐回椅子。无声的彷徨和犹豫,什么是爱情?爱情是一种神气的,能够带你克服任何阻碍的力量。什么是爱情?爱情是当对方需要,可以时刻准备着付出,爱情,让你变得不再是你,爱情,让你找到自己。都敏俊,恭喜你,开始找到自己。</p><p>Bb机,都敏俊的老人属性再次印证。这是一个极富有象征意义的形象,仅仅是为了体现他的老派吗?不,没有手机,是因为不需要和人联系,不需要和人交往,不希望被人打扰,都敏俊,活了400年,一直是这么自我封闭地活着。</p><p>病房外的散步,恩,一切为了放屁。颂尹挽着他,亦步亦趋,仿佛一对已经结婚多年的老夫老妻,嘁嘁切切、絮絮叨叨地讲着一些最日常,最普通的话题。不是甜得发腻,这种平凡的温情,却格外让人心动。</p><p>“你帮我问问,有没有红色的病号服”她向他撒娇。他低头嫌弃地一看,“你觉得我会问吗”,她撇嘴摇头。“走你的。”这些发自真实生活但又被赋予了浪漫诗意的细节,使《星你》显得那么细致动人,具有强烈的感染力。都敏俊和千颂尹的爱情,也许还没有正式开始,却已然让人觉得那么踏实、安定和舒畅。</p><p>“那个和我很像的女人,是你的初恋吗”“不是。。只是我亏欠的人”。编剧用一种草蛇灰线,伏脉千里的手法,一点。。一点。。。解开400年前的谜团。</p><p>“是初雪啊”千颂尹的这句叹息,把都敏俊的回忆再次带回了400年前。《星你》的制作团队有严重的拥月情结。继张导狠狠地向拥月导演插上一刀后,编剧小小得致了个敬。“是初雪啊,您知道吗,在朝鲜这个国家,初雪的这个时候,任何谎言都可以被原谅,甚至向王说谎,也一概不被追究。”“大人,其实我。。。。爱慕着大人,我好想快点大人,让大人看到我美丽的样子”看着都敏俊一脸的不知所措,“是假的,骗你的。我刚不是说了吗,今天所有的谎言都可以被原谅”徐宜花,这个命运多桀的少女,她的爱慕,她的假装不爱慕,格外让人怜惜。</p><p>姑娘,你长了400年,终于长大了,让大人看到了你美丽的样子。问题是,你怎么长歪了呢?!这大概是都敏俊看着,对着窗外初雪跟相声里报菜名似地数着美食的千颂尹,心里最悲切的吐槽。时光荏苒,岁月如常,400年前的淑女,到底怎么长成,这个脑子跟打了除皱剂似的草包美人呢?不,那是因为你还不够了解她,真实的她。</p><p>老学究果然是心理学教授,深切地了解颂尹姑娘的本性。“她不是一个肯听人劝的人。”所以假借星座之名,行劝戒之时。“水瓶座不宜坐船,小心水。”梦里预示的梗继续。</p><p>请告诉我原因,为什么我如此钟爱老妖精和律师基友的戏。不是不在意千颂尹的事吗,那不管就好了吗,干吗还要一直叫我出来,一直提这事,明明你心里是在意的吧。哈哈,果然好基友才是插刀教教主,这吐的一口好槽!“从前还觉得我很难相处,为什么现在变得随意起来了”“怎么说都是三十年的交情了,何况看外观,怎么看这都是爸爸和儿子啊,心里倒真是觉得随意了不少”干的好!上集他让你“喜当爹”,这次就让他把自己埋下的恶果给吞了!现世报,果然来得又快又准。</p><p>BB机响了,基友一脸疑惑(还带有那么点小醋意)地问,“知道你BB机的,难道不是只有我一个吗,我还是第一次看见老师的BB机响”都敏俊的奴隶生涯,在“BB声”中正式开始了,那扇封闭了400年的门,似乎也悄悄地打开了。</p><p>“能答应的请求我都答应了吧,你觉得这种请求我会答应吗?”话音刚落,都敏俊已经出现在漫画屋里,一脸尴尬地报出千颂尹指定的书名。(教授,你的脸还活着吗,它还好吗?)</p><p>“单间执事、邻家男人的华丽诱惑、和腹黑上司深夜共度办公室”漫画屋花痴主人那不怀好意的眼神,让人觉得不妙。果然,韩网爆料,这居然是18禁的小黄书漫画,居然还有BL的。(千演员的节操君,你还活着吗?)</p><p>教授对着病床上这一堆不知所谓的漫画,毫不留情地吐槽。叔可忍婶不可忍,对人生之书的鄙视,是对千演员人生观价值观审美观的严重挑衅。“那你看书的水准又有多高”“我的人生之书可是《九云梦》!”调戏了下度娘,挖槽,老学究叫兽的品味果然不同反响,号称韩国版的红楼梦,我们抛开一切华丽的辞藻,简单定义之“一个男人梦想和八个女人发生正常恋爱关系的幻想奇书”。(看到这里姐忍不住了,这对货不是奸夫淫妇的基本标配么?!求你们在一起吧,别再祸害别人了= =)</p><p>看着因为家人而难过的颂伊,所有的人都在做戏,所有的人以为颂尹也在做戏,只有坐在角落的都敏俊看到了她眼神里的哀伤。</p><p>“爱情,是在性欲的基础上,体会到性快感的奴隶。是人在那过程之中,制造出无比甜蜜无比快乐的幻想,各位偶尔会去想爱情真的是永恒的吗?我敢断言,那所谓爱情的永恒是不存在的,爱情的保质期是遵循自然法则的,人类绝对无法摆脱这个限制。只因为爱情带来的短暂的幻象,就牺牲自己最珍贵的东西的那种行为,请大家不要做。”弗大神的唯物观爱情论,真是百用不爽的打脸神器。“BB”声再次响起,一室喧哗,继“神”的形象坍塌后,教授的理性学者形象也离家出走了。</p><p>这个女人不听劝,星座论不起作用,教授只能暗地下黑手,直接偷鞋。对着兴师问罪的颂伊居然还大言不惭,毫无愧疚地说,“都说不是我拿了!”镜头一转,对着自家鞋柜里那双女鞋叹气。(道貌岸然的衣冠禽兽!最可恶的是,每次心虚想要转移话题,非拽文掉书包讲个典故。所谓的礼仪之道,所谓的中山狼,无耻程度让人叹为观止。)</p><p>“只因为爱情带来的短暂的幻象,就牺牲自己最珍贵的东西的那种行为,请大家不要做。”</p><p>是的,都敏俊,你不相信爱情,你只是用行动,向我们证明它的存在。</p><h2 id="第四集:生命是一场无可取代的修行"><a href="#第四集:生命是一场无可取代的修行" class="headerlink" title="第四集:生命是一场无可取代的修行"></a>第四集:生命是一场无可取代的修行</h2><p>很多人说编剧偏爱千颂尹这个角色,我说你们错了。她偏爱的,是都敏俊。</p><p>她把华丽的色彩赋予了千颂尹,却把纯粹的感情交给了都敏俊。这个男人的身上,似乎有她对人,大写的人,最深切的怀念。</p><p>400年前的都敏俊,刚入世的都敏俊,孩子一般的率真,不谙世故,刚出母胎,那个最童真最原始最简单的状态。就像他被宜花带回家,看着满桌热气腾腾的饭菜,眼神里莫名的欣喜和快乐,一脸着急的吃相。</p><p>可是,他入了世,就是堕了轮回,就要品尝这滚滚红尘的伤痛和罪孽。</p><p>月明星稀,当宜花被泪流满面的母亲掐住脖子,上演这一幕“吃人的礼教”悲剧时,人世的这只罪恶大手何尝不是在掐住都敏俊的脖子。昏暗里,他眼睁睁看着回家的路被断送,这一切,不过是因为他的善良和大爱。是人,教会了他,抛弃善良,不要爱。</p><p>“以为绝对不会忘记的痛苦,也随着时间的流逝而流走了”这是顿悟吗,不,这是他心底回荡了400年的悲鸣。无论怎样静谧之时,都无法阻挡尘嚣的纠缠,走在熙攘的人群,走过滚滚红尘密集的走廊,天地不知变了几回颜色,日月也早已起落多少,可是这寒意,真的已经散开了吗?心底深处,也只有一座光阴累累赘赘铸就的荒凉废墟。这个时候的都敏俊,很想上去给他一个温暖的拥抱。</p><p>坐在对面律师基友,慈爱地看着他,他嘴角的一抹笑意,是对他最大的懂得与慈悲。这个时候,他就像都敏俊的心灵导师。“这人啊,明知有一死,但却非常努力地活着,明知会有离别的那一天,但相爱时,却像明天不会到来一般,如此愚昧的东西,就是人类啊”絮絮叨叨的劝说,只是在帮都敏俊梳理一条寻求尘世回归的路径。人生不易,活着的每一天,都在向死亡更加靠近。战争,倾轧,饥饿,灾荒,父亲卖儿卖女,子女背叛母亲,情侣夫妻劳燕分飞,人的每一天都在忠诚和伪善之间来回游走,这400年间,都敏俊又看见了多少的悲欢离合。就像电影里的一句台词,“无论花开花败,所有徘徊与怅然都要在四季里释然。包容问题的存在,包容痛苦的发生,活在当下,这才是人生。”</p><p>从开播到现在,都敏俊和律师基友的每一场戏都让我很喜欢,编剧和导演也用了很多心思和气力在铺陈,茶室,棋馆,路边小径,深夜胡同里,每一个日常的场景,每一段日常的对话,都充满了诗情和禅意。它,在教都敏俊成人,何尝不是在教我们成人。我看到他们的对手戏时,心里会不由地温暖妥帖起来。</p><p>电视里颂尹的访谈,向都敏俊最后确认了她的身份,400年前的小宜花,兜兜转转,又走到了一起。他看着电视里那个明亮的小女孩,眼神很平静,关掉电视,只是想了一想,眼神无比的宁静和笃定,我知道,他一定会去救她,这个自400年前就已经和他牵上了姻缘的女人。</p><p>游艇上的戏,是这一集剧情的最高潮,也是《星你》剧情开始转折的交汇点,从这里开始,所有人物之间都产生了关联,爱的,恨的,追踪的,罪恶的,这张巨大交错的网,终于真正开始了。剪辑得很精巧,用监视器的黑白画面,带我们进入现场的彩色画面,用这种情节的非正常叙述,增加剧情的紧张感,张导的确是一个敢于挑战的导演。</p><p>叙事形式很复杂,叙述的内容却很简单。就像敏俊梦中预示的重演,酒醉的千颂尹,即将掉落江水命垂一线时,都敏俊用他的超能力,再一次拯救了千颂尹。</p><p>游艇上的戏,其实我很喜欢千颂尹和韩宥拉在洗手间的那段对话。两个都在名利场上打滚的老江湖,一句一句带着刀枪冷意的冷嘲热讽,你来我往,互不相让,可是越说到最后,两人越有几分惺惺相惜的感怀,嘴角的那一抹淡淡的笑意,让这两个女人在这一瞬间,化敌为友。编剧是一个很宽厚的人,她让这个女人惨烈地退场,却也给她留下了一个美丽的背景。</p><p>只是,纷争却不会因为人的退场而消散,反而,激化了矛盾。</p><p>韩宥拉的死,把千颂尹逼到了悬崖边上。</p><p>这一幕,与400年前的那一幕,隐隐地产生了呼应。</p><p>400年过去了,人类文明发展了翻天覆地的变化,科技的高速发展,文化的繁荣昌盛,可是人,却还是人,吃人的礼教,变成了吃人的舆论。</p><p>25岁的阮玲玉用毒药和一句“人言可畏”告别人世,据说她死后,有很长一段时间,各类小报还在津津乐道她的绯闻和花边小料。公众舆论,向来是无形的杀人利器对一个并未害人的公众人物恶意妄断,逼人自证清白,这就是尼采着力分析过的小市民的怨恨心理。</p><p>所有的人,都是凶手,所有的,包括你我。</p><p>记者、公众舆论,大众的恶评,把千颂尹逼到了悬崖边上,也把她逼进了都敏俊的生活。</p><p>怎么办,逃不开,逃不掉,逃吧,逃进都敏俊的怀里。</p><p>都敏俊是无意中推开那扇门的吗?别装了,大人!他分明就是等待着这个女人自投罗网。</p><p>曾有人把爱情比作两人的舞蹈,不是你退我进,你进我退。门外,是嘈杂的喧闹声,门里,却是两人爱情的萌芽,两人的舞蹈,分明起步。</p><p>还记得那句话吗, 不要听他在说什么,要看他在做什么?</p><p>一次又一次义正严词的拒绝,一次又一次欲拒还迎的推诿,他冷漠,他不假颜色,他只是低头看着眼前这个女人憋嘴、摇头、撒娇、耍赖的卖萌式表演,但是你看见他的眼神了吗?</p><p>有没有一个人,如此专注地看着你,他的所有眼睛,不只是双目,还有唇、鼻、舌、身、心,都是向着她的方向,看似不着痕迹却有迹可循地看她。每一次的语言拒绝,紧接着,行动上不打折扣地执行,这个男人啊,真是言语上的矮子,行动上的巨人,当然,这只是千颂尹才有的待遇。</p><p>一吻定情,让人猝不及防,这一吻,似乎打乱了都敏俊心里的平静。</p><p>准备好了吗?故事,要进入高潮了!</p><p>都敏俊,就像400年前那样,因为救这个女人,再次面临危险。检察官和警察的追踪,即将向他汹涌而来。</p><p>而千颂尹,也被毒蛇死死地盯上了。</p><p>所有的故事,仿佛又重来了一次,这一次,都敏俊,你,又会如何选择?</p><p>作为金秀贤的演技控,有话想说。</p><p>听见有人说,金秀贤似乎少了拥月时李暄强势的气场,被全姐给压住了。</p><p>怎么说呢,这话有道理,却也没道理。每一个角色,都有属于自己的节奏。不同的表演节奏,带来不同的眼神、微表情和肢体语言,以及台词。都敏俊,是一个很复杂的人物,但是他的这种复杂,却是被包裹在一层浓重的阴影里,隐隐绰绰,我们仿佛能够看见,却又无法真切触摸到。作为观众,这种感觉,也许会有那么一点点隔膜。但这种隔膜感、陌生感,甚至游离感,却恰恰是这个角色所需要的。在这个故事已经开展的这一段时间里,这个人物仿佛都游离在我们视线的景深处,我们通过一个罩子在观察着他。他的悲痛、哀伤,无法哭泣,无法高歌,更无法咆哮,就像王家卫电影里的台词,那些消失了的岁月仿佛隔着一块积着灰尘的玻璃,让这个人物看得见,抓不着。</p><p>金秀贤是一个擅长用外放的表演来塑造人物的演员,就像李暄,但是这一次,对都敏俊的塑造,他却选择了含蓄的,有分寸的节约型表演。有时候特别放慢表演节奏,使动作节奏不鲜明,使人物情绪不张扬,很沉静,很内敛,把所有的戏都集中在眼神里,幅度很小的微表情里。但是这种表演,却是最适合都敏俊这个人物的。</p><p>所有的压抑与沉静,都是为了后面的爆发与激烈作铺垫的,前面的抑,是为了后面的扬,只有前面压抑到了充分,后面我们才能被这个人物爆发出来的情绪所感染。</p><p>不妨,先不要下断言,静静看下去。</p>]]></content>
<summary type="html">400年过去了,人类文明发展了翻天覆地的变化,科技的高速发展,文化的繁荣昌盛,可是人,却还是人,吃人的礼教,变成了吃人的舆论。</summary>
<category term="书评影评" scheme="http://www.imrui.net/categories/%E4%B9%A6%E8%AF%84%E5%BD%B1%E8%AF%84/"/>
<category term="书评影评" scheme="http://www.imrui.net/tags/%E4%B9%A6%E8%AF%84%E5%BD%B1%E8%AF%84/"/>
</entry>
<entry>
<title>SpringMVC RequestMapping</title>
<link href="http://www.imrui.net/2016/07/04/SpringMVC-RequestMapping/"/>
<id>http://www.imrui.net/2016/07/04/SpringMVC-RequestMapping/</id>
<published>2016-07-03T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.839Z</updated>
<content type="html"><![CDATA[<p>Quick note about request mapping annotations in SpringMVC controller</p><a id="more"></a><h2 id="RequestMapping"><a href="#RequestMapping" class="headerlink" title="@RequestMapping"></a>@RequestMapping</h2><p>Annotation for <strong>mapping web requests onto specific handler classes and/or handler methods</strong>. It means <code>DispatcherServlet</code> intercepts the request, then it switches request to the corresponding method determined by @RequestMapping.</p><ol><li><code>@RequestMapping("path")</code> on class means all handling methods on this controller are relative to the given path.</li><li><code>@RequestMapping("path")</code> on method means mapping requests which match given path to this method</li></ol><h3 id="Properties"><a href="#Properties" class="headerlink" title="Properties"></a>Properties</h3><ul><li><p><code>value</code> indicates url to map. If no other properties, we could use its simplified form <code>@RequestMapping("path")</code>.</p></li><li><p><code>method</code> indicates HTTP methods. It will support all methods if not specified .</p></li></ul><figure class="highlight java"><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">method = RquestMethod.GET</span><br><span class="line">method = {RquestMethod.GET, RquestMethod.POST}</span><br></pre></td></tr></table></figure><ul><li><code>consumes</code> indicates Content-Type of the mapped request. A request will be mapped only when its Content-Type matches it.</li></ul><figure class="highlight java"><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">consumes = <span class="string">"application/json"</span></span><br><span class="line">consumes = {<span class="string">"application/json"</span>, <span class="string">"text/html"</span>}</span><br></pre></td></tr></table></figure><ul><li><code>produces</code> indicates the producible media types of the mapped request, <strong>a request will be mapped only when Accept matches it</strong>.</li></ul><figure class="highlight java"><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">produces = <span class="string">"application/json"</span></span><br><span class="line">produces = {<span class="string">"application/json"</span>, <span class="string">"charset=UTF-8"</span>}</span><br></pre></td></tr></table></figure><ul><li><code>headers</code> indicates only the requests having these headers can be mapped.</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">headers = <span class="string">"content-type=text/*"</span></span><br></pre></td></tr></table></figure><ul><li><code>params</code> indicates only the requests having these parameters can be mapped. We could also add <code>!=</code> pr <code>==</code> to add conditions.</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// myParam exists and its value is myValue</span></span><br><span class="line">params=<span class="string">"myParam = myValue"</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">// myParamA exists and its value is myValueA. // myParamB exists and its value is not myValueB</span></span><br><span class="line">params = {<span class="string">"myParamA = myValueA"</span>, <span class="string">"myParamB != myValueB"</span>}</span><br><span class="line"></span><br><span class="line"><span class="comment">// myParamA exists</span></span><br><span class="line">params = <span class="string">"myParamA"</span> </span><br><span class="line"></span><br><span class="line"><span class="comment">// myParamA exists and myParamB does not exits</span></span><br><span class="line">params = {<span class="string">"myParamA"</span>, <span class="string">"!myParamB"</span>} </span><br></pre></td></tr></table></figure><h3 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h3><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">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping("/users")</span> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Handler all /users GET request</span></span><br><span class="line"> <span class="meta">@RequestMapping(method = RequestMethod.GET)</span> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">functionA</span><span class="params">()</span> </span>{</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Handler all /users/new POST request</span></span><br><span class="line"> <span class="meta">@RequestMapping(value="/new", method = RequestMethod.POST)</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">functionC</span><span class="params">()</span> </span>{</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><h3 id="Ant-style-path-patterns-to-indicate-map-url"><a href="#Ant-style-path-patterns-to-indicate-map-url" class="headerlink" title="Ant-style path patterns to indicate map url."></a>Ant-style path patterns to indicate map url.</h3><ul><li><code>/user/*/login</code> matches /user/aaa/login</li><li><code>/user/**/login</code> matches /user/login or /user/aaa/login or /user/aaa/bbb/login</li><li><code>/user/login??</code> matches /user/loginAA or /user/loginBB</li><li><code>/user/{userId}</code> matches /user/123 or /user/342 (using <code>@PathVariable</code> to indicate userID)</li></ul><h2 id="PathVariable"><a href="#PathVariable" class="headerlink" title="@PathVariable"></a>@PathVariable</h2><p><strong>It can be used on a method argument to bind it to the value of a URI template variable</strong>. The argument can be of any simple type such as int, long, Date, etc. Spring automatically converts to the appropriate type or throws a <code>TypeMismatchException</code> if it fails to do.</p><blockquote><p>If we do not specify the url placeholder name like <code>@PathVariable('name')</code>, we must keep method parameter name same as url placeholder.</p></blockquote><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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping("/users")</span> </span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Handler all /users/{id} GET request</span></span><br><span class="line"> <span class="meta">@RequestMapping(value="/{id}", method = RequestMethod.GET)</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">functionA</span><span class="params">(<span class="meta">@PathVariable</span> <span class="keyword">int</span> id)</span> </span>{</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Or if you want another parameter name</span></span><br><span class="line"> <span class="comment">//@RequestMapping(value="/{id}", method = RequestMethod.GET)</span></span><br><span class="line"> <span class="comment">//public void functionB(@PathVariable("id") int anotherName) {</span></span><br><span class="line"> <span class="comment">//// ToDo</span></span><br><span class="line"> <span class="comment">//}</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>A more complex example:</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Controller</span></span><br><span class="line"><span class="meta">@RequestMapping("/users/{userId}")</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestController</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@RequestMapping("/book/{bookId}")</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(<span class="meta">@PathVariable</span> String userId, <span class="meta">@PathVariable</span> String bookId)</span> </span>{</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><h2 id="RequestParam"><a href="#RequestParam" class="headerlink" title="@RequestParam"></a>@RequestParam</h2><p>It is used to <strong>bind request parameters to a method parameter in the controller</strong>. Do not mix it with <code>@PathVariable</code> which is used to obtain placeholders from the uri only.</p><p>As usual, we do it like this <code>request.getParameter("name")</code>, now with annotation:</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value="/user/{userId}/books", method = RequestMethod.GET)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params"><span class="meta">@PathVariable("userId")</span> <span class="keyword">int</span> user,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="meta">@RequestParam(value = "date", required = false)</span> Date dateOrNull)</span> </span>{</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>It has three properties:</p><ol><li><code>value</code> is the key to get value from request</li><li><code>required</code> is to indicate whether request must have this parameter. By default is true.</li><li><code>defaultValue</code> is to set default value when parameter in request does not exist.</li></ol><blockquote><p>Same as <code>@PathVariable('name')</code>. If we do not specify <code>value</code>. We must need to keep method parameter name the same as key.</p></blockquote><h2 id="CookieValue"><a href="#CookieValue" class="headerlink" title="@CookieValue"></a>@CookieValue</h2><p>Same as <code>@RequestParam</code> but bind cookie values to a method parameter. It also has three properties <code>value</code>, <code>required</code> and <code>defaultValue</code> which are also the same </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></pre></td><td class="code"><pre><span class="line"><span class="meta">@RequestMapping(value="/user", method = RequestMethod.GET)</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test</span><span class="params">(<span class="meta">@CookieValue("foo")</span> String valueFromCookie)</span> </span>{</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="Ref"><a href="#Ref" class="headerlink" title="Ref"></a>Ref</h2><ul><li><a href="http://jiuye.jikexueyuan.com/play?id=2239&class_id=36">极客学院-常用注解类</a></li><li><a href="https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html">Annotation Type RequestMapping</a></li></ul>]]></content>
<summary type="html"><p>Quick note about request mapping annotations in SpringMVC controller</p></summary>
<category term="Spring" scheme="http://www.imrui.net/categories/Spring/"/>
<category term="Spring" scheme="http://www.imrui.net/tags/Spring/"/>
<category term="SpringMVC" scheme="http://www.imrui.net/tags/SpringMVC/"/>
</entry>
<entry>
<title>那些年</title>
<link href="http://www.imrui.net/2012/05/27/%E9%82%A3%E4%BA%9B%E5%B9%B4/"/>
<id>http://www.imrui.net/2012/05/27/%E9%82%A3%E4%BA%9B%E5%B9%B4/</id>
<published>2012-05-26T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="年少的那一年,青春的那一年,还是成长的那一年?"><a href="#年少的那一年,青春的那一年,还是成长的那一年?" class="headerlink" title="年少的那一年,青春的那一年,还是成长的那一年?"></a>年少的那一年,青春的那一年,还是成长的那一年?</h2><h3 id="那一年,毕业"><a href="#那一年,毕业" class="headerlink" title="那一年,毕业"></a>那一年,毕业</h3><p>二手的自行车再也载不动爱情</p><p>笔记本电脑代替PC,终点不再是游戏性</p><p>offer在纷飞,朋友要远行</p><p>散伙饭吃尽了四年的愁绪</p><p>青春,被西装领带包裹着</p><p>离开校园的那段路,走走停停……</p><h3 id="那一年,菜鸟"><a href="#那一年,菜鸟" class="headerlink" title="那一年,菜鸟"></a>那一年,菜鸟</h3><p>在钢筋水泥的职场丛林</p><p>我只是个雏儿</p><p>被调教、被训斥、被肯定、被鼓励</p><p>摸爬滚打的岁月里</p><p>稚嫩的肩膀上柔软的翎毛开始发亮</p><p>虽然孤单但日渐充实的人生……</p><h3 id="那一年,成蝶"><a href="#那一年,成蝶" class="headerlink" title="那一年,成蝶"></a>那一年,成蝶</h3><p>昔日的梦想变成每月的工资单</p><p>也许,只要一个卫生间的钱</p><p>你就可以把世界走遍</p><p>可是我拥有了成长,却走不出时间……</p>]]></content>
<summary type="html">年少的那一年,青春的那一年,还是成长的那一年?</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>看山不是山·看水不是水的境界</title>
<link href="http://www.imrui.net/2010/07/30/%E7%9C%8B%E5%B1%B1%E4%B8%8D%E6%98%AF%E5%B1%B1-%E7%9C%8B%E6%B0%B4%E4%B8%8D%E6%98%AF%E6%B0%B4%E7%9A%84%E5%A2%83%E7%95%8C/"/>
<id>http://www.imrui.net/2010/07/30/%E7%9C%8B%E5%B1%B1%E4%B8%8D%E6%98%AF%E5%B1%B1-%E7%9C%8B%E6%B0%B4%E4%B8%8D%E6%98%AF%E6%B0%B4%E7%9A%84%E5%A2%83%E7%95%8C/</id>
<published>2010-07-29T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="“看山不是山·看水不是水”的境界"><a href="#“看山不是山·看水不是水”的境界" class="headerlink" title="“看山不是山·看水不是水”的境界"></a>“看山不是山·看水不是水”的境界</h2><p>对于我来说。</p><p>未来,仅仅是用如果来诠释的种种假设。它在,却也不在。</p><p>过往,不过是早已付之湮灭的结局。它在,却更不在。</p><p>我在消逝的过往中向虚无的未来里缓缓踱去,我在,却真不在。</p><hr><p>我告诉寂夜,有人,回来了。</p><p>他,叫做忧伤——我的另一片影子。</p><p>正午,他蜷缩在角落里躲避光雨,微小却愈发清晰。</p><p>子夜,他徘徊在星斗间聆听籁寂,模糊而更加高大。</p><hr><p>街道是陌生的,可昏黄的晕圈里,我与脚下的影子熟稔。</p><p>建筑是陌生的,可破旧的墙壁上,伤疤般曝露在空气中的殷红色砖块,唤醒了我心底隐隐的刺痛。</p><p>人群是陌生的,可那些闯入我视线中的面孔,与暂别的那所城市中遇见的,同样陌生。</p><p>夜色藏匿不了的,不仅仅是城市的繁华,还有我心底的孤影。</p><p>音乐安抚不了的,不仅仅是灼烫的热浪,还有我思绪的波涛。</p><hr><p>上一个已故的十年.</p><p>我一年疯.两年狂.三四初拈言律.五六信手成章.劳七八九岁月.撰十载情殇.</p><p>下一个将至的十年</p><p>等待支离破碎的花事.卷土重来</p><hr><p>忽然发现.自己还是在那个“看山不是山,看水不是水”的境界里.转来转去找不到出口</p>]]></content>
<summary type="html">忽然发现.自己还是在那个“看山不是山,看水不是水”的境界里.转来转去找不到出口</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>晴晖云影·盘旋在谁的波澜不惊</title>
<link href="http://www.imrui.net/2010/07/24/%E6%99%B4%E6%99%96%E4%BA%91%E5%BD%B1-%E7%9B%98%E6%97%8B%E5%9C%A8%E8%B0%81%E7%9A%84%E6%B3%A2%E6%BE%9C%E4%B8%8D%E6%83%8A/"/>
<id>http://www.imrui.net/2010/07/24/%E6%99%B4%E6%99%96%E4%BA%91%E5%BD%B1-%E7%9B%98%E6%97%8B%E5%9C%A8%E8%B0%81%E7%9A%84%E6%B3%A2%E6%BE%9C%E4%B8%8D%E6%83%8A/</id>
<published>2010-07-23T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="晴晖云影·盘旋在谁的波澜不惊"><a href="#晴晖云影·盘旋在谁的波澜不惊" class="headerlink" title="晴晖云影·盘旋在谁的波澜不惊"></a>晴晖云影·盘旋在谁的波澜不惊</h2><p>夜色在寂静上空盘旋,愈发冷清。</p><p>当跛脚行走在旷寂里的孤独,遭遇埋头叹息的忧愁。</p><p>无人的陌生十字路口,他们席地而坐,细数彼此眸子中黯淡的星光。</p><p>这一瞬,时光倒流,回忆穿越了瞳孔,洄溯到曾经草长莺飞的荒芜地带。</p><p>年少时的情爱,迷如阑珊,幻如煌炎,仿佛一场不愿醒的酣梦,缭绕着栀子淡淡的芬芳。</p><p>也许,用冗长的文字来记叙短暂的梦寐,是我在每个无眠的长夜里,可以收获的唯一的慰藉。</p><hr><p>日子,就像一支不断变奏的钢琴曲。</p><p>时而匆匆,时而缓缓,任我不疾不徐的一一作别。</p><p>间或,一小节音符在仔细聆听前,已耳畔悄悄划过。</p><p>可以停顿,却不必停留;可以慨叹,却不需惆怅。</p><p>因为,只有一些人,一些事以残音的形式滞留于流年。</p><p>我才知晓,如何很久的停留在你世界的边缘,弹一首惆怅的曲子。</p><hr><p>越陌度阡,任我能飞越过阻隔你我的空间。</p><p>却始终留不住昔日,与你谈斗笔墨的时间。</p><p>也许,我就是那薄如蝉翼的晴晖和云影。</p><p>悄无声息的在你肩膀降落,又悄无声息的在你肩膀坠下。</p><p>但我仍愿在每一天的一明一暗里,为你弹奏饱经烟尘风沙的千年不变。</p><hr><p>光明隐匿在黑暗背后,才愈加灿亮。</p><p>金子埋藏在泥土深处,才愈加珍贵。</p><p>寂夜里,我在文字的渊邃里穿凿。</p><p>只为在下一个晴日,许你,最华美的佩饰。</p>]]></content>
<summary type="html">我愿在每一天的一明一暗里,为你弹奏饱经烟尘风沙的千年不变。</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>在眼前却在天边</title>
<link href="http://www.imrui.net/2010/07/15/%E5%9C%A8%E7%9C%BC%E5%89%8D%E5%8D%B4%E5%9C%A8%E5%A4%A9%E8%BE%B9/"/>
<id>http://www.imrui.net/2010/07/15/%E5%9C%A8%E7%9C%BC%E5%89%8D%E5%8D%B4%E5%9C%A8%E5%A4%A9%E8%BE%B9/</id>
<published>2010-07-14T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="在眼前却在天边"><a href="#在眼前却在天边" class="headerlink" title="在眼前却在天边"></a>在眼前却在天边</h2><p>不知从何时起,我开始习惯沉默了。</p><p>对身边的事物有了一种逃避感,开始以为是低调,现在发现那是淡然。</p><p>好像整个世界都是灰色的,没有任何色彩华丽。</p><p>那些熟悉的面孔,那些熟悉的声音,渐渐地被隐匿起来。</p><p>一下子孤独了很多,一下子苍老了很多。</p><hr><p>他们说我活的平淡了、平凡了,可我却怎么不觉得是。</p><p>为什么平淡中有很多不舍?为什么平凡中有些许遗憾?</p><p>我时刻都在探测自己的内心,苦思冥想,到底想要什么,到底该怎么做。</p><p>很久以前,自己也是那样无忧无虑、阳光灿烂。</p><p>曾几何时,按耐不住那颗好奇的心,无所畏惧的去追求自己的热爱。</p><p>而如今,除了孤独、寂寞之外我还剩下些什么?</p><p>或许还有那么一丝不舍,或许还有那么一丝遗憾,或许永远也只能仅存那么一点点留恋!</p><p>这个城市给了我太多的伤心,就像它的名字一样冰冷无情。</p><p>快乐的时光永远那么短暂,却印下了我们无法遗忘的点点滴滴,那么与众不同。</p><hr><p>不知从何时起,我的视线渐渐模糊,再也看不清你的容颜。</p><p>我心里所有的空间,只能容下一个你,满满的,无法消失更无法丢弃。</p><p>可我却再也感觉不到你,仿佛就在眼前,又好像远在天边。</p><p>很久很久没有你的声音,犹如失去了灵魂一样,茫然不知所错。</p><p>曾经,我知道你很在乎我,至少你心里有我,从来不曾怀疑过这种感觉。</p><p>如今,我真的不确定你是否在乎我,是否把我当做一个重要的人。</p><p>甚至,感觉你的记忆里从来没有过我的身影,一切只不过是我的想象。</p><hr><p>如果我们之间真的有过那么一丝情感,那么这种感情算是什么?</p><p>如果我们之间真的有过那么一次激情,那么这种行为算是什么?</p><p>如果说这些只是内心深处的好奇,那么清醒之后彼此如何面对?</p><p>我想应该是当做不曾发生的忘记,而不是一走了之的逃避。</p><p>我从来都不敢去回想我们之间的痛苦,因为我怕得到的结果是欺骗,因为我怕失去你。</p><p>其实,现在我们已经是最熟悉的陌生人了,根本谈不上什么失去。</p><p>无所谓了,反正注定我是孤独、寂寞,也不在乎这些了。</p><p>在我心里,我还是我,你还是你,只不过我们相隔的太远罢了。</p><p>那梦幻般的脑海和寂寞的心一直也永远被你占据着。</p><p>在眼前却在天边。。。</p>]]></content>
<summary type="html">快乐的时光永远那么短暂,却印下了我们无法遗忘的点点滴滴,那么与众不同。</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>生平小可之比</title>
<link href="http://www.imrui.net/2010/06/19/%E7%94%9F%E5%B9%B3%E5%B0%8F%E5%8F%AF%E4%B9%8B%E6%AF%94/"/>
<id>http://www.imrui.net/2010/06/19/%E7%94%9F%E5%B9%B3%E5%B0%8F%E5%8F%AF%E4%B9%8B%E6%AF%94/</id>
<published>2010-06-18T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="生平小可之比"><a href="#生平小可之比" class="headerlink" title="生平小可之比"></a>生平小可之比</h2><p>文字仿佛和我有着不朽的宿怨</p><p>在心头萦绕千年</p><p>现实里睡梦 狂纵不羁 八百贴壮志豪言</p><p>不是酒仙 却渴望饮一壶春水 吐半壁盛唐江山</p><p>睡梦里现实 顾影自怜 三五阙愁思款款</p><p>愿笑孔丘 解腰间环佩 赊七八斗酒馔</p><p>无人素手解连环</p><p>无人当歌怒拔剑</p><p>除了文字 任谁时光浩瀚</p><p>能追随我孤寂百年</p>]]></content>
<summary type="html">谁还没有个年少轻狂</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>近期的烦躁</title>
<link href="http://www.imrui.net/2010/06/19/%E8%BF%91%E6%9C%9F%E7%9A%84%E7%83%A6%E8%BA%81/"/>
<id>http://www.imrui.net/2010/06/19/%E8%BF%91%E6%9C%9F%E7%9A%84%E7%83%A6%E8%BA%81/</id>
<published>2010-06-18T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="我可以说是胡思乱想吗"><a href="#我可以说是胡思乱想吗" class="headerlink" title="我可以说是胡思乱想吗"></a>我可以说是胡思乱想吗</h2><p>自从发现自己已历经二十多个春秋时,心便开始隐隐不安了。</p><p>总是对回忆充满怀念,总是会浮想联翩。</p><p>每天的校园生活宛如行尸走肉</p><p>我活在自己编织的世界里</p><p>一个用幻想编制的梦……</p><hr><p>总站在风里的人,会感觉肌肤干涩。</p><p>总站在雨里的人,会感觉眼眶湿润。</p><p>总站在时光里的人,会感觉内心苍老。</p><hr><p>关于苍老的问题,每次提及,总会让我发笑。</p><p>它的范围太过宽泛了,轻松的跨越了几乎各个年龄梯段。</p><p>它的内涵太过深邃了,在思索它的所在时,我们不知不觉已被打上时光的印记。</p><hr><p>才发现,在时光面前我无能为力,只能眼睁睁的看着它一点一滴的消逝。</p><p>都说双鱼座的人很感性、爱幻想,大概我就是传说中的那只鱼。</p><hr><p>关于生活,我的短期理想是在烦躁过后可以变的从容。</p><p>如果生活是一种态度,</p><p>就像幸福是一种感受,骄傲其实是一种武器,回忆是对现实的嘲弄一样,</p><p>那么我们除了回不去过去,迷惘在现在的理由何在呢?</p><p>陷入思考中……</p><hr><p>STOP STOP</p><p>我的经验告诉我要STOP</p><p>陷入思考的结果肯定就是引发烦恼了</p>]]></content>
<summary type="html">总站在时光里的人,会感觉内心苍老。</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>想你却不能告诉你</title>
<link href="http://www.imrui.net/2010/05/14/%E6%83%B3%E4%BD%A0%E5%8D%B4%E4%B8%8D%E8%83%BD%E5%91%8A%E8%AF%89%E4%BD%A0/"/>
<id>http://www.imrui.net/2010/05/14/%E6%83%B3%E4%BD%A0%E5%8D%B4%E4%B8%8D%E8%83%BD%E5%91%8A%E8%AF%89%E4%BD%A0/</id>
<published>2010-05-13T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="想你却不能告诉你"><a href="#想你却不能告诉你" class="headerlink" title="想你却不能告诉你"></a>想你却不能告诉你</h2><p>我可以从意念,再写出、舞出一个世界。</p><p>深夜,对着孤灯,郊游在电脑前,我陷入深深的思念之中。此刻比任何时候都孤独,我怀疑自己的真实,怀疑现实的真实。好象被岁月开了一个玩笑,岁月对于人来说如同延伸的铁轨。</p><p>而现在的我如何来调整这个步伐。也许伤痛的心灵需要静静安抚,也许时间会将这一切尘封。</p><p>有些事情过去了就过去了,而有些事情过去了却过不去。不知怎样开始,难预料如何结束。生活有些时候带给我们的只是一场迷离的幻觉。繁华凋谢,荒草丛生。想要挣脱,却是被越缚越紧。冥冥中好像有一种强大的力量在控制着我们,我们逃不出他的掌心。追忆着旧日的时光,一些过去的人,一些过去的事,默默的念想,温暖冰冻的心灵。我们手中拥有的东西竟是那么少。情感脆弱,容易受伤也容易伤害别人。我们不停的追逐之后,发现自己离原定的目标越来越远。</p><p>有些人告别之后就不想再见,有些人告别着后依旧在默默的思念,期待者某一天的重逢。人与人分手便往往是永远。离别的站台,停靠的码头,生命的每一次告别都是一次蝉蜕的过程。慢慢的学会坚强。忍住泪,挥手说再见。</p><p>那时,我离开了你,我却忘了告诉你,你一直在我心中。</p><p>如果,有一种执着需要离开,那么离开就是对完美的补充,那种微妙的行程,就是那种神秘的距离,因为有了那神秘的距离而产生了美,因为这种美的距离的存在,才让爱达到了最高境界的完美。</p><p>一直这样想着,一直在放逐自己;一直的缠绵和追逐,而没有获得回头的机遇;一直不想放弃的朦胧,而没有确切的结果。</p><p>望着天空的星星,想尽力地去看清楚每一颗星星的模样和它们之间的距离。无论是在白天,还是在每一个黑夜,我都不能清晰的辨认它们的模样和距离。可是每一颗星星都安静地停留在属于它们的领地,而我的视线却是模糊的,它们就在我的模糊的视线里清晰的存在着。</p><p>想着,疑惑着,疑惑着,想着。</p><p>想着那个模糊的身影,疑惑着那个模糊的身影,对那个身影的思念是没有意义的思念,那个思念是属于另一个不属于我的世界里的思念,我应该离开那种被自己迷惑的身影,让自己从那种模糊的世界里游离出来,为自己找一片阳光,让自己的呼吸暖和起来。</p><p>那个身影的离开,是彻底的离开,是善意的离开,是清楚的离开,就是为了追求属于身影的完美,也是为了让我也去追求属于我的完美,原来那个身影的离开是那种和谐的完美。</p><p>离开,是对完美的补充。</p><p>留下一段文字,一些伤感的故事。生命又走过了寂寞的一程。回首往事,恍然入梦。是我们的生命脆弱,容易受伤,还是这个世界太过坚硬。</p><p>某一天我们熟悉。某一天我们陌生。</p><p>我们走过了一个繁华的季节。</p><p>其实我们都是熟悉的陌生人。只希望我们每个人都多一些宽容,多一些真诚,多一些爱!</p><p>人生充满了遗憾。有时候,遗憾也未尝不是一种美,只是,这美是要付出昂贵的代价的,常常会心痛,常常怀念,却永远深埋在那里,这一种爱是刻骨铭心的,无论怎样努力也无法从心头驱散。</p><p>一切是不是错?一切是不是很荒唐?我始终没想明白。</p><p>想你,却不能告诉你……</p>]]></content>
<summary type="html">其实我们都是熟悉的陌生人。只希望我们每个人都多一些宽容,多一些真诚,多一些爱!</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
<entry>
<title>忆高考誓言</title>
<link href="http://www.imrui.net/2010/04/28/%E5%BF%86%E9%AB%98%E8%80%83%E8%AA%93%E8%A8%80/"/>
<id>http://www.imrui.net/2010/04/28/%E5%BF%86%E9%AB%98%E8%80%83%E8%AA%93%E8%A8%80/</id>
<published>2010-04-27T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="备考宣言"><a href="#备考宣言" class="headerlink" title="备考宣言"></a>备考宣言</h2><blockquote><p>今日青云志,明朝题名时</p><p>敛翼立千仞,凝眸视八方</p><p>失意休气馁,得势莫猖狂</p><p>书山高,勤奋定有通天道</p><p>学海深,顽强能启探宝门</p><p>失败不是我要的归宿</p><p>状语亦不能解决问题</p><p>疾风知劲草,烈火见真金</p><p>只有行动方可表明自己的心迹</p><p>只有事实才能证明自己的能力</p><p>任凭艰难险阻</p><p>挡不住我勇往直前</p><p>纵横天高舒豪气</p><p>叱咤风云看今朝</p></blockquote><h2 id="临考誓言"><a href="#临考誓言" class="headerlink" title="临考誓言"></a>临考誓言</h2><blockquote><p>金戈作笔,岁月为马</p><p>时光荏苒,依稀之间早已过了二倚门</p><p>回首,那淅沥般如雨似雾的年龄</p><p>离开鸟巢,展翅高飞</p><p>去拼搏那变幻的云,傲视那短浅寡问的小生命</p><p>这是雏鹰的选择</p><p>冲击马棚,征战沙场</p><p>驰骋于硝烟弥漫的拼杀之中</p><p>感受灰飞烟灭的豪迈</p><p>这是骏马的选择</p><p>明知前路多崎岖,偏要追求风和雨</p><p>这是一个临考生——我的选择</p><p>十年寒窗读书苦,一朝题名幸福笑</p><p>辛勤积蓄待两日,战场拼搏两小时</p><p>千辛万苦迎面冲,希望之道条条通</p><p>从容自若倾心待,成功之门必然开</p></blockquote>]]></content>
<summary type="html">青春年少时高考宣誓</summary>
<category term="诗词" scheme="http://www.imrui.net/categories/%E8%AF%97%E8%AF%8D/"/>
<category term="诗词" scheme="http://www.imrui.net/tags/%E8%AF%97%E8%AF%8D/"/>
</entry>
<entry>
<title>诗词新编</title>
<link href="http://www.imrui.net/2009/03/06/%E8%AF%97%E8%AF%8D%E6%96%B0%E7%BC%96/"/>
<id>http://www.imrui.net/2009/03/06/%E8%AF%97%E8%AF%8D%E6%96%B0%E7%BC%96/</id>
<published>2009-03-05T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.838Z</updated>
<content type="html"><![CDATA[<h2 id="一剪梅·处女"><a href="#一剪梅·处女" class="headerlink" title="一剪梅·处女"></a>一剪梅·处女</h2><blockquote><p>少行多休eat足</p><p>盘膝于床</p><p>袒胸露乳</p><p>梦中谁在对天吼</p><p>醒来看时</p><p>酣睡正熟</p><p>电脑厕所忙不停</p><p>一指速敲</p><p>两耳似聋</p><p>网站广告赚钱途</p><p>才下QQ</p><p>钱上手头</p></blockquote><p><em>PS.在处女生日来临之际,特作此诗!祝处女生日快乐!</em></p><h2 id="如梦令"><a href="#如梦令" class="headerlink" title="如梦令"></a>如梦令</h2><blockquote><p>常记公署日暮</p><p>约会不知归路</p><p>兴尽晚回校</p><p>误入走廊深处</p><p>LOVE LOVE</p><p>让我一阵麻木</p></blockquote><h2 id="如梦令-1"><a href="#如梦令-1" class="headerlink" title="如梦令"></a>如梦令</h2><blockquote><p>校园雨疏风骤</p><p>学费岂可乱收</p><p>试问收款人</p><p>却道以后研究</p><p>知否 知否</p><p>应是你肥我瘦</p></blockquote><h2 id="山坡羊·爱情之路"><a href="#山坡羊·爱情之路" class="headerlink" title="山坡羊·爱情之路"></a>山坡羊·爱情之路</h2><blockquote><p>友情如雨</p><p>爱情如雾</p><p>相恋三年坎坷路</p><p>忘情人</p><p>意踌躇</p><p>伤心曾经走过的路</p><p>山盟海誓都作了土</p><p>聚 我也苦</p><p>散 我也苦</p></blockquote><h2 id="诉衷情"><a href="#诉衷情" class="headerlink" title="诉衷情"></a>诉衷情</h2><blockquote><p>当年梦里相追求</p><p>也曾有风流</p><p>爱情湮灭何处</p><p>尘暗旧情路</p><p>愿未遂 心已碎 泪空流</p><p>此生谁料</p><p>心系旧情 身老孤愁</p></blockquote><h2 id="学子吟"><a href="#学子吟" class="headerlink" title="学子吟"></a>学子吟</h2><blockquote><p>常常整夜不眠,深夜挑灯读卷。</p><p>个个犹如熊猫,实在不忍观看。</p><p>只为升学考试,被迫埋头苦干。</p><p>夜半三更惊起,题海腋窝打转。</p><p>无奈天生薄命,分数实在凄惨。</p><p>举杯仰天长叹,血誓来年再战。</p></blockquote>]]></content>
<summary type="html">古诗填新词,一时之娱也。</summary>
<category term="诗词" scheme="http://www.imrui.net/categories/%E8%AF%97%E8%AF%8D/"/>
<category term="诗词" scheme="http://www.imrui.net/tags/%E8%AF%97%E8%AF%8D/"/>
</entry>
<entry>
<title>幸福与孤独</title>
<link href="http://www.imrui.net/2009/02/14/%E5%B9%B8%E7%A6%8F%E4%B8%8E%E5%AD%A4%E7%8B%AC/"/>
<id>http://www.imrui.net/2009/02/14/%E5%B9%B8%E7%A6%8F%E4%B8%8E%E5%AD%A4%E7%8B%AC/</id>
<published>2009-02-13T16:00:00.000Z</published>
<updated>2021-11-25T15:05:37.837Z</updated>
<content type="html"><![CDATA[<h2 id="被懂得是一种幸福,等待被懂是一种孤独"><a href="#被懂得是一种幸福,等待被懂是一种孤独" class="headerlink" title="被懂得是一种幸福,等待被懂是一种孤独"></a>被懂得是一种幸福,等待被懂是一种孤独</h2><p>生命中总有些美丽的错误无法预料,就像总有些冷酷的分离无法避免一样</p><p>我意犹未尽的想你,以及有关你的所有</p><p>河畔的风,幽径的雨,冰封的河面</p><a id="more"></a><p>我醒来过后你温和的容颜</p><p>还有我在五楼窗台呼喊出的你的名字,一切做风雨逝</p><p>这些色彩游离的画面立刻构成了我的全部背景</p><p>你写在空气中的忧郁被夕阳带走</p><p>但我心如刀割,</p><p>我犹豫地、忐忑地、幼稚且执着地想向你暗示</p><p>几乎令你无可奈何</p><p>可你总是那么善良的鼓励着我向人生的目标去跋涉</p><p>但……</p><p>有一种爱叫放弃</p><p>有一种爱“挂着泪珠”,但很凄美……</p><p>有一种爱“叫放手”,放手不是放弃爱你……</p><p>每当夜晚来临的时候,思念就像潮水般涌向自己</p><p>当一切成了过眼云烟,当一切改变了模样</p><p>我是否还记得曾经的泪、曾经的笑、曾经的爱怜</p><p>是否要忘记过去的心</p><p>如果不是你的出现,就不会有我现在的失意</p><p>如果不是你,我不会日日夜夜为你牵挂</p><p>如果…… 如果……</p>]]></content>
<summary type="html">被懂得是一种幸福,等待被懂是一种孤独</summary>
<category term="文字随笔" scheme="http://www.imrui.net/categories/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
<category term="文字随笔" scheme="http://www.imrui.net/tags/%E6%96%87%E5%AD%97%E9%9A%8F%E7%AC%94/"/>
</entry>
</feed>