-
Notifications
You must be signed in to change notification settings - Fork 1
/
atom.xml
541 lines (289 loc) · 452 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>NanYin的博客</title>
<subtitle>记录生活点滴</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://nanyiniu.github.io/"/>
<updated>2023-07-30T09:34:44.042Z</updated>
<id>https://nanyiniu.github.io/</id>
<author>
<name>NanYin</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>ThreadLocal</title>
<link href="https://nanyiniu.github.io/2020/10/01/ThreadLocal/"/>
<id>https://nanyiniu.github.io/2020/10/01/ThreadLocal/</id>
<published>2020-10-01T12:00:00.000Z</published>
<updated>2023-07-30T09:34:44.042Z</updated>
<content type="html">< 中,谈到进程时,各个进程之间是相互独立的,拥有独立的运行空间、内存。而 ThreadLocal 完成的工作就是在线程层级,做了独立的空间。</p><h2 id="ThreadLocal-结构"><a href="#ThreadLocal-结构" class="headerlink" title="ThreadLocal 结构"></a>ThreadLocal 结构</h2><p>ThreadLocal 本身不具备什么数据结构,他的实现是依附于 ThreadLocalMap 。</p><p>每次 ThreadLocal 执行 set 方法后,实际上是使用 ThreadLocalMap 的 set 方法,将 ThreadLocal 作为 key,value 作为 value 放入到 ThreadLocalMap 中。</p><p>而实际上的 ThreadLocalMap 是由 Entry 来实现,而 Entry 中的 key 是 WeakReference 包裹的 ThreadLocal。</p><p>过程如下图:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200520162730.png" alt="图 1"></p><p>结构引用:其中虚线表示弱引用</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200520160700.png" alt="图 2"></p><h3 id="为什么要使用弱引用?"><a href="#为什么要使用弱引用?" class="headerlink" title="为什么要使用弱引用?"></a>为什么要使用弱引用?</h3><p>原因在于防止内存溢出,那为什么弱引用就能够够避免呢?再次之前需要先了解什么是弱引用 -><a href="https://nanyiniu.github.io/2020/09/28/Java%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/">Java 的四种引用类型</a>。</p><p>只要进行垃圾回收,那么弱引用就会被回收掉。那么,可以看图 2 中的结构。如果需要 ThreadLocal 引用被断开,也就是 TL ref 被设为 null,因为 ThreadLocal 与 Entry 中的 key 是弱引用关系,所以,只要这边的强引用(tl ref)断开,则下一次垃圾回收,就一定能把 ThreadLocal 回收掉。</p><p>如果不理解,那么举个反例,如果 ThreadLocal 与 Entry 中的 key 是强引用关系,此时 tl ref 和这个 threadlocal 断开。此时,还需要将 threadLocalMap 这边的引用断开,才能够将 threadlocal 进行回收,而 threadLocalMap 是线程 Thread 中的一部分,声明周期和 Thread 相同。所以,造成的结果就是,只要线程运行着,threadLocal 就不会被回收。</p><h2 id="ThreadLocal-的内存溢出"><a href="#ThreadLocal-的内存溢出" class="headerlink" title="ThreadLocal 的内存溢出"></a>ThreadLocal 的内存溢出</h2><p>除去上面分析的如果使用强引用会导致内存溢出外,还有一种情况会导致 ThreadLocal 的内存溢出</p><p>如果 tl ref 和 threadLocal 之间的强引用断开后,此时的 threadlocal 变为 null 存在于 Entry 中,也就是说内存中保存着 key 为 null 的 entry,同样无法回收,导致内存溢出。</p><p>解决方式是,用完 threadLocal 后,使用 remove 方法进行手动释放。</p>]]></content>
<summary type="html">
<h1 id="ThreadLocal"><a href="#ThreadLocal" class="headerlink" title="ThreadLocal"></a>ThreadLocal</h1><p>ThreadLocal 被称为线程本地变量,每个线程都单独有一份 T
</summary>
<category term="多线程" scheme="https://nanyiniu.github.io/categories/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="ThreadLocal" scheme="https://nanyiniu.github.io/tags/ThreadLocal/"/>
</entry>
<entry>
<title>Java的四种引用类型</title>
<link href="https://nanyiniu.github.io/2020/09/28/Java%E7%9A%84%E5%9B%9B%E7%A7%8D%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/"/>
<id>https://nanyiniu.github.io/2020/09/28/Java的四种引用类型/</id>
<published>2020-09-28T00:00:00.000Z</published>
<updated>2020-10-01T08:47:41.269Z</updated>
<content type="html"><![CDATA[<h1 id="Java-的四种引用类型"><a href="#Java-的四种引用类型" class="headerlink" title="Java 的四种引用类型"></a>Java 的四种引用类型</h1><h2 id="强引用"><a href="#强引用" class="headerlink" title="强引用"></a>强引用</h2><p>任何时候声明一个对象使用<code>=</code>关联的引用,都是强引用,只要该强引用存在,就不会被垃圾回收器回收掉。当内存空间不足时,也不会随意清除任何一个强引用的对象。</p><h2 id="软引用"><a href="#软引用" class="headerlink" title="软引用"></a>软引用</h2><p>软引用使用 <code>SoftReference</code> 进行声明,和强引用不同的是,如果堆内存空间不足时,会优先回收软引用。可以用于缓存数据。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">SoftReference<String[]> softBean = </span><br><span class="line"><span class="keyword">new</span> SoftReference<String[]>(<span class="keyword">new</span> String[]{<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>});</span><br></pre></td></tr></table></figure></div><h2 id="弱引用"><a href="#弱引用" class="headerlink" title="弱引用"></a>弱引用</h2><p>弱引用使用 <code>WeakReference</code> 进行声明,和软引用的区别是,只要发生垃圾回收,弱引用就一定会被回收掉。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">WeakReference <String[]> weakBean = </span><br><span class="line"><span class="keyword">new</span> WeakReference<String[]>(<span class="keyword">new</span> String[]{<span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>});</span><br></pre></td></tr></table></figure></div><h2 id="虚引用"><a href="#虚引用" class="headerlink" title="虚引用"></a>虚引用</h2><p>虚引用使用 <code>PhantomReference</code> 进行声明,虚引用和其他引用不同,需要和一个<code>ReferenceQueue</code>一同绑定使用。如果再回收前,发现是虚引用,会现将虚引用放到<code>ReferenceQueue</code>中,执行完相关 hook 方法后,进行真正的回收。其目的是<strong>监测、跟踪垃圾回收器的行为</strong>。用于回收堆外内存。</p><ol><li>当发现是虚引用,放到<code>ReferenceQueue</code>中。</li><li>另外一个线程,发现<code>ReferenceQueue</code>不为空,执行相关方法,清空堆外内存。</li><li>清空堆外内存后,对虚引用进行真正的回收。</li></ol>]]></content>
<summary type="html">
<h1 id="Java-的四种引用类型"><a href="#Java-的四种引用类型" class="headerlink" title="Java 的四种引用类型"></a>Java 的四种引用类型</h1><h2 id="强引用"><a href="#强引用" class
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
</entry>
<entry>
<title>Markdown语法入门</title>
<link href="https://nanyiniu.github.io/2020/09/03/markdown%E5%9F%BA%E6%9C%AC%E8%AF%AD%E6%B3%95/"/>
<id>https://nanyiniu.github.io/2020/09/03/markdown基本语法/</id>
<published>2020-09-03T12:00:00.000Z</published>
<updated>2020-10-13T12:43:43.072Z</updated>
<content type="html"><![CDATA[<h1 id="Markdown语法总结"><a href="#Markdown语法总结" class="headerlink" title="Markdown语法总结"></a>Markdown语法总结</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>其实markdown是一种标记型的语法,简洁好用,通过工具能够进行预览,不用担心格式问题,并且由很多工具支持markdown的预览。</p><h2 id="正文"><a href="#正文" class="headerlink" title="正文"></a>正文</h2><p>一个 Markdown 段落是由一个或多个连续的文本行组成,它的前后要有一个以上的空行(空行的定义是显示上看起来像是空的,便会被视为空行。比方说,若某一行只包含空格和制表符,则该行也会被视为空行)。普通段落不该用空格或制表符来缩进。</p><p>在编辑的过程当中每编辑一块的内容,就要空一行,否则可能出现渲染失败,出现格式乱掉的情况。</p><h3 id="标题"><a href="#标题" class="headerlink" title="标题"></a><strong>标题</strong></h3><p>markdown支持两种标题的语法,类 Setext 和类 atx 形式。</p><h4 id="类-Setext-形式是用底线的形式,利用-(最高阶标题)和-(第二阶标题),例如:"><a href="#类-Setext-形式是用底线的形式,利用-(最高阶标题)和-(第二阶标题),例如:" class="headerlink" title="类 Setext 形式是用底线的形式,利用 = (最高阶标题)和 - (第二阶标题),例如:"></a>类 Setext 形式是用底线的形式,利用 = (最高阶标题)和 - (第二阶标题),例如:</h4><blockquote><p>This is an H1</p><p>=============</p><p>This is an H2</p><p>-————</p></blockquote><p>效果</p><blockquote><h1 id="This-is-an-H1"><a href="#This-is-an-H1" class="headerlink" title="This is an H1"></a>This is an H1</h1><h2 id="This-is-an-H2"><a href="#This-is-an-H2" class="headerlink" title="This is an H2"></a>This is an H2</h2></blockquote><h4 id="也能使用atx-使用-来进行1-6阶的划分-如:"><a href="#也能使用atx-使用-来进行1-6阶的划分-如:" class="headerlink" title="也能使用atx 使用 # 来进行1-6阶的划分 如:"></a>也能使用atx 使用 # 来进行1-6阶的划分 如:</h4><blockquote><p># This is an H1</p><p>## This is an H2</p></blockquote><p>效果</p><blockquote><h1 id="This-is-an-H1-1"><a href="#This-is-an-H1-1" class="headerlink" title="This is an H1"></a>This is an H1</h1><h2 id="This-is-an-H2-1"><a href="#This-is-an-H2-1" class="headerlink" title="This is an H2"></a>This is an H2</h2></blockquote><h3 id="区块引用"><a href="#区块引用" class="headerlink" title="区块引用"></a><strong>区块引用</strong></h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu 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">> 区块应用 使用 > 符号进行标识 。</span><br><span class="line">能每行都使用 > 进行标识,也能只在第一行标识,两种效果相同。</span><br></pre></td></tr></table></figure></div><p>效果:</p><blockquote><p>区块应用 使用 <code>></code> 符号进行标识 。<br>能每行都使用 <code>></code> 进行标识,也能只在第一行标识,两种效果相同。</p></blockquote><h3 id="列表"><a href="#列表" class="headerlink" title="列表"></a><strong>列表</strong></h3><ol><li>无序列表 可以使用*号或者+号或者-号都是一样的</li><li>有序列表 可以使用数字加英文.加空格使用有序列表</li></ol><h3 id="分割线"><a href="#分割线" class="headerlink" title="分割线"></a>分割线</h3><p>三个以上的<code>*</code>就是分割线,和三个<code>---</code>的效果相同。一般使用<code>---</code></p><hr><h3 id="链接"><a href="#链接" class="headerlink" title="链接"></a>链接</h3><p>Markdown 支持两种形式的链接语法: 行内式和参考式两种形式。</p><p>不管是哪一种,链接文字都是用 [方括号] 来标记。</p><p>要建立一个行内式的链接,只要在方块括号后面紧接着圆括号并插入网址链接即可,如果你还想要加上链接的 title 文字,只要在网址后面,用双引号把 title 文字包起来即可,例如:</p><blockquote><p>This is [an example](<a href="http://example.com/" target="_blank" rel="noopener">http://example.com/</a> “Title”) inline link.</p></blockquote><blockquote><p>[This link](<a href="http://example.net/" target="_blank" rel="noopener">http://example.net/</a>) has no title attribute.</p></blockquote><p> 效果</p><p> This is <a href="http://example.com/" target="_blank" rel="noopener" title="Title">an example</a> inline link.</p><p><a href="http://example.net/" target="_blank" rel="noopener">This link</a> has no title attribute.</p><p>下面是一个参考式链接的范例:</p><p>I get 10 times more traffic from [Google] [1] than from<br>[Yahoo] [2] or [MSN] [3].</p><p>[1]: <a href="http://google.com/" target="_blank" rel="noopener">http://google.com/</a> “Google”</p><p>[2]: <a href="http://search.yahoo.com/" target="_blank" rel="noopener">http://search.yahoo.com/</a> “Yahoo Search”</p><p>[3]: <a href="http://search.msn.com/" target="_blank" rel="noopener">http://search.msn.com/</a> “MSN Search”</p><p>I get 10 times more traffic from <a href="http://google.com/" target="_blank" rel="noopener" title="Google">Google</a> than from<br><a href="http://search.yahoo.com/" target="_blank" rel="noopener" title="Yahoo Search">Yahoo</a> or <a href="http://search.msn.com/" target="_blank" rel="noopener" title="MSN Search">MSN</a>.</p><h3 id="强调"><a href="#强调" class="headerlink" title="强调"></a><strong>强调</strong></h3><p>使用两个*<em>或者_表示强调 使用一个</em>或者一个_表示斜体</p><p>如: <strong>helloworld</strong> <em>helloworld</em></p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a><strong>代码</strong></h3><p>使用`符号来使用代码块<br>行内代码使用`包起来 如 <code>print()</code></p><p>可以使用三个`+语言名称 来标注是哪种语言,能够使用特定的语法高亮(前提是编辑器支持)</p><h3 id="图片"><a href="#图片" class="headerlink" title="图片"></a><strong>图片</strong></h3><p>同样的图片也有行内式和参考式<br>向链接一样只不过在前面多个 !</p><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a><strong>其他</strong></h3><p>大部分markdown编辑工具支持html语法,如常用的 <code><mark></mark></code> 使用起来<mark>效果就是这样</mark>.</p><p>同样加粗等语法可以使用对应的标签<code><b></b></code></p><h2 id="工具推荐"><a href="#工具推荐" class="headerlink" title="工具推荐"></a>工具推荐</h2><ul><li>windows、mac、linux上推荐 <code>typero</code>,可以实现编辑时预览,所见即所得。</li><li>其中mac还可以使用<code>macdown</code>工具,简洁好用。</li></ul>]]></content>
<summary type="html">
<h1 id="Markdown语法总结"><a href="#Markdown语法总结" class="headerlink" title="Markdown语法总结"></a>Markdown语法总结</h1><h2 id="概述"><a href="#概述" class="
</summary>
<category term="markdown" scheme="https://nanyiniu.github.io/categories/markdown/"/>
<category term="语法" scheme="https://nanyiniu.github.io/tags/%E8%AF%AD%E6%B3%95/"/>
<category term="markdown" scheme="https://nanyiniu.github.io/tags/markdown/"/>
</entry>
<entry>
<title>IDEA设置代码注释模板和JavaDoc文档生成</title>
<link href="https://nanyiniu.github.io/2020/09/01/%E4%BB%A3%E7%A0%81%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83/"/>
<id>https://nanyiniu.github.io/2020/09/01/代码注释规范/</id>
<published>2020-09-01T12:00:00.000Z</published>
<updated>2023-07-30T09:34:44.086Z</updated>
<content type="html"><![CDATA[<p>为了解决接口复用的问题,建议使用规范,从而能够让Java自动生成相关文档,有助于以后代码的二次开发。</p><p>主要分为两部分</p><ul><li>第一部分是如何利用ide帮助我们为方法、为类自动生成一套标准模板;</li><li>第二部分是如何使用ide生成JavaDoc</li></ul><h2 id="设置注释模板"><a href="#设置注释模板" class="headerlink" title="设置注释模板"></a>设置注释模板</h2><p>在设置注释模板之前,需要先自行了解有关于JavaDoc的标准的相关参数:<a href="https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javadoc.html" target="_blank" rel="noopener">JavaDoc</a></p><h3 id="1-设置类注释"><a href="#1-设置类注释" class="headerlink" title="1. 设置类注释"></a>1. 设置类注释</h3><p>进入到设置中,找到 <code>file and code template</code> 中的 <code>include</code> 中的 <code>file header</code>,点击在右侧添加内容:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"> * 类<code>Doc</code>用于:TODO</span></span><br><span class="line"><span class="comment"> *</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> ${USER}</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@version</span> 1.0</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@date</span> ${YEAR}-${MONTH}-${DAY}</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure></div><h3 id="设置方法注释"><a href="#设置方法注释" class="headerlink" title="设置方法注释"></a>设置方法注释</h3><p>进入到设置中,找到 <code>live template</code>,点击 <code>add</code> 首先添加一个自定义组, 然后在左侧选中组后,再添加一个模板:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200901223156.png" alt=""></p><p>下面分为四步操作:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200901223556.png" alt=""></p><ol><li>填写触发的前缀,这里使用 <code>**</code> 作为前缀</li><li>选择触发方式,这里使用<code>Enter</code>,也就是回车键进行触发,当输入<code>**</code>后,再敲回车,则会自动生成③中的内容。</li><li>在此处填写模板内容</li><li>需要勾选为那些内容生成,这里需要勾选整个<code>Java</code></li></ol><p>在③中制作完模板内容后,需要点击 <code>edit variables</code> 设置其中参数的值;</p><h4 id="模板参考"><a href="#模板参考" class="headerlink" title="模板参考"></a>模板参考</h4><h5 id="方法模板参考"><a href="#方法模板参考" class="headerlink" title="方法模板参考"></a>方法模板参考</h5><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">**</span><br><span class="line"> * 方法<code>$methodName$</code>作用为:</span><br><span class="line"> *</span><br><span class="line"> * <span class="meta">@author</span> $author$</span><br><span class="line"> * $params$ </span><br><span class="line"> * <span class="meta">@throws</span> $<span class="keyword">throws</span>$</span><br><span class="line"> * <span class="meta">@return</span> $<span class="keyword">return</span>$</span><br><span class="line"> */</span><br></pre></td></tr></table></figure></div><p>点击<code>edit variables</code>参考内容:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200901225125.png" alt=""></p><p>其中的<code>params</code>变量,如果要实现多参数分开展示,需要使用groovy脚本实现,脚本内容参考:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="GROOVY"><figure class="iseeu highlight /groovy"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">groovyScript(<span class="string">" def result=''; def params=\"${_1}\".replaceAll('[\\\\[|\\\\]|\\\\s]', '').split(',').toList(); for(i = 0; i < params.size(); i++) { if(i!=0)result+= ' * '; result+='@param ' + params[i] + ((i < (params.size() - 1)) ? '\\n' + '\\t' : ''); }; return result"</span>, methodParameters())</span><br></pre></td></tr></table></figure></div><h5 id="类模板参考"><a href="#类模板参考" class="headerlink" title="类模板参考"></a>类模板参考</h5><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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><br><span class="line"> * 类<code>$className$</code>用于:TODO</span><br><span class="line"> *</span><br><span class="line"> * <span class="meta">@author</span> $USER$</span><br><span class="line"> * <span class="meta">@version</span> <span class="number">1.0</span></span><br><span class="line"> * <span class="meta">@date</span> $date$</span><br><span class="line"> */</span><br></pre></td></tr></table></figure></div><p>点击<code>edit variables</code>参考内容:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200901225727.png" alt=""></p><h2 id="生成JavaDoc"><a href="#生成JavaDoc" class="headerlink" title="生成JavaDoc"></a>生成JavaDoc</h2><p>在Idea中的上方栏目中,找到 <code>tools</code>,打开菜单中的 <code>Generate JavaDoc..</code> ,在弹出菜单中进行如下设置:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200901225938.png" alt=""></p><p>然后选择位置,直接导出即可。</p>]]></content>
<summary type="html">
<p>为了解决接口复用的问题,建议使用规范,从而能够让Java自动生成相关文档,有助于以后代码的二次开发。</p>
<p>主要分为两部分</p>
<ul>
<li>第一部分是如何利用ide帮助我们为方法、为类自动生成一套标准模板;</li>
<li>第二部分是如何使用ide生成J
</summary>
<category term="编程规约" scheme="https://nanyiniu.github.io/categories/%E7%BC%96%E7%A8%8B%E8%A7%84%E7%BA%A6/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="注释规范" scheme="https://nanyiniu.github.io/tags/%E6%B3%A8%E9%87%8A%E8%A7%84%E8%8C%83/"/>
</entry>
<entry>
<title>基于Java的WebService实践</title>
<link href="https://nanyiniu.github.io/2020/08/01/%E5%9F%BA%E4%BA%8EWebService%E7%9A%84Java%E5%BC%80%E5%8F%91%E5%AE%9E%E8%B7%B5/"/>
<id>https://nanyiniu.github.io/2020/08/01/基于WebService的Java开发实践/</id>
<published>2020-08-01T15:00:00.000Z</published>
<updated>2020-08-02T02:48:12.128Z</updated>
<content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>在很多Java项目中,仍然会使用基于WebService的数据传输接口,而webservice实际上就是http+xml。而在实践过程中,主要需要注意接收和发送的格式。</p><h1 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h1><h2 id="被调用接口"><a href="#被调用接口" class="headerlink" title="被调用接口"></a>被调用接口</h2><p>如果处于被调用方,则需要声明出一个WebService服务,也就是生成、发布一个WSDL文件。</p><blockquote><p> 什么是WSDL?</p><p> WSDL – <strong>WebService Description Language</strong> – Web服务描述语言。</p><ul><li>通过XML形式说明服务在什么地方-地址。</li><li>通过XML形式说明服务提供什么样的方法 – 如何调用。</li></ul></blockquote><h3 id="将接口声明为一个ws服务"><a href="#将接口声明为一个ws服务" class="headerlink" title="将接口声明为一个ws服务"></a>将接口声明为一个ws服务</h3><p>在Java中可以依靠javax.jws包中的相关注解来实现一个WebService接口</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@WebService</span></span><br><span class="line"><span class="meta">@SOAPBinding</span>(style = SOAPBinding.Style.RPC)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">testWebservice</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@WebMethod</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> ResponseRoot <span class="title">test</span><span class="params">(@WebParam(name = <span class="string">"item"</span>)</span> Item item, </span></span><br><span class="line"><span class="function"> @<span class="title">WebParam</span><span class="params">(name = <span class="string">"MessageHeader"</span>)</span> MessageHeaderRoot header) </span>;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol><li>使用 @WebService 注解标注该接口为一个WebService接口,可以修改服务名、端口名、命名空间等信息,如<br><code>@WebService(targetNamespace = "http://ujn.cn/",serviceName = "UserService", portName = "UserPort")</code></li><li>使用 @SOAPBinding 注解标注绑定的形式,根据绑定的不同的形式,客户端使用不同的调用约定进行调用。<ol><li>Document Wrapped(默认使用方式)</li><li>Document Bare</li><li>RPC</li></ol></li><li>使用 @WebMethod 方法标注为一个WebService中的一个方法服务</li><li>最后实现接口中的方法即可,如果需要返回信息,直接封装为一个对象即可,如上面代码中的ResponseRoot对象。最后会解析出一个对象。</li></ol><h3 id="添加XML对象"><a href="#添加XML对象" class="headerlink" title="添加XML对象"></a>添加XML对象</h3><p>在上面的代码中可以看到不管是接受还是返回,都是面向对象的,然而最后都需要形成为一个XML文档,那么,是如何将这些对象解析为有一整个XML对象的呢?</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="meta">@XmlRootElement</span>(name = <span class="string">"MessageHeader"</span>)</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MessageHeaderRoot</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1848365930921924635L</span>;</span><br><span class="line"></span><br><span class="line"> <span class="meta">@XmlElement</span>(name = <span class="string">"GUID"</span>)</span><br><span class="line"> <span class="keyword">private</span> String guid;</span><br><span class="line"> <span class="meta">@XmlElement</span>(name = <span class="string">"SEND_DATE"</span>)</span><br><span class="line"> <span class="keyword">private</span> String sendDate;</span><br><span class="line"> <span class="meta">@XmlTransient</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getGuid</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> guid;</span><br><span class="line"> }</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">setGuid</span><span class="params">(String guid)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.guid = guid;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="meta">@XmlTransient</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">getSendDate</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> sendDate;</span><br><span class="line"> }</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">setSendDate</span><span class="params">(String sendDate)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.sendDate = sendDate;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol><li>使用 <code>@XmlRootElement</code> 注解标注在对象上,说明此对象是一个可解析为XML节点的对象。</li><li>复杂对象必须继承 <code>Serializable</code> 接口,进行序列化。</li><li>使用 <code>@XmlElement(name = "GUID")</code> 注解,声明出其中的子节点和子节点的名称。</li><li>在get方法上添加 <code>@XmlTransient</code> 注解,否则会报对象节点重复的错误。</li><li>对对象通过以上的配置,可以完整的XML类型的数据映射到对象上,从而实现进一步的处理。</li></ol><h3 id="形成可接受的XML格式"><a href="#形成可接受的XML格式" class="headerlink" title="形成可接受的XML格式"></a>形成可接受的XML格式</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="XML"><figure class="iseeu highlight /xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">soapenv:Envelope</span> <span class="attr">xmlns:soapenv</span>=<span class="string">"http://schemas.xmlsoap.org/soap/envelope/"</span> <span class="attr">xmlns:web</span>=<span class="string">"http://webService.test.com/"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">soapenv:Header</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">soapenv:Body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">web:addPersonAndUnit</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">item</span>></span></span><br><span class="line"> <span class="comment"><!--内部对象体:--></span></span><br><span class="line"> <span class="tag"></<span class="name">item</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">MessageHeader</span>></span></span><br><span class="line"> <span class="comment"><!--内部对象体:--></span></span><br><span class="line"> <span class="tag"></<span class="name">MessageHeader</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">web:addPersonAndUnit</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">soapenv:Body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">soapenv:Envelope</span>></span></span><br></pre></td></tr></table></figure></div><blockquote><p> 可以使用SOAP-UI 这个工具进行生成,使用免费版,添加SOAP服务,后点击方法的request则会自动生成格式。</p></blockquote><h2 id="接口调用"><a href="#接口调用" class="headerlink" title="接口调用"></a>接口调用</h2><p>在调用其他服务的接口时,对方法等没有特殊的要求,只需要遵守对方特定的接口格式即可。在接口调用时,同样有多种调用的方式,在此处我使用xml格式拼接,http服务直接调用的方式。</p><h3 id="XML格式拼接"><a href="#XML格式拼接" class="headerlink" title="XML格式拼接"></a>XML格式拼接</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">ResQuestionBean resQuestionBean = <span class="keyword">new</span> ResQuestionBean();</span><br><span class="line">resQuestionBean.setQuestionCode(questionCode);</span><br><span class="line">resQuestionBean.setQuestionStatus(statusName);</span><br><span class="line">resQuestionBean.setUploader(personCode);</span><br><span class="line">resQuestionBean.setUploaderTime(DateUtil.format(<span class="keyword">new</span> Date()));</span><br><span class="line">StringBuilder resultXml = <span class="keyword">new</span> StringBuilder();</span><br><span class="line">resultXml.append(XmlUtils.objectToXML(resQuestionBean));</span><br></pre></td></tr></table></figure></div><ol><li>声明出一个返回对象 resQuestionBean ,对对象中的数据进行装载。</li><li>使用objectToXML方法将对象转换为XML字符串。</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> <span class="keyword">static</span> String <span class="title">objectToXML</span><span class="params">(Object object)</span> <span class="keyword">throws</span> JAXBException </span>{</span><br><span class="line"> JAXBContext context = JAXBContext.newInstance(object.getClass());</span><br><span class="line"> Marshaller m = context.createMarshaller();</span><br><span class="line"> <span class="comment">// 设置格式化输出</span></span><br><span class="line"> m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);</span><br><span class="line"> m.setProperty(Marshaller.JAXB_FRAGMENT, <span class="keyword">true</span>);</span><br><span class="line"> <span class="comment">// 不进行转义字符的处理</span></span><br><span class="line"> m.setProperty(CharacterEscapeHandler.class.getName(), <span class="keyword">new</span> CharacterEscapeHandler() {</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">void</span> <span class="title">escape</span><span class="params">(<span class="keyword">char</span>[] ch, <span class="keyword">int</span> start,<span class="keyword">int</span> length, <span class="keyword">boolean</span> isAttVal, Writer writer)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> writer.write(ch, start, length);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> Writer w = <span class="keyword">new</span> StringWriter();</span><br><span class="line"> m.marshal(object, w);</span><br><span class="line"> <span class="keyword">return</span> w.toString();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="通过HTTP调用方法,推送数据"><a href="#通过HTTP调用方法,推送数据" class="headerlink" title="通过HTTP调用方法,推送数据"></a>通过HTTP调用方法,推送数据</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">synchronized</span> String <span class="title">doSend</span><span class="params">(String address, String invokeMethod, </span></span></span><br><span class="line"><span class="function"><span class="params"> String guid, String recvSys, </span></span></span><br><span class="line"><span class="function"><span class="params"> String invokeDate, String dataXml)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="comment">//enum 选择系统,地址url http post方式</span></span><br><span class="line"> StringBuffer strBuf = <span class="keyword">null</span>;</span><br><span class="line"> URL url = <span class="keyword">new</span> URL(address);</span><br><span class="line"> HttpURLConnection huc = (HttpURLConnection) url.openConnection();</span><br><span class="line"> huc.setRequestMethod(<span class="string">"POST"</span>);</span><br><span class="line"> <span class="comment">//设置数据格式</span></span><br><span class="line"> huc.setRequestProperty(<span class="string">"content-type"</span>, <span class="string">"text/xml;charset=utf-8"</span>);</span><br><span class="line"> huc.setConnectTimeout(<span class="number">10000</span>);</span><br><span class="line"> huc.setUseCaches(<span class="keyword">false</span>);</span><br><span class="line"> huc.setReadTimeout(<span class="number">10000</span>);</span><br><span class="line"> huc.setDoInput(<span class="keyword">true</span>);</span><br><span class="line"> huc.setDoOutput(<span class="keyword">true</span>);</span><br><span class="line"> huc.setInstanceFollowRedirects(<span class="keyword">false</span>);</span><br><span class="line"> DataOutputStream dos = <span class="keyword">new</span> DataOutputStream(huc.getOutputStream());</span><br><span class="line"> dos.write(buildSendXml(invokeMethod, guid, recvSys, invokeDate, dataXml).getBytes(<span class="string">"utf-8"</span>));</span><br><span class="line"></span><br><span class="line"> dos.flush();</span><br><span class="line"> BufferedReader reader = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> InputStreamReader(huc.getInputStream(), <span class="string">"utf-8"</span>));</span><br><span class="line"> String line = <span class="keyword">null</span>;</span><br><span class="line"> strBuf = <span class="keyword">new</span> StringBuffer();</span><br><span class="line"> <span class="keyword">while</span> ((line = reader.readLine()) != <span class="keyword">null</span>) {</span><br><span class="line"> strBuf.append(line);</span><br><span class="line"> }</span><br><span class="line"> dos.close();</span><br><span class="line"> reader.close();</span><br><span class="line"> <span class="keyword">return</span> strBuf.toString();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol><li>设置地址、调用方式等http的基本信息后,使用write方法将数据写入到请求中。</li><li>使用reader读取返回数据,并返回、解析,形成最终数据后返回给前台做最终的处理</li></ol><h4 id="最后XML整体拼接"><a href="#最后XML整体拼接" class="headerlink" title="最后XML整体拼接"></a>最后XML整体拼接</h4><p>在 buildSendXml 方法中,将第一步格式化为XML的Object进行拼接。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> String <span class="title">buildSendXml</span><span class="params">(String invokeMethod, String guid, </span></span></span><br><span class="line"><span class="function"><span class="params"> String invokeDate,</span></span></span><br><span class="line"><span class="function"><span class="params"> String data, String itemTag, String methodTag)</span> </span>{</span><br><span class="line"> StringBuffer sb = <span class="keyword">new</span> StringBuffer();</span><br><span class="line"> sb.append(<span class="string">"<soapenv:Envelope xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\" xmlns:urn=\"urn:sap-com:document:sap:rfc:functions\">"</span> +</span><br><span class="line"> <span class="string">"<soapenv:Header/>"</span> +</span><br><span class="line"> <span class="string">"<soapenv:Body>"</span> +</span><br><span class="line"> <span class="string">"<"</span> + methodTag + <span class="string">":"</span> + invokeMethod + <span class="string">">"</span> +</span><br><span class="line"> <span class="string">" <MessageHeader>"</span> +</span><br><span class="line"> <span class="string">" <GUID>"</span> + guid + <span class="string">"</GUID>"</span> +</span><br><span class="line"> <span class="string">" <SEND_DATE>"</span> + invokeDate + <span class="string">"</SEND_DATE>"</span> +</span><br><span class="line"> <span class="string">" </MessageHeader>"</span> +</span><br><span class="line"> <span class="string">" <"</span> + itemTag + <span class="string">">"</span>);</span><br><span class="line"> sb.append(data);</span><br><span class="line"> sb.append(<span class="string">" <"</span> + itemTag + <span class="string">">"</span> +</span><br><span class="line"> <span class="string">"</urn:"</span> + invokeMethod + <span class="string">"> "</span> +</span><br><span class="line"> <span class="string">" </soapenv:Body>"</span> +</span><br><span class="line"> <span class="string">"</soapenv:Envelope>"</span>);</span><br><span class="line"> <span class="keyword">return</span> sb.toString();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><ol><li>接口无法联通后的再次重试问题</li><li>此篇文章写于WebService接口联合调试前,可能存在问题与风险未被发现。如果有请与我联系并进行规避。</li><li>如果有更好、更便捷的接口编写方案,欢迎一起讨论。</li></ol></blockquote>]]></content>
<summary type="html">
<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>在很多Java项目中,仍然会使用基于WebService的数据传输接口,而webservice实际上就是http+xml。而在实践过程中,主
</summary>
<category term="WebService" scheme="https://nanyiniu.github.io/categories/WebService/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="WebService" scheme="https://nanyiniu.github.io/tags/WebService/"/>
</entry>
<entry>
<title>多线程实践</title>
<link href="https://nanyiniu.github.io/2020/06/10/%E5%A4%9A%E7%BA%BF%E7%A8%8B%E4%B8%8E%E9%AB%98%E5%B9%B6%E5%8F%91/"/>
<id>https://nanyiniu.github.io/2020/06/10/多线程与高并发/</id>
<published>2020-06-10T15:00:00.000Z</published>
<updated>2023-07-30T09:34:44.086Z</updated>
<content type="html"><![CDATA[<h1 id="多线程"><a href="#多线程" class="headerlink" title="多线程"></a>多线程</h1><h2 id="线程和进程的概念"><a href="#线程和进程的概念" class="headerlink" title="线程和进程的概念"></a>线程和进程的概念</h2><h3 id="什么是进程?"><a href="#什么是进程?" class="headerlink" title="什么是进程?"></a>什么是进程?</h3><p>进程是程序的一次执行过程,是系统运行程序的基本单位。系统运行一个程序即是一个创建进程的过程。</p><p>进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。</p><h3 id="什么是线程?"><a href="#什么是线程?" class="headerlink" title="什么是线程?"></a>什么是线程?</h3><p>线程是比进程更小的执行单位,线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。</p><h2 id="线程和进程的区别"><a href="#线程和进程的区别" class="headerlink" title="线程和进程的区别"></a>线程和进程的区别</h2><ol><li>一个程序执行时会至少会启动一个进程,但一个进程内会启动一个或多个线程</li><li>各个进程之间是相互独立的,拥有独立的运行空间、内存,但是线程不能够独立运行,多个线程依赖、共享进程中的资源。</li><li>线程相比于进程是更小的执行单位,但没有独立资源,所以在运行时线v程切换的开销小,但不利于资源的管理和保护;而进程正相反。</li></ol><h2 id="线程的生命周期"><a href="#线程的生命周期" class="headerlink" title="线程的生命周期"></a>线程的生命周期</h2><p><strong>线程生命周期</strong></p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200328192955.png" alt="线程生命周期"></p><p>上图展示了线程从创建到结束的整个生命周期,下面从状态和控制两个方面分解图中的内容</p><p><strong>线程状态</strong></p><p>线程具有5中基本的状态:</p><ol><li><p>NEW(新建状态): 创建线程,还未启动</p></li><li><p>RUNNABLE(就绪状态): 可运行状态,但还未获得时间片,等待执行</p></li><li><p>RUNNING(执行状态): 运行状态,在就绪状态获得了时间片,进入运行状态</p></li><li><p>BLOCKED(阻塞状态):当某些情况下,线程被阻止运行,进入阻塞状态,阻塞的状态可分为三种:</p></li><li><ul><li>第一种为执行了wait()后会进入放入线程等待队列中,这种情况叫等待阻塞.<ul><li>第二种为等待获取synchronized()同步锁时,会讲线程放入同步锁队列中,等待前一个线程执行完synchronized中的内容,这种情况叫同步阻塞.</li><li>第三种为执行了sleep()或join()时,和wait()不同,它<strong>不会释放</strong>对象锁.</li></ul></li></ul></li><li><p>TERMINATED(终止状态):当线程正常结束或异常退出时,会到达终止状态</p></li></ol><p><strong>线程方法</strong></p><ul><li>run/start 需要并行处理的代码放在run()方法中,start()方法启动线程将自动调用 run() 方法,这是由Java的内存机制规定的。并且run()方法必须是public访问权限,返回值类型为void。</li><li>wait 当前线程暂停执行并释放对象锁标志,让其他线程可以进入synchronized数据块,当前线程被放入对象等待池中</li><li>nodify/nodifyAll 唤醒等待(wait)的线程</li><li>sleep 休眠一段时间后,会自动唤醒。但它并不释放对象锁。也就是如果有 synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕获异常</li><li>join 当前线程停下来等待,直至另一个调用join方法的线程终止,线程在被激活后不一定马上就运行,而是进入到可运行线程的队列中</li><li>yield 停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么yield()方法将不会起作用</li></ul><h2 id="线程同步"><a href="#线程同步" class="headerlink" title="线程同步"></a>线程同步</h2><h3 id="synchronized关键字"><a href="#synchronized关键字" class="headerlink" title="synchronized关键字"></a>synchronized关键字</h3><p>前面讲过Synchronized关键字总结以及使用synchronized实现单例中的 <code>double-check</code> 实例。</p><ul><li>锁定方式,使用<strong>sync锁定的是对象</strong>,而锁定对象带来的是其他线程无法获取对象锁,而无法执行synchronized下面的代码。而不是锁定代码!!<ol><li>使用synchronized修饰静态方法,锁定的是 <code>.class</code> 对象</li><li>使用synchronized修饰普通方法,锁定的是 <code>this</code> 实例对象</li><li>使用synchronized(obj),锁定特定obj对象</li></ol></li><li>使用sync的方法和非sync方法的调用时互不影响</li><li>因为synchronized是<strong>可重入的</strong>,所以两个sync方法可相互调用,不会产生死锁。</li><li>在sync中的代码出现异常时,锁会自动被释放,其他线程可拿到对象锁。</li><li><strong>synchronized锁升级</strong>,在早起的java中,使用sync相当于向操作系统申请锁,这个锁是<strong>重量级锁</strong>,而在1.5之后,出现了锁升级的概念:<ol><li>偏向锁,当使用sync时,会将对象中的头部信息中的markword中记录当前锁定线程的线程id。</li><li>自旋锁,如果多线程争用锁,则升级为自旋锁,占用CPU,通过线程自旋等待获取对象锁。</li><li>重量级锁,当自旋次数超过一定次数(10次),升级为重量级锁。</li></ol></li></ul><h3 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h3><p>vaolaile关键字具有两个特性:</p><ul><li>保证线程可见性</li></ul><p>多个线程读取写入对象时在非同步进行的时候是如图中的步骤一样</p><ol><li>从内存中读取对象o,读取到线程2本地</li><li>然后在线程2中修改了o的值,再写回内存。</li><li>线程1从内存读取对象,得到最新的对象o。</li></ol><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518230104.png" alt="Untitled"></p><p>上面的步骤是正常,sync的情况下的情况下。然而在很多情况下,线程之间是同步进行的,在第一步线程2读取到内容,线程1也读取到了内容,线程2再写回内容,此时如果线程1再次修改和写回是,内容就出现了覆盖。</p><p>这种方式就是将对象使用volatile修饰。此时,保证了对象在所有线程间的可见性。</p><p>在这种情况下,最直接的方式就是在这个对象o被修改、写入到内存时,其他线程立刻能知道这个对象已经被修改了,进行再次获取最新的内容来保证最终的一致。</p><ul><li>禁止指令重排序</li></ul><p>一个new语句可以创建一个对象,而创建这个对象虚拟机会分为多条指令让CPU进行处理,而处理的过程会发生指令的重新排序。</p><p>volatile关键字对于基本类型的修改可以在随后对多个线程的读保持一致,但是对于引用类型如数组,实体bean,仅仅保证<strong>引用的可见性</strong>,但并不保证引用内容的可见性。</p><h3 id="CAS-Compare-and-Swap"><a href="#CAS-Compare-and-Swap" class="headerlink" title="CAS(Compare and Swap)"></a>CAS(Compare and Swap)</h3><p>因为类似count++这种操作时非原子性的,所以每次在需要同步使用时必须要在count++处添加<code>synchronized</code>,保证此处只能有一个线程操作。</p><p>此时出现了Atomicxxx类,该类中的所有操作都是原子性的,从此区别于使用sync包裹count++,而是使用CAS乐观锁的方式。<br>在Atomic类中,实现”原子“这一性质的根源为UnSafe类中的CompareAndSwap相关方法,这些方法就是Java实现的CAS。</p><ul><li>CAS分析</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> AtomicInteger integer = <span class="keyword">new</span> AtomicInteger(<span class="number">2</span>);</span><br><span class="line"> integer.incrementAndGet();</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">incrementAndGet</span><span class="params">()</span> </span>{ </span><br><span class="line"><span class="keyword">for</span> (;;) {</span><br><span class="line"> <span class="keyword">int</span> current = get();</span><br><span class="line"> <span class="keyword">int</span> next = current + <span class="number">1</span>; </span><br><span class="line"> <span class="keyword">if</span> (compareAndSet(current, next))</span><br><span class="line"> <span class="keyword">return</span> next; </span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">compareAndSet</span><span class="params">(<span class="keyword">int</span> expect, <span class="keyword">int</span> update)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> unsafe.compareAndSwapInt(<span class="keyword">this</span>, valueOffset, expect, update);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在incrementAndGet中使用了CAS操作,每次从内存中读取数据然后将此数据和+1后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。</p><p>整个compareAndSwapInt是一个原子的操作,在含义上可以理解为:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">compareAndSwapInt(<span class="keyword">this</span>, valueOffset, expect, update){</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span> == expect) {</span><br><span class="line"><span class="keyword">this</span> = update</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">} <span class="keyword">else</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">}</span><br></pre></td></tr></table></figure></div><ul><li>ABA问题</li></ul><p>因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。</p><p>对于一个基本数据类型如Integer等,不会出现ABA问题,只要保证最终的一致就可以了。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518230119.png" alt="Untitled 1"></p><p>但是对于一个普通对象,ABA问题的影响就体现出来了。如右图中。在第2步中,已经将c的内容进行了变更,然而在CAS判断的时候,会判断B有没有变。</p><p>了解即可。。。</p><h2 id="相关同步类"><a href="#相关同步类" class="headerlink" title="相关同步类"></a>相关同步类</h2><h3 id="ReentrantLock"><a href="#ReentrantLock" class="headerlink" title="ReentrantLock"></a>ReentrantLock</h3><p><a href="https://crossoverjie.top/2018/01/25/ReentrantLock/" target="_blank" rel="noopener">ReentrantLock 实现原理</a></p><p>由名字就可以得知这个 <code>ReentrantLock</code> 是可重入的锁,对比同样可重入的<code>synchronized</code>,它有什么优势有什么劣势呢?</p><ul><li>使用lock()和unlock()方法加锁</li><li>使用tryLock() 尝试获得锁,能够限制获得锁的时间,更灵活</li><li>设置公平锁,各线程公平竞争</li></ul><h3 id="CountDownLatch"><a href="#CountDownLatch" class="headerlink" title="CountDownLatch"></a>CountDownLatch</h3><p>latch是门闩的意思,所以countDownLatch的意思大致就为,每次线程运行后将count-1,直至减为0后,打开门闩,执行后面的程序。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">useCountDownLatch</span><span class="params">()</span></span>{</span><br><span class="line"> Thread[] threads = <span class="keyword">new</span> Thread[<span class="number">10</span>];</span><br><span class="line"> <span class="comment">// set countDownLatch size</span></span><br><span class="line"> CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> threads[i] = <span class="keyword">new</span> Thread(){</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">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> System.out.println(getName());</span><br><span class="line"> countDownLatch.countDown();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> threads[i].setName(<span class="string">"thread-"</span>+i);</span><br><span class="line"> <span class="comment">// execute Thread run then countDown..</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < threads.length; i++) {</span><br><span class="line"> threads[i].start();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"Task Start!"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="comment">// causes current thread wait until the countDownLatch down to zero</span></span><br><span class="line"> <span class="comment">// effect all thread finish run()</span></span><br><span class="line"> countDownLatch.await();</span><br><span class="line"> }<span class="keyword">catch</span> (Exception e){</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"All Task is Done!"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>上面代码中,建立了10个线程,每个线程几乎同时开始,并且sleep1秒钟。输出的结果为:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">Task Start!</span><br><span class="line">thread-<span class="number">1</span></span><br><span class="line">thread-<span class="number">3</span></span><br><span class="line">thread-<span class="number">4</span></span><br><span class="line">thread-<span class="number">0</span></span><br><span class="line">thread-<span class="number">6</span></span><br><span class="line">thread-<span class="number">2</span></span><br><span class="line">thread-<span class="number">5</span></span><br><span class="line">thread-<span class="number">9</span></span><br><span class="line">thread-<span class="number">8</span></span><br><span class="line">thread-<span class="number">7</span></span><br><span class="line">All Task is Done!</span><br></pre></td></tr></table></figure></div><p>可以看到,只有当所有线程执行完成后,才会执行后面的程序,否则所有线程就一直block在<code>countDownLatch.await();</code>方法处。</p><h3 id="CyclicBarrier"><a href="#CyclicBarrier" class="headerlink" title="CyclicBarrier"></a>CyclicBarrier</h3><p>多个线程互相等待,直到到达同一个同步点,再继续一起执行。</p><p>CyclicBarrier 和 CountDownLatch 不一样的地方在于</p><ol><li>发展的方向不同:前者是递增发展,而后者为递减</li><li>使用次数不同:CountDownLatch只能使用一次,而CyclicBarrier可以多次使用</li><li>最重要的是概念、用法不同:CountDownLatch为<strong>一个或者多个线程,等待其他多个线程完成某件事情之后才能执行。而</strong>CyclicBarrier则为<strong>多个线程互相等待,直到到达同一个同步点,再继续一起执行</strong>。</li></ol><h3 id="ReadWriteLock"><a href="#ReadWriteLock" class="headerlink" title="ReadWriteLock"></a>ReadWriteLock</h3><p>如名字一样,是读写锁,在<code>ReadWriteLock</code>中通过<code>readLock()</code>获取读锁,通过 <code>writeLock()</code> 获取写锁。</p><p>为什么要将写锁和读锁进行分离?这就好考虑应用场景。读写锁的应用场景通常是需要特别考虑效率的地方,并且读多写少的情况。</p><p><strong>读的时候使用共享锁</strong>:在多线程读的时候,没有必要将方法锁住,多线程一块读效率更高。</p><p><strong>写的时候使用排他锁</strong>:写的时候,其他线程不能同时读和写,以防止产生脏读的现象。</p><p>也就是说,读的时候是多线程并行读,写的时候只能单线程写。下面简单举个读写的例子。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC03_ReadWriteLock</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">static</span> ReadWriteLock readWriteLock = <span class="keyword">new</span> ReentrantReadWriteLock();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Integer msg = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="comment">/*synchronized*/</span> <span class="function"><span class="keyword">void</span> <span class="title">read</span><span class="params">()</span></span>{</span><br><span class="line"> Lock read = readWriteLock.readLock();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> read.lock();</span><br><span class="line"> System.out.println(<span class="string">"reading msg is "</span>+msg);</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> read.unlock();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="comment">/*synchronized*/</span> <span class="function"><span class="keyword">void</span> <span class="title">write</span><span class="params">()</span></span>{</span><br><span class="line"> Lock write = readWriteLock.writeLock();</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> write.lock();</span><br><span class="line"> msg = <span class="keyword">new</span> Random().nextInt();</span><br><span class="line"> System.out.println(<span class="string">"write msg .."</span>+ msg);</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> }<span class="keyword">catch</span> (Exception e){</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> write.unlock();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC03_ReadWriteLock juc03_readWriteLock = <span class="keyword">new</span> JUC03_ReadWriteLock();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">9</span>; i++) {</span><br><span class="line"> Thread readThread = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> juc03_readWriteLock.read();</span><br><span class="line"> });</span><br><span class="line"> readThread.start();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">2</span>; i++) {</span><br><span class="line"> Thread writeThread = <span class="keyword">new</span> Thread(()->{</span><br><span class="line"> juc03_readWriteLock.write();</span><br><span class="line"> });</span><br><span class="line"> writeThread.start();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这时,如果使用synchronized将read和write都上锁的话,那么每次读的时候都需要经过一秒的等待时间,所以总共需要等待11秒。但是,如果使用读写锁,总的读的时间可能也就一秒,总共也就3秒。</p><p>所以,在读多写少的情况下,使用读写锁,效率会非常高。</p><h3 id="Semaphore"><a href="#Semaphore" class="headerlink" title="Semaphore"></a>Semaphore</h3><p>信号量,通过使用这个同步工具类,可以限制执行线程的数量。当特定数量的线程执行完,其他线程才可以执行。</p><p>类似买汽车票时去窗口排队一样,一共四个窗口,100个人,同时进行买票的只能是在四个窗口排队的那四个人。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC03_Semaphore</span> </span>{</span><br><span class="line"></span><br><span class="line"> Semaphore semaphore = <span class="keyword">new</span> Semaphore(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"> Thread t1 = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> semaphore.acquire();</span><br><span class="line"> Thread.sleep(<span class="number">2000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> semaphore.release();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"t1.....end"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> Thread t2 = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> semaphore.acquire();</span><br><span class="line"> Thread.sleep(<span class="number">2000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> semaphore.release();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"t2.....end"</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> Thread t3 = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> semaphore.acquire();</span><br><span class="line"> Thread.sleep(<span class="number">2000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> semaphore.release();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"t3.....end"</span>);</span><br><span class="line"> });</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">run</span><span class="params">()</span></span>{</span><br><span class="line"> t1.start();</span><br><span class="line"> t2.start();</span><br><span class="line"> t3.start();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC03_Semaphore juc03_semaphore = <span class="keyword">new</span> JUC03_Semaphore();</span><br><span class="line"> juc03_semaphore.run();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>上面声明了3个线程,设置的限号量的permit数量为2,也就是说能够同时运行2个线程,每次线程运行时会acquire,也就是拿走一个信号量,执行完再放回。</p><p>所以结果也可想而知,t1和t2几乎同时被打印,t3过了两秒才被打印出来。</p><p>所以,如果需要对执行的线程数量需要控制,使用Semaphore,起到限流的作用。</p><h2 id="线程通信"><a href="#线程通信" class="headerlink" title="线程通信"></a>线程通信</h2><p>线程间通信,也就是线程a 通知到线程 b,可以有很多种方式,</p><ul><li>wait/notify /nodifyAll</li><li>Lock/Condition</li><li>countDownLatch</li><li>LockSupport</li></ul><p>根据实例代码来了解过程。</p><h4 id="一、线程-b-监听线程-a-的工作过程"><a href="#一、线程-b-监听线程-a-的工作过程" class="headerlink" title="一、线程 b 监听线程 a 的工作过程"></a>一、线程 b 监听线程 a 的工作过程</h4><h5 id="使用-wait-和-notify-实现"><a href="#使用-wait-和-notify-实现" class="headerlink" title="使用 wait 和 notify 实现"></a>使用 wait 和 notify 实现</h5><ol><li>线程 b 先运行,进行等待</li><li>线程a 随后运行,当打印到 5 的时候,唤醒线程 b</li><li>唤醒线程 b 后,打印信息后,立刻唤醒线程a</li><li>线程 a 被唤醒后,继续打印</li></ol><p>实现如图中的所显示的一样:当出现红色条时,证明当前线程为阻塞的。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518233224.png" alt="Untitled"></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_StopOtherThread01</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> number = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">intercept</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (number != <span class="number">5</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"print that message !!!"</span>);</span><br><span class="line"> <span class="keyword">this</span>.notify();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"intercept finished!!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">increase</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">while</span> (number <= <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">if</span> (number == <span class="number">5</span>) {</span><br><span class="line"> <span class="keyword">this</span>.notify();</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> number++;</span><br><span class="line"> System.out.println(<span class="string">"current number size is :"</span> + number);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>.notify();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC04_StopOtherThread01 stopOtherThread01 = <span class="keyword">new</span> JUC04_StopOtherThread01();</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> stopOtherThread01.intercept();</span><br><span class="line"> }).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> stopOtherThread01.increase();</span><br><span class="line"> }).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h5 id="使用ReentrantLock和Condition实现"><a href="#使用ReentrantLock和Condition实现" class="headerlink" title="使用ReentrantLock和Condition实现"></a>使用ReentrantLock和Condition实现</h5><p>与 wait/notify 的结构相同</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_StopOtherThread02</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> number = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">increase</span><span class="params">()</span> </span>{</span><br><span class="line"> number++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC04_StopOtherThread02 s = <span class="keyword">new</span> JUC04_StopOtherThread02();</span><br><span class="line"></span><br><span class="line"> Lock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"> Condition condition = lock.newCondition();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> lock.lock();</span><br><span class="line"> <span class="keyword">if</span> (s.size() != <span class="number">5</span>) {</span><br><span class="line"> condition.await();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"print that message !!!"</span>);</span><br><span class="line"> condition.signal();</span><br><span class="line"> condition.await();</span><br><span class="line"> System.out.println(<span class="string">"all finished!!"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"> }).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> lock.lock();</span><br><span class="line"> <span class="keyword">while</span> (s.size() < <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">if</span> (s.size() == <span class="number">5</span>) {</span><br><span class="line"> condition.signal();</span><br><span class="line"> condition.await();</span><br><span class="line"> }</span><br><span class="line"> s.increase();</span><br><span class="line"> System.out.println(<span class="string">"current number size is :"</span> + s.size());</span><br><span class="line"> }</span><br><span class="line"> condition.signal();</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h5 id="CountDownLatch-1"><a href="#CountDownLatch-1" class="headerlink" title="CountDownLatch"></a>CountDownLatch</h5><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_StopOtherThread03</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> number = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">increase</span><span class="params">()</span> </span>{</span><br><span class="line"> number++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC04_StopOtherThread03 s = <span class="keyword">new</span> JUC04_StopOtherThread03();</span><br><span class="line"></span><br><span class="line"> CountDownLatch latch1 = <span class="keyword">new</span> CountDownLatch(<span class="number">1</span>);</span><br><span class="line"> CountDownLatch latch2 = <span class="keyword">new</span> CountDownLatch(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (s.size() != <span class="number">5</span>) {</span><br><span class="line"> latch1.await();</span><br><span class="line"> latch2.countDown();</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"print that message !!!"</span>);</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"all finished!!"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> }</span><br><span class="line"> }).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">while</span> (s.size() < <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">if</span> (s.size() == <span class="number">5</span>) {</span><br><span class="line"> latch1.countDown();</span><br><span class="line"></span><br><span class="line"> latch2.await();</span><br><span class="line"> }</span><br><span class="line"> s.increase();</span><br><span class="line"> System.out.println(<span class="string">"current number size is :"</span> + s.size());</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h5 id="LockSupport"><a href="#LockSupport" class="headerlink" title="LockSupport"></a>LockSupport</h5><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_StopOtherThread04</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> number = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> Thread t1 = <span class="keyword">null</span>;</span><br><span class="line"> Thread t2 = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">increase</span><span class="params">()</span> </span>{</span><br><span class="line"> number++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> number;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"></span><br><span class="line"> JUC04_StopOtherThread04 s = <span class="keyword">new</span> JUC04_StopOtherThread04();</span><br><span class="line"></span><br><span class="line"> s.t1 = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (s.size() != <span class="number">5</span>) {</span><br><span class="line"> LockSupport.park();</span><br><span class="line"> LockSupport.unpark(s.t2);</span><br><span class="line"> }</span><br><span class="line"> System.out.println(<span class="string">"print that message !!!"</span>);</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"all finished!!"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> s.t2 = <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">while</span> (s.size() < <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">if</span> (s.size() == <span class="number">5</span>) {</span><br><span class="line"> LockSupport.unpark(s.t1);</span><br><span class="line"> LockSupport.park();</span><br><span class="line"> }</span><br><span class="line"> s.increase();</span><br><span class="line"> System.out.println(<span class="string">"current number size is :"</span> + s.size());</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> s.t1.start();</span><br><span class="line"> s.t2.start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>除了监听,实现打断线程输出的这个例子,还有一个非常经典的就是生产者消费者的问题。</p><h4 id="二、多线程中的生产者消费者问题"><a href="#二、多线程中的生产者消费者问题" class="headerlink" title="二、多线程中的生产者消费者问题"></a>二、多线程中的生产者消费者问题</h4><h5 id="使用-wait-和-nodify-实现"><a href="#使用-wait-和-nodify-实现" class="headerlink" title="使用 wait 和 nodify 实现"></a>使用 wait 和 nodify 实现</h5><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_CustomerAndProducer01</span> </span>{</span><br><span class="line"> List<Integer> repostory = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> MAX = <span class="number">15</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(Integer value)</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"> <span class="keyword">while</span> (size == MAX) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" size:"</span> + size + <span class="string">" block producer thread!!"</span>);</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> }</span><br><span class="line"> repostory.add(value);</span><br><span class="line"> size++;</span><br><span class="line"> System.out.println(<span class="string">"current thread "</span>+Thread.currentThread().getName()+<span class="string">" increase size , current size is :"</span> + size);</span><br><span class="line"> <span class="keyword">this</span>.notifyAll();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"> <span class="keyword">while</span> (size <= <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" size:"</span> + size + <span class="string">" block customer thread!!"</span>);</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> }</span><br><span class="line"> repostory.remove(<span class="number">0</span>);</span><br><span class="line"> size--;</span><br><span class="line"> System.out.println(<span class="string">"current thread "</span>+Thread.currentThread().getName()+<span class="string">" increase size , current size is :"</span> + size);</span><br><span class="line"> <span class="keyword">this</span>.notifyAll();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC04_CustomerAndProducer01 c = <span class="keyword">new</span> JUC04_CustomerAndProducer01();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < <span class="number">5</span>; j++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> c.get();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"c_"</span> + i).start();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">2</span>; i++) {</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < <span class="number">25</span>; j++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> c.add(<span class="keyword">new</span> Random().nextInt());</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"p_"</span> + i).start();</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></div><p>使用 nodifyAll 会全部叫醒所有线程,如何实现单独叫醒某些线程?可以使用 ReentrantLock 来实现</p><h5 id="使用ReentrantLock实现"><a href="#使用ReentrantLock实现" class="headerlink" title="使用ReentrantLock实现"></a>使用ReentrantLock实现</h5><p>ReentrantLock 通过使用多个 newCondition 来实现对不同类的线程单独唤醒。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JUC04_CustomerAndProducer02</span> </span>{</span><br><span class="line"> List<Integer> repostory = <span class="keyword">new</span> LinkedList<>();</span><br><span class="line"> <span class="keyword">private</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"> <span class="keyword">private</span> Condition add = lock.newCondition();</span><br><span class="line"> <span class="keyword">private</span> Condition get = lock.newCondition();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> size = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> MAX = <span class="number">15</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">add</span><span class="params">(Integer value)</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> lock.lock();</span><br><span class="line"> <span class="keyword">while</span> (size == MAX) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" size:"</span> + size + <span class="string">" block producer thread!!"</span>);</span><br><span class="line"> add.await();</span><br><span class="line"> }</span><br><span class="line"> repostory.add(value);</span><br><span class="line"> size++;</span><br><span class="line"> System.out.println(<span class="string">"current thread "</span>+Thread.currentThread().getName()+<span class="string">" increase size , current size is :"</span> + size);</span><br><span class="line"> get.signal();</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">get</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> lock.lock();</span><br><span class="line"> <span class="keyword">while</span> (size <= <span class="number">0</span>) {</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" size:"</span> + size + <span class="string">" block customer thread!!"</span>);</span><br><span class="line"> get.await();</span><br><span class="line"> }</span><br><span class="line"> repostory.remove(<span class="number">0</span>);</span><br><span class="line"> size--;</span><br><span class="line"> System.out.println(<span class="string">"current thread "</span>+Thread.currentThread().getName()+<span class="string">" increase size , current size is :"</span> + size);</span><br><span class="line"> add.signal();</span><br><span class="line"> }<span class="keyword">catch</span> (Exception e){</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }<span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> JUC04_CustomerAndProducer02 c = <span class="keyword">new</span> JUC04_CustomerAndProducer02();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < <span class="number">5</span>; j++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> c.get();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"c_"</span> + i).start();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> TimeUnit.SECONDS.sleep(<span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">2</span>; i++) {</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j < <span class="number">25</span>; j++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> c.add(<span class="keyword">new</span> Random().nextInt());</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"p_"</span> + i).start();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<h1 id="多线程"><a href="#多线程" class="headerlink" title="多线程"></a>多线程</h1><h2 id="线程和进程的概念"><a href="#线程和进程的概念" class="headerlink" title="线程和进程
</summary>
<category term="多线程" scheme="https://nanyiniu.github.io/categories/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="多线程" scheme="https://nanyiniu.github.io/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>(四)AOP切面使用与原理</title>
<link href="https://nanyiniu.github.io/2020/05/22/%EF%BC%88%E5%9B%9B%EF%BC%89AOP%E5%88%87%E9%9D%A2%E4%BD%BF%E7%94%A8%E4%B8%8E%E5%8E%9F%E7%90%86/"/>
<id>https://nanyiniu.github.io/2020/05/22/(四)AOP切面使用与原理/</id>
<published>2020-05-22T07:26:46.085Z</published>
<updated>2020-07-12T08:54:13.987Z</updated>
<content type="html"><![CDATA[<h1 id="SpringAOP的使用和源码分析"><a href="#SpringAOP的使用和源码分析" class="headerlink" title="SpringAOP的使用和源码分析"></a>SpringAOP的使用和源码分析</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>现在来看(Aspect-oriented Programming) 面向切面编程aop,是和oop(Object-oriented Programming)是对编程结果的不同的思考的方向。在面向对象中,形成组合的组件是类(class)。而在面向切面,形成组合的组件是切面(Aspect),切面能够对关注的地方模块化,横切多种不同的类型和对象(比如事务管理)。而这个所谓的“关注的地方”,也可以在AOP中称为<strong>跨领域关注点</strong>。</p><p>Spring提供 配置文件 和 @AspectJ注解 方式编写AOP,两种方式都支持各种的 Advice(通知),如果使用@AspectJ注解,仍然会通过Spring的进行织入。</p><h2 id="AOP中关键字的概念"><a href="#AOP中关键字的概念" class="headerlink" title="AOP中关键字的概念"></a>AOP中关键字的概念</h2><h3 id="1-Aspect"><a href="#1-Aspect" class="headerlink" title="1.Aspect"></a>1.Aspect</h3><p>切面: 对多种不同的类的横切关注点,这个关注点被模块化为一个类,这个类就叫做切面(切面类),通过在普通类上标注 <code>@Aspect</code>注解来实现。</p><h3 id="2-Join-point"><a href="#2-Join-point" class="headerlink" title="2.Join point"></a>2.Join point</h3><p>连接点:程序执行过程中能够执行通知(Advice)的点。</p><h3 id="3-Advice"><a href="#3-Advice" class="headerlink" title="3.Advice"></a>3.Advice</h3><p>通知:通知指的是切面在指定的连接点处执行的操作,不同的通知类型包括:“around”, “before” and “after”</p><h3 id="4-Pointcut"><a href="#4-Pointcut" class="headerlink" title="4.Pointcut"></a>4.Pointcut</h3><p>切点:切点会匹配通知所要织入的一个或者多个连接点,通知是与切点向关联的,并且在任何匹配到切点的地方执行在连接点上的操作。</p><h3 id="5-Target-object"><a href="#5-Target-object" class="headerlink" title="5.Target object"></a>5.Target object</h3><p>目标对象:一个【被一个或多个切面进行通知】的对象叫做目标对象,也叫代理对象。</p><h3 id="6-AOP-proxy"><a href="#6-AOP-proxy" class="headerlink" title="6.AOP proxy"></a>6.AOP proxy</h3><p>AOP代理:在Spring中,一个是JDK动态代理,一个是 CGLIB 代理。</p><h3 id="7-Weaving"><a href="#7-Weaving" class="headerlink" title="7.Weaving"></a>7.Weaving</h3><p>织入:织入是将切面应用到目标对象来创建的代理对象过程</p><h2 id="使用注解方式进行AOP的配置和使用"><a href="#使用注解方式进行AOP的配置和使用" class="headerlink" title="使用注解方式进行AOP的配置和使用"></a>使用注解方式进行AOP的配置和使用</h2><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 1. 使用 <span class="doctag">@Aspect</span> 注解创建切面类</span></span><br><span class="line"><span class="comment"> * 2. 使用 <span class="doctag">@Pointcut</span> 注解注明切点(使用Aspect表达式)</span></span><br><span class="line"><span class="comment"> * 3. 声明通知(Before,After,Around,AfterReturning,AfterThrowing)具体含义可以参考Ref文档 5.4.4. Declaring Advice</span></span><br><span class="line"><span class="comment"> * 只是以上三步是不行的,因为没有开启Spring对注解版AOP的支持</span></span><br><span class="line"><span class="comment"> * 所以,需要使用 <span class="doctag">@EnableAspectJAutoProxy</span> 开启对<span class="doctag">@Aspect</span>注解的支持</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"><span class="meta">@Aspect</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">LogAspects</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="meta">@Pointcut</span>(value=<span class="string">"within(com.nanyin.service.*)"</span>)</span><br><span class="line"><span class="comment">// @Pointcut("execution(public * (..))")</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">pointCut</span><span class="params">()</span></span>{</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Before</span>(<span class="string">"pointCut()"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">logBefore</span><span class="params">(JoinPoint jp)</span></span>{</span><br><span class="line">System.out.println(jp.getSignature().getName());</span><br><span class="line">System.out.println(<span class="string">"before..."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@After</span>(<span class="string">"pointCut()"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">logAfter</span><span class="params">()</span></span>{</span><br><span class="line">System.out.println(<span class="string">"after..."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@AfterReturning</span>(<span class="string">"pointCut()"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">logAfterReturn</span><span class="params">()</span></span>{</span><br><span class="line">System.out.println(<span class="string">"after return..."</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@AfterThrowing</span>(value = <span class="string">"pointCut()"</span>,throwing = <span class="string">"ex"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">logAfterThrowing</span><span class="params">(JoinPoint joinPoint,Exception ex)</span></span>{</span><br><span class="line">joinPoint.getSignature().getName();</span><br><span class="line">System.out.println(<span class="string">"after throwing..."</span>);</span><br><span class="line">System.out.println(ex.getMessage());</span><br><span class="line">}</span><br><span class="line"><span class="comment">// around在proceed 之前是在 before之前执行</span></span><br><span class="line"><span class="comment">// around在proceed 之前是在 after之前执行</span></span><br><span class="line"><span class="comment">// 可兼容before和after</span></span><br><span class="line"><span class="meta">@Around</span>(<span class="string">"pointCut()"</span>)</span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">logAround</span><span class="params">(ProceedingJoinPoint joinPoint)</span> <span class="keyword">throws</span> Throwable </span>{</span><br><span class="line"><span class="comment">// 方法开始</span></span><br><span class="line">System.out.println(<span class="string">"around methods begin ..."</span>);</span><br><span class="line">Object object = joinPoint.proceed();</span><br><span class="line">System.out.println(<span class="string">"around methods end ..."</span>);</span><br><span class="line"><span class="keyword">return</span> object;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="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">@Test</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">test1</span><span class="params">()</span></span>{</span><br><span class="line">AnnotationConfigApplicationContext applicationContext = <span class="keyword">new</span> AnnotationConfigApplicationContext(BasicConfig.class);</span><br><span class="line">BasicService basicService = (BasicService) applicationContext.getBean(<span class="string">"basicService"</span>);</span><br><span class="line"><span class="comment">// 因为切点包括这个basicService,所以执行hello就会触发AOP</span></span><br><span class="line">basicService.hello();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 结果</span></span><br><span class="line">around methods begin ...</span><br><span class="line">hello</span><br><span class="line">before...</span><br><span class="line">hello world!!</span><br><span class="line">around methods end ...</span><br><span class="line">after...</span><br><span class="line">after <span class="keyword">return</span>...</span><br></pre></td></tr></table></figure></div><h2 id="AOP代码原理分析"><a href="#AOP代码原理分析" class="headerlink" title="AOP代码原理分析"></a>AOP代码原理分析</h2><p>通过以上实践,可以看到如果需要使用Spring的AOP功能,需要先使用 <code>@EnableAspectJAutoProxy</code> 注解标注在配置类中,使 <code>@Aspect</code> 生效为一个切面类。</p><h3 id="1-EnableAspectJAutoProxy-开启springAOP支持"><a href="#1-EnableAspectJAutoProxy-开启springAOP支持" class="headerlink" title="1. @EnableAspectJAutoProxy 开启springAOP支持"></a>1. @EnableAspectJAutoProxy 开启springAOP支持</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Target</span>(ElementType.TYPE) <span class="comment">//标注在类上</span></span><br><span class="line"><span class="meta">@Retention</span>(RetentionPolicy.RUNTIME)</span><br><span class="line"><span class="meta">@Documented</span></span><br><span class="line"><span class="meta">@Import</span>(AspectJAutoProxyRegistrar.class) <span class="comment">//引入 AspectJAutoProxyRegistrar</span></span><br><span class="line"><span class="keyword">public</span> <span class="meta">@interface</span> EnableAspectJAutoProxy {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 是否使用CGLIB进行代理,默认是false,即默认使用JDK动态代理</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">proxyTargetClass</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">false</span></span>;</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 是否需要 代理应由AOP框架公开为{ThreadLocal},以便通过{AopContext}类进行检索。</span></span><br><span class="line"><span class="comment"> **/</span></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">exposeProxy</span><span class="params">()</span> <span class="keyword">default</span> <span class="keyword">false</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>可以看到这里使用了Import注解,将<code>AspectJAutoProxyRegistrar</code> 引入到配置中。引入后,会通过继承自<code>ImportBeanDefinitionRegistrar</code>接口的方法 <code>registerBeanDefinitions</code> 做后续处理。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">registerBeanDefinitions</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)</span> </span>{</span><br><span class="line"><span class="comment">//如果需要 注册AspectJ注解自动代理创建类 internalAutoProxyCreator</span></span><br><span class="line">AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> BeanDefinition <span class="title">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">BeanDefinitionRegistry registry, @Nullable Object source)</span> </span>{</span><br><span class="line"><span class="comment">// 注册或升级AutoProxyCreator AnnotationAwareAspectJAutoProxyCreator 这个类是用来处理所有的AspectJ注解的切面</span></span><br><span class="line"><span class="keyword">return</span> registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最后通过 <code>registerOrEscalateApcAsRequired</code> 方法将 <code>cls</code> 也就是 <code>AnnotationAwareAspectJAutoProxyCreator</code> 注册到容器中,名称为 <code>internalAutoProxyCreator</code>。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Nullable</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> BeanDefinition <span class="title">registerOrEscalateApcAsRequired</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source)</span> </span>{</span><br><span class="line"><span class="comment">//cls=AnnotationAwareAspectJAutoProxyCreator</span></span><br><span class="line">Assert.notNull(registry, <span class="string">"BeanDefinitionRegistry must not be null"</span>);</span><br><span class="line"><span class="comment">// 是否存在internalAutoProxyCreator</span></span><br><span class="line"><span class="keyword">if</span> (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 获得一个 AnnotationAwareAspectJAutoProxyCreator 的 beanDefinition</span></span><br><span class="line">RootBeanDefinition beanDefinition = <span class="keyword">new</span> RootBeanDefinition(cls);</span><br><span class="line">beanDefinition.setSource(source);</span><br><span class="line">beanDefinition.getPropertyValues().add(<span class="string">"order"</span>, Ordered.HIGHEST_PRECEDENCE);</span><br><span class="line">beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);</span><br><span class="line"><span class="comment">// 注册名为 org.springframework.aop.config.internalAutoProxyCreator 的类</span></span><br><span class="line"><span class="comment">// 实际对应的是 AnnotationAwareAspectJAutoProxyCreator 类,而这个类是用来使@Aspect起作用的</span></span><br><span class="line">registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);</span><br><span class="line"><span class="keyword">return</span> beanDefinition;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>经过上面的分析能够了解通过使用 <code>@EnableAspectJAutoProxy</code> 这个注解,能够将<code>AnnotationAwareAspectJAutoProxyCreator</code>引入到容器中,从而达到解析,使用AOP。</p><p>那么 <strong>AnnotationAwareAspectJAutoProxyCreator</strong> 是如何工作的呢?为何引入这个类,才能使用Spring的AOP功能呢?</p><h3 id="2-AnnotationAwareAspectJAutoProxyCreator"><a href="#2-AnnotationAwareAspectJAutoProxyCreator" class="headerlink" title="2. @AnnotationAwareAspectJAutoProxyCreator"></a>2. @AnnotationAwareAspectJAutoProxyCreator</h3><h4 id="类结构初步分析"><a href="#类结构初步分析" class="headerlink" title="类结构初步分析"></a>类结构初步分析</h4><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20191117135450.jpg" alt="图1"></p><p>对<code>AnnotationAwareAspectJAutoProxyCreator</code> 生成类结构图,可以看到它继承自有两个顶级接口,分别是 <code>BeanPostProcesser</code>和 <code>Aware</code> 接口。</p><ul><li><p>Aware 接口主要用于标记超级接口,用于指示bean有资格通过回调样式方法由Spring容器通知特定框架对象。</p></li><li><p>BeanPostProcesser 可以通过实现 BeanPostProcesser的两个后置处理器来实现对Bean的前后置处理:1.<code>postProcessBeforeInitialization(Object bean, String beanName)</code> 2.<code>postProcessAfterInitialization(Object bean, String beanName)</code> 分别针对初始化前后的拦截处理。</p></li></ul><h5 id="a-AbstractAutoProxyCreator"><a href="#a-AbstractAutoProxyCreator" class="headerlink" title="a. AbstractAutoProxyCreator"></a>a. AbstractAutoProxyCreator</h5><p>在通过上面图片中所看到的 <code>AbstractAutoProxyCreator</code> 继承了 <code>ProxyProcessorSupport</code> 类</p><table><thead><tr><th>继承类/接口</th><th>作用</th><th>需要实现方法</th></tr></thead><tbody><tr><td>ProxyConfig类</td><td>用于创建代理的配置的便利超类,保证所有的代理创建类有一致的属性</td><td></td></tr><tr><td>Ordered接口</td><td>表示继承类是有序的</td><td></td></tr><tr><td>BeanClassLoaderAware接口</td><td>使用指定的classLoader来加载Bean</td><td>setBeanClassLoader(ClassLoader classLoader)</td></tr><tr><td>AopInfrastructureBean接口</td><td>标示该Bean是作为Spring AOP的基础组件进行使用,因此不会被代理,即使被PointCut指定到也不会被代理到</td><td></td></tr><tr><td>BeanFactoryAware</td><td>使用指定的 BeanFactory 来实例化Bean</td><td>void setBeanFactory(BeanFactory beanFactory)</td></tr></tbody></table><h5 id="b-SmartInstantiationAwareBeanPostProcessor"><a href="#b-SmartInstantiationAwareBeanPostProcessor" class="headerlink" title="b. SmartInstantiationAwareBeanPostProcessor"></a>b. SmartInstantiationAwareBeanPostProcessor</h5><p>AbstractAutoProxyCreator -> 实现自 <code>SmartInstantiationAwareBeanPostProcessor</code> -> 继承自 <code>InstantiationAwareBeanPostProcessor</code> -> 继承自 <code>BeanPostProcessor</code>.</p><ul><li>InstantiationAwareBeanPostProcessor</li></ul><p>在这个接口中,不仅仅有继承于 <code>BeanPostProcessor</code> 的 <code>postProcessBeforeInitialization(Object bean, String beanName)</code> 和 <code>postProcessAfterInitialization(Object bean, String beanName)</code> 两个Bean初始化前后的处理方法,本身还定义了 <code>postProcessProperties</code> 和 <code>postProcessPropertyValues</code>方法。作用是在针对Bean的初始化后对属性进行后置处理。</p><ul><li>SmartInstantiationAwareBeanPostProcessor</li></ul><p>在这个接口中定义两个方法,分别为 <code>default Class<?> predictBeanType(Class<?> beanClass, String beanName)</code> 用来预测使用BeanPostProcessor的postProcessBeforeInstantiation方法的返回类型,默认为null。<code>default Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName)</code> 用来决定使用哪个候选的构造器。</p><blockquote><p>此时,相比已经在前面了解到什么是AOP,同时可以在前面的类结构分析中得知了AnnotationAwareAspectJAutoProxyCreator 继承自<code>InstantiationAwareBeanPostProcessor</code> 从而实现了对Bean的前后处理。所以下面来着重找和 对Bean 前后置处理相关的方法。</p></blockquote><h4 id="AbstractAutoProxyCreator类源码分析"><a href="#AbstractAutoProxyCreator类源码分析" class="headerlink" title="AbstractAutoProxyCreator类源码分析"></a>AbstractAutoProxyCreator类源码分析</h4><p>在 <code>AbstractAutoProxyCreator</code> 作为 <code>AnnotationAwareAspectJAutoProxyCreator</code> 的父类,起到了非常重要的作用。</p><p>在 <code>AbstractAutoProxyCreator</code> 中,实现了 <code>BeanPostProcessor</code> 的初始化前后处理方法,用来针对每次初始化Bean前后对切面的识别判断和对符合条件的Bean进行通知的代理。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// postProcessBeforeInitialization ,BeanPostProcessor的实现方法。</span></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">postProcessBeforeInstantiation</span><span class="params">(Class<?> beanClass, String beanName)</span> </span>{</span><br><span class="line">Object cacheKey = getCacheKey(beanClass, beanName); <span class="comment">//获得beanClass</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!StringUtils.hasLength(beanName) || !<span class="keyword">this</span>.targetSourcedBeans.contains(beanName)) {</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.advisedBeans.containsKey(cacheKey)) {</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="keyword">if</span> (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {</span><br><span class="line"> <span class="comment">// 通过判断是否使用了@AspectJ注解定义的切面类还是 </span></span><br><span class="line"> <span class="comment">// 实现Advice、Pointcut、Advisor、AopInfrastructureBean</span></span><br><span class="line"><span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.FALSE); <span class="comment">//添加到advisedBeans里面,等待处理</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><br><span class="line"><span class="comment">//如果不是基础设施的类</span></span><br><span class="line">TargetSource targetSource = getCustomTargetSource(beanClass, beanName); <span class="comment">//获得targetSource</span></span><br><span class="line"><span class="keyword">if</span> (targetSource != <span class="keyword">null</span>) {</span><br><span class="line"><span class="keyword">if</span> (StringUtils.hasLength(beanName)) {</span><br><span class="line"><span class="keyword">this</span>.targetSourcedBeans.add(beanName);</span><br><span class="line">}</span><br><span class="line">Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);</span><br><span class="line">Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); <span class="comment">//创建代理类</span></span><br><span class="line"><span class="keyword">this</span>.proxyTypes.put(cacheKey, proxy.getClass());</span><br><span class="line"><span class="keyword">return</span> proxy;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在<code>postProcessBeforeInstantiation</code>实现类中,具体流程大致为 </p><ul><li>如果是<code>Aspect</code>切面类,则将类添加到<code>advisedBeans</code>中等待进行<code>Advice</code>的组装</li><li>如果不是切面类,则会先去判断是否自定义一个 <code>customTargetSourceCreators</code> 数组 <ul><li>如果定义了,则组装成自定义的代理类进行返回。</li></ul></li></ul><h5 id="1-是否为AspectJ配置类"><a href="#1-是否为AspectJ配置类" class="headerlink" title="1.是否为AspectJ配置类"></a>1.是否为AspectJ配置类</h5><p>如果是AspectJ定义的切面类,或者由Advice、Pointcut、Advisor、AopInfrastructureBean实现类,则添加到advisedBeans中,并制定值为FLASE。</p><p>在 <code>AnnotationAwareAspectJAutoProxyCreator</code> 中存在 的<code>isInfrastructureClass</code> 实现方法。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isInfrastructureClass</span><span class="params">(Class<?> beanClass)</span> </span>{</span><br><span class="line"> <span class="comment">// 加上父类中的isInfrastructureClass判断规则</span></span><br><span class="line"><span class="keyword">return</span> (<span class="keyword">super</span>.isInfrastructureClass(beanClass) ||</span><br><span class="line">(<span class="keyword">this</span>.aspectJAdvisorFactory != <span class="keyword">null</span> && <span class="keyword">this</span>.aspectJAdvisorFactory.isAspect(beanClass)));</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">isAspect</span><span class="params">(Class<?> clazz)</span> </span>{</span><br><span class="line"><span class="keyword">return</span> (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 在这里判断是否存在 Aspect 注解</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">hasAspectAnnotation</span><span class="params">(Class<?> clazz)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (AnnotationUtils.findAnnotation(clazz, Aspect.class) != <span class="keyword">null</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// AbstractAutoProxyCreator 中的方法</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">isInfrastructureClass</span><span class="params">(Class<?> beanClass)</span> </span>{</span><br><span class="line"><span class="keyword">boolean</span> retVal = Advice.class.isAssignableFrom(beanClass) || <span class="comment">//是否实现 Advice</span></span><br><span class="line">Pointcut.class.isAssignableFrom(beanClass) || <span class="comment">//是否实现 Pointcut</span></span><br><span class="line">Advisor.class.isAssignableFrom(beanClass) || <span class="comment">//是否实现 Advisor</span></span><br><span class="line">AopInfrastructureBean.class.isAssignableFrom(beanClass); <span class="comment">//是否实现 AopInfrastructureBean</span></span><br><span class="line"><span class="keyword">if</span> (retVal && logger.isTraceEnabled()) {</span><br><span class="line">logger.trace(<span class="string">"Did not attempt to auto-proxy infrastructure class ["</span> + beanClass.getName() + <span class="string">"]"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> retVal;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h5 id="2-进行代理"><a href="#2-进行代理" class="headerlink" title="2.进行代理"></a>2.进行代理</h5><p>真正的代理是发生在实现的 <code>beanPostProcessor</code> 的 <code>postProcessAfterInitialization</code></p><p>在 <code>AbstractAutoProxyCreator</code> 中提供类一个初始化后置处理器 <code>postProcessAfterInitialization</code> 。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">postProcessAfterInitialization</span><span class="params">(@Nullable Object bean, String beanName)</span> </span>{</span><br><span class="line"><span class="keyword">if</span> (bean != <span class="keyword">null</span>) {</span><br><span class="line">Object cacheKey = getCacheKey(bean.getClass(), beanName);</span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.earlyProxyReferences.remove(cacheKey) != bean) { <span class="comment">// 如果不存在</span></span><br><span class="line"><span class="comment">// 如果需要进行代理的包装</span></span><br><span class="line"><span class="keyword">return</span> wrapIfNecessary(bean, beanName, cacheKey);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> bean;</span><br><span class="line">}</span><br><span class="line"><span class="comment">//如有必要,包装给定的bean,即是否有资格被代理</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> Object <span class="title">wrapIfNecessary</span><span class="params">(Object bean, String beanName, Object cacheKey)</span> </span>{</span><br><span class="line"><span class="keyword">if</span> (StringUtils.hasLength(beanName) && <span class="keyword">this</span>.targetSourcedBeans.contains(beanName)) {</span><br><span class="line"><span class="comment">// 如果在 postProcessBeforeInitialization 中定义了targetSourcedBeans,则直接返回</span></span><br><span class="line"><span class="keyword">return</span> bean;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (Boolean.FALSE.equals(<span class="keyword">this</span>.advisedBeans.get(cacheKey))) {</span><br><span class="line"><span class="comment">// 如果已经添加到advisedBeans中也直接返回</span></span><br><span class="line"><span class="keyword">return</span> bean;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 如果是一个基本配置Aspect类,则放到advisedBeans中。</span></span><br><span class="line"><span class="keyword">if</span> (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {</span><br><span class="line"><span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.FALSE);</span><br><span class="line"><span class="keyword">return</span> bean;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建Advice</span></span><br><span class="line">Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, <span class="keyword">null</span>);</span><br><span class="line"><span class="comment">// Advice存在,则表示需要代理</span></span><br><span class="line"><span class="keyword">if</span> (specificInterceptors != DO_NOT_PROXY) {</span><br><span class="line"><span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.TRUE);</span><br><span class="line"><span class="comment">// 创建代理</span></span><br><span class="line">Object proxy = createProxy(</span><br><span class="line">bean.getClass(), beanName, specificInterceptors, <span class="keyword">new</span> SingletonTargetSource(bean));</span><br><span class="line"><span class="keyword">this</span>.proxyTypes.put(cacheKey, proxy.getClass());</span><br><span class="line"><span class="comment">// 返回代理对象。</span></span><br><span class="line"><span class="keyword">return</span> proxy;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.FALSE);</span><br><span class="line"><span class="keyword">return</span> bean;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>创建Advice的Bean增强过程如下:</p><ol><li>每个Bean在执行<code>invokeInitMethods</code>进行Init,然后进入到Init后置处理器中</li><li>通过在 <code>AbstractAutoProxyCreator</code> 的 <code>postProcessAfterInitialization</code> 对代理类的包装<ol><li>通过<code>getAdvicesAndAdvisorsForBean</code>获取所有的可用的<code>advice</code>增强器。</li><li>通过 <code>createProxy</code> 创建代理Bean,并返回代理对象。</li></ol></li></ol><h5 id="3-Advice获取过程"><a href="#3-Advice获取过程" class="headerlink" title="3.Advice获取过程"></a>3.Advice获取过程</h5><p> Advice是进行代理的重要的组成部分,代码中可以通过 <code>@AspectJ</code> 注解在切面类上, 同时使用Advice相关注解如 <code>@After</code> <code>@Before</code> 等进行标注,声明为一个 <code>Advice</code>。</p><p> 实际源码分析时会发现不管是 <code>postProcessBeforeInstantiation</code> 还是 <code>postProcessAfterInstantiation</code>,最后都会通过 <code>buildAspectJAdvisors</code> 方法寻找所有的候选的 advice。其中有两个比较重要的地方需要注意:</p><ul><li>为了避免重复,使用了对<code>aspectBeanNames</code>的判断,如果判断为空,才会进行<code>Advice</code>的查找</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">List<String> aspectNames = <span class="keyword">this</span>.aspectBeanNames;</span><br><span class="line"><span class="keyword">if</span> (aspectNames == <span class="keyword">null</span>) {</span><br><span class="line"><span class="keyword">synchronized</span> (<span class="keyword">this</span>) {</span><br><span class="line">aspectNames = <span class="keyword">this</span>.aspectBeanNames;</span><br><span class="line"><span class="keyword">if</span> (aspectNames == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="comment">// ....</span></span><br></pre></td></tr></table></figure></div><ul><li>通过<code>getAdvices</code>获取所有的备选的<code>Advice</code></li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">List<Advisor> classAdvisors = <span class="keyword">this</span>.advisorFactory.getAdvisors(factory);<span class="comment">// 获取Aspect类中的Advice</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.beanFactory.isSingleton(beanName)) {<span class="comment">// 如果是单例的</span></span><br><span class="line"> <span class="keyword">this</span>.advisorsCache.put(beanName, classAdvisors);<span class="comment">// advisorsCache 是以 BeanName为Key,List<Advice>为value的Map</span></span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><ul><li>通过<code>getAdvices</code>中的<code>getAdvisorMethods</code>获取<strong>排除除去</strong><code>Pointcut</code> 标注的所有方法。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">private</span> List<Method> <span class="title">getAdvisorMethods</span><span class="params">(Class<?> aspectClass)</span> </span>{</span><br><span class="line"><span class="keyword">final</span> List<Method> methods = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line">ReflectionUtils.doWithMethods(aspectClass, method -> {</span><br><span class="line"><span class="comment">// Exclude pointcuts</span></span><br><span class="line"><span class="keyword">if</span> (AnnotationUtils.getAnnotation(method, Pointcut.class) == <span class="keyword">null</span>) { <span class="comment">// 除去标注了Pointcut注解的方法 添加到 "method"中</span></span><br><span class="line">methods.add(method);</span><br><span class="line">}</span><br><span class="line">}, ReflectionUtils.USER_DECLARED_METHODS);</span><br><span class="line">methods.sort(METHOD_COMPARATOR);</span><br><span class="line"><span class="keyword">return</span> methods;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>最后通过<code>getAdvices</code>中的<code>getAdvisor</code>中的<code>getPointcut</code>方法,通过 <code>findAspectJAnnotationOnMethod</code>对类中不存在 <code>Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class</code>进行过滤。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="keyword">protected</span> <span class="keyword">static</span> AspectJAnnotation<?> findAspectJAnnotationOnMethod(Method method) {</span><br><span class="line"><span class="keyword">for</span> (Class<?> clazz : ASPECTJ_ANNOTATION_CLASSES) {</span><br><span class="line">AspectJAnnotation<?> foundAnnotation = findAnnotation(method, (Class<Annotation>) clazz);</span><br><span class="line"><span class="keyword">if</span> (foundAnnotation != <span class="keyword">null</span>) {</span><br><span class="line"><span class="keyword">return</span> foundAnnotation;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>最后在<code>buildAspectJAdvisors</code>方法中放入到<code>advisorsCache</code>,作为缓存。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">List<Advisor> classAdvisors = <span class="keyword">this</span>.advisorFactory.getAdvisors(factory);<span class="comment">// 获取Aspect类中的Advice</span></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.beanFactory.isSingleton(beanName)) {<span class="comment">// 如果是单例的</span></span><br><span class="line"> <span class="keyword">this</span>.advisorsCache.put(beanName, classAdvisors);<span class="comment">// advisorsCache 是以 BeanName为Key,List<Advice>为value的Map</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h5 id="4-方法的匹配"><a href="#4-方法的匹配" class="headerlink" title="4.方法的匹配"></a>4.方法的匹配</h5><p>在进行代理的时候,如何知道对那些方法进行了代理。</p><ul><li>在后置处理器使用的时候,通过<code>getAdvicesAndAdvisorsForBean</code>获取缓存中的Advice</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// Create proxy if we have advice.</span></span><br><span class="line">Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, <span class="keyword">null</span>);</span><br><span class="line"><span class="keyword">if</span> (specificInterceptors != DO_NOT_PROXY) {</span><br><span class="line"> <span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.TRUE);</span><br><span class="line"> Object proxy = createProxy(</span><br><span class="line"> bean.getClass(), beanName, specificInterceptors, <span class="keyword">new</span> SingletonTargetSource(bean));</span><br><span class="line"> <span class="keyword">this</span>.proxyTypes.put(cacheKey, proxy.getClass());</span><br><span class="line"> <span class="keyword">return</span> proxy;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>通过其中的 <code>findEligibleAdvisors</code>方法,获取可用的Advice</li><li>一步一步点进去后,发现最后使用<code>canApply</code>方法进行判断,是否可以对当前<code>Bean</code>进行代理</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">boolean</span> <span class="title">canApply</span><span class="params">(Pointcut pc, Class<?> targetClass, <span class="keyword">boolean</span> hasIntroductions)</span> </span>{</span><br><span class="line"> Assert.notNull(pc, <span class="string">"Pointcut must not be null"</span>);</span><br><span class="line"> <span class="keyword">if</span> (!pc.getClassFilter().matches(targetClass)) {</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">// 使用Matcher进行类级别的匹配验证</span></span><br><span class="line"> MethodMatcher methodMatcher = pc.getMethodMatcher();</span><br><span class="line"> <span class="keyword">if</span> (methodMatcher == MethodMatcher.TRUE) {</span><br><span class="line"> <span class="comment">// No need to iterate the methods if we're matching any method anyway...</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><blockquote><p> 综上解析中,能够解决一下几个疑问:</p><ol><li><p>AnnotationAwareAspectJAutoProxyCreator 作用是什么</p><p> 最重要的是引入了<code>AbstractAutoProxyCreator</code>,实现对Bean的代理包装;同时在<code>postProcessBeforeInstantiation</code>时,在使用 <code>shouldSkip</code> 时,会获取所有的Advice,并加入到cache中。</p></li><li><p>Spring在什么时候进行AOP代理?<br> Spring在生成Bean单例时会执行Init方法初始化,通过初始化后postProcessor方法,先获取所有可用的Advice增强器,之后使用ProxyFactory创建Proxy并返回。</p></li><li><p>容器获取Advice的过程?</p><p> 在前置处理器中,获取@AspectJ中下的所有Advice方法,并添加到缓存中</p></li><li><p>哪些方法可以被匹配到可以进行AOP?<br> 在PointCut中执行范围中的Bean可以被AOP拦截使用</p></li></ol></blockquote><h3 id="3-创建代理的方式"><a href="#3-创建代理的方式" class="headerlink" title="3. 创建代理的方式"></a>3. 创建代理的方式</h3><p>SpringAOP创建代理有两种方式:</p><ul><li>第一种:使用JDK动态代理</li><li>第二种:使用CGLIB代理。</li></ul><p>下面来针对代理方式的选择方式,查看源码一探究竟。</p><p>在上一节中提到的在 <code>AbstractAutoProxyCreator</code> 中的在后置处理器中 <code>postProcessAfterInitialization</code> 的 <code>wrapIfNecessary</code>中进行了对Bean的代理包装处理.</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">// 如果存在可用的通知,则对Bean进行代理</span></span><br><span class="line">Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, <span class="keyword">null</span>);</span><br><span class="line"><span class="keyword">if</span> (specificInterceptors != DO_NOT_PROXY) {</span><br><span class="line"><span class="keyword">this</span>.advisedBeans.put(cacheKey, Boolean.TRUE);</span><br><span class="line"><span class="comment">// 创建代理对象</span></span><br><span class="line">Object proxy = createProxy(</span><br><span class="line">bean.getClass(), beanName, specificInterceptors, <span class="keyword">new</span> SingletonTargetSource(bean));</span><br><span class="line"><span class="keyword">this</span>.proxyTypes.put(cacheKey, proxy.getClass());</span><br><span class="line"><span class="keyword">return</span> proxy;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在 <code>createProxy</code> 中决定了使用哪种方式(JDK动态代理、CGLIB代理)进行代理。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> Object <span class="title">createProxy</span><span class="params">(Class<?> beanClass, @Nullable String beanName,</span></span></span><br><span class="line"><span class="function"><span class="params">@Nullable Object[] specificInterceptors, TargetSource targetSource)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (<span class="keyword">this</span>.beanFactory <span class="keyword">instanceof</span> ConfigurableListableBeanFactory) {</span><br><span class="line">AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) <span class="keyword">this</span>.beanFactory, beanName, beanClass);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">ProxyFactory proxyFactory = <span class="keyword">new</span> ProxyFactory();</span><br><span class="line">proxyFactory.copyFrom(<span class="keyword">this</span>);<span class="comment">// 复制配置</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> (!proxyFactory.isProxyTargetClass()) {<span class="comment">//返回是否直接代理目标类以及任何接口。</span></span><br><span class="line"><span class="keyword">if</span> (shouldProxyTargetClass(beanClass, beanName)) {</span><br><span class="line"><span class="comment">// 如果 proxy-target-class 标签属性返回true,则表明需要基于类的代理,</span></span><br><span class="line"><span class="comment">// 反之则需要基于接口的代理</span></span><br><span class="line">proxyFactory.setProxyTargetClass(<span class="keyword">true</span>); <span class="comment">//使用CGLIB代理(true)</span></span><br><span class="line">}<span class="keyword">else</span> {</span><br><span class="line">evaluateProxyInterfaces(beanClass, proxyFactory); <span class="comment">//检查是否实现了接口</span></span><br><span class="line"><span class="comment">// 如果实现了接口,则设置interface,否则设置proxyTargetClass为true,但前提是</span></span><br><span class="line"><span class="comment">// 用户没有自己设置proxyTargetClass属性</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * if (hasReasonableProxyInterface) {</span></span><br><span class="line"><span class="comment"> * Must allow for introductions; can't just set interfaces to the target's interfaces only.</span></span><br><span class="line"><span class="comment"> *for (Class<?> ifc : targetInterfaces) {</span></span><br><span class="line"><span class="comment"> *proxyFactory.addInterface(ifc);</span></span><br><span class="line"><span class="comment"> *}</span></span><br><span class="line"><span class="comment"> *}else {</span></span><br><span class="line"><span class="comment"> *proxyFactory.setProxyTargetClass(true);</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><br><span class="line">}</span><br><span class="line">Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);</span><br><span class="line">proxyFactory.addAdvisors(advisors);</span><br><span class="line">proxyFactory.setTargetSource(targetSource);</span><br><span class="line">customizeProxyFactory(proxyFactory);</span><br><span class="line"></span><br><span class="line">proxyFactory.setFrozen(<span class="keyword">this</span>.freezeProxy);</span><br><span class="line"><span class="keyword">if</span> (advisorsPreFiltered()) {</span><br><span class="line">proxyFactory.setPreFiltered(<span class="keyword">true</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> proxyFactory.getProxy(getProxyClassLoader());</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在这里设置了有关于proxyFactory的相关属性, 如是否继承接口的interface,和 ProxyTargetClass 。如果没有实现interface,则会自动将ProxyTargeClass为true,也就是说这两个属性是相对的。</p><p>在最后的retrun的 <code>getProxy()</code> 方法最终会调用 <code>DefaultAopProxyFactory</code> 类中的 <code>createAopProxy</code> 方法决定使用哪中代理方式进行代理。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> AopProxy <span class="title">createAopProxy</span><span class="params">(AdvisedSupport config)</span> <span class="keyword">throws</span> AopConfigException </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="comment">*</span></span><br><span class="line"><span class="comment">* 条件1:config.isOptimize() - 是否需要优化</span></span><br><span class="line"><span class="comment">* 条件2:config.isProxyTargetClass() - 检测 proxyTargetClass 的值,</span></span><br><span class="line"><span class="comment">* 条件3:hasNoUserSuppliedProxyInterfaces(config) - 目标 bean 是否实现了接口</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="keyword">if</span> (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {</span><br><span class="line"><span class="comment">// 如果设置了optimize 或者 proxyTargetClass 或者设置了进行代理的接口</span></span><br><span class="line">Class<?> targetClass = config.getTargetClass();</span><br><span class="line"><span class="keyword">if</span> (targetClass == <span class="keyword">null</span>) {</span><br><span class="line"><span class="keyword">throw</span> <span class="keyword">new</span> AopConfigException(<span class="string">"TargetSource cannot determine target class: "</span> +</span><br><span class="line"><span class="string">"Either an interface or a target is required for proxy creation."</span>);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">if</span> (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> JdkDynamicAopProxy(config); <span class="comment">//如果代理的是接口,使用JDK动态代理</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> ObjenesisCglibAopProxy(config);<span class="comment">//否则使用CGlib代理</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">else</span> { <span class="comment">//否则都使用JDK进行代理</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> JdkDynamicAopProxy(config);</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>前面说的如果自己设置类proxyTargetClass属性为true,就不会判断是继承了interface。如果没有单独设置,则会先判断是否继承了接口,如果没有则会自动设置proxyTargetClass属性为true。</p><blockquote><p> 所以得出结论:</p><ol><li><strong>如果设置了proxyTargetClass为true,则使用CGLIB代理</strong>。</li><li><strong>如果没有设置proxyTargetClass,如果是接口,则使用JDK动态代理。如果没有实现接口,则还是会使用CGLIB进行代理。</strong></li></ol></blockquote>]]></content>
<summary type="html">
<h1 id="SpringAOP的使用和源码分析"><a href="#SpringAOP的使用和源码分析" class="headerlink" title="SpringAOP的使用和源码分析"></a>SpringAOP的使用和源码分析</h1><h2 id="前言"><
</summary>
</entry>
<entry>
<title>(三)自动装配</title>
<link href="https://nanyiniu.github.io/2020/05/22/%EF%BC%88%E4%B8%89%EF%BC%89%E8%87%AA%E5%8A%A8%E8%A3%85%E9%85%8D/"/>
<id>https://nanyiniu.github.io/2020/05/22/(三)自动装配/</id>
<published>2020-05-22T07:26:46.078Z</published>
<updated>2020-06-30T15:02:05.311Z</updated>
<content type="html"><![CDATA[<ul><li><a href="#spring中的自动装配">Spring中的自动装配</a><ul><li><a href="#autowire-qualifierprimary">@Autowire、 @Qualifier、@Primary</a></li><li><a href="#injectnamed">@Inject、@Named</a></li></ul></li></ul><h1 id="Spring中的自动装配"><a href="#Spring中的自动装配" class="headerlink" title="Spring中的自动装配"></a>Spring中的自动装配</h1><h2 id="Autowire、-Qualifier、-Primary"><a href="#Autowire、-Qualifier、-Primary" class="headerlink" title="@Autowire、 @Qualifier、@Primary"></a>@Autowire、 @Qualifier、@Primary</h2><p><code>@Autowire</code> 注解标注在constructor, field, setter method 上,方便在Spring中进行依赖的注入(<code>dependency injection</code>)。和JSR-330标准javax.inject.Inject达到一样的效果,不过autowired时Spring持有的。</p><ul><li>默认使用Autowire是按照类型进行注入的,但是如果在容器中存在多个类型相同的bean时,@Autowired也会按照名称进行匹配。</li><li>使用<code>@Qualifier</code>注解,在获取Bean时,指定要获取的bean的名称。</li><li>使用<code>@Primary</code>注解,在声明Bean时,指定如果注入,则首选的bean。</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><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">Person</span> </span>{</span><br><span class="line"><span class="keyword">private</span> String firstName;</span><br><span class="line"><span class="keyword">private</span> String lastName;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String firstName, String lastName)</span> </span>{</span><br><span class="line"><span class="keyword">this</span>.firstName = <span class="string">"w"</span>;</span><br><span class="line"><span class="keyword">this</span>.lastName = <span class="string">"y"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Autowired</span></span><br><span class="line"><span class="meta">@Qualifier</span>(<span class="string">"abc"</span>)</span><br><span class="line"><span class="keyword">private</span> Pet pet;</span><br><span class="line"><span class="comment">// 省略getter setter</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者在声明Bean时,使用Primary进行指定。</span></span><br><span class="line"><span class="meta">@Bean</span>(<span class="string">"dotdot"</span>)</span><br><span class="line"><span class="meta">@Primary</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Pet <span class="title">pet2</span><span class="params">()</span></span>{</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> Pet(<span class="string">"dotdot"</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>需要注意的是:在同时指定Qualifier和Primary时,会以<strong>Qualifier</strong>为主。</p></blockquote><h2 id="Inject、-Named"><a href="#Inject、-Named" class="headerlink" title="@Inject、@Named"></a>@Inject、@Named</h2><p>@Inject、@Named 都是Javax规范的注解,并不是Spring的注解。</p><p>使用 Inject 注解同样能够标注在constructor, field, setter method 上。基本能够代替 Autowired 注解,并且能够使用 Named 注解,指定名称进行实例的注入,可以代替 Qualifier 注解。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><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">Person</span> </span>{</span><br><span class="line"><span class="keyword">private</span> String firstName;</span><br><span class="line"><span class="keyword">private</span> String lastName;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Person</span><span class="params">(String firstName, String lastName)</span> </span>{</span><br><span class="line"><span class="keyword">this</span>.firstName = <span class="string">"w"</span>;</span><br><span class="line"><span class="keyword">this</span>.lastName = <span class="string">"y"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">@Inject</span></span><br><span class="line"><span class="meta">@Named</span>(<span class="string">"abc"</span>)</span><br><span class="line"><span class="keyword">private</span> Pet pet;</span><br><span class="line"><span class="comment">// 省略getter setter</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><blockquote><p>区别点在于 Autowired 注解支持 required() 属性。而 Inject 注解并不支持。可以使用java.util.Optional或者Spring Framework 5.0 后提供的 @Nullable 注解,标志在构造方法上。</p></blockquote><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SimpleMovieLister</span> </span>{</span><br><span class="line"><span class="meta">@Autowired</span> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setMovieFinder</span><span class="params">(@Nullable MovieFinder movieFinder)</span> </span>{</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="Resource"><a href="#Resource" class="headerlink" title="@Resource"></a>@Resource</h2><p>使用 <code>@Resource</code> 同样能够达到自动装配的目的,与 <code>@Autowire</code> 的区别在于以下几点:</p><ul><li>处理这2个注解的BeanPostProcessor不一样</li><li>匹配Bean的方式不同。<ul><li><code>@Resource</code><ul><li>默认按照名称进行匹配注入。</li><li>如果指定了 <code>type</code>,则按照类型进行匹配。当无法找到正确的类型或者找到多个时都会报错。如果都没有指定时,并且无法按照名称匹配到Bean时,会回退到按照类型寻找Bean。</li></ul></li><li><code>@Autowire</code> 默认就是按照类型进行匹配的,如果需要指定特定Bean,则需要使用 <code>@Qualifier</code> 注解进行指定。</li></ul></li></ul>]]></content>
<summary type="html">
<ul>
<li><a href="#spring中的自动装配">Spring中的自动装配</a><ul>
<li><a href="#autowire-qualifierprimary">@Autowire、 @Qualifier、@Primary</a></li>
<li><
</summary>
</entry>
<entry>
<title>(二)Bean的生命周期</title>
<link href="https://nanyiniu.github.io/2020/05/22/%EF%BC%88%E4%BA%8C%EF%BC%89Bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
<id>https://nanyiniu.github.io/2020/05/22/(二)Bean的生命周期/</id>
<published>2020-05-22T07:26:46.066Z</published>
<updated>2020-06-29T13:49:18.590Z</updated>
<content type="html"><![CDATA[<ul><li><a href="#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">Bean的生命周期</a><ul><li><a href="#bean%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E9%94%80%E6%AF%81">Bean的初始化和销毁</a><ul><li><a href="#%E4%B8%80%E4%BD%BF%E7%94%A8bean%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E5%92%8C%E9%94%80%E6%AF%81%E5%B1%9E%E6%80%A7">一、使用@Bean的初始化和销毁属性</a></li><li><a href="#%E4%BA%8C%E4%BD%BF%E7%94%A8-initializingbean-%E5%92%8C-disposablebean-%E6%8E%A5%E5%8F%A3%E6%8C%87%E5%AE%9A">二、使用 InitializingBean 和 DisposableBean 接口指定</a></li><li><a href="#%E4%B8%89%E4%BD%BF%E7%94%A8jsr%E6%A0%87%E5%87%86%E7%9A%84%E6%B3%A8%E8%A7%A3-postconstruce-%E5%92%8C-predestroy">三、使用JSR标准的注解 @PostConstruce 和 @PreDestroy</a></li><li><a href="#%E5%9B%9Bbeanpostprocessor%E8%87%AA%E5%AE%9A%E4%B9%89%E5%88%9D%E5%A7%8B%E5%8C%96%E5%89%8D%E5%90%8E%E6%96%B9%E6%B3%95">四、BeanPostProcessor自定义初始化前后方法</a></li></ul></li></ul></li></ul><h1 id="Bean的生命周期"><a href="#Bean的生命周期" class="headerlink" title="Bean的生命周期"></a>Bean的生命周期</h1><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20191020160536.png" alt=""></p><h2 id="Bean的初始化和销毁"><a href="#Bean的初始化和销毁" class="headerlink" title="Bean的初始化和销毁"></a>Bean的初始化和销毁</h2><h3 id="一、使用-Bean注解的初始化和销毁属性"><a href="#一、使用-Bean注解的初始化和销毁属性" class="headerlink" title="一、使用@Bean注解的初始化和销毁属性"></a>一、使用@Bean注解的初始化和销毁属性</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在dog实例中指定init和destroy方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Dog</span> </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">init</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"instance dog execute init method....."</span>);</span><br><span class="line"> }</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">destroy</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"instance dog execute destroy method....."</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 声明bean的时候指定 initMethod = "init",destroyMethod = "destroy"</span></span><br><span class="line"><span class="meta">@Configurable</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LifeCycleConfig</span> </span>{</span><br><span class="line"> <span class="meta">@Bean</span>(initMethod = <span class="string">"init"</span>,destroyMethod = <span class="string">"destroy"</span>)</span><br><span class="line"> <span class="function"><span class="keyword">public</span> Dog <span class="title">dog</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"begin create dog"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Dog();</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="meta">@Test</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">LifeCycleTest</span><span class="params">()</span></span>{</span><br><span class="line"> ApplicationContext applicationContext = <span class="keyword">new</span> AnnotationConfigApplicationContext(LifeCycleConfig.class);</span><br><span class="line"> System.out.println(<span class="string">"instance has created !!!"</span>);</span><br><span class="line"><span class="comment">// Dog dog = (Dog) applicationContext.getBean("dog");</span></span><br><span class="line"> ((AnnotationConfigApplicationContext) applicationContext).close();</span><br><span class="line"> System.out.println(<span class="string">"context has closed !!!"</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">// begin create dog</span></span><br><span class="line"><span class="comment">// instance dog execute init method.....</span></span><br><span class="line"><span class="comment">// instance has created !!!</span></span><br><span class="line"><span class="comment">// instance dog execute destroy method.....</span></span><br><span class="line"><span class="comment">// context has closed !!!</span></span><br></pre></td></tr></table></figure></div><p>从测试结果来看当创建对象时,通过 <code>initMethod</code>,<code>destroyMethod</code>指定初始化和销毁方法时可行的。</p><p><code>init</code>方法在<strong>对象创建后</strong>执行。<code>destroy</code>在<strong>容器销毁前</strong>执行.</p><h3 id="二、继承-InitializingBean-和-DisposableBean-接口"><a href="#二、继承-InitializingBean-和-DisposableBean-接口" class="headerlink" title="二、继承 InitializingBean 和 DisposableBean 接口"></a>二、继承 InitializingBean 和 DisposableBean 接口</h3><p>方法同上,不过需要对象类实现这两个接口</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Cat</span> <span class="keyword">implements</span> <span class="title">InitializingBean</span>, <span class="title">DisposableBean</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接口中的destroy 方法,执行销毁bean</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> System.out.println(<span class="string">"instance cat execute destroy method....."</span>);</span><br><span class="line"> }</span><br><span class="line"><span class="comment">// 接口中的afterPropertiesSet方法,用来创建bean后的初始化</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">afterPropertiesSet</span><span class="params">()</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> System.out.println(<span class="string">"instance cat execute init method....."</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不需要在bean注解中指定相关方法</span></span><br><span class="line"><span class="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Cat <span class="title">cat</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"begin create cat"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Cat();</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">// begin create cat</span></span><br><span class="line"><span class="comment">// instance dog execute init method.....</span></span><br><span class="line"><span class="comment">// instance has created !!!</span></span><br><span class="line"><span class="comment">// instance dog execute destroy method.....</span></span><br><span class="line"><span class="comment">// context has closed !!!</span></span><br></pre></td></tr></table></figure></div><h3 id="三、使用JSR标准注解-PostConstruce-和-PreDestroy"><a href="#三、使用JSR标准注解-PostConstruce-和-PreDestroy" class="headerlink" title="三、使用JSR标准注解 @PostConstruce 和 @PreDestroy"></a>三、使用JSR标准注解 @PostConstruce 和 @PreDestroy</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Bug</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="meta">@PostConstruct</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"instance Bug execute init method....."</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@PreDestroy</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">destroy</span><span class="params">()</span></span>{</span><br><span class="line"> System.out.println(<span class="string">"instance Bug execute destroy method....."</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="四、BeanPostProcessor自定义初始化前后方法"><a href="#四、BeanPostProcessor自定义初始化前后方法" class="headerlink" title="四、BeanPostProcessor自定义初始化前后方法"></a>四、BeanPostProcessor自定义初始化前后方法</h3><p>先自定义一个 <code>MyBeanPostProcessor</code> ,并将它放到<strong>容器中</strong>,在启动后,每次生成bean时,都会执行下面的 <code>postProcessBeforeInitialization</code> 和 <code>postProcessAfterInitializationde</code> 前置方法和后置方法。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyBeanPostProcessor</span> <span class="keyword">implements</span> <span class="title">BeanPostProcessor</span> </span>{</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">postProcessBeforeInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>{</span><br><span class="line"><span class="comment">// 根据BeanName和bean可以获取bean的类信息接下来就可以进行筛选,在初始化前进行相关操作</span></span><br><span class="line">System.out.println(beanName);</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="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Object <span class="title">postProcessAfterInitialization</span><span class="params">(Object bean, String beanName)</span> <span class="keyword">throws</span> BeansException </span>{</span><br><span class="line">System.out.println(beanName);</span><br><span class="line"><span class="comment">// 同理初始化后</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><br></pre></td></tr></table></figure></div><p></p>]]></content>
<summary type="html">
<ul>
<li><a href="#bean%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F">Bean的生命周期</a><ul>
<li><a href="#bean%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%
</summary>
</entry>
<entry>
<title>深入JVM(三)JVM调优</title>
<link href="https://nanyiniu.github.io/2020/05/20/%E6%B7%B1%E5%85%A5JVM%EF%BC%88%E4%B8%89%EF%BC%89JVM%E8%B0%83%E4%BC%98/"/>
<id>https://nanyiniu.github.io/2020/05/20/深入JVM(三)JVM调优/</id>
<published>2020-05-20T15:00:00.000Z</published>
<updated>2023-07-30T09:34:44.042Z</updated>
<content type="html"><![CDATA[<h1 id="参数分类"><a href="#参数分类" class="headerlink" title="参数分类"></a>参数分类</h1><p>在多数项目中不需要进行JVM调优,在出现OOM或CPU飙高,往往是程序问题,应该想办法优化程序。</p><p>但是,在一般情况下,应该在<strong>上线前</strong>将JVM调整到最优,保证虚拟机:</p><ul><li>GC低停顿</li><li>GC低频率</li><li>低内存占用</li><li>高吞吐量</li></ul><p>调优中一般会遇到两种参数,一种是任何虚拟机版本都相同的标准参数,这些参数不需要人为调整,另一种是非标准参数,也是不稳定的参数,需要根据实际情况进行设定。</p><h2 id="标准参数"><a href="#标准参数" class="headerlink" title="标准参数"></a>标准参数</h2><p>标准参数,以 <code>-</code> 开头,如 <code>-version</code></p><h2 id="非标准参数"><a href="#非标准参数" class="headerlink" title="非标准参数"></a>非标准参数</h2><p>非标准参数 ,以 <code>-X</code> 或<code>-XX</code>开头,这些参数非常多,并且需要根据不同的情况进行不同的调整。</p><p>虚拟机的调优就是在不同的情况下针对这些非标准参数进行调整。</p><h1 id="常用参数"><a href="#常用参数" class="headerlink" title="常用参数"></a>常用参数</h1><ul><li>-Xms 初始化堆内存的最小值</li><li>-Xmx 初始化堆内存的最大值,一般和 Xms的值设为相同的值。</li><li>-Xmn 初始化堆中的新生代大小</li><li>-XX:ParallelGCThreads=8:新生代并行收集器的线程数。</li><li>-Xloggc: /opt/xxx/logs/xxx-xxx-gc-%t.log GC日志输出文件</li><li><strong>-XX:+UseGCLogFileRotation</strong></li><li><strong>-XX:NumberOfGCLogFiles=5</strong></li><li><strong>-XX:GCLogFileSize=20M</strong></li><li><strong>-XX:+PrintGCTimeStamps 打印GC耗时</strong></li><li><strong>-XX:+PrintGCDetails 打印GC回收的细节</strong></li><li><strong>-XX:HeapDumpPath=./java_pid<pid>.hprof :堆内存快照的存储文件路径。文件名一般为java_<pid>_<date>_<time>_heapDump.hprof。</strong></li><li><strong>-XX:+HeapDumpOnOutOfMemoryError 在OOM时,输出一个dump文件</strong></li></ul><h1 id="常用工具"><a href="#常用工具" class="headerlink" title="常用工具"></a>常用工具</h1><h2 id="命令行工具"><a href="#命令行工具" class="headerlink" title="命令行工具"></a>命令行工具</h2><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518223939.jpeg" alt="308C8A7B-5396-4C49-8E39-3BDC9F1D820E"></p><h3 id="jps"><a href="#jps" class="headerlink" title="jps"></a>jps</h3><p>JPS(Java Virtual Machine Process Status Tool),可以显示进行中的Java线程。</p><p>使用方式:<code>jps [options] [hostid]</code></p><h3 id="jinfo"><a href="#jinfo" class="headerlink" title="jinfo"></a>jinfo</h3><p>jinfo(Java Configuration Info),实时查看和调整JVM配置参数.</p><p>使用方式: <code>jinfo -flag <name> PID</code> 或者 <code>jinfo -flags PID</code> </p><h3 id="jstat-gc"><a href="#jstat-gc" class="headerlink" title="jstat -gc"></a>jstat -gc</h3><p>jstat(Java Virtual Machine statistics monitoring tool),能够查看JVM的使用情况</p><p>使用方式:<code>jstat [ generalOption | outputOptions vmid [ interval [ s|ms ] [ count ] ] ]</code></p><p>如: jstat -gc -h3 31736 1000 10</p><h3 id="jstack"><a href="#jstack" class="headerlink" title="jstack"></a>jstack</h3><p>jstack(Java stack trace)是Java的堆栈分析工具。</p><p>两个功能:</p><ol><li>针对活着的进程做本地的或远程的线程dump;</li><li>针对core文件做线程dump。</li></ol><p>使用方式:<code>jstack [ option ] pid</code></p><p>可将堆栈输出到指定文件中:jstack -l PID >> jstack.out</p><h3 id="jmap"><a href="#jmap" class="headerlink" title="jmap"></a>jmap</h3><p>jmap(Java memory map),它可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。</p><p>命令:jmap pid</p><p>描述:查看进程的内存映像信息,类似 Solaris pmap 命令。</p><p>命令:jmap -heap pid</p><p>描述:显示Java堆详细信息</p><p>命令:jmap -histo:live pid</p><p>描述:显示堆中对象的统计信息</p><p>命令:jmap -dump:format=b,file=heapdump.phrof pid</p><p>描述:生成堆转储快照dump文件。</p><h3 id="arthas-线上排查工具"><a href="#arthas-线上排查工具" class="headerlink" title="arthas 线上排查工具"></a>arthas 线上排查工具</h3><p>arthas 能够完成以上所有的工作,但是需要注意的是,使用jmap或jstack</p><p><a href="https://github.com/alibaba/arthas/blob/master/README_CN.md" target="_blank" rel="noopener">alibaba/arthas</a></p><h2 id="日志-堆栈分析工具"><a href="#日志-堆栈分析工具" class="headerlink" title="日志/堆栈分析工具"></a>日志/堆栈分析工具</h2><h3 id="jvirsualvm"><a href="#jvirsualvm" class="headerlink" title="jvirsualvm"></a>jvirsualvm</h3>]]></content>
<summary type="html">
<h1 id="参数分类"><a href="#参数分类" class="headerlink" title="参数分类"></a>参数分类</h1><p>在多数项目中不需要进行JVM调优,在出现OOM或CPU飙高,往往是程序问题,应该想办法优化程序。</p>
<p>但是,在一般
</summary>
<category term="JVM" scheme="https://nanyiniu.github.io/categories/JVM/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="JVM" scheme="https://nanyiniu.github.io/tags/JVM/"/>
</entry>
<entry>
<title>深入JVM(二)JVM垃圾回收器</title>
<link href="https://nanyiniu.github.io/2020/05/15/%E6%B7%B1%E5%85%A5JVM%EF%BC%88%E4%BA%8C%EF%BC%89JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6%E5%99%A8/"/>
<id>https://nanyiniu.github.io/2020/05/15/深入JVM(二)JVM垃圾回收器/</id>
<published>2020-05-15T15:00:00.000Z</published>
<updated>2023-07-30T09:34:44.019Z</updated>
<content type="html"><![CDATA[<h1 id="深入JVM(二)JVM垃圾回收器"><a href="#深入JVM(二)JVM垃圾回收器" class="headerlink" title="深入JVM(二)JVM垃圾回收器"></a>深入JVM(二)JVM垃圾回收器</h1><h2 id="什么垃圾?为什么要回收?"><a href="#什么垃圾?为什么要回收?" class="headerlink" title="什么垃圾?为什么要回收?"></a>什么垃圾?为什么要回收?</h2><p>前提:在java中创建对象的过程就是在内存中分配区域的过程,每次创建,虚拟机会为对象分配一块区域。</p><p>定义垃圾:和实际生活一样,没用用处的东西就叫做垃圾,对于Java虚拟机来说,如果这个对象不会被利用,那么这个对象就是垃圾对象。</p><p>为什么要回收:其实看完前面已经解释了为什么要回收,因为如果不回收,既不会被使用,而且还会占着内存空间,导致内存溢出,最终OOM异常,这是不允许出现的问题。所以需要对垃圾对象进行回收。在有些语言中需要使用命令、指令手动回收空间,如C、C++,一不小心就忘记回收或多次回收,都会产生问题。而在Java中使用的是垃圾回收器自动回收。</p><h2 id="垃圾回收器如何找到垃圾"><a href="#垃圾回收器如何找到垃圾" class="headerlink" title="垃圾回收器如何找到垃圾?"></a>垃圾回收器如何找到垃圾?</h2><p>有两种方式可以让垃圾回收器来寻找在到在堆中死亡(不能再被任何途径使用的对象)的对象。也就是对对象进行存活分析。</p><h3 id="引用计数(Refrence-Count)"><a href="#引用计数(Refrence-Count)" class="headerlink" title="引用计数(Refrence Count)"></a>引用计数(Refrence Count)</h3><p>给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1,任何时候引用为 0 的对象就是不可能再被使用的。</p><p>这种方式即简单,效率又高,但现在的java虚拟机并没有采用这种方式,原因是这种方式有一个致命的缺陷就是<strong>无法找到循环引用的对象</strong>。</p><h3 id="根可达算法(Root-Searching)"><a href="#根可达算法(Root-Searching)" class="headerlink" title="根可达算法(Root Searching)"></a>根可达算法(Root Searching)</h3><p>根可达算法根据一系列的<code>GC Root</code> 作为起点向下搜索,搜索所走过的路径称为引用链(<code>Reference Chain</code>),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。</p><p>在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。</p><p>在Java语言中,可作为GCRoots的对象包括下面几种:</p><ul><li>虚拟机栈(栈帧中的本地变量表)中引用的对象。</li><li>方法区中类静态属性引用的对象。</li><li>方法区中常量引用的对象。</li><li>本地方法栈中JNI(即一般说的Native方法)引用的对象。</li></ul><p><img src="http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-27/72762049.jpg" alt="http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-8-27/72762049.jpg"></p><h2 id="GC-算法"><a href="#GC-算法" class="headerlink" title="GC 算法"></a>GC 算法</h2><p>在对对象的存活分析后,知道了哪些对象已死亡,哪些对象还存活,会通过下面三种方法对死亡的对象进行清除。</p><h3 id="标记清除(Mark-Sweep)"><a href="#标记清除(Mark-Sweep)" class="headerlink" title="标记清除(Mark-Sweep)"></a>标记清除(Mark-Sweep)</h3><p>首先会为死亡的对象添加标记,然后在完成标记后,清除标记的对象。此算法为基础的清除算法,因为后面的算法都是基于此改进而来。</p><p>这种算法产生两个问题,一是效率问题,标记和清除两步的效率都不高,二是空间碎片化问题,在内存中产生大量的内存<strong>碎片</strong>。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518210705.png" alt="Untitled"></p><h3 id="拷贝算法(Copying)"><a href="#拷贝算法(Copying)" class="headerlink" title="拷贝算法(Copying)"></a>拷贝算法(Copying)</h3><p>将内存一分为二,每次分配内存只分配在第一块里面,将存活的对象顺序拷贝的另外一块中,然后将第一块中的对象全部抹除掉。这种算法实现简单运行高效,但是带来了一个问题就是<strong>内存的利用率不高</strong>,每次只能用一半。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213844.png" alt="Untitled 1"></p><h3 id="标记压缩(Mark-Compact)"><a href="#标记压缩(Mark-Compact)" class="headerlink" title="标记压缩(Mark-Compact)"></a>标记压缩(Mark-Compact)</h3><p>和标记清除相似,同样是做标记,但是后续步骤却有大不同,标记压缩不同于标记清除,直接将死亡对象进行清除,而是在标记死亡的对象后,将存活的对象向一端移动,最后清理到端边界以外的内存。相当于将存活的对象压缩到顺序的内存中后,清理掉其他内容。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213855.png" alt="Untitled 2"></p><h2 id="垃圾回收器"><a href="#垃圾回收器" class="headerlink" title="垃圾回收器"></a>垃圾回收器</h2><p>现在JVM中一共有10中垃圾回收器,按照模型来分,可分为分代模型和不分代模型(.8之后)</p><h3 id="分代模型"><a href="#分代模型" class="headerlink" title="分代模型"></a>分代模型</h3><p>虚拟机的垃圾收集可能会采用“分代收集”(GenerationalCollection)算法,这种算法并没有什么新的思想,只是根据对象存活周期的不同将内存划分为几块。根据不同的区域不同的特性使用不同的垃圾回收算法。</p><p>默认新生代与老年代的占比默认是1:2。新产生的对象默认会生成在新生代中,经过gc增加对象的年龄(age),当age超过一定数值时,将对象从新生代转移到老年代中。</p><h4 id="新生代"><a href="#新生代" class="headerlink" title="新生代"></a>新生代</h4><p>因为在新生代产生的对象,大部分都会被回收掉,也就是说有很少的对象会存活下来,因此,<strong>新生代适合使用复制算法</strong>能够尽可能少的挪动活的对象,又能够尽可能快的清除死亡的对象。在这个基础上,来谈一谈新生代中的分区。</p><p>因为新生代的存活对象非常少,大概占比1/9,所以新生代的分区并不像上面介绍拷贝算法一样是均分为1:1,而是将新生代分为 Eden:survive1:survive2为 8:1:1。具体为何分为三个区域通过下面的图就能够了解:</p><p>第一次回收会扫描Eden区,将存活对象从eden区挪到S1中,清空Eden区,第二次会扫描Eden区和S1区,将存活的对象放到S2中,清空Eden和S1区,一次</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213906.png" alt="Untitled 3"></p><h4 id="老年代"><a href="#老年代" class="headerlink" title="老年代"></a>老年代</h4><p>当对象经历了多次GC仍然没有被回收掉时,会被挪到老年代中,因为老年代中的回收次数和回收效率并不想新生代一样,经历一次gc只有少量的对象被回收掉。所以老年代采用标记整理算法,能够减少老年代中出现内存碎片的概率。</p><p>TLAB<br>多线程同时在堆中分配内存时,为了避免多线程冲突,操作同一地址,所以需要对整堆进行加锁,进而影响分配速度。TLAB 能够解决这个问题。<br>通过对 Eden 区域再进行划分, Thread Local Allocation Buffer(TLAB),这是 JVM 在eden区中为每个线程分配的一个私有缓存区域。多线程分配时,会优先在自己的TLAB中分配,分配不下再在Eden区中分配。</p><h4 id="对象在内存中的生命周期"><a href="#对象在内存中的生命周期" class="headerlink" title="对象在内存中的生命周期"></a>对象在内存中的生命周期</h4><ol><li><strong>首先会尝试在栈上分配</strong>。在栈上分配的好处:执行速度快,不需要的对象直接弹出,不需要垃圾回收器的介入即可完成内存的释放。</li><li>小对象会现在Eden中的TLAB中进行分配,如果TLAB中分配不下会在Eden区域公共部分进行分配。</li><li>如果对象太大,无法在新生代找到足够长的连续空闲空间, JVM 会直接将对象分配到老年代。</li><li>GC过程,GC过程可以分为对新生代、老年代的回收、和全部回收的过程。其中新生代的YGC(young GC)的发生频率最高。</li></ol><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213917.png" alt="Untitled 4"></p><h4 id="垃圾回收器的类型"><a href="#垃圾回收器的类型" class="headerlink" title="垃圾回收器的类型"></a>垃圾回收器的类型</h4><p>在列举垃圾回收器的类型时,先强调几个Hotspot在垃圾器算法的实现:</p><ul><li><p>OopMap:因为Hotspot在GC之前,查找内存中所有存活的对象使用<strong>可达性算法</strong>寻找死亡的对象,对于一个庞大的系统,每次都进行遍历查找引用是不可行的。因为在进行可达性算法的寻找对象时,需要将所有线程保持一个冻结的状态,Hotspot将这种状态成为”<strong>Stop The World</strong>“,也就是STW,来配合可达性分析,这样会停顿的时间会非常长。</p><p> 所以为了避免STW的时间过长,Hotspot使用一个<strong>OopMap的数据结构</strong>,让虚拟机知道哪些地址存放着对象引用。当类加载完成的时候,在类加载完成的时候,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。</p></li><li><p>SafePoint:使用OopMap可以减少STW的时间,但是在运行过程中,因为引用关系随时会发生变化,OopMap的内容也要随之变化,但不是所有指令都会及时的更改内容,这时引入SafePoint的概念。</p><p> 程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。</p></li><li><p>SafeRegion:SafePoint的扩充,停滞的线程,内容不会发生变化,所以这段代码区域内任何地方进行GC都是没有问题的。</p></li></ul><p>JVM共有10中垃圾回收器,使用分代模型的共有六种,三种新生代的、三种老年代的,需要配对使用。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213931.png" alt="Untitled 5"></p><p>一般的配对类型为:</p><ul><li>Serial&Serial-Old 在暂停所有工作线程后,使用单个GC线程进行垃圾回收</li></ul><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213941.png" alt="Untitled 6"></p><ul><li>Parallel Scavenge & Parallel Old 暂停所有工作线程使用多个GC线程进行回收</li></ul><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518213950.png" alt="Untitled 7"></p><ul><li>ParNew & CMS , ParNew和 Parallel Scavenge 其实在功能上是相同的,只是名字不同,CMS在回收老年代时,会经历下图中的三个标记阶段、一个清理阶段:<ul><li>初始标记</li><li>并发标记</li><li>重新标记</li><li>并发清理</li></ul></li></ul><p>CMS无法清理<strong>浮动垃圾,</strong>只能等到下一次清理时才能清除。</p><p>因为CMS是一款基于“标记—清除”算法实现的收集器,如果读者对前面这种算法介绍还有印象的话,就可能想到这意味着收集结束时会有大量 空间碎片产生。空间碎片过多时,将会给大对象分配带来很大麻烦,往往会出现老年代还有 很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,当遇到这种问题时,会通过Serial Old,也就是单线程使用标记-整理在进行垃圾回收,此时的回收效率非常低。</p><blockquote><p>由于CMS<strong>并发清理阶段用户线程还在运行着</strong>,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”</p></blockquote><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518214000.png" alt="Untitled 8"></p><h3 id="不分代模型"><a href="#不分代模型" class="headerlink" title="不分代模型"></a>不分代模型</h3><p>所谓的不分代模型指的是物理区域不分代,但是在逻辑上风染保留年轻代老年代的概念</p><h4 id="G1"><a href="#G1" class="headerlink" title="G1"></a>G1</h4><p>G1的出现是为了替代掉在1.5时发布的CMS垃圾回收器。</p><ul><li>并行与并发,和CMS相似,G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者 CPU核心)来缩短Stop-The-World停顿的时间。</li><li>分代收集:分代概念在G1中依然得以保留。虽然G1可以不需要其 他收集器配合就能独立管理整个GC堆。</li><li>收集算法:CMS使用标记-清除算法,意味着会产生大量的空间碎片,而G1使用标记-整理,在收集后能提供规整的可用内存</li><li>可预测的停顿:G1不像于分代模型中的收集器收集范围是整个年轻代或者老年代,在G1中产生一个概念叫做Region。G1会将堆分为大小相等的Region,在G1中所谓的老年代和年轻代就是不连续的Region区域。因为以上的特性,在G1中进行垃圾回收可以避免对整个区域回收,避免了停顿和增加了回收的效率。</li></ul><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200518214020.png" alt="Untitled 9"></p>]]></content>
<summary type="html">
<h1 id="深入JVM(二)JVM垃圾回收器"><a href="#深入JVM(二)JVM垃圾回收器" class="headerlink" title="深入JVM(二)JVM垃圾回收器"></a>深入JVM(二)JVM垃圾回收器</h1><h2 id="什么垃圾?为什么要
</summary>
<category term="JVM" scheme="https://nanyiniu.github.io/categories/JVM/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="JVM" scheme="https://nanyiniu.github.io/tags/JVM/"/>
</entry>
<entry>
<title>深入JVM(一)类加载过程和内存分配</title>
<link href="https://nanyiniu.github.io/2020/05/10/%E6%B7%B1%E5%85%A5JVM%EF%BC%88%E4%B8%80%EF%BC%89%E7%B1%BB%E5%8A%A0%E8%BD%BD%E8%BF%87%E7%A8%8B%E5%92%8C%E5%86%85%E5%AD%98%E5%88%86%E9%85%8D/"/>
<id>https://nanyiniu.github.io/2020/05/10/深入JVM(一)类加载过程和内存分配/</id>
<published>2020-05-10T15:00:00.000Z</published>
<updated>2020-07-02T01:12:56.237Z</updated>
<content type="html"><![CDATA[<h1 id="深入JVM(一)类加载过程和内存分配"><a href="#深入JVM(一)类加载过程和内存分配" class="headerlink" title="深入JVM(一)类加载过程和内存分配"></a>深入JVM(一)类加载过程和内存分配</h1><blockquote><p>本篇需要完成的内容是完成对对象从加载到创建过程的分析,与过程中的内存分配的方式等进行总结</p></blockquote><h1 id="内存区域"><a href="#内存区域" class="headerlink" title="内存区域"></a>内存区域</h1><p>在分析对象创建过程前,需要先了解Java虚拟机中对内存区域的划分与管理。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200518205233.png" alt="Untitled"></p><p><strong>红色部分的方法区在1.8之后已经被移除,变为元数据区,红色内的运行时常量池已经成为堆中的一部分使用!!</strong></p><p>上图中展示了Java虚拟机在运行时对管理的内存区域的划分。其中,分为线程私有的程序计数器、本地方法栈、虚拟机栈和线程共享的堆与方法区和直接内存。</p><h2 id="1-程序计数器"><a href="#1-程序计数器" class="headerlink" title="1. 程序计数器"></a>1. 程序计数器</h2><p>程序计数器是线程私有的、当前线程所执行的字节码的行号指示器,就像名字一样,它是可以看做计数的,每一条指令执行完后,会挪动程序计数器的指针,并指向下一条指令。(就像在平时debug一样)。</p><ul><li>占用大小:程序计数器占用非常小的部分</li><li>生命周期:跟随线程的创建而创建,销毁而销毁</li><li>作用:简单来说可以执行该线程执行字节码的位置。在多线程切换的情况下也能够知道当前线程执行的位置。</li></ul><h2 id="2-Java虚拟机栈"><a href="#2-Java虚拟机栈" class="headerlink" title="2. Java虚拟机栈"></a>2. Java虚拟机栈</h2><p>虚拟机栈有一个一个的栈针组成,而每一个栈针内由<strong>局部变量表、操作数栈、动态链接、方法出口信息</strong>组成。线程的方法调用过程(方法调用链)会将其中的每一个方法依次压入栈中,最后在调用过后,依次出栈,也就是说,方法执行的过程也就是栈针压栈到弹出的过程。</p><p>一个线程中方法的调用链可能会很长,很多方法都同时处于执行状态。对于JVM说,在活动线程中,只有位于JVM虚拟机栈栈顶的元素才是有效的,即称为<strong>当前栈帧,</strong>也就是<strong>当前执行的方法。</strong></p><h3 id="局部变量表"><a href="#局部变量表" class="headerlink" title="局部变量表"></a>局部变量表</h3><p>局部变量表是我们在使用和了解Java虚拟机栈的主要部分,是变量值的存储空间,主要存放了编译时可知的基本数据类型(boolean、byte、char、short、int、float、long、double)和方法引用(reference)。在编译时,局部变量表的空间就确定下来。</p><h3 id="操作数栈"><a href="#操作数栈" class="headerlink" title="操作数栈"></a>操作数栈</h3><p>操作数栈在方法执行的过程中,起到了对常量或者变量暂存和计算的作用,当方法开始时,操作数栈时空的,随着方法的进行,会将局部变量表中或者实例对象中,复制常量变量到操作数栈,再随着方法的执行,进行数据的运算。最后出栈返回给方法调用者。</p><p>和局部变量表相同的是,操作数栈的栈的深度在编译时就已经确认下来。</p><h3 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h3><p>一个方法调用另一个方法时,会通过寻找符号引用的方式查找到直接内存地址。而方法的符号引用存在于<strong>运行时常量池</strong>中。</p><p>在每一个栈针中,会通过栈针中的动态链接,来持有方法的符号引用。能够动态的寻找到方法对应的内存地址完成调用。</p><h3 id="方法出口信息"><a href="#方法出口信息" class="headerlink" title="方法出口信息"></a>方法出口信息</h3><p>方法返回时可能需要在栈帧中保存一些信息,用来于恢复调用者(调用当前方法的方法)的执行状态。</p><p>如果正常返回,会在调用者的栈针中保存调用方法的返回值,而被调用者的栈针可能会保存调用者当前程序计数器的值。</p><p>如果异常退出,也就是在调用过程中发现异常,调用者的返回值将由异常处理器返回,而被调用者中就不可能存在调用者的计数器值。</p><h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><p>通过下面的代码,来看虚拟机栈的调用返回过程和操作数栈的入栈出栈过程。</p><p>源程序:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">NewObject</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> NewObject newObject = <span class="keyword">new</span> NewObject();</span><br><span class="line"> newObject.add();</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">add</span><span class="params">()</span></span>{</span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">int</span> b = <span class="number">6</span>;</span><br><span class="line"> <span class="keyword">return</span> a+b;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>反编译后的程序:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(java.lang.String[])</span></span>;</span><br><span class="line"> Code:</span><br><span class="line"> 0: new #2 // class com/java8/Jvm/NewObject 半初始化申请内存地址</span><br><span class="line"> <span class="number">3</span>: dup <span class="comment">// 复制栈顶数值并将复制值压入栈顶</span></span><br><span class="line"> 4: invokespecial #3 // Method "<init>":()V 构造初始化</span><br><span class="line"> <span class="number">7</span>: astore_1 <span class="comment">// 建立关联,将初始化后的存到变量newObject上</span></span><br><span class="line"> <span class="number">8</span>: aload_1 <span class="comment">// 从本地变量上加载newObject</span></span><br><span class="line"> 9: invokevirtual #4 // Method add:()I 执行add()方法</span><br><span class="line"> <span class="number">12</span>: pop <span class="comment">// 栈顶数值弹出</span></span><br><span class="line"> <span class="number">13</span>: <span class="keyword">return</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">add</span><span class="params">()</span></span>;</span><br><span class="line"> Code:</span><br><span class="line"> <span class="number">0</span>: iconst_5 <span class="comment">// 将5推至操作数栈顶</span></span><br><span class="line"> <span class="number">1</span>: istore_1 <span class="comment">// 将栈顶int型数值存入第1个本地变量 也就是a,保存到局部变量表中</span></span><br><span class="line"> <span class="number">2</span>: bipush <span class="number">6</span> <span class="comment">//将单字节的常量值6推送至操作数栈顶</span></span><br><span class="line"> <span class="number">4</span>: istore_2 <span class="comment">// 将栈顶int型数值存入第2个本地变量 也就是b,保存到局部变量表中</span></span><br><span class="line"> <span class="number">5</span>: iload_1 <span class="comment">// 将第1个int型本地变量推送至操作数栈顶</span></span><br><span class="line"> <span class="number">6</span>: iload_2 <span class="comment">// 将第2个int型本地变量推送至操作数栈顶</span></span><br><span class="line"> <span class="number">7</span>: iadd <span class="comment">// 将栈顶两int型数值相加并将结果压入操作数栈顶</span></span><br><span class="line"> <span class="number">8</span>: ireturn <span class="comment">//返回</span></span><br></pre></td></tr></table></figure></div><p>调用过程是:先通过在main方法里面创建一个newObject对象,再调用其中的add方法。其中newObject的过程先略过,后面讲,先看add方法执行的过程。</p><p>在add方法内,每次会将int类型的值先推到栈顶,然后执行赋值操作,将赋值的变量保存到局部变量表中,最后计算后将结果return。</p><h2 id="3-本地方法栈"><a href="#3-本地方法栈" class="headerlink" title="3. 本地方法栈"></a>3. 本地方法栈</h2><p>虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。</p><p>和虚拟机栈相同,每次方法执行时,都会创建一个栈针,存放本地方法的局部变量表、操作数栈、动态链接、方法出口信息。</p><h2 id="4-堆"><a href="#4-堆" class="headerlink" title="4. 堆"></a>4. 堆</h2><p>堆是Java虚拟机管理内存中的最大的部分,也是进行GC主要部分。几乎所有的实例变量在堆中分配内存,少数小的对象可能在栈上分配。</p><blockquote><p>在栈上分配的好处,首先快,其次不需要额外的垃圾回收的介入即可完成内存的释放。</p></blockquote><p>在1.7之后,字符串常量池从方法区中挪到了堆中</p><h2 id="5-方法区"><a href="#5-方法区" class="headerlink" title="5. 方法区"></a>5. 方法区</h2><p>方法区和堆一样是线程共享的内容,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。</p><p>方法区是Java虚拟机定义的规范,在1.7之前,Hotspot虚拟机使用永久代实现方法区。在1.7后,使用了元空间区实现方法区。两者的差异在于元空间使用的是直接内存,整个永久代有一个 JVM 本身设置固定大小上限,无法进行调整,而元空间使用的是直接内存,受本机可用内存的限制,虽然元空间仍旧可能溢出,但是比原来出现的几率会更小。</p><h3 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h3><p>有三个比较容易混淆的概念,什么是类常量池,什么是运行时常量池,什么是字符串常量池,三者的区别是什么。</p><ul><li>类常量池存在于字节码文件中,在字节码文件中存在类的常量池,其中存放两个类常量:字面量和符号引用,其中字面量的含义就是文本字符串和final修饰的常量值等。符号引用包含import的包、类和接口的全限定名、字段名称和描述符、方法类型和引用等等。</li><li>运行时常量池存在于内存中,在类加载的过程时,会将存在于字节码中的类常量池中的内容加载到运行时常量池中。相较于类常量池,运行时常量池具有动态性,不一定是在编译时期就确定的放入的。</li><li>字符串常量池,也叫做StringTable,在java7之后被放到了堆内,存放了字符串常量和字符串对象的引用。在class中的内容加载到运行时常量池后,在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。</li></ul><p>下图表示运行时常量池中的内容:</p><p><img src="http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-14/26038433.jpg" alt="http://my-blog-to-use.oss-cn-beijing.aliyuncs.com/18-9-14/26038433.jpg"></p><p><a href="https://blog.csdn.net/wangbiao007/article/details/78545189" target="_blank" rel="noopener">方法区和常量池_Java_屌丝程序员的奋斗之路-CSDN博客</a></p><h2 id="6-直接内存"><a href="#6-直接内存" class="headerlink" title="6. 直接内存"></a>6. 直接内存</h2><p>直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。</p><h1 id="对象创建过程和对象布局"><a href="#对象创建过程和对象布局" class="headerlink" title="对象创建过程和对象布局"></a>对象创建过程和对象布局</h1><h2 id="对象的创建过程"><a href="#对象的创建过程" class="headerlink" title="对象的创建过程"></a>对象的创建过程</h2><p>使用new关键字创建对象的过程分为5步,类加载检查、内存分配、初始化零值、设置对象头、执行init方法。参考下图:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200518205307.png" alt="Untitled 1"></p><h3 id="1-类加载检查"><a href="#1-类加载检查" class="headerlink" title="1. 类加载检查"></a>1. 类加载检查</h3><p>虚拟机遇到new指令时,会先进行类加载检查,如果再运行时常量池中未发现类的符号引用,则先去加载、解析、初始化类这一些列类加载到内存中的过程。</p><h3 id="2-分配内存"><a href="#2-分配内存" class="headerlink" title="2. 分配内存"></a>2. 分配内存</h3><p>在类加载到内存后,对象在堆中分配的大小已经确定,利用<code>指针碰撞</code>或者<code>空闲列表</code>的方式将堆中指定大小的空间分配给新创建的类对象。</p><h3 id="3-初始化零值"><a href="#3-初始化零值" class="headerlink" title="3. 初始化零值"></a>3. 初始化零值</h3><p>在为对象分配完内存后,首先需要对对象的初始化零值,来保证对象不手动赋值也能够正常被访问。此时的对象处于半初始化状态。</p><h3 id="4-设置对象头"><a href="#4-设置对象头" class="headerlink" title="4. 设置对象头"></a>4. 设置对象头</h3><p>在初始化零值后,还需要为对象设置头部信息,包括Mark Word、Kclass Word、数组长度(如果是数组的话),具体会在后面提到。</p><h3 id="5-执行init方法"><a href="#5-执行init方法" class="headerlink" title="5. 执行init方法"></a>5. 执行init方法</h3><p>通过init方法,为对象真正的赋值,当执行init方法后,一个需要的对象才产生出来。</p><p>下面通过反编译的代码来看创建一个Object对象的过程:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(java.lang.String[])</span></span>;</span><br><span class="line"> Code:</span><br><span class="line"> 0: new #2 // class java/lang/Object 申请内存空间</span><br><span class="line"> <span class="number">3</span>: dup</span><br><span class="line"> 4: invokespecial #1 // Method java/lang/Object."<init>":()V 构造初始化</span><br><span class="line"> <span class="number">7</span>: astore_1 <span class="comment">// 建立关联,将初始化后的存到变量Object上</span></span><br><span class="line"> <span class="number">8</span>: <span class="keyword">return</span></span><br></pre></td></tr></table></figure></div><p>通过javap反编译可以看出一个对象,通过使用new命令后在堆中申请一块内存空间后,将o推到栈顶,使用invokespecial命令进行初始化,最后使用astore命令将对象o和创建的对象关联到一起。</p><h2 id="对象布局"><a href="#对象布局" class="headerlink" title="对象布局"></a>对象布局</h2><p>一个对象包含三个部分,分别为对象头、实例数据(对象体)、Padding对其字节。</p><h3 id="对象头(Header)"><a href="#对象头(Header)" class="headerlink" title="对象头(Header)"></a>对象头(Header)</h3><p>在对象头中包含了三个部分:Mark Word、Class Pointer、数组长度(如果是数组)</p><ul><li>Mark Word:MarkWord中包含了自身的hashcode,gc的分代信息和锁标志位等,其中当上锁、上不同的锁的时候,头部信息都会出现不同的变化,具体占用长度和信息可以参考下图:</li></ul><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200518205334.png" alt="Untitled 2"></p><ul><li>Class Pointer: 在Class Pointer中包含了对方法区中的类元数据的引用,在64位系统下,如果不开启指针压缩时,会比32位多占二分之一的空间,所以在1.6之后,通过使用JVM的参数<code>-XX:+UseCompressedOops</code>可以开启指针压缩将原来的8字节压缩为现在的4字节。</li><li>数组长度:如果当前对象时数组时,会额外产生一部分空间存储数组的长度</li></ul><h3 id="实例数据(Instance-Data)"><a href="#实例数据(Instance-Data)" class="headerlink" title="实例数据(Instance Data)"></a>实例数据(Instance Data)</h3><p>对象中主要的内容,存放实例数据,包括了对象的所有成员变量,其大小由各个成员变量的大小决定。</p><h3 id="对其字节(Padding)"><a href="#对其字节(Padding)" class="headerlink" title="对其字节(Padding)"></a>对其字节(Padding)</h3><p>Java对象占用空间是8字节对齐的,即所有Java对象占用bytes数必须是8的倍数。</p><h3 id="观察对象空间实例"><a href="#观察对象空间实例" class="headerlink" title="观察对象空间实例"></a>观察对象空间实例</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> Object o = <span class="keyword">new</span> Object();</span><br><span class="line"> System.out.println(ClassLayout.parseInstance(o).toPrintable());</span><br><span class="line"> <span class="keyword">int</span>[] arr = <span class="keyword">new</span> <span class="keyword">int</span>[<span class="number">10</span>];</span><br><span class="line"> System.out.println(ClassLayout.parseInstance(arr).toPrintable());</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>当使用上面的方式创建一个对象o和一个数组对象arr时,利用openjdk的jol(Java Object Layout)工具进行内存的分析,产生如下的分析结果:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line">java.lang.Object object internals:</span><br><span class="line"> OFFSET SIZE TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">4</span> (object header) <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000001</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">1</span>)</span><br><span class="line"> <span class="number">4</span> <span class="number">4</span> (object header) <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (object header) e5 <span class="number">01</span> <span class="number">00</span> f8 (<span class="number">11100101</span> <span class="number">00000001</span> <span class="number">00000000</span> <span class="number">11111000</span>) (-<span class="number">134217243</span>)</span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> (loss due to the next object alignment)</span><br><span class="line">Instance size: <span class="number">16</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">4</span> bytes external = <span class="number">4</span> bytes total</span><br><span class="line"></span><br><span class="line">[I object internals:</span><br><span class="line"> OFFSET SIZE TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">4</span> (object header) <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000001</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">1</span>)</span><br><span class="line"> <span class="number">4</span> <span class="number">4</span> (object header) <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (object header) <span class="number">6</span>d <span class="number">01</span> <span class="number">00</span> f8 (<span class="number">01101101</span> <span class="number">00000001</span> <span class="number">00000000</span> <span class="number">11111000</span>) (-<span class="number">134217363</span>)</span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> (object header) <span class="number">0</span>a <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00001010</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">10</span>)</span><br><span class="line"> <span class="number">16</span> <span class="number">40</span> <span class="keyword">int</span> [I.<elements> N/A</span><br><span class="line">Instance size: <span class="number">56</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">0</span> bytes external = <span class="number">0</span> bytes total</span><br></pre></td></tr></table></figure></div><p>因为当前环境为64位,所以生成对象的大小应为8的倍数。可以看到对象o的layout中,前8个字节表示64位的MarkWord,之后4个字节表示KlassWord,最后四个字节并没有内容,是loss掉的,用于补齐字节用的,保证能够被8整除。</p><p>而在下面的数组对象arr中,可以看到最后的四个字节并不是对其,而是存储量数组的长度,而最后的字节表示了对象体,也就是初始化的数组内容。</p><p>在使用 <code>-XX:-UseCompressedOops</code> 关闭指针压缩时,观察对象o所占大小,发现大小未发生变化,然而原来的padding字节不见了,class pointer 变为了8个字节,一共16个字节,所以不需要padding来对其空间。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">java.lang.Object object internals:</span><br><span class="line"> OFFSET SIZE TYPE DESCRIPTION VALUE</span><br><span class="line"> <span class="number">0</span> <span class="number">4</span> (object header) <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000001</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">1</span>)</span><br><span class="line"> <span class="number">4</span> <span class="number">4</span> (object header) <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">0</span>)</span><br><span class="line"> <span class="number">8</span> <span class="number">4</span> (object header) <span class="number">00</span> <span class="number">1</span>c b6 <span class="number">0</span>d (<span class="number">00000000</span> <span class="number">00011100</span> <span class="number">10110110</span> <span class="number">00001101</span>) (<span class="number">230038528</span>)</span><br><span class="line"> <span class="number">12</span> <span class="number">4</span> (object header) <span class="number">01</span> <span class="number">00</span> <span class="number">00</span> <span class="number">00</span> (<span class="number">00000001</span> <span class="number">00000000</span> <span class="number">00000000</span> <span class="number">00000000</span>) (<span class="number">1</span>)</span><br><span class="line">Instance size: <span class="number">16</span> bytes</span><br><span class="line">Space losses: <span class="number">0</span> bytes internal + <span class="number">0</span> bytes external = <span class="number">0</span> bytes total</span><br></pre></td></tr></table></figure></div><h2 id="补充:javap和jol工具"><a href="#补充:javap和jol工具" class="headerlink" title="补充:javap和jol工具"></a>补充:javap和jol工具</h2><ol><li>使用JDK中自带的javap对class文件进行反编译,能够获得反编译到的执行过程。</li><li>使用IDEA中的插件:Jclasslib可以更具体的看反编译的代码和常量池中的内容</li><li>使用jol可以查看对象布局,使用方法,将OpenJDK中的jol的jar包加入到项目中,在main方法中使用ClassLayout静态方法解析对象后输出:</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="XML"><figure class="iseeu highlight /xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.openjdk.jol<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>jol-core<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>0.10<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<h1 id="深入JVM(一)类加载过程和内存分配"><a href="#深入JVM(一)类加载过程和内存分配" class="headerlink" title="深入JVM(一)类加载过程和内存分配"></a>深入JVM(一)类加载过程和内存分配</h1><blockquo
</summary>
<category term="JVM" scheme="https://nanyiniu.github.io/categories/JVM/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="JVM" scheme="https://nanyiniu.github.io/tags/JVM/"/>
</entry>
<entry>
<title>线上内存溢出分析</title>
<link href="https://nanyiniu.github.io/2020/05/02/%E7%BA%BF%E4%B8%8A%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA%E5%88%86%E6%9E%90/"/>
<id>https://nanyiniu.github.io/2020/05/02/线上内存溢出分析/</id>
<published>2020-05-02T15:00:00.000Z</published>
<updated>2023-07-30T09:34:44.086Z</updated>
<content type="html"><![CDATA[<h1 id="线上内存溢出分析"><a href="#线上内存溢出分析" class="headerlink" title="线上内存溢出分析"></a>线上内存溢出分析</h1><p>系统变慢,打开页面变卡,使用top命令查看cpu和内存情况后,发现在使用系统时,CPU 飙高。</p><p>针对此问题,展开排查。</p><h2 id="排查产生原因"><a href="#排查产生原因" class="headerlink" title="排查产生原因"></a>排查产生原因</h2><p>首先,使用 arthas 工具或者使用 jstack 查看 java 运行中的线程状态,查看哪些线程占用cpu过高,在使用时,发现为gc线程。</p><p>然后,使用 jstat 或者 arthas 中的 dashboard 命令 进行确认,发现堆中新生代老年代空间已满,每过十几秒就在进行一次full gc。所以,可以分析出CPU飙高的原因为内存溢出。</p><h2 id="内存溢出原因分析"><a href="#内存溢出原因分析" class="headerlink" title="内存溢出原因分析"></a>内存溢出原因分析</h2><p>因为要进行内存溢出情况,需要拿出堆转储信息,所以,在无人使用系统时,使用 jmap 命令获取堆转储信息:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">jmap -dump:format=b,file=heapdump.hprof pid</span><br></pre></td></tr></table></figure></div><p>需要注意的是,使用 jmap 时,会使 JVM 处于 STW(stop the world) 状态,所以尽量不要在生产上有人使用时使用该命令,否则会导致非常长的时间停顿。</p><p>在拿到堆转储信息后,使用MAT(Memory Analyzer Tool)工具进行分析,装载文件后,得到如下图中的内容:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205207.png" alt="a1"></p><p>发现DefaultSVNRepositoryPoll引出的对象占用了3.6个G的空间(整个JVM分配空间为4个G),所以可以确定,是这个类生产 RunnableSecheduleFuture 对象的问题。</p><h3 id="对-DefaultSVNRepositoryPool-进行分析,找问题"><a href="#对-DefaultSVNRepositoryPool-进行分析,找问题" class="headerlink" title="对 DefaultSVNRepositoryPool 进行分析,找问题"></a>对 DefaultSVNRepositoryPool 进行分析,找问题</h3><p>在前面已经分析出是DefaultSVNRepositoryPool引出的内存溢出,所以直接在IDEA中搜索到这个类,进入到该类,查看是否有相关 RunnableSecheduleFuture 方法。</p><ul><li>首先确认哪里引用到了该类</li></ul><p>一切还要从SVN上传文件说起,SVN上传文件有几个步骤:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">//1. 判断路径是否存在;</span></span><br><span class="line">SVNClientManipulator rp = <span class="keyword">new</span> SVNClientManipulator(user);</span><br><span class="line"><span class="comment">//2. 得到版本库;</span></span><br><span class="line">SVNRepository rps = rp.getRepository(svnDao.getSvnUrlByRepository(repository) + </span><br><span class="line">repository + <span class="string">"/"</span> + newPath);</span><br><span class="line"><span class="comment">//3. 判断是否存在;</span></span><br><span class="line">SVNNodeKind nodeKind = rps.checkPath(<span class="string">""</span>, -<span class="number">1</span>);</span><br><span class="line"><span class="keyword">if</span> (nodeKind == SVNNodeKind.NONE) {<span class="comment">//如果存在则更新路径;</span></span><br><span class="line"> svnConfigDevelopService.addDir(svnDao.getSvnUrlByRepository(repository) +</span><br><span class="line"> repository + <span class="string">"/"</span> + newPath, desc, rp, <span class="keyword">true</span>);</span><br><span class="line">}</span><br><span class="line">importFolder = newPath;</span><br><span class="line"><span class="comment">// 4. 上传文件</span></span><br><span class="line">String msg = svnCommonService.addFileToSVN(svnFilePath, gdFileName, </span><br><span class="line">repository, <span class="string">"/"</span> + importFolder + <span class="string">"/"</span>, user, desc);</span><br></pre></td></tr></table></figure></div><p>其中对获取SVNClientManipulator时,其实是获取一个client客户端,整个过程可以用一个简单的sequence图来说明:</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205241.png" alt="a2"></p><ul><li>然后对DefaultSVNRepositoryPool内部排查</li></ul><p>在引用到的 DefaultSVNRepositoryPool 的构造方法内,可以看到</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205258.png" alt="a3"></p><p>这里有两个Timer,作为全局的变量,可以看到作用是从10秒开始,没10秒执行一次 TimeOutTask() 方法。而这里的 <code>myScheduledTimeoutTask</code> 正是 <code>ScheduledFuture</code> 类型的。</p><p>如果不确认该类是否为溢出的对象,可以再进入到scheduleWithFixedDelay中。可以看到返回的正是<code>RunnableScheduledFuture</code> 对象。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205311.png" alt="a4"></p><p>此时就可以由刚才的seq图往回推</p><p>最终问题有可能出现在在 svnCommonService 中创建了过多的 SVNClientManipulator 。</p><h3 id="SVN上传代码排查"><a href="#SVN上传代码排查" class="headerlink" title="SVN上传代码排查"></a>SVN上传代码排查</h3><p>是否为上述分析的原因,需要在svnCommonService代码中确认。</p><ul><li>对现有的SVN上传代码过程分析</li></ul><p>svnFileJsonArray 将json解析为array,循环每个数组中的内容,进行文件上传。</p><p>首先<strong>SVNClientManipulator的创建发生在循环内</strong>,也就是说一次请求上传多个文件,则会创建多个客户端。此处为导致问题的主要原因。</p><p>其次,在代码内没有发现同步区域,而且大部分内容是需要查询表、插入表,在高并发的情况下可能会出现 mysql 的 lock wait 异常,最终导致文件无法上传的错误。</p><p>最后,现有因为代码是一个请求创建一个线程,n次请求就产生n个线程,这在高请求量的情况下出现问题的概率非常大。</p><p>基于以上三点进行代码上的修改</p><h3 id="问题代码修改"><a href="#问题代码修改" class="headerlink" title="问题代码修改"></a>问题代码修改</h3><ul><li>针对<strong>SVNClientManipulator的创建发生在循环内</strong></li></ul><p>将json中取出一人或者直接传进一个处理人,创建client,减少client的创建次数</p><ul><li>针对一次请求创建一个线程,修改为使用线程池,默认线程池的coreSize为8,核心数*2</li><li>针对同步代码块,使用sycronized进行同步代码块。</li></ul><p>进阶修改:</p><p>上述修改后,可以满足过程,但是实际上大部分代码会使用sycronized进行包裹,同时进行的线程只有一个,所以提前创建多线程是占用内存的时间的。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205327.png" alt="a5"></p><p>最终修改为:</p><ol><li>使用单线程处理请求,单线程处理业务代码,避免了加锁解锁的消耗</li><li>仅仅在上传文件时,使用线程池进行文件上传</li></ol><h3 id="插曲:为什么DefaultSVNRepositoryPool没有回收掉"><a href="#插曲:为什么DefaultSVNRepositoryPool没有回收掉" class="headerlink" title="插曲:为什么DefaultSVNRepositoryPool没有回收掉"></a>插曲:为什么DefaultSVNRepositoryPool没有回收掉</h3><p>这里涉及到客户端连接SVN的状态,默认连接状态时 keepAlive 的,但是不需要手动进行连接的关闭。</p><p>一个客户端启动一个全局的Timer,这个Timer没10秒检测一下是否可以关闭连接,可以关闭连接的条件是 这个连接在 60 秒内没有再次访问过SVN。</p><p>但是,因为task是需要线程进行执行的,当创建非常多的pool时,timer可能取不到CPU时间片来执行task,所以就在一直等待,导致链上的所有对象,虚拟机都无法进行回收,最终导致内存溢出。</p><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/b563194e22fada4ec157ab3c2a06cbdf5d0cb018/img/20200627205339.png" alt="a6"></p><hr><h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="一、JVM调优参数"><a href="#一、JVM调优参数" class="headerlink" title="一、JVM调优参数"></a>一、JVM调优参数</h3><p>线上分为三台机器,而应用占其中一台,总内存为16g,针对此环境修改JAVA_OPTS</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="BASH"><figure class="iseeu highlight /bash"><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">JAVA_OPTS=<span class="string">"-server -Xms8192m -Xmx8192m </span></span><br><span class="line"><span class="string">-XX:PermSize=1024M -XX:MaxPermSize=1024M -Duser.language=zh -Djava.util.Arrays.useLegacyMergeSort=true </span></span><br><span class="line"><span class="string">-Djava.awt.headless=true </span></span><br><span class="line"><span class="string">-Xloggc:/app/okit/java/gc-%t.log </span></span><br><span class="line"><span class="string">-XX:+UseGCLogFileRotation </span></span><br><span class="line"><span class="string">-XX:NumberOfGCLogFiles=5 </span></span><br><span class="line"><span class="string">-XX:GCLogFileSize=50M -XX:+PrintGCTimeStamps </span></span><br><span class="line"><span class="string">-XX:+PrintGCDetails </span></span><br><span class="line"><span class="string">-XX:HeapDumpPath=./java_dump.hprof </span></span><br><span class="line"><span class="string">-XX:+HeapDumpOnOutOfMemoryError"</span></span><br></pre></td></tr></table></figure></div><p>针对其中参数含义解释:</p><ul><li><strong>-XX:+UseGCLogFileRotation GCLog文件输出</strong></li><li><strong>-XX:NumberOfGCLogFiles=5 GCLog文件数量</strong></li><li><strong>-XX:GCLogFileSize=20M GCLog文件大小</strong></li><li><strong>-XX:+PrintGCTimeStamps 打印GC耗时</strong></li><li><strong>-XX:+PrintGCDetails 打印GC回收的细节</strong></li><li><strong>-XX:HeapDumpPath=./java_pid<pid>.hprof :堆内存快照的存储文件路径。文件名一般为java_<pid>_<date>_<time>_heapDump.hprof。</strong></li><li><strong>-XX:+HeapDumpOnOutOfMemoryError 在OOM时,自动输出一个dump文件</strong></li></ul><h3 id="二、JDK自带工具使用"><a href="#二、JDK自带工具使用" class="headerlink" title="二、JDK自带工具使用"></a>二、JDK自带工具使用</h3><ul><li>jps</li></ul><p>JPS(Java Virtual Machine Process Status Tool),可以显示进行中的Java线程。</p><p>使用方式:<code>jps [options] [hostid]</code></p><ul><li>jstat -gc</li></ul><p>jstat(Java Virtual Machine statistics monitoring tool),能够查看JVM的使用情况</p><p>使用方式:<code>jstat [ generalOption | outputOptions vmid [ interval [ s|ms ] [ count ] ] ]</code></p><p>如: jstat -gc -h3 31736 1000 10</p><ul><li>jstack</li></ul><p>jstack(Java stack trace)是Java的堆栈分析工具。</p><p>两个功能:</p><ol><li>针对活着的进程做本地的或远程的线程dump;</li><li>针对core文件做线程dump。</li></ol><p>使用方式:<code>jstack [ option ] pid</code></p><p>可将堆栈输出到指定文件中:jstack -l PID >> jstack.out</p><ul><li>jmap</li></ul><p>jmap(Java memory map),它可以生成 java 程序的 dump 文件, 也可以查看堆内对象示例的统计信息、查看 ClassLoader 的信息以及 finalizer 队列。</p><p>jmap -dump:format=b,file=heapdump.hprof pid</p><ul><li>JVisualVM</li></ul><p>用来监测JVM内存和线程使用情况,可以远程连接</p><h3 id="三、其他分析工具"><a href="#三、其他分析工具" class="headerlink" title="三、其他分析工具"></a>三、其他分析工具</h3><ul><li>MAT</li></ul><p>用来分析堆转储信息,能够分析内存溢出问题</p><ul><li>Arthas</li></ul><p>可以实现JDK中工具所有功能,更直观。还能够线上<strong>热部署。</strong></p>]]></content>
<summary type="html">
<h1 id="线上内存溢出分析"><a href="#线上内存溢出分析" class="headerlink" title="线上内存溢出分析"></a>线上内存溢出分析</h1><p>系统变慢,打开页面变卡,使用top命令查看cpu和内存情况后,发现在使用系统时,CPU 飙高
</summary>
<category term="JVM" scheme="https://nanyiniu.github.io/categories/JVM/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="JVM" scheme="https://nanyiniu.github.io/tags/JVM/"/>
<category term="内存溢出" scheme="https://nanyiniu.github.io/tags/%E5%86%85%E5%AD%98%E6%BA%A2%E5%87%BA/"/>
</entry>
<entry>
<title>Java的深拷贝和浅拷贝</title>
<link href="https://nanyiniu.github.io/2020/04/16/Java%E7%9A%84%E6%B7%B1%E6%8B%B7%E8%B4%9D%E5%92%8C%E6%B5%85%E6%8B%B7%E8%B4%9D/"/>
<id>https://nanyiniu.github.io/2020/04/16/Java的深拷贝和浅拷贝/</id>
<published>2020-04-16T00:00:00.000Z</published>
<updated>2020-05-23T05:31:38.172Z</updated>
<content type="html"><![CDATA[<h1 id="Java的深拷贝和浅拷贝"><a href="#Java的深拷贝和浅拷贝" class="headerlink" title="Java的深拷贝和浅拷贝"></a>Java的深拷贝和浅拷贝</h1><h2 id="对象拷贝"><a href="#对象拷贝" class="headerlink" title="对象拷贝"></a>对象拷贝</h2><p>在展开说深拷贝和浅拷贝之前,先来阐述阐述一下什么是对象拷贝。对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。</p><p>可以简单类比为在电脑上复制文件,这时候,复制普通文件和复制链接就产生了差异,这个差异就是接下来需要分析的深拷贝和浅拷贝的差异。</p><h3 id="对象拷贝的实现"><a href="#对象拷贝的实现" class="headerlink" title="对象拷贝的实现"></a>对象拷贝的实现</h3><p>在Java中如果想要实现拷贝(忽略对象之间使用<code>=</code>号),只能使用<code>clone</code>方法。clone方法使用<code>protect</code>修饰,声明在Object上,也就是所有Object子对象都可以使用clone方法进行对象拷贝。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">native</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException</span>;</span><br></pre></td></tr></table></figure></div><p>在注释中可以看到,如果这个类没有继承自<code>Cloneable</code>接口,那么它会抛出<code>CloneNotSupportedException</code> 异常。</p><p>在实现接口,并调用clone时,就能完成对象的拷贝。</p><h2 id="深拷贝和浅拷贝"><a href="#深拷贝和浅拷贝" class="headerlink" title="深拷贝和浅拷贝"></a>深拷贝和浅拷贝</h2><p>在对象拷贝章节类比电脑上复制文件一样,针对普通文件和链接文件有不同的处理方式,这种处理方式在Java对象拷贝的上的体现就是深拷贝。</p><p>在 Java 中,除了<strong>基本数据类型</strong>(元类型)之外,还存在 <strong>类的实例对象</strong> 这个引用数据类型。如果再拷贝对象的过程中,只对基本类型的变量进行了值得复制,却对引用类型只做了引用的复制(也就是内存地址引用),没有真正复制引用到的对象。此时的对象拷贝就叫做<strong>浅拷贝</strong>。</p><p>与之相反,不光对基本数据类型执行了值得复制,而且在复制引用类型复制时,不是仅仅传递引用,而是将引用到的对象真正的复制(分配内存),此时的对象拷贝就叫做<strong>深拷贝</strong>。</p><h3 id="深拷贝和浅拷贝的区别"><a href="#深拷贝和浅拷贝的区别" class="headerlink" title="深拷贝和浅拷贝的区别"></a>深拷贝和浅拷贝的区别</h3><p>其实上面在引申概念时,就已经得出深拷贝和浅拷贝的区别了,深拷贝不光要拷贝进本数据类型的值,还要完成对引用类型创建一个新的对象,并复制其内的成员变量。</p><h3 id="深拷贝和浅拷贝的实例"><a href="#深拷贝和浅拷贝的实例" class="headerlink" title="深拷贝和浅拷贝的实例"></a>深拷贝和浅拷贝的实例</h3><ul><li>浅拷贝用例<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CloneTest</span> <span class="keyword">implements</span> <span class="title">Cloneable</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> x;</span><br><span class="line"> <span class="keyword">public</span> SonClone son;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getX</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line"> }</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">setX</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.x = x;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> SonClone <span class="title">getSon</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> son;</span><br><span class="line"> }</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">setSon</span><span class="params">(SonClone son)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.son = son;</span><br><span class="line"> }</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">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.clone();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testClone</span><span class="params">()</span></span>{</span><br><span class="line"> CloneTest test = <span class="keyword">new</span> CloneTest();</span><br><span class="line"> test.setX(<span class="number">127</span>);</span><br><span class="line"> test.setSon(<span class="keyword">new</span> SonClone());</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> CloneTest clone = (CloneTest) test.clone();</span><br><span class="line"> <span class="comment">// 比较test和复制对象copy</span></span><br><span class="line"> System.out.println(<span class="string">"test == clone --> "</span></span><br><span class="line"> +(test == clone));</span><br><span class="line"> System.out.println(<span class="string">"test.hash == clone.hash --> "</span></span><br><span class="line"> +(test.hashCode() == clone.hashCode()));</span><br><span class="line"> System.out.println(<span class="string">"test.getClass() == clone.getClass() --> "</span></span><br><span class="line"> + (test.getClass() == clone.getClass()));</span><br><span class="line"> System.out.println(<span class="string">"test.son == clone.son --> "</span> </span><br><span class="line"> +(test.getSon() == clone.getSon()));</span><br><span class="line"> System.out.println(<span class="string">"test.son.hash == clone.son.hash --> "</span> </span><br><span class="line"> +(test.getSon().hashCode() == clone.getSon().hashCode()));</span><br><span class="line"> } <span class="keyword">catch</span> (CloneNotSupportedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">SonClone</span> <span class="keyword">implements</span> <span class="title">Cloneable</span></span>{</span><br><span class="line"> <span class="keyword">int</span> a;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul><p>浅拷贝的执行结果如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="PLAIN"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line">test == clone --> false</span><br><span class="line">test.hash == clone.hash --> false</span><br><span class="line">test.getClass() == clone.getClass() --> true</span><br><span class="line">test.son == clone.son --> true</span><br><span class="line">test.son.hash == clone.son.hash --> true</span><br></pre></td></tr></table></figure></div><p>可以看到,使用clone可以复制对象,对象的hashcode已经不相同了,但是引用对象却没有执行复制对象的过程,返回的hashcode值仍然是相同的,也就是仅仅复制了引用。</p><ul><li>深拷贝用例<div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeepClone</span> <span class="keyword">implements</span> <span class="title">Cloneable</span></span>{ <span class="keyword">public</span> <span class="keyword">int</span> x; <span class="keyword">public</span> SonClone son;</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getX</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> x;</span><br><span class="line"> }</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">setX</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.x = x;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> SonClone <span class="title">getSon</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> son;</span><br><span class="line"> }</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">setSon</span><span class="params">(SonClone son)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.son = son;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">SonClone</span> <span class="keyword">implements</span> <span class="title">Cloneable</span></span>{</span><br><span class="line"> <span class="keyword">int</span> name;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getName</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> name;</span><br><span class="line"> }</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">setName</span><span class="params">(<span class="keyword">int</span> name)</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</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">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.clone();</span><br><span class="line"> }</span><br><span class="line"> }</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">protected</span> Object <span class="title">clone</span><span class="params">()</span> <span class="keyword">throws</span> CloneNotSupportedException </span>{</span><br><span class="line"> DeepClone clone = (DeepClone) <span class="keyword">super</span>.clone();</span><br><span class="line"> clone.son = (SonClone) <span class="keyword">this</span>.son.clone();</span><br><span class="line"> <span class="keyword">return</span> clone;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Test</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">testClone</span><span class="params">()</span></span>{</span><br><span class="line"> DeepClone test = <span class="keyword">new</span> DeepClone();</span><br><span class="line"> test.setX(<span class="number">127</span>);</span><br><span class="line"> test.setSon(<span class="keyword">new</span> SonClone());</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> DeepClone clone = (DeepClone) test.clone();</span><br><span class="line"> <span class="comment">// 比较test和复制对象copy</span></span><br><span class="line"> System.out.println(<span class="string">"test == clone --> "</span></span><br><span class="line"> +(test == clone));</span><br><span class="line"> System.out.println(<span class="string">"test.hash == clone.hash --> "</span></span><br><span class="line"> +(test.hashCode() == clone.hashCode()));</span><br><span class="line"></span><br><span class="line"> System.out.println(<span class="string">"test.getClass() == clone.getClass() --> "</span></span><br><span class="line"> + (test.getClass() == clone.getClass()));</span><br><span class="line"> System.out.println(<span class="string">"test.x == clone.x --> "</span></span><br><span class="line"> +(test.getX() == clone.getX()));</span><br><span class="line"> System.out.println(<span class="string">"test.son == clone.son --> "</span></span><br><span class="line"> +(test.getSon() == clone.getSon()));</span><br><span class="line"> System.out.println(<span class="string">"test.son.hash == clone.son.hash --> "</span></span><br><span class="line"> +(test.getSon().hashCode() == clone.getSon().hashCode()));</span><br><span class="line"> } <span class="keyword">catch</span> (CloneNotSupportedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ul><p>这里深度拷贝可以看出在父类使用clone时,会手动将clone出的父类中的引用指向复制clone出来的子类对象。这时对父类执行了深拷贝,但实则对子类进行了一次浅拷贝。结果显而易见,最后的引用类型值和hashcode都不相同。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>拷贝对象需要使用clone方法,并需要继承Cloneable接口,如果不手动重写clone方法,则默认会只能执行浅拷贝。</p><p>浅拷贝只会复制基本数据类型的值,而不会复制引用类型的对象,而深拷贝需要手动编写clone方法来达到既能复制基本数据值,又能够完成对引用类型的对象的复制。</p>]]></content>
<summary type="html">
<h1 id="Java的深拷贝和浅拷贝"><a href="#Java的深拷贝和浅拷贝" class="headerlink" title="Java的深拷贝和浅拷贝"></a>Java的深拷贝和浅拷贝</h1><h2 id="对象拷贝"><a href="#对象拷贝" cla
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
</entry>
<entry>
<title>Java IO浅析</title>
<link href="https://nanyiniu.github.io/2020/04/05/Jav%20IO%E6%B5%85%E6%9E%90/"/>
<id>https://nanyiniu.github.io/2020/04/05/Jav IO浅析/</id>
<published>2020-04-05T00:00:00.000Z</published>
<updated>2020-04-09T00:36:52.946Z</updated>
<content type="html"><![CDATA[<h1 id="Java中的IO操作"><a href="#Java中的IO操作" class="headerlink" title="Java中的IO操作"></a>Java中的IO操作</h1><p>Java总的来说有三类IO,效率不高,操作简单的BIO(blocking IO),非阻塞的NIO(New IO),和异步非阻塞IO,也就是升级版的NIO(Asynchronous I/O).</p><h2 id="IO分类"><a href="#IO分类" class="headerlink" title="IO分类"></a>IO分类</h2><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200405193926.png" alt="IO分类"></p><p>在学习这三类IO前,需要了解什么是阻塞.什么是异步.两个的含义有什么区别.</p><blockquote><p>同步和异步关注的是消息通信机制 (synchronous communication/ asynchronous communication)所谓同步,就是在发出一个<em>调用*时,在没有得到结果之前,该 *调用</em> 就不返回。但是一旦调用返回,就得到返回值了。换句话说,就是由<em>调用者</em>主动等待这个<em>调用</em>的结果。而异步则是相反,<em>调用</em>在发出之后,这个调用就直接返回了,但是没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在<em>调用</em>发出后,<em>被调用者</em>通过状态来通知调用者。</p></blockquote><blockquote><p>阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会重启线程。非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。</p></blockquote><h3 id="BIO"><a href="#BIO" class="headerlink" title="BIO"></a>BIO</h3><p>BIO过程就如同名字一样,是一个阻塞的IO,服务端通常为每一个客户端都建立一个独立的线程来通过调用accept()来监听客户端消息.如果想处理多个客户端请求则服务端需要建立等同数量的线程来处理这些消息,这就是普遍的一请求一应答的模型.处理完成后返回应答给客户端后销毁线程,因为线程是一个昂贵的资源,这样重复的新建线程,销毁线程,很浪费处理器资源,所以使用BIO同时能够尽可能的少创建线程,就可以用到线程池的方式实现,来达到服务端创建线程数远远小于客户端数的目的,但这种方法只是伪异步IO.</p><p>在处理链接数量少的情况下,BIO的效率还不错,并且主要逻辑模型清晰明了,代码简单.但是在上万的链接的情况下,BIO处理起来就非常吃紧了.</p><h3 id="NIO"><a href="#NIO" class="headerlink" title="NIO"></a>NIO</h3><p>NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。</p><blockquote><p>NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。</p></blockquote><h4 id="NIO特性和NIO与传统IO的区别"><a href="#NIO特性和NIO与传统IO的区别" class="headerlink" title="NIO特性和NIO与传统IO的区别"></a>NIO特性和NIO与传统IO的区别</h4><ul><li>传统IO(BIO)是一种阻塞IO模型,而NIO是非阻塞的IO模型,区别为当线程读取数据的时候,非阻塞IO可以不用等,而阻塞IO需要一直等待IO完成后才能继续.</li><li>IO面向流,而NIO面向缓冲区.</li><li>通道(channel) NIO通过通道进行数据读写.通道是双向的,而传统的IO是单向的.通道链接的都是Buffer,所以通道可以异步的读写.</li><li>选择器(Selectors) NIO拥有选择器,而IO没有.选择器的作用就是用来使用单个线程来处理多个通道(NIO面向buffer,通道只与buffer交互).</li></ul><p><img src="https://camo.githubusercontent.com/3a68153ce17be90275df07a47409afaea91aff83/68747470733a2f2f6d792d626c6f672d746f2d7573652e6f73732d636e2d6265696a696e672e616c6979756e63732e636f6d2f323031392d322f536c6563746f722e706e67" alt="Selector图解"></p><h3 id="AIO"><a href="#AIO" class="headerlink" title="AIO"></a>AIO</h3><p>AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。</p><p>AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。</p><h2 id="BIO的流操作"><a href="#BIO的流操作" class="headerlink" title="BIO的流操作"></a>BIO的流操作</h2><p>根据上面的IO分类图可以看到IO流按照流的类型可以分为两类,一类是字节流,一类是字符流</p><p>两者之间的区别在于</p><p>操作单位不同,字节流以字节为单位进行数据传输,而字符流是以字符为单位进行传输<br>处理元素不同,字节流可处理所有类型数据,但字符流只可以处理以字符类型的数据,也就是说字符流只可处理纯文本数据</p><h3 id="输入输出流"><a href="#输入输出流" class="headerlink" title="输入输出流"></a>输入输出流</h3><p>输入输出按照字面理解,就是流中的输入和输出</p><table><thead><tr><th>流类型</th><th>输入</th><th>输出</th></tr></thead><tbody><tr><td>字节流</td><td>InputStream</td><td>OutputStream</td></tr><tr><td>字符流</td><td>Reader</td><td>Writer</td></tr></tbody></table><h3 id="字节流"><a href="#字节流" class="headerlink" title="字节流"></a>字节流</h3><h4 id="输入字节流-InputStream"><a href="#输入字节流-InputStream" class="headerlink" title="输入字节流 InputStream"></a>输入字节流 InputStream</h4><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200405200148.png" alt="InputStream"></p><p>inputStream作为抽象类,必须依靠子类实现具体的操作.在抽象类中定义了如下几个方法:</p><ol><li>三个重载的read方法,用来读取数据,其中必须在子类中实现抽象的read方法</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">()</span> <span class="keyword">throws</span> IOException</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义b.length读取范围</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">(<span class="keyword">byte</span> b[])</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="keyword">return</span> read(b, <span class="number">0</span>, b.length);</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">(<span class="keyword">byte</span> b[], <span class="keyword">int</span> off, <span class="keyword">int</span> len)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="keyword">if</span> (b == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (off < <span class="number">0</span> || len < <span class="number">0</span> || len > b.length - off) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> IndexOutOfBoundsException();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (len == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 使用子类中的read进行数据读取</span></span><br><span class="line"> <span class="keyword">int</span> c = read();</span><br><span class="line"> <span class="comment">//....</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="2"><li><code>skip(long n)</code> 方法,用来掉过并丢弃n个字节的数据,并返回被丢弃的数据</li><li><code>available()</code>方法,用来返回输入流中可以读取的字节数,子类需要单独实现该方法,否则会返回0;</li><li><code>void close()</code> 方法,子类实现,用来关闭流</li><li><code>synchronized void mark(int readlimit)</code>方法,用来标记输入流的当前位置,同样由子类来具体实现</li><li><code>synchronized void reset()</code>方法,用来返回输入流最后一次调用mark方法的位置</li></ol><p>来看看几种不同的InputStream:</p><ol><li><code>FileInputStream</code> 把一个文件作为InputStream,实现对文件的读取操作</li><li><code>ByteArrayInputStream</code> 把内存中的一个缓冲区作为InputStream使用</li><li><code>StringBufferInputStream</code> 把一个String对象作为InputStream</li><li><code>PipedInputStream</code> 实现了pipe的概念,主要在线程中使用</li><li><code>SequenceInputStream</code>把多个InputStream合并为一个InputStream</li></ol><h4 id="输出字节流-outputStream"><a href="#输出字节流-outputStream" class="headerlink" title="输出字节流 outputStream"></a>输出字节流 outputStream</h4><ol><li>OutputStream提供了3个重载的write方法来做数据的输出</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">// 将参数b中的字节写到输出流 </span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">(<span class="keyword">byte</span> b[ ])</span></span></span><br><span class="line"><span class="function"><span class="comment">// 将参数b的从偏移量off开始的len个字节写到输出流</span></span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">(<span class="keyword">byte</span> b[ ], <span class="keyword">int</span> off, <span class="keyword">int</span> len)</span></span></span><br><span class="line"><span class="function"><span class="comment">// 先将int转换为byte类型,把低字节写入到输出流中</span></span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">write</span><span class="params">(<span class="keyword">int</span> b)</span></span></span><br></pre></td></tr></table></figure></div><ol start="2"><li><code>public void flush()</code> 将数据缓冲区中数据全部输出,并清空缓冲区。</li><li><code>public void close()</code> 关闭输出流并释放与流相关的系统资源。</li></ol><h3 id="字符流"><a href="#字符流" class="headerlink" title="字符流"></a>字符流</h3><h4 id="字符输入流-Reader"><a href="#字符输入流-Reader" class="headerlink" title="字符输入流 Reader"></a>字符输入流 Reader</h4><p>字符输入流和字节流相似,同样定义了read相关方法,但是不同的点在于Reader操作char而不是byte,并且在声明Reader时,将自身作为一个对象,在相关操作上使用synchronized进行同步操作.</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">protected</span> <span class="title">Reader</span><span class="params">()</span> </span>{</span><br><span class="line"> <span class="keyword">this</span>.lock = <span class="keyword">this</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 实现方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">read</span><span class="params">(<span class="keyword">char</span> cbuf[], <span class="keyword">int</span> off, <span class="keyword">int</span> len)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"> <span class="keyword">synchronized</span> (lock) {</span><br><span class="line"> <span class="comment">// ...</span></span><br></pre></td></tr></table></figure></div><p>同理字符输出流 Writer</p><h3 id="如何使用BIO流"><a href="#如何使用BIO流" class="headerlink" title="如何使用BIO流"></a>如何使用BIO流</h3><ol><li>首先确定是输入还是输出</li><li>其次确认对象是否为纯文本,如果是纯文本可以选择 字符流的 <code>Reader</code> 和 <code>Wirter</code> ,否则需要使用字节流的 <code>inputStream</code> 和 <code>outputStream</code></li><li>然后确定是否要通过流转换来达到增加处理效率的目的,如果需要则使用 <code>InputStreamReader</code> 等进行转换</li><li>最后,需要确认是否需要使用buffer缓冲来提高效率</li></ol><ul><li>inputStream 字节输入流</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">inputStreamTest</span><span class="params">()</span></span>{</span><br><span class="line"> String filePath = <span class="string">"/Users/gaoguoxing/Work/temp/attachment/20200403/65bf6a2b0dbf3049dc08e800c2ac617385bb.xml"</span>;</span><br><span class="line"> <span class="keyword">try</span> (InputStream in</span><br><span class="line"> = <span class="keyword">new</span> FileInputStream(filePath)</span><br><span class="line"> ) {</span><br><span class="line"> <span class="comment">// 获取文件IO流</span></span><br><span class="line"> <span class="keyword">byte</span>[] content = <span class="keyword">new</span> <span class="keyword">byte</span>[<span class="number">100</span>];</span><br><span class="line"> StringBuffer bf = <span class="keyword">new</span> StringBuffer();</span><br><span class="line"> <span class="comment">// 没有返回 -1</span></span><br><span class="line"> <span class="keyword">while</span> (<span class="keyword">true</span>) {</span><br><span class="line"> <span class="keyword">if</span> (in.read(content) < <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> bf.append(<span class="keyword">new</span> String(content));</span><br><span class="line"> }</span><br><span class="line"> System.out.println(bf.toString());</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>outputStream 字节输出流</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">outputStreamTest</span><span class="params">()</span></span>{</span><br><span class="line"> String content = <span class="string">"this is my content"</span>;</span><br><span class="line"> <span class="keyword">try</span> (OutputStream out = <span class="keyword">new</span> FileOutputStream(<span class="string">"copy.txt"</span>)) {</span><br><span class="line"> out.write(content.getBytes());</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>Reader 字符输入流</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">readerTest</span><span class="params">()</span></span>{</span><br><span class="line"> String filePath = <span class="string">"/Users/gaoguoxing/Work/temp/attachment/20200403/65bf6a2b0dbf3049dc08e800c2ac617385bb.xml"</span>;</span><br><span class="line"> String s;</span><br><span class="line"> <span class="keyword">try</span>(BufferedReader in = <span class="keyword">new</span> BufferedReader(<span class="keyword">new</span> FileReader(filePath))) {</span><br><span class="line"> <span class="keyword">while</span> ((s = in.readLine()) != <span class="keyword">null</span>){</span><br><span class="line"> System.out.println(s);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ul><li>Writer 字符输出流</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">writerTest</span><span class="params">()</span></span>{</span><br><span class="line"> String content = <span class="string">"this is my content"</span>;</span><br><span class="line"> <span class="keyword">try</span> (FileWriter out = <span class="keyword">new</span> FileWriter(<span class="string">"copy.txt"</span>);) {</span><br><span class="line"> out.write(content + <span class="string">"\n"</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="NIO操作"><a href="#NIO操作" class="headerlink" title="NIO操作"></a>NIO操作</h2><p>在NIO特性一章中提到了NIO是面向Buffer的,双向的数据处理形式。因为NIO分为buffer缓冲区和Channel管道,NIO使用管道操作缓冲区,可以说Channel不与数据打交道,它只负责运输数据。更可以抽象的简单理解为Channel管道为铁路,buffer缓冲区为火车(运载着货物),火车可以去,同样也可以回(双向的)。</p><h3 id="Buffer-缓冲区"><a href="#Buffer-缓冲区" class="headerlink" title="Buffer 缓冲区"></a>Buffer 缓冲区</h3><p>Buffer是具体的原始类型的容器,Buffer是一个线性的、有限的原始类型元素的集合。Buffer中有三个必要的属性:capacity、limit、position</p><ul><li>capacity:buffer中包含的元素数量</li><li>limit:buffer中的limit是缓冲区里的数据的总数</li><li>position:buffer中position是下一个即将被读写的元素</li></ul><p>每个实现子类都需要实现两种方法:get和put,两个是相对的操作,也就是理解为读写数据,每次操作时,都会从buffer中的当前position开始,增长transferred个数量的元素,这里的transferred就是get和put的元素数量。如果使用get操作,超出了limit,那么会出现 BufferUnderflowException ,相反如果使用get超出limit,就会出现 BufferOverflowException,这两种情况下,数据都不会被改变。</p><p>Buffer中提供了clear()、 filp()、 rewind()方法用来访问Buffer中的position, limit, 和capacity的值。</p><ul><li>clear() 清空读缓冲区中的内容,之后可以使用put写数据,将limit设置为capacity,将position设置为0</li><li>filp() 切换成读模式,之后可以使用 get 读数据,将limlit设置为当前position,再将position设置为0</li><li>rewind() 可重复读缓冲区的内容</li></ul><p>下面通过几个实例来看Buffer相关的方法:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 分配capacity大小</span></span><br><span class="line">ByteBuffer byteBuffer = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line"><span class="comment">// 初始时4个核心变量的值</span></span><br><span class="line">System.out.println(<span class="string">"limit:"</span>+byteBuffer.limit()); <span class="comment">// 1024</span></span><br><span class="line">System.out.println(<span class="string">"position:"</span>+byteBuffer.position()); <span class="comment">// 0</span></span><br><span class="line">System.out.println(<span class="string">"capacity:"</span>+byteBuffer.capacity()); <span class="comment">// 1024</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line"><span class="comment">//mark:java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]</span></span><br><span class="line"></span><br><span class="line">byteBuffer.put(<span class="string">"hello world"</span>.getBytes());</span><br><span class="line"><span class="comment">// 调用mark方法会返回this也就会输出当前buffer</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line"><span class="comment">// mark:java.nio.HeapByteBuffer[pos=11 lim=1024 cap=1024]</span></span><br><span class="line"><span class="comment">// 在执行put操作后,当前位置发生了变化</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 切换成读模式</span></span><br><span class="line">byteBuffer.flip();</span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line"><span class="comment">// mark:java.nio.HeapByteBuffer[pos=0 lim=11 cap=1024]</span></span><br><span class="line"><span class="comment">// 切换成读模式,limit设置为原来的当前位置,当前位置设置为0</span></span><br><span class="line"><span class="keyword">byte</span>[] bytes = <span class="keyword">new</span> <span class="keyword">byte</span>[byteBuffer.limit()];</span><br><span class="line">byteBuffer.get(bytes);</span><br><span class="line"><span class="comment">// 这样使用get读的时候,只能获取到 0~limit 之间的内容</span></span><br><span class="line">System.out.println(<span class="keyword">new</span> String(bytes));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 清空,再次可以向buffer中写数据</span></span><br><span class="line">byteBuffer.clear();</span><br><span class="line">byteBuffer.put(<span class="string">"HELLO WORLD"</span>.getBytes());</span><br><span class="line"><span class="comment">//mark:java.nio.HeapByteBuffer[pos=11 lim=1024 cap=1024]</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line"></span><br><span class="line"><span class="comment">// 切换成读模式</span></span><br><span class="line">byteBuffer.flip();</span><br><span class="line"><span class="comment">//mark:java.nio.HeapByteBuffer[pos=0 lim=11 cap=1024]</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line">bytes = <span class="keyword">new</span> <span class="keyword">byte</span>[byteBuffer.limit()];</span><br><span class="line">byteBuffer.get(bytes);</span><br><span class="line">System.out.println(<span class="keyword">new</span> String(bytes));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 重复读</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line"><span class="comment">//mark:java.nio.HeapByteBuffer[pos=11 lim=11 cap=1024]</span></span><br><span class="line"><span class="comment">// 使用get之后,pos变为了limit,在读的话会出现异常,</span></span><br><span class="line"><span class="comment">// 如果再读,只能使用rewind方法</span></span><br><span class="line">byteBuffer.rewind();</span><br><span class="line"><span class="comment">//mark:java.nio.HeapByteBuffer[pos=0 lim=11 cap=1024]</span></span><br><span class="line">System.out.println(<span class="string">"mark:"</span> + byteBuffer.mark());</span><br><span class="line">bytes = <span class="keyword">new</span> <span class="keyword">byte</span>[byteBuffer.limit()];</span><br><span class="line">byteBuffer.get(bytes);</span><br><span class="line">System.out.println(<span class="keyword">new</span> String(bytes));</span><br></pre></td></tr></table></figure></div><h3 id="Channel-管道"><a href="#Channel-管道" class="headerlink" title="Channel 管道"></a>Channel 管道</h3><p>Channel是IO操作的核心,Channel表示与一些硬件设备,文件,网络等实体的开放连接,能够进行多个不同的IO操作(读与写).</p><p>Channel有开关的状态,只要channel创建,则channel的状态就是open的,如果chennel一旦被关闭,那么如果再有后续调用channel的io操作,都会出现异常.(类似jdbc的connection),以防万一,可以调用isopen()方法检测是否被关闭了</p><p>再NIO中几个重要的Channel实现类:</p><ul><li><strong>FileChannel:</strong> 用于文件的数据读写</li><li><strong>DatagramChannel:</strong> 用于UDP的数据读写</li><li><strong>SocketChannel:</strong> 用于TCP的数据读写,一般是客户端实现</li><li><strong>ServerSocketChannel:</strong> 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现</li></ul><p>用FileChannel来演示创建和传输的过程.</p><h4 id="创建channel"><a href="#创建channel" class="headerlink" title="创建channel"></a>创建channel</h4><p>有两种方式创建channel,一种是使用file或者fileStream创建cannel,另一种使用FileChannel的静态方法创建</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// 1. 使用randomAccessFile 创建channel</span></span><br><span class="line">RandomAccessFile randomAccessFile = <span class="keyword">new</span> RandomAccessFile(<span class="string">"a.txt"</span>,<span class="string">"rw"</span>);</span><br><span class="line">FileChannel in = randomAccessFile.getChannel();</span><br><span class="line"><span class="comment">// 2. 使用静态方法创建</span></span><br><span class="line">FileChannel out = FileChannel.open(Paths.get(<span class="string">"b.txt"</span>), StandardOpenOption.WRITE);</span><br><span class="line"><span class="comment">// 3. 通过FileInputStream 创建channel</span></span><br><span class="line">FileInputStream inputStream = <span class="keyword">new</span> FileInputStream(<span class="string">"xxx.txt"</span>);</span><br><span class="line">inputStream.getChannel();</span><br></pre></td></tr></table></figure></div><h4 id="数据读写"><a href="#数据读写" class="headerlink" title="数据读写"></a>数据读写</h4><p>使用channel进行数据读写,类似普通的BIO使用buffer.但是需要注意的是,每次都需要将buffer打开读模式,再读,读完后使用clear清空buffer</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">ByteBuffer readBuffer = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line"><span class="keyword">while</span>(in.read(readBuffer) != -<span class="number">1</span>){</span><br><span class="line"> readBuffer.flip();</span><br><span class="line"> out.write(readBuffer);</span><br><span class="line"> readBuffer.clear();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>还可以直接使用通道的transferTo直接复制到另外一个通道中,完成复制</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// 使用通道的transferTo</span></span><br><span class="line">in.transferTo(<span class="number">0</span>,in.size(),out);</span><br></pre></td></tr></table></figure></div><h4 id="关闭资源"><a href="#关闭资源" class="headerlink" title="关闭资源"></a>关闭资源</h4><p>最后需要手动将channel关掉(必须)</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">in.close();</span><br><span class="line">out.close();</span><br></pre></td></tr></table></figure></div><p>另外这时候可以使用 try-with-resource 进行简化,整体过程可以参考如下代码:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> (</span><br><span class="line"> <span class="comment">// 创建资源</span></span><br><span class="line"> RandomAccessFile randomAccessFile = <span class="keyword">new</span> RandomAccessFile(<span class="string">"a.txt"</span>, <span class="string">"rw"</span>);</span><br><span class="line"> FileChannel in = randomAccessFile.getChannel();</span><br><span class="line"> FileChannel out = FileChannel.open(Paths.get(<span class="string">"b.txt"</span>), StandardOpenOption.WRITE);</span><br><span class="line">) {</span><br><span class="line"> <span class="comment">// 设置buffer</span></span><br><span class="line"> ByteBuffer readBuffer = ByteBuffer.allocate(<span class="number">1024</span>);</span><br><span class="line"> <span class="comment">// 将通道中的数据放到缓冲区</span></span><br><span class="line"> <span class="keyword">while</span> (in.read(readBuffer) != -<span class="number">1</span>) {</span><br><span class="line"> <span class="comment">// 切换成读模式</span></span><br><span class="line"> readBuffer.flip();</span><br><span class="line"> <span class="comment">// 向通道内写入buffer</span></span><br><span class="line"> out.write(readBuffer);</span><br><span class="line"> <span class="comment">// 清空本次的buffer</span></span><br><span class="line"> readBuffer.clear();</span><br><span class="line"> }</span><br><span class="line">} <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<h1 id="Java中的IO操作"><a href="#Java中的IO操作" class="headerlink" title="Java中的IO操作"></a>Java中的IO操作</h1><p>Java总的来说有三类IO,效率不高,操作简单的BIO(blocking I
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
</entry>
<entry>
<title>线程生命周期</title>
<link href="https://nanyiniu.github.io/2020/03/29/%E7%BA%BF%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
<id>https://nanyiniu.github.io/2020/03/29/线程生命周期/</id>
<published>2020-03-29T00:00:00.000Z</published>
<updated>2020-03-28T11:32:18.491Z</updated>
<content type="html"><![CDATA[<h1 id="线程生命周期"><a href="#线程生命周期" class="headerlink" title="线程生命周期"></a>线程生命周期</h1><p><img src="https://raw.githubusercontent.com/NanYinIU/PicRoom/master/img/20200328192955.png" alt="线程生命周期"></p><p>上图展示了线程从创建到结束的整个生命周期,下面从状态和控制两个方面分解图中的内容</p><h2 id="线程状态"><a href="#线程状态" class="headerlink" title="线程状态"></a>线程状态</h2><p>线程具有5中基本的状态:</p><ol><li>NEW(新建状态): 创建线程,还未启动</li><li>RUNNABLE(就绪状态): 可运行状态,但还未获得时间片,等待执行</li><li>RUNNING(执行状态): 运行状态,在就绪状态获得了时间片,进入运行状态</li><li>BLOCKED(阻塞状态):当某些情况下,线程被阻止运行,进入阻塞状态,阻塞的状态可分为三种:<ul><li>第一种为执行了<code>wait()</code>后会进入放入线程等待队列中,这种情况叫等待阻塞.</li><li>第二种为等待获取<code>synchronized()</code>同步锁时,会讲线程放入同步锁队列中,等待前一个线程执行完<code>synchronized</code>中的内容,这种情况叫同步阻塞.</li><li>第三种为执行了<code>sleep()</code>或<code>join()</code>时,和<code>wait()</code>不同,它<strong>不会释放</strong>对象锁.</li></ul></li><li>TERMINATED(终止状态):当线程正常结束或异常退出时,会到达终止状态</li></ol><h2 id="线程方法"><a href="#线程方法" class="headerlink" title="线程方法"></a>线程方法</h2><ul><li>run/start<br> 需要并行处理的代码放在<code>run()</code>方法中,<code>start()</code>方法启动线程将自动调用 <code>run()</code> 方法,这是由Java的内存机制规定的。并且<code>run()</code>方法必须是public访问权限,返回值类型为void。</li><li>wait<br> 当前线程暂停执行并释放对象锁标志,让其他线程可以进入<code>synchronized</code>数据块,当前线程被放入对象等待池中</li><li>nodify/nodifyAll<br> 唤醒等待(wait)的线程</li><li>sleep<br> 休眠一段时间后,会自动唤醒。但它并不释放对象锁。也就是如果有 <code>synchronized</code>同步块,其他线程仍然不能访问共享数据。注意该方法要捕获异常</li><li>join<br> 当前线程停下来等待,直至另一个调用join方法的线程终止,线程在被激活后不一定马上就运行,而是进入到可运行线程的队列中</li><li>yield<br> 停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么yield()方法将不会起作用</li></ul>]]></content>
<summary type="html">
<h1 id="线程生命周期"><a href="#线程生命周期" class="headerlink" title="线程生命周期"></a>线程生命周期</h1><p><img src="https://raw.githubusercontent.com/NanYinIU/P
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Thread" scheme="https://nanyiniu.github.io/tags/Thread/"/>
</entry>
<entry>
<title>Java中的位运算</title>
<link href="https://nanyiniu.github.io/2020/03/28/%E4%BD%8D%E8%BF%90%E7%AE%97/"/>
<id>https://nanyiniu.github.io/2020/03/28/位运算/</id>
<published>2020-03-28T12:00:00.000Z</published>
<updated>2020-03-28T09:57:49.363Z</updated>
<content type="html"><![CDATA[<h1 id="Java中的位运算"><a href="#Java中的位运算" class="headerlink" title="Java中的位运算"></a>Java中的位运算</h1><p>Java提供了多种位运算,包括 左移( << )、右移( >> ) 、无符号右移( >>> ) 、位与( & ) 、位或( | )、位非( ~ )、位异或( ^ ),除了 ~ 为一元操作符,其他都为二元操作符。</p><h2 id="左移和右移"><a href="#左移和右移" class="headerlink" title="左移和右移"></a>左移和右移</h2><h3 id="什么是左右移操作,产生的结果是什么"><a href="#什么是左右移操作,产生的结果是什么" class="headerlink" title="什么是左右移操作,产生的结果是什么"></a>什么是左右移操作,产生的结果是什么</h3><p>使用 符号 <code><<</code> 对数字产生的影响就是左移,同理右移。下面通过例子来看左移(右移)的结果:</p><p>在 <code>ArrayList</code> 中,使用了<code>grow()</code>方法进行List的扩容操作,其实,在<code>grow()</code>方法内部就使用到了右移操作。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">grow</span><span class="params">(<span class="keyword">int</span> minCapacity)</span> </span>{</span><br><span class="line"> <span class="comment">// overflow-conscious code</span></span><br><span class="line"> <span class="keyword">int</span> oldCapacity = elementData.length;</span><br><span class="line"> <span class="keyword">int</span> newCapacity = oldCapacity + (oldCapacity >> <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (newCapacity - minCapacity < <span class="number">0</span>)</span><br><span class="line"> newCapacity = minCapacity;</span><br><span class="line"> <span class="keyword">if</span> (newCapacity - MAX_ARRAY_SIZE > <span class="number">0</span>)</span><br><span class="line"> newCapacity = hugeCapacity(minCapacity);</span><br><span class="line"> <span class="comment">// minCapacity is usually close to size, so this is a win:</span></span><br><span class="line"> elementData = Arrays.copyOf(elementData, newCapacity);</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><p>grow() 函数的作用就是对 List 进行倍数的扩容,这个倍数就是 <code>x + x >> 1</code> ,具体产生的结果用主函数进行测试。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">System.out.println(<span class="number">10</span>+(<span class="number">10</span>>><span class="number">1</span>));</span><br></pre></td></tr></table></figure></div><p>产生的结果为15.也就是说 <code>10>>1</code>的结果为5.那么计算过程是什么样的呢,才会产生这样的结果?</p><h4 id="转化为二进制"><a href="#转化为二进制" class="headerlink" title="转化为二进制"></a>转化为二进制</h4><p>对位的运算,第一步一定是先转化为二进制,再对二进制进行计算。</p><p>例子中的 10 转化为二进制为:</p><p><code>0000 0000 0000 0000 0000 0000 0000 1010</code></p><h4 id="进行为运算"><a href="#进行为运算" class="headerlink" title="进行为运算"></a>进行为运算</h4><p>如果是左移,则在右侧补0,同理如果是右移,则在左侧补0。</p><p>比如上面的 10>>1 , 右移一位,则在左侧补一个0。</p><p>结果为 <code>...... 0101</code> 结果为 5</p><p>如果 10<<1 ,左移一位,则在右侧补一个0</p><p>结果为 <code>......1 0100</code> 结果为 20</p><h4 id="无符号左移右移"><a href="#无符号左移右移" class="headerlink" title="无符号左移右移"></a>无符号左移右移</h4><p>负数以原码的补码形式表达,如果是负数时,比如 -10 ,转化为二进制就是</p><p><code>1111 1111 1111 1111 1111 1111 1111 1011</code></p><p>也就是高位为1,与正数相反,同理,在进行移动时,也需要将原来的补0,调整为补1;</p><p>如 -10>>1,右移一位,左侧补1</p><p><code>1......1101</code> ,结果为 -5</p><p>无符号则始终补0;</p><p>综上:</p><ol><li>转化为二进制</li><li>正数反向补0</li><li>负数反向补1</li><li>无符号反向补0</li></ol><h2 id="位与(-amp-)"><a href="#位与(-amp-)" class="headerlink" title="位与(&)"></a>位与(&)</h2><p>在HashMap中进行put元素的时候,会通过 <code>putVal()</code> 方法进行实际的计算和添加。</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="function"><span class="keyword">final</span> V <span class="title">putVal</span><span class="params">(<span class="keyword">int</span> hash, K key, V value, <span class="keyword">boolean</span> onlyIfAbsent,</span></span></span><br><span class="line"><span class="function"><span class="params"> <span class="keyword">boolean</span> evict)</span> </span>{</span><br><span class="line"> Node<K,V>[] tab; Node<K,V> p; <span class="keyword">int</span> n, i;</span><br><span class="line"> <span class="keyword">if</span> ((tab = table) == <span class="keyword">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line"> n = (tab = resize()).length;</span><br><span class="line"> <span class="comment">// 使用 (n-1)&hash 查找位置是否已经被占用</span></span><br><span class="line"> <span class="keyword">if</span> ((p = tab[i = (n - <span class="number">1</span>) & hash]) == <span class="keyword">null</span>)</span><br><span class="line"> tab[i] = newNode(hash, key, value, <span class="keyword">null</span>);</span><br></pre></td></tr></table></figure></div><p>这里使用 & 的作用其实是为了保证 hash 的值不超过范围。数组具有2的幂数长度,以用&替换昂贵的取模运算。</p><p>运算规则:第一个数的的第n位和第二个数的第n位如果都是1,那么结果的第n为也为1,否则为0 简化描述规则:<strong>同一为一,否则为零</strong></p><p>5转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0101</code></p><p>3转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0011</code></p><p>结果:<code>0000 0000 0000 0000 0000 0000 0000 0001</code></p><p>转化为10进制为 1</p><p>可以作用为关闭(屏蔽)特定位的手段</p><h2 id="位或(|)"><a href="#位或(|)" class="headerlink" title="位或(|)"></a>位或(|)</h2><p>运算规则:第一个数的的第n位于第二个数的第n位 只要有一个是1,那么结果的第n为也为1,否则为0. 简化规则描述: <strong>有一则一,否则为零</strong></p><p>5转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0101</code></p><p>3转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0011</code></p><p>结果:<code>0000 0000 0000 0000 0000 0000 0000 0110</code></p><p>转化为10进制为 7</p><p>可以作为将特定位置为1的手段</p><h2 id="异或(-)"><a href="#异或(-)" class="headerlink" title="异或(^)"></a>异或(^)</h2><p>运算规则:第一个数的的第n位于第二个数的第n位 <strong>相反</strong>,那么结果的第n为也为1,否则为0,简化规则描述: <strong>相反为一,否则为零</strong></p><p>5转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0101</code></p><p>3转换为二进制:<code>0000 0000 0000 0000 0000 0000 0000 0011</code></p><p>结果:<code>0000 0000 0000 0000 0000 0000 0000 0110</code></p><p>转化为二进制为 7</p><ol><li>按位“异或”运算可以使特定的位取反</li><li>直接交换两个变量的值</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">a^=b</span><br><span class="line">b^=a</span><br><span class="line">a^=b</span><br></pre></td></tr></table></figure></div><p>这样a的值与b的值就形成了互换。</p>]]></content>
<summary type="html">
<h1 id="Java中的位运算"><a href="#Java中的位运算" class="headerlink" title="Java中的位运算"></a>Java中的位运算</h1><p>Java提供了多种位运算,包括 左移( &lt;&lt; )、右移( &gt;&gt
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
</entry>
<entry>
<title>lambda表达式学习和应用</title>
<link href="https://nanyiniu.github.io/2020/03/24/Lambda%20expression/"/>
<id>https://nanyiniu.github.io/2020/03/24/Lambda expression/</id>
<published>2020-03-23T19:45:00.000Z</published>
<updated>2020-03-23T12:26:21.529Z</updated>
<content type="html"><![CDATA[<h1 id="Lambda表达式"><a href="#Lambda表达式" class="headerlink" title="Lambda表达式"></a>Lambda表达式</h1><p>Lamdba 表达式是Java 8 的新特性,也是Java 8 中最重要的的新功能。Lamdaba表达式促进了Java使用函数方式进行编程。</p><p>在进行语法的讲解之前,需要了解和Lambda息息相关的<code>Function Interface</code>,可以说只有函数式接口的存在,才有lambda表达式的存在,换句话可以说lambda表达式是函数式接口的实现.</p><h2 id="函数式接口"><a href="#函数式接口" class="headerlink" title="函数式接口"></a>函数式接口</h2><p>Function 接口具有单个功能,也就是有单个抽象方法.如 <code>Comparable</code> 接口只有一个方法 <code>compareTo</code> 用来进行比较。</p><p>Java8定义了函数接口被用来拓展Lamdba表达式。在 <code>java.util.Function</code> 包下有非常多的函数接口。</p><p>常用的接口包括;</p><table><thead><tr><th>接口</th><th>抽象方法</th><th>用法</th></tr></thead><tbody><tr><td>Function<T,R></td><td>R apply(T t);</td><td>接受T类型参数,返回R类型结果</td></tr><tr><td>Consumer<T></td><td>void accept(T t);</td><td>接受T类型参数,不返回结果</td></tr><tr><td>Predicate<T></td><td>boolean test(T t);</td><td>接受T类型参数,返回Boolean结果</td></tr><tr><td>Suppler<T></td><td>T get();</td><td>无参数,返回T类型结果</td></tr></tbody></table><h3 id="定义函数接口"><a href="#定义函数接口" class="headerlink" title="定义函数接口"></a>定义函数接口</h3><ol><li>使用 <code>@FunctionalInterface</code> 注解,该注解不是必须的,但是如果标注,则编译器会检查这个接口下是否只有单个抽象方法,如果不是会报错.</li><li>新增default方法,是为了在现有的类库中中新增功能而不影响他们的实现类</li><li>新增接口内static方法,可定义一个或者多个静态方法,和普通的静态方法没有区别,都是<code>接口名.方法名</code>进行调用</li><li>在函数式接口的定义中是只允许有且只有一个抽象方法(必须),但是可以有多个static方法和default方法。</li></ol><h2 id="Lambda语法"><a href="#Lambda语法" class="headerlink" title="Lambda语法"></a>Lambda语法</h2><p>一个Lambda表达式有如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">paramter -> expression body</span><br></pre></td></tr></table></figure></div><p>以下是lambda表达式的几个特性</p><ul><li><strong>可选的类型描述</strong> - 不需要声明参数的类型,编译器可根据参数的值进行推断</li><li><strong>可选的参数两旁的括号</strong> - 声明单个参数的时候不需要使用括号,但是如何声明多个参数,括号还是必须的。</li><li><strong>可选的大括号</strong> - 如果函数体中只有单行,则不需要在函数体两侧添加大括号</li><li><strong>可选的返回值</strong> - 如果函数中只有单行,编译器自动返回这个单行的返回值</li></ul><p>下面根据实例来看上面的四个特性:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Java8Tester</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String args[])</span> </span>{</span><br><span class="line"> GreetingService greetService1 = message -> System.out.println(<span class="string">"Say:"</span>+message);</span><br><span class="line"> greetService1.sayMessage(<span class="string">"Hello!!"</span>);</span><br><span class="line"> MathOperation add = (<span class="keyword">int</span> a,<span class="keyword">int</span> b) -> a+b;</span><br><span class="line"> MathOperation sub= (a,b) -> a-b;</span><br><span class="line"> MathOperation muti = (a,b) -> {<span class="keyword">return</span> a*b;};</span><br><span class="line"> System.out.println(<span class="string">"add 函数结果:"</span>+add.operation(<span class="number">2</span>,<span class="number">2</span>));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">interface</span> <span class="title">GreetingService</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">void</span> <span class="title">sayMessage</span><span class="params">(String message)</span></span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="class"><span class="keyword">interface</span> <span class="title">MathOperation</span></span>{</span><br><span class="line"> <span class="function"><span class="keyword">int</span> <span class="title">operation</span><span class="params">(<span class="keyword">int</span> a,<span class="keyword">int</span> b)</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">Say:Hello!!</span><br><span class="line">add 函数结果:<span class="number">4</span></span><br></pre></td></tr></table></figure></div><p>通过上面的例子,可以得出以下重要的两点:</p><ul><li>Lambda 表达式主要用来定义功能接口的内联实现,如:一个接口里只有一个接口方法。在上面的例子里面我们使用了对MathOperation接口进行了多种实现。</li><li>Lambda 表达式消除了对匿名类的需求,如<code>new Thread(new Runnable{..})</code> ,为Java提供了非常简单而强大的函数编程能力。</li></ul><h2 id="使用方法引用"><a href="#使用方法引用" class="headerlink" title="使用方法引用"></a>使用方法引用</h2><p>方法引用(Method Refrences)帮助通过方法名称指向方法。方法引用使用符号“::”表示。方法引用可以用爱指向如下类型的方法:</p><ul><li>静态方法</li><li>实例方法</li><li>构造方法使用new关键字(TreeSet::new)</li></ul><p>[方法引用]的格式是 类名::方法名</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Arrays.stream(objectArray).forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><h2 id="使用Lambda"><a href="#使用Lambda" class="headerlink" title="使用Lambda"></a>使用Lambda</h2><blockquote><p>熟能生巧,常常练习肯定能记住</p></blockquote><h3 id="1-创建Thread"><a href="#1-创建Thread" class="headerlink" title="1. 创建Thread"></a>1. 创建Thread</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">// 不使用Lambda创建Thread</span></span><br><span class="line"><span class="comment">// 可以看出内部直接创建了Runnable的匿名类</span></span><br><span class="line"><span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() {</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">void</span> <span class="title">run</span><span class="params">()</span> </span>{</span><br><span class="line"> System.out.println(<span class="string">"新建线程。。"</span>);</span><br><span class="line"> }</span><br><span class="line">}).start();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用lambda创建Thread</span></span><br><span class="line"><span class="keyword">new</span> Thread(() -> System.out.println(<span class="string">"使用Lambda新建线程。。"</span>)).start();</span><br></pre></td></tr></table></figure></div><h3 id="2-列表迭代"><a href="#2-列表迭代" class="headerlink" title="2. 列表迭代"></a>2. 列表迭代</h3><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">List<Integer> list = Arrays.asList(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>);</span><br><span class="line"><span class="comment">// 不使用Lambda</span></span><br><span class="line"><span class="keyword">for</span> (Integer integer : list) {</span><br><span class="line"> System.out.println(integer);</span><br><span class="line">}</span><br><span class="line"><span class="comment">//使用Lambda</span></span><br><span class="line">list.forEach((s) -> System.out.println(s));</span><br><span class="line"><span class="comment">//方法引用</span></span><br><span class="line">list.forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><h3 id="3-Map-amp-Reduce"><a href="#3-Map-amp-Reduce" class="headerlink" title="3. Map&Reduce"></a>3. Map&Reduce</h3><ul><li>使用Map处理列表内所有元素全部加2</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">// 不使用Lambda</span></span><br><span class="line"><span class="keyword">for</span> (Integer integer : list) {</span><br><span class="line"> <span class="keyword">int</span> order = integer + <span class="number">2</span>;</span><br><span class="line"> System.out.println(order);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 使用Lambda</span></span><br><span class="line">list.stream().map((a) -> a + <span class="number">2</span>).forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><ul><li>使用 Map + collect 完成对每个元素计算后返回新的结果列表</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line">List<Integer> newList = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不使用Lambda</span></span><br><span class="line"><span class="keyword">for</span> (Integer integer : list) {</span><br><span class="line"> <span class="keyword">int</span> order = integer + <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">int</span> result = order / integer;</span><br><span class="line"> newList.add(result);</span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> (Integer integer : newList) {</span><br><span class="line"> System.out.println(integer);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 使用Lambda</span></span><br><span class="line">newList = list.stream().map((a) -> (a + <span class="number">2</span>)/a).collect(Collectors.toList());</span><br><span class="line">newList.forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><h3 id="4-使用filter实现元素的过滤"><a href="#4-使用filter实现元素的过滤" class="headerlink" title="4. 使用filter实现元素的过滤"></a>4. 使用filter实现元素的过滤</h3><p>使用lambda,可直接使用filter进行元素的过滤</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// 不使用lambda时</span></span><br><span class="line">List<Integer> list_1 = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"><span class="keyword">for</span> (Integer integer : list) {</span><br><span class="line"> <span class="keyword">if</span>(integer != <span class="number">3</span>){</span><br><span class="line"> list_1.add(integer);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用 filter 进行过滤</span></span><br><span class="line">List<Integer> list_2 = list.stream().filter((a) -> a!=<span class="number">3</span>).collect(Collectors.toList());</span><br><span class="line">list_2.forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><h3 id="5-执行函数"><a href="#5-执行函数" class="headerlink" title="5. 执行函数"></a>5. 执行函数</h3><p>稍复杂的字符串处理,用lambda合适不过了,下面的例子,只将特定字符的字符串进行 toUpperCase</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">List<String> s = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line">s.add(<span class="string">"hello "</span>);</span><br><span class="line">s.add(<span class="string">"world "</span>);</span><br><span class="line">s.add(<span class="string">"!! "</span>);</span><br><span class="line"></span><br><span class="line">List<String> s_1 = s.stream()</span><br><span class="line"> .filter(a -> a.startsWith(<span class="string">"h"</span>) || a.startsWith(<span class="string">"w"</span>))</span><br><span class="line"> .map(String::toUpperCase)</span><br><span class="line"> .collect(Collectors.toList());</span><br><span class="line">s_1.forEach(System.out::println);</span><br></pre></td></tr></table></figure></div><h3 id="6-算数运算"><a href="#6-算数运算" class="headerlink" title="6. 算数运算"></a>6. 算数运算</h3><p>支持使用min、max等直接实现Comparator进行运算,如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Optional<Integer> min = list.stream().min(Integer::compareTo);</span><br></pre></td></tr></table></figure></div><p>也支持使用 IntSummaryStatistics 状态,如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">IntSummaryStatistics intSummaryStatistics = list.stream().mapToInt(x -> x).summaryStatistics();</span><br><span class="line">System.out.println(<span class="string">"最大值:"</span>+intSummaryStatistics.getMax());</span><br><span class="line">System.out.println(<span class="string">"最小值:"</span>+intSummaryStatistics.getMin());</span><br><span class="line">System.out.println(<span class="string">"平均值:"</span>+intSummaryStatistics.getAverage());</span><br><span class="line">System.out.println(<span class="string">"数量:"</span>+intSummaryStatistics.getCount());</span><br></pre></td></tr></table></figure></div><h3 id="7-自定义方法使用Function-Inteface实现复杂功能"><a href="#7-自定义方法使用Function-Inteface实现复杂功能" class="headerlink" title="7. 自定义方法使用Function Inteface实现复杂功能"></a>7. 自定义方法使用Function Inteface实现复杂功能</h3><p>Java 8 提供了三个常用的函数接口包括;</p><ul><li>Function<T,R></li><li>Predicate<T> 判断</li><li>Consumer<T> 消费</li></ul><p>通过定义方法使用这三类接口,能完成更复杂的调用逻辑,在这里仅仅举一个小例子🌰</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function">List<Integer> <span class="title">functionMethods2</span><span class="params">(List<Integer> a, Function<List<Integer>, List<Integer>> function)</span> </span>{</span><br><span class="line"> a = a.stream()</span><br><span class="line"> .filter(demoFilter(<span class="number">2</span>))</span><br><span class="line"> .collect(Collectors.toList());</span><br><span class="line"> <span class="keyword">return</span> function.apply(a);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function">Predicate<Integer> <span class="title">demoFilter</span><span class="params">(Integer number)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> (a) -> !a.equals(number);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 客户端调用</span></span><br><span class="line">JavaTester javaTester = <span class="keyword">new</span> JavaTester();</span><br><span class="line">List<Integer> resList = javaTester.functionMethods2(list, a -> {</span><br><span class="line"> a.add(<span class="number">2</span>);</span><br><span class="line"> <span class="keyword">return</span> a;</span><br><span class="line"> });</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<h1 id="Lambda表达式"><a href="#Lambda表达式" class="headerlink" title="Lambda表达式"></a>Lambda表达式</h1><p>Lamdba 表达式是Java 8 的新特性,也是Java 8 中最重要的的新功能。
</summary>
<category term="Java" scheme="https://nanyiniu.github.io/categories/Java/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="基础" scheme="https://nanyiniu.github.io/tags/%E5%9F%BA%E7%A1%80/"/>
</entry>
<entry>
<title>Mysql中的INSTR和FIND_IN_SET函数应用</title>
<link href="https://nanyiniu.github.io/2020/03/09/INSTR%E5%92%8CFIND_IN_SET%E5%87%BD%E6%95%B0/"/>
<id>https://nanyiniu.github.io/2020/03/09/INSTR和FIND_IN_SET函数/</id>
<published>2020-03-09T12:00:00.000Z</published>
<updated>2020-03-09T01:11:36.728Z</updated>
<content type="html"><![CDATA[<h1 id="INSTR函数"><a href="#INSTR函数" class="headerlink" title="INSTR函数"></a>INSTR函数</h1><p>The INSTR() function returns the position of the first occurrence of a string in another string.</p><p>This function performs a case-insensitive search.</p><p>能够找到第一个匹配到位置,类似java中的firstIndexOf,并且是大小写不敏感的搜索</p><h2 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h2><p>首先:了解到 INSTR 函数的基本功能时能够 <strong>返回字符串在某一个字段的内容中的位置, 没有找到字符串返回0,否则返回位置(从1开始)</strong> .比如:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SQL"><figure class="iseeu highlight /sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 结果为10</span></span><br><span class="line"><span class="keyword">select</span> <span class="keyword">INSTR</span>(<span class="string">"this is mysql"</span>,<span class="string">"y"</span>) </span><br><span class="line"><span class="comment">-- 结果为0</span></span><br><span class="line"><span class="keyword">select</span> <span class="keyword">INSTR</span>(<span class="string">"this is mysql"</span>,<span class="string">"a"</span>)</span><br></pre></td></tr></table></figure></div><p>2如果instr当作查询条件,就能起到<strong>类似in的作用</strong></p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SQL"><figure class="iseeu highlight /sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 返回用户id为12的数据,同样的,也会返回id为 1,2的数据</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> <span class="keyword">user</span> u <span class="keyword">where</span> <span class="keyword">INSTR</span>(<span class="string">'12'</span>,u.id);</span><br></pre></td></tr></table></figure></div><p>更复杂的能够查询数据为字符串,并且用特定符号分隔的特定的数据</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SQL"><figure class="iseeu highlight /sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> (</span><br><span class="line"><span class="keyword">select</span> <span class="keyword">GROUP_CONCAT</span>(u.username) <span class="keyword">as</span> <span class="keyword">name</span> </span><br><span class="line"><span class="keyword">from</span> <span class="string">`user`</span> u </span><br><span class="line"><span class="keyword">where</span> <span class="keyword">INSTR</span>(<span class="keyword">concat</span>(<span class="string">','</span>,(p.leaderIds),<span class="string">','</span>),<span class="keyword">concat</span>(<span class="string">','</span>,(u.id),<span class="string">','</span>))</span><br><span class="line">)</span><br><span class="line"><span class="keyword">from</span> product p </span><br><span class="line"><span class="keyword">WHERE</span> p.product_uid =<span class="string">'02124ff31eac4c14934b045358b10cca'</span></span><br></pre></td></tr></table></figure></div><h1 id="FIND-IN-SET-函数"><a href="#FIND-IN-SET-函数" class="headerlink" title="FIND_IN_SET 函数"></a>FIND_IN_SET 函数</h1><p>The FIND_IN_SET() function returns the position of a string within a list of strings.</p><p>能够返回list中的字符串位置(使用逗号分隔)</p><p>基础的就不多说,直接举例</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="SQL"><figure class="iseeu highlight /sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 会只返回id为12,13的数据</span></span><br><span class="line"><span class="keyword">select</span> * <span class="keyword">from</span> <span class="keyword">user</span> u <span class="keyword">where</span> FIND_IN_SET(u.id,<span class="string">'12,13'</span>)</span><br></pre></td></tr></table></figure></div><p>find_in_set 和 instr 函数做例子上的对比时,发现instr是模糊匹配,只要符合都会查出来,而find_in_set是针对使用逗号分隔的对instr的进一步处理,只有出现在逗号之间的进行精确匹配.</p>]]></content>
<summary type="html">
<h1 id="INSTR函数"><a href="#INSTR函数" class="headerlink" title="INSTR函数"></a>INSTR函数</h1><p>The INSTR() function returns the position of the f
</summary>
<category term="MYSQL" scheme="https://nanyiniu.github.io/categories/MYSQL/"/>
<category term="MYSQL" scheme="https://nanyiniu.github.io/tags/MYSQL/"/>
</entry>
<entry>
<title>SpringBoot整合JPA使用</title>
<link href="https://nanyiniu.github.io/2020/03/05/SpringBoot%E6%95%B4%E5%90%88JPA%E4%BD%BF%E7%94%A8/"/>
<id>https://nanyiniu.github.io/2020/03/05/SpringBoot整合JPA使用/</id>
<published>2020-03-05T12:00:00.000Z</published>
<updated>2020-03-05T13:25:27.008Z</updated>
<content type="html"><![CDATA[<h1 id="SpringBoot整合JPA使用"><a href="#SpringBoot整合JPA使用" class="headerlink" title="SpringBoot整合JPA使用"></a>SpringBoot整合JPA使用</h1><h2 id="整合JPA"><a href="#整合JPA" class="headerlink" title="整合JPA"></a>整合JPA</h2><p>SpringBoot整合JPA十分方便,在Pom中添加如下:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="XML"><figure class="iseeu highlight /xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>org.springframework.boot<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>spring-boot-starter-data-jpa<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="comment"><!-- 数据库配置,使用druid --></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.alibaba<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>druid-spring-boot-starter<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.1.16<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"><span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>mysql<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>mysql-connector-java<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>5.1.47<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"><span class="tag"></<span class="name">dependency</span>></span></span><br></pre></td></tr></table></figure></div><h3 id="JPA基本配置"><a href="#JPA基本配置" class="headerlink" title="JPA基本配置"></a>JPA基本配置</h3><p>在application.properties 或者 application.yaml 文件中配置数据库链接和JPA的基本配置(本例使用yaml文件):</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="YAML"><figure class="iseeu highlight /yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">spring:</span></span><br><span class="line"><span class="attr"> datasource:</span></span><br><span class="line"><span class="attr"> driver-class-name:</span> <span class="string">com.mysql.jdbc.Driver</span></span><br><span class="line"><span class="attr"> druid:</span></span><br><span class="line"><span class="attr"> initial-size:</span> <span class="number">5</span></span><br><span class="line"><span class="attr"> max-active:</span> <span class="number">20</span></span><br><span class="line"><span class="attr"> max-wait:</span> <span class="number">60000</span></span><br><span class="line"><span class="attr"> stat-view-servlet:</span></span><br><span class="line"><span class="attr"> login-password:</span> <span class="string">admin</span></span><br><span class="line"><span class="attr"> login-username:</span> <span class="string">admin</span></span><br><span class="line"><span class="attr"> password:</span> <span class="number">123456</span></span><br><span class="line"><span class="attr"> url:</span> <span class="attr">jdbc:mysql://127.0.0.1:3306/web?useUnicode=true&characterEncoding=UTF-8&useSSL=false</span></span><br><span class="line"><span class="attr"> username:</span> <span class="string">root</span></span><br><span class="line"><span class="attr"> jpa:</span></span><br><span class="line"> <span class="comment"># 配置创建表使用的SQLDialect</span></span><br><span class="line"><span class="attr"> database-platform:</span> <span class="string">org.hibernate.dialect.MySQL5InnoDBDialect</span></span><br><span class="line"><span class="attr"> show-sql:</span> <span class="literal">true</span></span><br><span class="line"> <span class="comment"># 自动更新数据库表</span></span><br><span class="line"><span class="attr"> hibernate:</span></span><br><span class="line"><span class="attr"> ddl-auto:</span> <span class="string">update</span></span><br><span class="line"><span class="attr"> properties:</span></span><br><span class="line"><span class="attr"> hibernate:</span></span><br><span class="line"> <span class="comment"># 使用@Query时SQL的dialect方言</span></span><br><span class="line"><span class="attr"> dialect:</span> <span class="string">org.hibernate.dialect.MySQL5InnoDBDialect</span></span><br></pre></td></tr></table></figure></div><h2 id="使用JPA"><a href="#使用JPA" class="headerlink" title="使用JPA"></a>使用JPA</h2><p>在SpringBoot中使用JPA十分简单,只要引用JPA的starter即可</p><h3 id="实体类中属性"><a href="#实体类中属性" class="headerlink" title="实体类中属性"></a>实体类中属性</h3><p>使用Spring Data JPA,与Mybatis不同,更多的是面向对象查询.所以构造对象实体类就非常重要,如果配置了自动更新表结构,就需要注意如何在实体中使用特定的注解完成对实体的配置.</p><h4 id="1-Entity"><a href="#1-Entity" class="headerlink" title="1. @Entity"></a>1. @Entity</h4><p>使用 <code>@Entity</code> 注解在类上,用来表示该类为一个实体类</p><h4 id="2-Table"><a href="#2-Table" class="headerlink" title="2. @Table"></a>2. @Table</h4><p>使用 @Table 注解标注在类上, 可以指定使用 @Entity 注解标注的类所生成的数据库表信息,如表名称等.</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Entity</span> <span class="comment">// 指定这个类为实体类</span></span><br><span class="line"><span class="meta">@Table</span>(name=<span class="string">"user"</span>) <span class="comment">// 指定@Entity标注的主表</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">User</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>{</span><br><span class="line"><span class="comment">// ...</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="3-Id-Column"><a href="#3-Id-Column" class="headerlink" title="3. @Id,@Column"></a>3. @Id,@Column</h4><p>使用 @Id 注解标注在变量或者set方法上,用来标注该变量为主键ID.</p><p>使用 @Column 注解同样标注在变量上,能够设置生成表的各种详细信息,如字段名称,字段长度等等…</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@Column</span>(columnDefinition = <span class="string">"INT(11)"</span>)</span><br><span class="line"><span class="meta">@GeneratedValue</span>(strategy=GenerationType.IDENTITY)</span><br><span class="line"><span class="keyword">private</span> Integer id;</span><br><span class="line"></span><br><span class="line"><span class="meta">@Column</span>(name = <span class="string">"is_deleted"</span>,columnDefinition = <span class="string">"TINYINT(1)"</span>,nullable =</span><br><span class="line"><span class="keyword">false</span>)</span><br><span class="line"><span class="keyword">private</span> Short isDeleted=<span class="number">0</span>;</span><br></pre></td></tr></table></figure></div><h4 id="4-GeneratedValue"><a href="#4-GeneratedValue" class="headerlink" title="4. @GeneratedValue"></a>4. @GeneratedValue</h4><p>使用 @GeneratedValue 注解标注在 和 @Id 相同的位置,用来表示主键ID的生成策略.该注解有两个属性,第一个是常用到的 strategy 即生成策略,第二个是不常用到的generator指定生成器.</p><h5 id="strategy"><a href="#strategy" class="headerlink" title="strategy"></a>strategy</h5><p>GenerationType 包含四种策略:</p><ul><li>TABLE,使用特定的表来存储生成的主键,也就是说会自动生成一种记录主键的表,一般会包含两个字段,第一个字段是字段生成策略的名称,第二个是ID的最大序列值.通常会和 @TableGenerator 一起来使用,能够指定特定表来生成主键,如果不指定,会默认生成一个名称为 sequence 的表来记录.</li><li>SEQUENCE,在特定的数据库,如ORACLE,不支持自增主键,但是会提供一种叫做序列的方式生成主键,此时就需要指定 SEQUENCE 为生成主键的策略,和TABLE相似,通常会使用 @SequenceGenerator 一起使用</li><li>IDENTITY ,遇到向MYSQL能够让主键自增的数据库,就可以指定生成策略为IDENTITY,生成表后, Mysql会默认将该字段设置为 ‘auto_increment’,</li><li>AUTO ,使用 @GeneratedValue 默认的生成策略,把具体生成主键的规则交给持久化引擎来实现,也是我使用最多的一种方式.</li></ul><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用 GenerationType.TABLE 指定生成的记录表,其中name必须,其他可选</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue</span>(strategy = GenerationType.TABLE, generator = <span class="string">"roleSeq"</span>)</span><br><span class="line"><span class="meta">@TableGenerator</span>(name = <span class="string">"roleSeq"</span>, allocationSize = <span class="number">1</span>, table = <span class="string">"seq_table"</span>, pkColumnName = <span class="string">"seq_id"</span>, valueColumnName = <span class="string">"seq_count"</span>)</span><br><span class="line"><span class="keyword">private</span> Integer id;</span><br><span class="line"><span class="comment">// 使用 SEQUENCE </span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue</span>(strategy = GenerationType.SEQUENCE, generator = <span class="string">"menuSeq"</span>)</span><br><span class="line"><span class="meta">@SequenceGenerator</span>(name = <span class="string">"menuSeq"</span>, initialValue = <span class="number">1</span>, allocationSize = <span class="number">1</span>, sequenceName = <span class="string">"MENU_SEQUENCE"</span>)</span><br><span class="line"><span class="keyword">private</span> Integer id;</span><br><span class="line"><span class="comment">// 使用 IDENTITY 进行自增</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue</span>(strategy = GenerationType.IDENTITY)</span><br><span class="line"><span class="keyword">private</span> Integer id;</span><br><span class="line"><span class="comment">// 使用AUTO,或者不指定策略</span></span><br><span class="line"><span class="meta">@Id</span></span><br><span class="line"><span class="meta">@GeneratedValue</span>(strategy = GenerationType.AUTO)</span><br><span class="line"><span class="keyword">private</span> Integer id</span><br></pre></td></tr></table></figure></div><h5 id="generator"><a href="#generator" class="headerlink" title="generator"></a>generator</h5><p>通过上面对策略的描述,也就明白generator的作用,就是指定特定name的Generator</p><h4 id="5-Transient"><a href="#5-Transient" class="headerlink" title="5. @Transient"></a>5. @Transient</h4><p>使用 @Transient 注解,标注在字段上,在JPA生成表字段时能够忽略该字段,让字段不出现在数据库中.</p><h4 id="6-Embedded和-Embeddable"><a href="#6-Embedded和-Embeddable" class="headerlink" title="6. @Embedded和@Embeddable"></a>6. @Embedded和@Embeddable</h4><p>当实体A出现在实体B中,但实体A不需要单独生成一张表的使用,使用@@Embedded和@Embeddable标注在类上,能够防止实体A生成数据库表.</p><h4 id="7-Temporal"><a href="#7-Temporal" class="headerlink" title="7. @Temporal"></a>7. @Temporal</h4><p>使用 @Temporal 注解特殊标记在需要持久化的字段上,并且字段类型为 <code>java.util.Date</code> 或 <code>java.util.Calendar</code>的时候,可指定 TemporalType 来生成对应数据库中的时间格式(java.sql.xxxx). </p><h4 id="8-一对一-一对多-多对多关系"><a href="#8-一对一-一对多-多对多关系" class="headerlink" title="8. 一对一,一对多,多对多关系"></a>8. 一对一,一对多,多对多关系</h4><p>在使用关系型数据库中,免不了上述关系的生成,下面就来说一下,如何针对一对一关系、一对多关系、多对多关系的通用设置,在分别实验前,先了解一下 @JoinColumn 的作用</p><p>我认为的JoinColumn是特殊的@Column,当需要使用到“关系”时,需要使用到JoinColumn来替代普通的Column注解</p><h5 id="OneToOne"><a href="#OneToOne" class="headerlink" title="OneToOne"></a>OneToOne</h5><p>使用OneToOne注解,指定两方关系是一对一的.比如User和Status,Status是User的一个属性,在User表中只需记录Stauts表中的主键,所以需要在User表(主)中声明一个Status(从)即可:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@OneToOne</span>()</span><br><span class="line"><span class="meta">@JoinColumn</span>(columnDefinition = <span class="string">"INT(11)"</span>,name = <span class="string">"status_id"</span>)</span><br><span class="line"><span class="keyword">private</span> Status status;</span><br></pre></td></tr></table></figure></div><h5 id="OneToMany、ManyToOne"><a href="#OneToMany、ManyToOne" class="headerlink" title="OneToMany、ManyToOne"></a>OneToMany、ManyToOne</h5><p>使用 @OneToMany 或 @ManyToOne 注解来表示一对多、多对一的关系.典型的如Project之于Task,一个Project包含多个Task,所以Project为One,Task为Many.</p><p>如果在Project(主)中声明Task(从)时,可以使用 OneToMany 表示一对多关系</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@OneToMany</span>(cascade = CascadeType.ALL)</span><br><span class="line"><span class="meta">@JoinColumn</span>(name=<span class="string">"project_id"</span>) <span class="comment">// task表指向project表的外键</span></span><br><span class="line">Set<Task> tasks;</span><br></pre></td></tr></table></figure></div><p>也可以在多的一方使用 ManyToOne 注解,表示多对一的关系</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">@ManyToOne</span></span><br><span class="line"><span class="comment">// 在 one 的一方生成 name为 project_id 的字段,指向task</span></span><br><span class="line"><span class="meta">@JoinColumn</span>(name=<span class="string">"project_id"</span>) </span><br><span class="line">Project project;</span><br></pre></td></tr></table></figure></div><p>以上是单独使用 @OneToMany 或 @ManyToOne 构造单向的关系,但是这样有缺点,注定有一方找不到另外一方(只建立了单向关系).所以稳妥的方式是构建双向关系,在One和Many双方,使用两个注解相互指定</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// Project中,其中mappedBy是双向关系中必须要使用的属性</span></span><br><span class="line"><span class="meta">@OneToMany</span>(cascade = CascadeType.ALL,mappedBy =<span class="string">"project_id"</span>)</span><br><span class="line">Set<Task> tasks;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Task中</span></span><br><span class="line"><span class="meta">@ManyToOne</span></span><br><span class="line"><span class="meta">@JoinColumn</span>(name=<span class="string">"project_id"</span>)</span><br><span class="line">Project project</span><br></pre></td></tr></table></figure></div><h5 id="ManyToMany"><a href="#ManyToMany" class="headerlink" title="ManyToMany"></a>ManyToMany</h5><p>使用 @ManyToMany 注解来标注多对多关系,比如用户和角色之间就可以理解为多对多的关系,双方需要互相拥有多个.多地多关系需要使用中间表来维护两方关系.建立关系表使用@JoinTable注解来指定实现.</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// User表中</span></span><br><span class="line"><span class="meta">@JSONField</span>(name = <span class="string">"roles"</span>)</span><br><span class="line"><span class="meta">@ManyToMany</span>(mappedBy = <span class="string">"users"</span>,fetch = FetchType.EAGER,cascade = CascadeType.ALL)</span><br><span class="line"><span class="keyword">private</span> Set<Role> roles;</span><br><span class="line"></span><br><span class="line"><span class="comment">// Role 表中</span></span><br><span class="line"><span class="meta">@JSONField</span>(serialize = <span class="keyword">false</span>)</span><br><span class="line"><span class="meta">@ManyToMany</span>(cascade = CascadeType.ALL,fetch = FetchType.EAGER)</span><br><span class="line"><span class="meta">@JoinTable</span>(name = <span class="string">"r_user_role"</span>,</span><br><span class="line"> joinColumns = {<span class="meta">@JoinColumn</span>(name = <span class="string">"role_id"</span>)},</span><br><span class="line"> inverseJoinColumns = {<span class="meta">@JoinColumn</span>(name = <span class="string">"user_id"</span>)})</span><br><span class="line"><span class="keyword">private</span> List<User> users;</span><br></pre></td></tr></table></figure></div><h5 id="关系使用时的坑"><a href="#关系使用时的坑" class="headerlink" title="关系使用时的坑"></a>关系使用时的坑</h5><ol><li>双向关系需要双方都进行维护,否则保存不上 :)</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">User user = one.get();</span><br><span class="line"><span class="comment">// 多对多关系需要双向关联保存才起作用</span></span><br><span class="line">user.getRoles().add(role);</span><br><span class="line">role.getUsers().add(user);</span><br></pre></td></tr></table></figure></div><ol start="2"><li>使用JSON时,如果出现循环引用导出溢出时,在一方加入设置防止序列化</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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">// 我使用的是FastJosn,Jackson有不同的实现方式</span></span><br><span class="line"><span class="meta">@JSONField</span>(serialize = <span class="keyword">false</span>)</span><br><span class="line"><span class="meta">@ManyToMany</span>(cascade = CascadeType.ALL,fetch = FetchType.EAGER)</span><br><span class="line"><span class="meta">@JoinTable</span>(name = <span class="string">"r_user_routemeta"</span>,</span><br><span class="line"> joinColumns = {<span class="meta">@JoinColumn</span>(name = <span class="string">"role_id"</span>)},</span><br><span class="line"> inverseJoinColumns = {<span class="meta">@JoinColumn</span>(name = <span class="string">"meta_id"</span>)})</span><br><span class="line"><span class="keyword">private</span> Set<RouteMeta> routeMeta;</span><br></pre></td></tr></table></figure></div><h3 id="使用-Repository-查询"><a href="#使用-Repository-查询" class="headerlink" title="使用 Repository 查询"></a>使用 Repository 查询</h3><p>Repository 是 Spring Data JPA 中最重要的接口,使用实体类和实体类中的主键ID作为类型参数.其中CURDRepository为实体类提供了复杂增删改查的函数.</p><h4 id="1-CrudRepository接口"><a href="#1-CrudRepository接口" class="headerlink" title="1. CrudRepository接口"></a>1. CrudRepository接口</h4><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu highlight /java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">CrudRepository</span><<span class="title">T</span>, <span class="title">ID</span>> <span class="keyword">extends</span> <span class="title">Repository</span><<span class="title">T</span>, <span class="title">ID</span>> </span>{}</span><br></pre></td></tr></table></figure></div><p>同样提供了特定的Repository,比如JPARepository、MongoRepository,这几个都是继承的 CrudRepository 接口,根据不同的技术特性暴露不同的接口方法.一般使用上直接继承CurdRepository就可以完成基本的工作.</p><h4 id="2-PagingAndSortingRepository接口"><a href="#2-PagingAndSortingRepository接口" class="headerlink" title="2. PagingAndSortingRepository接口"></a>2. PagingAndSortingRepository接口</h4><p>如果使用分页或者排序需要继承 PagingAndSortingRepository 接口,这个接口中包含 <code>Iterable<T> findAll(Sort sort);</code> 和 <code>Page<T> findAll(Pageable pageable);</code>,这样能够具有查询时分页的特性</p><h4 id="3-接口中的名称推导查询"><a href="#3-接口中的名称推导查询" class="headerlink" title="3. 接口中的名称推导查询"></a>3. 接口中的名称推导查询</h4><ul><li>通过命名查询,具体可查看相关文档,写的非常全 <a href="https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.query-methods.query-creation" target="_blank" rel="noopener">create-query</a></li></ul><table><thead><tr><th>关键字</th><th>方法命名</th><th>sql where字句</th></tr></thead><tbody><tr><td>And</td><td>findByNameAndPwd</td><td>where name= ? and pwd =?</td></tr><tr><td>Or</td><td>findByNameOrSex</td><td>where name= ? or sex=?</td></tr><tr><td>Is,Equals</td><td>findById,findByIdEquals</td><td>where id= ?</td></tr><tr><td>Between</td><td>findByIdBetween</td><td>where id between ? and ?</td></tr><tr><td>LessThan</td><td>findByIdLessThan</td><td>where id < ?</td></tr><tr><td>LessThanEquals</td><td>findByIdLessThanEquals</td><td>where id <= ?</td></tr><tr><td>GreaterThan</td><td>findByIdGreaterThan</td><td>where id > ?</td></tr><tr><td>GreaterThanEquals</td><td>findByIdGreaterThanEquals</td><td>where id > = ?</td></tr><tr><td>After</td><td>findByIdAfter</td><td>where id > ?</td></tr><tr><td>Before</td><td>findByIdBefore</td><td>where id < ?</td></tr><tr><td>IsNull</td><td>findByNameIsNull</td><td>where name is null</td></tr><tr><td>isNotNull,NotNull</td><td>findByNameNotNull</td><td>where name is not null</td></tr><tr><td>Like</td><td>findByNameLike</td><td>where name like ?</td></tr><tr><td>NotLike</td><td>findByNameNotLike</td><td>where name not like ?</td></tr><tr><td>StartingWith</td><td>findByNameStartingWith</td><td>where name like ‘?%’</td></tr><tr><td>EndingWith</td><td>findByNameEndingWith</td><td>where name like ‘%?’</td></tr><tr><td>Containing</td><td>findByNameContaining</td><td>where name like ‘%?%’</td></tr><tr><td>OrderBy</td><td>findByIdOrderByXDesc</td><td>where id=? order by x desc</td></tr><tr><td>Not</td><td>findByNameNot</td><td>where name <> ?</td></tr><tr><td>In</td><td>findByIdIn(Collection<?> c)</td><td>where id in (?)</td></tr><tr><td>NotIn</td><td>findByIdNotIn(Collection<?> c)</td><td>where id not in (?)</td></tr><tr><td>True</td><td>findByAaaTue</td><td>where aaa = true</td></tr><tr><td>False</td><td>findByAaaFalse</td><td>where aaa = false</td></tr><tr><td>IgnoreCase</td><td>findByNameIgnoreCase</td><td>where UPPER(name)=UPPER(?)</td></tr></tbody></table><h4 id="4-使用-Repository-查询实例"><a href="#4-使用-Repository-查询实例" class="headerlink" title="4. 使用 Repository 查询实例"></a>4. 使用 Repository 查询实例</h4><ol><li>声明一个接口继承特定的Repository</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment"> * JpaRepository 继承自 PagingAndSortingRepository,其中类型参数</span></span><br><span class="line"><span class="comment"> * 第一个是操作实体类的类型</span></span><br><span class="line"><span class="comment"> * 第二个是操作实体类的标识ID</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">UserRepository</span> <span class="keyword">extends</span> <span class="title">JpaRepository</span><<span class="title">User</span>,<span class="title">Integer</span>> </span>{}</span><br></pre></td></tr></table></figure></div><ol start="2"><li>(可选)在接口中定义方法</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">// 具体名称推导查询可以查看上面的表格</span></span><br><span class="line"><span class="function">List<User> <span class="title">findByLastname</span><span class="params">(String lastname)</span></span>;</span><br></pre></td></tr></table></figure></div><ol start="3"><li>使用@AutoWire注解inject到特定的service中调用</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="meta">@Autowired</span></span><br><span class="line">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> User <span class="title">getUserFromUserName</span><span class="params">(String name)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> userRepository.findUserByName(name);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>到这里,增删改查基本都能够实现,复杂的在于分页、多条件复杂查询.</p><h4 id="5-使用-PagingAndSortingRepository-进行分页查询"><a href="#5-使用-PagingAndSortingRepository-进行分页查询" class="headerlink" title="5. 使用 PagingAndSortingRepository 进行分页查询"></a>5. 使用 PagingAndSortingRepository 进行分页查询</h4><p>前面说到 PagingAndSortingRepository 提供了两个方法,一个是返回Iterable的 findAll(Sort) 方法,另一个是返回Page的findAll方法</p><ol><li>在没有其他查询条件的情况下,直接定义一个Pageable变量,然后使用特定方法即可:</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Override</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> Page<Book> <span class="title">findBookNoCriteria</span><span class="params">(Integer page,Integer size)</span> </span>{</span><br><span class="line"> Pageable pageable = <span class="keyword">new</span> PageRequest(page, size, Sort.Direction.ASC, <span class="string">"id"</span>);</span><br><span class="line"> <span class="keyword">return</span> bookRepository.findAll(pageable);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><ol start="2"><li>在存在多样的查询条件的情况下,还需要接口继承 <code>JpaSpecificationExecutor</code></li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Page<Book> <span class="title">findBookCriteria</span><span class="params">(Integer page, Integer size, <span class="keyword">final</span> BookQuery bookQuery)</span> </span>{</span><br><span class="line"> Pageable pageable = <span class="keyword">new</span> PageRequest(page, size, Sort.Direction.ASC, <span class="string">"id"</span>);</span><br><span class="line"> Page<Book> bookPage = bookRepository.findAll(<span class="keyword">new</span> Specification<Book>(){</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> Predicate <span class="title">toPredicate</span><span class="params">(Root<Book> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder)</span> </span>{</span><br><span class="line"> Predicate p1 = criteriaBuilder.equal(root.get(<span class="string">"name"</span>).as(String.class), bookQuery.getName());</span><br><span class="line"> Predicate p2 = criteriaBuilder.equal(root.get(<span class="string">"isbn"</span>).as(String.class), bookQuery.getIsbn());</span><br><span class="line"> Predicate p3 = criteriaBuilder.equal(root.get(<span class="string">"author"</span>).as(String.class), bookQuery.getAuthor());</span><br><span class="line"> query.where(criteriaBuilder.and(p1,p2,p3));</span><br><span class="line"> <span class="keyword">return</span> query.getRestriction();</span><br><span class="line"> }</span><br><span class="line"> },pageable);</span><br><span class="line"> <span class="keyword">return</span> bookPage;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="使用-Query语句查询"><a href="#使用-Query语句查询" class="headerlink" title="使用@Query语句查询"></a>使用@Query语句查询</h3><p>尽管通过上面的方法能够非常方便快捷的使用查询,但是有时候包含特殊字段的查询或者使用名称组合出来的方法名又长有丑…而且担心效率低,这时候JPA提供了一种类似Hibernate的方法,就是使用@Query进行查询</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Query</span>(<span class="string">"select u from User u where u.emailAddress = ?1"</span>)</span><br><span class="line"><span class="function">User <span class="title">findByEmailAddress</span><span class="params">(String emailAddress)</span></span>;</span><br></pre></td></tr></table></figure></div><p>或者使用原生SQL查询,将Query的参数nativeQuery设置为true</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="meta">@Query</span>(nativeQuery = <span class="keyword">true</span>,value = <span class="string">"select u.name from user u where u.id:id limit 1"</span>)</span><br><span class="line"><span class="function">String <span class="title">getUserNameById</span><span class="params">(Integer id)</span></span>;</span><br></pre></td></tr></table></figure></div><h3 id="使用QueryDSL进行复杂查询"><a href="#使用QueryDSL进行复杂查询" class="headerlink" title="使用QueryDSL进行复杂查询"></a>使用QueryDSL进行复杂查询</h3><p>如果上面的查询方式仍然满足不了需求,那么可以尝试使用QueryDSL进行查询 <a href="http://www.querydsl.com/static/querydsl/latest/reference/html/ch02.html#jpa_integration" target="_blank" rel="noopener">QueryDSL</a></p><h4 id="1-过程"><a href="#1-过程" class="headerlink" title="1. 过程"></a>1. 过程</h4><ol><li>添加MAVEN依赖</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="XML"><figure class="iseeu highlight /xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><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="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.querydsl<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>querydsl-apt<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>${querydsl.version}<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">scope</span>></span>provided<span class="tag"></<span class="name">scope</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">dependency</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.querydsl<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>querydsl-jpa<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>${querydsl.version}<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">dependency</span>></span></span><br><span class="line"></span><br><span class="line"><span class="comment"><!-- 添加APT PLUGIN --></span></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">plugin</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">groupId</span>></span>com.mysema.maven<span class="tag"></<span class="name">groupId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">artifactId</span>></span>apt-maven-plugin<span class="tag"></<span class="name">artifactId</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">version</span>></span>1.1.3<span class="tag"></<span class="name">version</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">executions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">goal</span>></span>process<span class="tag"></<span class="name">goal</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">goals</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">outputDirectory</span>></span>target/generated-sources/java<span class="tag"></<span class="name">outputDirectory</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">processor</span>></span>com.querydsl.apt.jpa.JPAAnnotationProcessor<span class="tag"></<span class="name">processor</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">execution</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">executions</span>></span></span><br><span class="line"><span class="tag"></<span class="name">plugin</span>></span></span><br></pre></td></tr></table></figure></div><p>JPAAnnotationProcessor 会查找所有的 Entity 注解标注的实体类在目标路径下生成 Qxxx 的类.第一次需要使用 maven install 生成一下.</p><ol start="2"><li>使用查询</li></ol><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 注入 jpaQueryFactory</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> entityManager</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="meta">@Bean</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> JPAQueryFactory <span class="title">jpaQueryFactory</span><span class="params">(EntityManager entityManager)</span></span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> JPAQueryFactory(entityManager);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>在配置文件中使用@Bean把JPAQueryFactory注入到容器中,然后使用JpaQueryFactory查询.</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="function"><span class="keyword">private</span> List<User> <span class="title">doGetrolePerson</span><span class="params">(Integer role,<span class="keyword">boolean</span> in,List<User> users)</span></span>{</span><br><span class="line"> QUser user = QUser.user;</span><br><span class="line"> Predicate predicate = user.isNotNull().or(user.isNull());</span><br><span class="line"> <span class="keyword">if</span>(in){</span><br><span class="line"> predicate = role == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.roles.any().id.in(role));</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> predicate = role == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.notIn(users));</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> jpaQueryFactory.selectFrom(user).where(predicate).fetch();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>以上是使用基本的QueryDSL进行查询的方式,更多的增删改查的方法可以参考上面引用的文档,这里不在过多描述.</p><h4 id="2-结合查询"><a href="#2-结合查询" class="headerlink" title="2. 结合查询"></a>2. 结合查询</h4><p>SpringDataJPA 对 QueryDSL 提供了一个通用的Repository – QuerydslPredicateExecutor</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">QuerydslPredicateExecutor</span><<span class="title">T</span>> </span>{</span><br><span class="line"> <span class="function">Optional<T> <span class="title">findOne</span><span class="params">(Predicate var1)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Iterable<T> <span class="title">findAll</span><span class="params">(Predicate var1)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Iterable<T> <span class="title">findAll</span><span class="params">(Predicate var1, Sort var2)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Iterable<T> <span class="title">findAll</span><span class="params">(Predicate var1, OrderSpecifier<?>... var2)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Iterable<T> <span class="title">findAll</span><span class="params">(OrderSpecifier<?>... var1)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function">Page<T> <span class="title">findAll</span><span class="params">(Predicate var1, Pageable var2)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">long</span> <span class="title">count</span><span class="params">(Predicate var1)</span></span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">boolean</span> <span class="title">exists</span><span class="params">(Predicate var1)</span></span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>使用需要让定义的Repository继承这个QuerydslPredicateExecutor,然后调用方法即可:</p><div class="highlight-wrap"autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" contenteditable="true"data-rel="JAVA"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Page<User> <span class="title">findUsers</span><span class="params">(Integer offset, Integer limit, String order, String search, Integer status, Integer sex, Integer role)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> Sort sort = <span class="keyword">null</span>;</span><br><span class="line"> String propertie = order.substring(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">if</span> (order.startsWith(<span class="string">"-"</span>)) {</span><br><span class="line"> sort = Sort.by(propertie).descending();</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (order.startsWith(<span class="string">"+"</span>)) {</span><br><span class="line"> sort = Sort.by(propertie).ascending();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(offset == <span class="keyword">null</span> || limit == <span class="keyword">null</span>){</span><br><span class="line"> offset = <span class="number">1</span>;</span><br><span class="line"> limit = Integer.MAX_VALUE;</span><br><span class="line"> }</span><br><span class="line"> PageRequest pageRequest = <span class="keyword">new</span> PageRequest(offset - <span class="number">1</span>, limit, sort);</span><br><span class="line"> QUser user = QUser.user;</span><br><span class="line"> com.querydsl.core.types.Predicate predicate = user.isNotNull().or(user.isNull());</span><br><span class="line"> predicate = search == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.name.like(<span class="string">"%"</span> + search + <span class="string">"%"</span>));</span><br><span class="line"> predicate = sex == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.sex.id.eq(sex));</span><br><span class="line"> predicate = status == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.status.id.eq(status));</span><br><span class="line"> predicate = role == <span class="keyword">null</span> ? predicate : ExpressionUtils.and(predicate, user.roles.any().id.eq(role));</span><br><span class="line"> <span class="keyword">return</span> userRepository.findAll(predicate, pageRequest);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">
<h1 id="SpringBoot整合JPA使用"><a href="#SpringBoot整合JPA使用" class="headerlink" title="SpringBoot整合JPA使用"></a>SpringBoot整合JPA使用</h1><h2 id="整合JPA
</summary>
<category term="SpringBoot" scheme="https://nanyiniu.github.io/categories/SpringBoot/"/>
<category term="Java" scheme="https://nanyiniu.github.io/tags/Java/"/>
<category term="SpringBoot" scheme="https://nanyiniu.github.io/tags/SpringBoot/"/>
</entry>
</feed>