-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsearch.xml
More file actions
1620 lines (777 loc) · 864 KB
/
search.xml
File metadata and controls
1620 lines (777 loc) · 864 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>责任链模式</title>
<link href="posts/46142/"/>
<url>posts/46142/</url>
<content type="html"><![CDATA[<blockquote><p>责任链模式是定义n个请求处理器,连成一条链,请求可以第一个传递到最后一个,可以被任意一个处理器处理</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230723134501405.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Request</span><span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token keyword">abstract</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token comment">// 后继者,也就是下一个处理器</span> <span class="token keyword">protected</span> <span class="token class-name">Handler</span> successor<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">Handler</span><span class="token punctuation">(</span><span class="token class-name">Handler</span> successor<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>successor <span class="token operator">=</span> successor<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 处理请求</span> <span class="token keyword">protected</span> <span class="token keyword">abstract</span> <span class="token keyword">void</span> <span class="token function">handle</span><span class="token punctuation">(</span><span class="token class-name">Request</span> request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>定义两个处理器</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteHandlerA</span> <span class="token keyword">extends</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token class-name">ConcreteHandlerA</span><span class="token punctuation">(</span><span class="token class-name">Handler</span> successor<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>successor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">handle</span><span class="token punctuation">(</span><span class="token class-name">Request</span> request<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token comment">// 请求传给下一个处理器处理</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>successor <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> successor<span class="token punctuation">.</span><span class="token function">handle</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteHandlerB</span> <span class="token keyword">extends</span> <span class="token class-name">Handler</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token class-name">ConcreteHandlerB</span><span class="token punctuation">(</span><span class="token class-name">Handler</span> successor<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">super</span><span class="token punctuation">(</span>successor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">handle</span><span class="token punctuation">(</span><span class="token class-name">Request</span> request<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token comment">// 请求传给下一个处理器处理</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>successor <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> successor<span class="token punctuation">.</span><span class="token function">handle</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Handler</span> handler1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteHandlerA</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Handler</span> handler2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteHandlerA</span><span class="token punctuation">(</span>handler1<span class="token punctuation">)</span><span class="token punctuation">;</span>handler2<span class="token punctuation">.</span><span class="token function">handle</span><span class="token punctuation">(</span>request<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>模板模式</title>
<link href="posts/46141/"/>
<url>posts/46141/</url>
<content type="html"><![CDATA[<blockquote><p>模板方法模式是定义好算法的步骤,其中一些步骤由子类去实现</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230723133658614.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token keyword">abstract</span> <span class="token class-name">Cook</span> <span class="token punctuation">{</span> <span class="token comment">// 做饭的步骤1,希望子类提供</span> <span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">void</span> <span class="token function">step1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">step2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// 步骤2</span> <span class="token punctuation">}</span> <span class="token comment">// 模板方法</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">cook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">step1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">step2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>子类</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ChineseCook</span> <span class="token keyword">implements</span> <span class="token class-name">Cook</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">step1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Cook</span> cook <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ChineseCook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>cook<span class="token punctuation">.</span><span class="token function">cook</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>命令模式</title>
<link href="posts/46140/"/>
<url>posts/46140/</url>
<content type="html"><![CDATA[<blockquote><p>命令模式将请求封装成对象,将请求调用者和接受者解耦</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p>命令模式要素:</p><ul><li>Invoker:发起命令请求的人</li><li>Command:封装命令</li><li>Receiver:真正执行命令的人</li></ul><p><img src="/images/image-20230723130414328.png"></p><p>为什么客户端不直接调用Receiver,而需要Inovker呢?这里Invoker有两个作用:</p><ol><li>将客户端和Receiver解耦</li><li>能够附加一些能力,例如打日志,执行命令前的前置校验等</li></ol><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>执行命令的人,厨师</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Cook</span> <span class="token punctuation">{</span> <span class="token comment">// 煮饭</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">cookRice</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// 炒菜</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">stirFry</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>命令</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Command</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">// 煮饭命令</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">CookRiceCommand</span> <span class="token keyword">implements</span> <span class="token class-name">Command</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Cook</span> cook<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">CookRiceCommand</span><span class="token punctuation">(</span><span class="token class-name">Cook</span> cook<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>cook <span class="token operator">=</span> cook<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>cook<span class="token punctuation">.</span><span class="token function">cookRice</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 炒菜命令</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">StirFryCommand</span> <span class="token keyword">implements</span> <span class="token class-name">Command</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Cook</span> cook<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">CookRiceCommand</span><span class="token punctuation">(</span><span class="token class-name">Cook</span> cook<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>cook <span class="token operator">=</span> cook<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>cook<span class="token punctuation">.</span><span class="token function">stirFry</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>发起命令的人,客人</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Customer</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Command</span> command<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setCommand</span><span class="token punctuation">(</span><span class="token class-name">Command</span> command<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>command <span class="token operator">=</span> command<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> command<span class="token punctuation">.</span><span class="token function">execute</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Customer</span> customer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Customer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>customer<span class="token punctuation">.</span><span class="token function">setCommand</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">CookRiceCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>customer<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>customer<span class="token punctuation">.</span><span class="token function">setCommand</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StirFryCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>customer<span class="token punctuation">.</span><span class="token function">call</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>观察者模式</title>
<link href="posts/46139/"/>
<url>posts/46139/</url>
<content type="html"><![CDATA[<blockquote><p>观察者模式定义了多个观察者,监听某个对象的状态,当状态改变,观察者将收到通知</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p>观察者模式其实就是“发布-订阅”模式,当subject状态更新,就调用<code>nofityObservers()</code>方法,订阅者将收到<code>update()</code>回调通知</p><p><img src="/images/image-20230723113949670.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>被观察者</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Subject</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">registryObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> observer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">removeObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> observer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">void</span> <span class="token function">notifyObservers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteSubject</span> <span class="token keyword">implements</span> <span class="token class-name">Subject</span> <span class="token punctuation">{</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Observer</span><span class="token punctuation">></span></span> observers <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">registryObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> observer<span class="token punctuation">)</span><span class="token punctuation">{</span> observers<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>observer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">removeObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> observer<span class="token punctuation">)</span><span class="token punctuation">{</span> observers<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>observer<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">registryObserver</span><span class="token punctuation">(</span><span class="token class-name">Observer</span> observer<span class="token punctuation">)</span><span class="token punctuation">{</span> observers<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>observer <span class="token operator">-></span> observer<span class="token punctuation">.</span><span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>观察者</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Observer</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteObserver</span> <span class="token keyword">implements</span> <span class="token class-name">Observer</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">update</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// do something</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Subject</span> subject <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteSubject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Observer</span> observer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteObserver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>subject<span class="token punctuation">.</span><span class="token function">registryObserver</span><span class="token punctuation">(</span>observer<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// subject通知观察者</span>subject<span class="token punctuation">.</span><span class="token function">notifyObservers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>策略模式</title>
<link href="posts/46138/"/>
<url>posts/46138/</url>
<content type="html"><![CDATA[<blockquote><p>策略模式定义了算法族,分别封装起来,让他们之间可以互相替换,此模式让算法的变化独立于使用算法的客户</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230723112258474.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>定义策略接口</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Strategy</span> <span class="token punctuation">{</span> <span class="token keyword">void</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>多种策略实现:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteStrategyA</span> <span class="token keyword">implements</span> <span class="token class-name">Strategy</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteStrategyB</span> <span class="token keyword">implements</span> <span class="token class-name">Strategy</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>策略管理类:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Context</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Strategy</span> strategy<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">Context</span><span class="token punctuation">(</span><span class="token class-name">Strategy</span> strategy<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>strategy <span class="token operator">=</span> strategy<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">void</span> <span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>strategy<span class="token punctuation">.</span><span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 使用策略A</span><span class="token class-name">Context</span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Context</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ConcreteStrategyA</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>context<span class="token punctuation">.</span><span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 使用策略B</span><span class="token class-name">Context</span> context <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Context</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ConcreteStrategyB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>context<span class="token punctuation">.</span><span class="token function">doSomething</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>建造者模式</title>
<link href="posts/46137/"/>
<url>posts/46137/</url>
<content type="html"><![CDATA[<blockquote><p>建造者模式是将对象复杂的构建过程封装起来,客户端通过选择不同的构建者来构想自己想要的对象</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230723105254825.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>我们来写一个动物的构建者</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token keyword">class</span> <span class="token class-name">AbstractAnimal</span> <span class="token punctuation">{</span> <span class="token comment">// 构造头</span> <span class="token keyword">void</span> <span class="token function">buildHead</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// 构造脚</span> <span class="token keyword">void</span> <span class="token function">buildFoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// 构造尾巴</span> <span class="token keyword">void</span> <span class="token function">buildTail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token comment">// 得到构造好的动物</span> <span class="token keyword">abstract</span> <span class="token class-name">Animal</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>猪构建者:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">PigBuilder</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractAnimal</span><span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">buildHead</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">buildFoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">buildTail</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>熊构建者:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BearBuilder</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractAnimal</span><span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">void</span> <span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token function">buildHead</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">buildFoot</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 熊没有尾巴,无需要构造尾巴</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>原型模式</title>
<link href="posts/46136/"/>
<url>posts/46136/</url>
<content type="html"><![CDATA[<blockquote><p>原型模式就是复制一个已有的对象,而客户端不需要知道具体是如何复制的</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230723003051326.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>java中已经有现成的clone接口</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Data</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Thing</span> <span class="token keyword">implements</span> <span class="token class-name">Cloneable</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">String</span> field1<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">Integer</span> field2<span class="token punctuation">;</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token class-name">Thing</span> <span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 此处实现复杂的克隆过程</span> <span class="token class-name">Thing</span> thing <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thing</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> thing<span class="token punctuation">.</span><span class="token function">setField1</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>field1<span class="token punctuation">)</span><span class="token punctuation">;</span> thing<span class="token punctuation">.</span><span class="token function">setField2</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>field12<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> thing<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Thing</span> thing <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thing</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>thing<span class="token punctuation">.</span><span class="token function">setField1</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>field1<span class="token punctuation">)</span><span class="token punctuation">;</span>thing<span class="token punctuation">.</span><span class="token function">setField2</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>field12<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 复制一个</span><span class="token class-name">Thing</span> thing2 <span class="token operator">=</span> thing<span class="token punctuation">.</span><span class="token function">clone</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>抽象工厂</title>
<link href="posts/46135/"/>
<url>posts/46135/</url>
<content type="html"><![CDATA[<blockquote><p>抽象工厂是基于工厂方法模式,支持创建多种n种对象</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p>回顾一下工厂方法,定义了一种要创建的目标Thing,而抽象工厂,则是定义了n种要创建的目标,例如Thing1,Thing2,我们可以认为n个工厂方法组合起来就是抽象工厂。</p><p>工厂方法和抽象工厂的接口区别如下:</p><p><img src="/images/image-20230723000212563.png"></p><p>抽象工厂详细类图:</p><p><img src="/images/image-20230722235918744.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>第一种创建的目标:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Thing1</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThing1A</span> <span class="token keyword">implements</span> <span class="token class-name">Thing1</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThing1B</span> <span class="token keyword">implements</span> <span class="token class-name">Thing1</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>第二种要创建的目标:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Thing2</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThing2A</span> <span class="token keyword">implements</span> <span class="token class-name">Thing2</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThing2B</span> <span class="token keyword">implements</span> <span class="token class-name">Thing2</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>定义抽象工厂,支持创建2种目标</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">abstract</span> <span class="token class-name">AbstractFactory</span> <span class="token punctuation">{</span> <span class="token keyword">abstract</span> <span class="token class-name">Thing1</span> <span class="token function">createThing1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">abstract</span> <span class="token class-name">Thing2</span> <span class="token function">createThing2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteFactory1</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractFactory</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing1</span> <span class="token function">createThing1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThing1A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing2</span> <span class="token function">createThing2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThing2A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteFactory2</span> <span class="token keyword">extends</span> <span class="token class-name">AbstractFactory</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing1</span> <span class="token function">createThing1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThing1B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing2</span> <span class="token function">createThing2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThing2B</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>工厂方法</title>
<link href="posts/46134/"/>
<url>posts/46134/</url>
<content type="html"><![CDATA[<blockquote><p>工厂方法模式是定义一个创建对象的接口,由它的子类来创建对象,每一个子类提供一种创建实现</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230722235953073.png"></p><p>工厂方法比起简单工厂来说:</p><ol><li>定义了一个工厂接口SimpleFactory</li><li>具体的工厂类实现了SimpleFactory接口</li><li>每个工厂类分别负责创建一种对象</li></ol><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>定义要创建的目标:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThingA</span> <span class="token keyword">implements</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThingB</span> <span class="token keyword">implements</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>定义工厂接口和实现类:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">SimpleFactory</span> <span class="token punctuation">{</span> <span class="token class-name">Thing</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteFactoryA</span> <span class="token keyword">implements</span> <span class="token class-name">SimpleFactory</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingA</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteFactoryB</span> <span class="token keyword">implements</span> <span class="token class-name">SimpleFactory</span> <span class="token punctuation">{</span> <span class="token annotation punctuation">@overrite</span> <span class="token keyword">public</span> <span class="token class-name">Thing</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>简单工厂</title>
<link href="posts/46133/"/>
<url>posts/46133/</url>
<content type="html"><![CDATA[<blockquote><p>简单工厂将复杂的创建过程集中到一个简单工厂类中</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230722231137754.png"></p><h2 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h2><p>普通写法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">interface</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThingA</span> <span class="token keyword">implements</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ConcreteThingB</span> <span class="token keyword">implements</span> <span class="token class-name">Thing</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Thing</span> thing<span class="token punctuation">;</span><span class="token keyword">if</span> <span class="token punctuation">(</span>xxx<span class="token punctuation">)</span> <span class="token punctuation">{</span> thing <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingA</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>xxx<span class="token punctuation">)</span> <span class="token punctuation">{</span> thing <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>简单工厂写法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">SimpleFactory</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Thing</span> <span class="token function">create</span><span class="token punctuation">(</span><span class="token class-name">XXX</span> xxx<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>xxx<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingA</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>xxx<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingB</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ConcreteThingXXX</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Thing</span> thing <span class="token operator">=</span> <span class="token class-name">SimpleFactory</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>xxx<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>单例模式</title>
<link href="posts/46132/"/>
<url>posts/46132/</url>
<content type="html"><![CDATA[<blockquote><p>单例模式确保一个类只有一个实例,并提供一个全局访问点</p></blockquote><h2 id="类图"><a href="#类图" class="headerlink" title="类图"></a>类图</h2><p><img src="/images/image-20230722133215874.png"></p><h2 id="线程安全问题"><a href="#线程安全问题" class="headerlink" title="线程安全问题"></a>线程安全问题</h2><p>有线程安全问题的单例模式:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Singleton</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> instance<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面代码会有一个问题,当多个线程同时调用 <code>getInstance()</code> 方法时,可能会产生多个<code>instance</code> 实例,因此这种方式并不是真正的单例。</p><p>所以我们实现单例模式的核心就在于两点:</p><ol><li>保证系统中只有一个实例</li><li>解决获取单例时线程安全</li></ol><h2 id="饿汉式"><a href="#饿汉式" class="headerlink" title="饿汉式"></a>饿汉式</h2><p>虚拟机加载时直接实例化好:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="懒汉式"><a href="#懒汉式" class="headerlink" title="懒汉式"></a>懒汉式</h2><p>需要的时候才创建实例,但需要加<code>synchronized</code>锁来保证线程安全:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">synchronized</span> <span class="token class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> instance<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种方式有个问题,每次执行<code>getInstance()</code>都会加锁,操作比较重,实际上我们只需要实例化的那一刻加锁就可以了</p><h2 id="双重校验锁"><a href="#双重校验锁" class="headerlink" title="双重校验锁"></a>双重校验锁</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Singleton</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">volatile</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token class-name">Singleton</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> instance<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种方式有两个关键点:</p><ol><li>instance要使用<code>volatile</code>关键字修饰,可以防止JVM指令重排</li><li><code>if (instance == null)</code>这行有可能多个线程同时执行从而导致被多次实例化,因此synchronized同步块中需要再做一次判断</li></ol><h2 id="静态内部类"><a href="#静态内部类" class="headerlink" title="静态内部类"></a>静态内部类</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Singleton</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">class</span> <span class="token class-name">SingletonHolder</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">Singleton</span> INSTANCE <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token class-name">SingletonHolder</span><span class="token punctuation">.</span>INSTANCE<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种写法有一个特点,内部静态类<code>SingletonHolder</code>默认情况不会被JVM加载,只有当<code>getInstance()</code>被调用时,<code>SingletonHolder</code>才会被加载。</p><p>这种方式的线程安全是由JVM来确保</p><h2 id="枚举方式"><a href="#枚举方式" class="headerlink" title="枚举方式"></a>枚举方式</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">Singleton</span><span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">enum</span> <span class="token class-name">SingletonEnum</span> <span class="token punctuation">{</span> INSTANCE<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token class-name">Singleton</span> instance<span class="token punctuation">;</span> <span class="token class-name">SingletonEnum</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Singleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token class-name">Singleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> instance<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当我们定义了一个<code>SingletonEnum</code>类型的枚举<code>INSTANCE</code>,JVM帮我们创建好了<code>INSTANCE</code>实例,而我们在构造方法中初始化了<code>Singleton</code>实例。</p><p>这种方式其实是利用了一个枚举值只会被初始化一次的这种特性,从而保证了单例</p>]]></content>
<categories>
<category> 设计模式 </category>
</categories>
</entry>
<entry>
<title>常见问题与性能优化</title>
<link href="posts/14564/"/>
<url>posts/14564/</url>
<content type="html"><![CDATA[<h2 id="如何防止消息丢失"><a href="#如何防止消息丢失" class="headerlink" title="如何防止消息丢失"></a>如何防止消息丢失</h2><p>生产者:</p><ul><li>ack设置成1</li><li>如果对消息防丢失要求非常高,可以配置<code>ack=all</code>,<code>min.insync.replicas</code>配置成分区备份数</li></ul><p>消费者:</p><ul><li>使用手动提交,业务处理完成才提交offset</li></ul><h2 id="防止消息重复消费"><a href="#防止消息重复消费" class="headerlink" title="防止消息重复消费"></a>防止消息重复消费</h2><p>生产者:</p><ul><li>生产者要避免生产重复消息,因此需要关闭重试</li></ul><p>消费者:</p><ul><li>实现幂等性,万一出现重复消息时,业务上不会重复处理</li></ul><h2 id="消息顺序性"><a href="#消息顺序性" class="headerlink" title="消息顺序性"></a>消息顺序性</h2><p>生产者:</p><ul><li>生产消息时要保证顺序,因此需使用同步发送,且关闭重试</li><li>只能往一个分区里发,多分区之间是不保证顺序的</li></ul><p>消费者:</p><ul><li>只能消费一个分区里的数据,多分区之间不保证顺序</li></ul><p>因此为了保证消息顺序性,往往需要牺牲性能,只能一个分区工作,也只能一个消费者工作</p><h2 id="消息积压"><a href="#消息积压" class="headerlink" title="消息积压"></a>消息积压</h2><p>当消费速度跟不上生产速度导致消息积压时,可以采用以下手段:</p><ol><li>增加分区,增加消费者</li><li>把部分消息再往另一个主题上发,再增加消费者,该方法本质上与第一个方法一样,但实操步骤不一样</li><li>如果是硬件性能瓶颈,可以增加服务器来增加单个消费者的消费能力</li></ol><h2 id="如何提高生产者吞吐"><a href="#如何提高生产者吞吐" class="headerlink" title="如何提高生产者吞吐"></a>如何提高生产者吞吐</h2><p>有以下几项配置可以优化来提高吞吐量:</p><table><thead><tr><th>配置项</th><th>描述</th></tr></thead><tbody><tr><td>buffer.memory</td><td>缓冲区大小,默认32m,可以调大,例如64m</td></tr><tr><td>batch.size</td><td>缓冲区一批数据量,默认16k,可以调成例如32k</td></tr><tr><td>linger.ms</td><td>如果数据量未达到batch.size,linger.ms时间之后就会发送,默认是0ms,可以调成5-100ms之间</td></tr><tr><td>compression.type</td><td>压缩方式,默认不压缩,支持gzip, snappy, lz4, zstd</td></tr></tbody></table>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>HW与LEO</title>
<link href="posts/49979/"/>
<url>posts/49979/</url>
<content type="html"><![CDATA[<ul><li><p>HW,High Watermark,中文高水位。</p></li><li><p>LEO,Latest End Offset</p></li></ul><p>如下图所示,假设当前有三个broker(3个ISR):broker-0为某个主题分区的leader,生产者已经生产到了offset为5的消息,其中broker-1的副本已经同步到了offset 3,broker-2同步到了offset2。</p><p><img src="/images/image-20220821155816076.png"></p><p>此时,对于分区来说:</p><ul><li>每一份数据offset最大值就是LEO(latest end offset)</li><li>LEO中最小的那个就是HW(高水位)</li></ul><p>此时由于3、4、5这三条数据还未完全同步给两个副本,消费者只能消费到offset为2的数据,这就是hw的作用:消费者只能消费小等于HW的数据。</p><p>为什么这么设计呢?</p><p>这是为了防止leader宕机时,数据还未同步给其他副本而导致数据丢失。</p>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>Rebalance机制</title>
<link href="posts/3038/"/>
<url>posts/3038/</url>
<content type="html"><![CDATA[<p>当消费者启动之后,kafka会为消费者分配分区进行消费。当消费者和分区的关系发生了变化之后,就会重新分配分区给消费者,例如:</p><ul><li>某个消费者挂了</li><li>来了个新的消费者</li><li>主题增加了分区</li></ul><p>重新分配分区这个动作就叫做Rebalance。</p><h2 id="Rebalance过程"><a href="#Rebalance过程" class="headerlink" title="Rebalance过程"></a>Rebalance过程</h2><p>假设现在有3个分区,2个消费者c1和c2,然后新来了一个消费者c3,触发了Rebalance,整个过程如下:</p><ol><li>broker中的组协调者(Group Coordinator)收到c3的<code>Join Group</code>请求,知道有新消费者来了</li><li>组协调者在心跳中告知c1和c2</li><li>c1和c2放弃已持有的分区,此时c1和c2也相当于一个新的消费者,给组协调者发送<code>Join Group</code>请求</li><li>组协调者按定好分配方案,将分配方案发给每个消费者</li><li>3个消费者开始消费</li></ol><h2 id="分配策略"><a href="#分配策略" class="headerlink" title="分配策略"></a>分配策略</h2><p>分配分区有几种策略,可以使用<code>partition.assignment.strategy</code>进行配置,默认值为:</p><p><code>org.apache.kafka.clients.consumer.RangeAssignor,org.apache.kafka.clients.consumer.CooperativeStickyAssignor</code></p><h3 id="range策略"><a href="#range策略" class="headerlink" title="range策略"></a>range策略</h3><p><code>partition.assignment.strategy</code>设置为<code>org.apache.kafka.clients.consumer.RangeAssignor</code></p><p>这种策略下,分配方式是在平均分的基础上,多出来的先给排在前面的消费者,举几个例子:</p><ul><li><p>假设3个分区,3个消费者,则是每个消费者分配1个分区</p><p> <img src="/images/image-20220817151422745.png"></p></li><li><p>假设4个分区,3个消费者,则是0,1分区给第一个消费,2,3分区分别给其他2个消费者</p></li></ul><p><img src="/images/image-20220817151511554.png"></p><h3 id="轮训策略"><a href="#轮训策略" class="headerlink" title="轮训策略"></a>轮训策略</h3><p><code>partition.assignment.strategy</code>设置为<code>org.apache.kafka.clients.consumer.RoundRobinAssignor</code></p><p>这种策略下,分区会按顺序依次分配给消费者,例如有10个分区,3个消费者:</p><ol><li>0分区给消费者0</li><li>1分区给消费者1</li><li>2分区给消费者2</li><li>3分区给消费者0</li><li>4分区给消费者1</li><li>依次类推</li></ol><h3 id="sticky策略"><a href="#sticky策略" class="headerlink" title="sticky策略"></a><strong>sticky策略</strong></h3><p><code>partition.assignment.strategy</code>设置为<code>org.apache.kafka.clients.consumer.StickyAssignor</code></p><p>这是带有粘性的策略,它会在发生Rebalance时:</p><ul><li>尽可能保持原来的分配关系</li><li>尽可能均匀的分配分区。</li></ul><h3 id="Cooperative-Sticky策略"><a href="#Cooperative-Sticky策略" class="headerlink" title="Cooperative Sticky策略"></a>Cooperative Sticky策略</h3><p><code>partition.assignment.strategy</code>设置为<code>org.apache.kafka.clients.consumer.CooperativeStickyAssignor</code></p><p>这是2.4版本新增的策略,原有的sticky策略虽然可以尽量保证原有的分配分案,但是每一个消费者仍然需要先释放已持有的分区资源。</p><p>于是就有新<code>Cooperative Sticky</code>策略,这个策略相比于旧的策略有什么优势呢?为此,我们需要先了解旧的策略有什么劣势。</p><p>在旧策略中,当发生再平衡时,会发生STW(Stop The Word):所有的消费者需要先放弃当前已经持有的分区资源,等待重新分配。</p><p>也就是说在这段时间内,主题资源处于不可用状态,直到rebalance结束,才会重新开始消费。</p><p>如果是大规模集群,这种大规模的STW是一种灾难。</p><p>而新的<code>Cooperative Sticky</code>策略,将原来的一次大规模rebalance操作,拆分成了多次小规模的rebalance,直至最终平衡完成。</p><p>仍然假设现在有3个分区,2个消费者c1和c2,c1消费分区0和1,c2消费分区2。</p><p>然后新来了一个消费者c3,触发了Rebalance,具体步骤如下:</p><ol><li>新消费者来时,组协调者通知消费者,和旧策略不同,c1,c2发送<code>Join Group</code>给组协调者时,不必放弃已持有的分区,并将当前持有的分区信息告知组协调者</li><li>组协调者综合现在所有的情况,通知c1放弃分区1,此时算一次小规模rebalance完成。</li><li>接着继续rebalance,将暂时无人消费的分区1分配给新来的c2即可,rebalance全部完成。</li></ol>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>Kafka控制器</title>
<link href="posts/1537/"/>
<url>posts/1537/</url>
<content type="html"><![CDATA[<h2 id="什么是Kafka控制器"><a href="#什么是Kafka控制器" class="headerlink" title="什么是Kafka控制器"></a>什么是Kafka控制器</h2><p>在Kafka集群中,控制器主要处理:</p><ul><li>分区Leader选举</li><li>分区状态管理</li><li>副本管理</li><li>等等</li></ul><h2 id="控制器选举"><a href="#控制器选举" class="headerlink" title="控制器选举"></a>控制器选举</h2><p>当kafka集群启动时,每个broker都会到zookeeper上创建名为<code>/controller</code>的节点,但只有一个broker可以创建成功。创建成功的那个borker就成为了控制器。</p><p>当控制器broker挂掉时,与zk的连接会话失效,<code>/controller</code>节点会被zk自动删除,剩余的broker就会尝试创建该<code>/controller</code>,创建成功的就称为了新的控制器。</p><p>我们可以使用<code>zkCli.sh</code>连接zk,然后可以使用<code>ls /</code>看到<code>controller</code>节点:</p><p><img src="/images/image-20220817123512198.png"></p><p>使用<code>get /controller</code>可以看到当前控制器是id为1的broker:</p><p><img src="/images/image-20220817123546319.png"></p><h2 id="分区Leader选举"><a href="#分区Leader选举" class="headerlink" title="分区Leader选举"></a>分区Leader选举</h2><p>为分区选举leader也是控制器的工作之一,我们先创建一个2分区,3副本的主题用于测试:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-topics.sh --bootstrap-server <span class="token number">10.211</span>.55.3:9092 --create --topic <span class="token builtin class-name">test</span> <span class="token punctuation">\</span>--partitions <span class="token number">2</span> <span class="token punctuation">\</span>--replication-factor <span class="token number">3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>其中分区0的leader是0,Isr是<code>0,1,2</code>:</p><p><img src="/images/image-20220817142317651.png"></p><p>Isr是候选broker集合,并且是有序排列的,kafka会根据节点之间的网络情况,性能等排列出候选集,当broker0挂了之后,分区0就需要重新选举Leader。</p><p>选举的规则很简单,就是从Isr中选择一个作为新的Leader。</p><p>做个简单的试验,关闭broker-0,再次查看主题详情:</p><p><img src="/images/image-20220817142635559.png"></p><p>可以看到,两个分区的Leader都变了。</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>总结一下kafka控制器的作用:</p><ul><li>分区Leader出现故障时,为分区选举新的Leader</li><li>分区ISR集合发生变化时,通知其他broker更新元数据信息</li><li>增加分区时,通知其他broker</li></ul>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>Java客户端使用</title>
<link href="posts/59768/"/>
<url>posts/59768/</url>
<content type="html"><![CDATA[<p>我们通常使用Kafka有两种方式,一种是通过Kafka自带的命令行工具,一种是通过Kafka提供的API。</p><p>Kafka提供了五类API:</p><ol><li>Producer API:生产者api</li><li>Consumer API:消费者api</li><li>Admin API:管理api</li><li>Streams API:大数据流api</li><li>Connect API:从别的数据库导入数据到kafka中或从kafka导出数据的api</li></ol><p>以Java为例,首先导入Kafka客户端:</p><pre class="line-numbers language-xml" data-language="xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.apache.kafka<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>kafka-clients<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>2.8.0<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Admin-API"><a href="#Admin-API" class="headerlink" title="Admin API"></a>Admin API</h2><p>admin api用于管理kafka,比如topic管理,下面举几个例子</p><p>创建adminClient实例:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">private</span> <span class="token class-name">AdminClient</span> adminClient<span class="token punctuation">;</span><span class="token annotation punctuation">@BeforeEach</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">initAdminClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">Properties</span> properties <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Properties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">AdminClientConfig</span><span class="token punctuation">.</span>BOOTSTRAP_SERVERS_CONFIG<span class="token punctuation">,</span> <span class="token string">"10.211.55.3:9092"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> adminClient <span class="token operator">=</span> <span class="token class-name">AdminClient</span><span class="token punctuation">.</span><span class="token function">create</span><span class="token punctuation">(</span>properties<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG</code>即kafka集群地址,其他配置参数参考<code>AdminClientConfig</code></p><h3 id="创建topic"><a href="#创建topic" class="headerlink" title="创建topic"></a>创建topic</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">createTopic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> numPartitions <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// partition数量</span> <span class="token keyword">short</span> replication <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token comment">// 副本数</span> <span class="token class-name">NewTopic</span> topic <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">NewTopic</span><span class="token punctuation">(</span><span class="token string">"product"</span><span class="token punctuation">,</span> numPartitions<span class="token punctuation">,</span> replication<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">CreateTopicsResult</span> result <span class="token operator">=</span> adminClient<span class="token punctuation">.</span><span class="token function">createTopics</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singleton</span><span class="token punctuation">(</span>topic<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="列举topic"><a href="#列举topic" class="headerlink" title="列举topic"></a>列举topic</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">listTopic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>adminClient<span class="token punctuation">.</span><span class="token function">listTopics</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">listings</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>从结果上可以看到:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token punctuation">[</span><span class="token punctuation">(</span>name<span class="token operator">=</span>product<span class="token punctuation">,</span> internal<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>其中<code>internal=false</code>表示这个主题不是一个内部主题,所谓内部主题,就是由kafka自己创建的,内部使用的主题</p><h3 id="删除topic"><a href="#删除topic" class="headerlink" title="删除topic"></a>删除topic</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">deleteTopic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">DeleteTopicsResult</span> result <span class="token operator">=</span> adminClient<span class="token punctuation">.</span><span class="token function">deleteTopics</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singleton</span><span class="token punctuation">(</span><span class="token string">"product"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="查看topic详情"><a href="#查看topic详情" class="headerlink" title="查看topic详情"></a>查看topic详情</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">describeTopic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">DescribeTopicsResult</span> result <span class="token operator">=</span> adminClient<span class="token punctuation">.</span><span class="token function">describeTopics</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singleton</span><span class="token punctuation">(</span><span class="token string">"product"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看到名称和分区信息等:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token punctuation">{</span>product<span class="token operator">=</span><span class="token punctuation">(</span> name<span class="token operator">=</span>product<span class="token punctuation">,</span> internal<span class="token operator">=</span><span class="token boolean">false</span><span class="token punctuation">,</span> partitions<span class="token operator">=</span><span class="token punctuation">(</span> partition<span class="token operator">=</span><span class="token number">0</span><span class="token punctuation">,</span> leader<span class="token operator">=</span><span class="token number">192.168</span><span class="token number">.0</span><span class="token number">.103</span><span class="token operator">:</span><span class="token number">9092</span> <span class="token punctuation">(</span>id<span class="token operator">:</span> <span class="token number">1001</span> rack<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">,</span> replicas<span class="token operator">=</span><span class="token number">192.168</span><span class="token number">.0</span><span class="token number">.103</span><span class="token operator">:</span><span class="token number">9092</span> <span class="token punctuation">(</span>id<span class="token operator">:</span> <span class="token number">1001</span> rack<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">,</span> isr<span class="token operator">=</span><span class="token number">192.168</span><span class="token number">.0</span><span class="token number">.103</span><span class="token operator">:</span><span class="token number">9092</span> <span class="token punctuation">(</span>id<span class="token operator">:</span> <span class="token number">1001</span> rack<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> authorizedOperations<span class="token operator">=</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="查看主题配置"><a href="#查看主题配置" class="headerlink" title="查看主题配置"></a>查看主题配置</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">describeTopicConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">ConfigResource</span> configResource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConfigResource</span><span class="token punctuation">(</span><span class="token class-name">ConfigResource<span class="token punctuation">.</span>Type</span><span class="token punctuation">.</span>TOPIC<span class="token punctuation">,</span> <span class="token string">"product"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">DescribeConfigsResult</span> result <span class="token operator">=</span> adminClient<span class="token punctuation">.</span><span class="token function">describeConfigs</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singleton</span><span class="token punctuation">(</span>configResource<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>result<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>配置内容很长,就不贴出来了,</p><h3 id="修改主题配置"><a href="#修改主题配置" class="headerlink" title="修改主题配置"></a>修改主题配置</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">modifyConfig</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ExecutionException</span><span class="token punctuation">,</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">ConfigResource</span><span class="token punctuation">,</span> <span class="token class-name">Collection</span><span class="token punctuation"><</span><span class="token class-name">AlterConfigOp</span><span class="token punctuation">></span><span class="token punctuation">></span></span> configs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">ConfigResource</span> configResource <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConfigResource</span><span class="token punctuation">(</span><span class="token class-name">ConfigResource<span class="token punctuation">.</span>Type</span><span class="token punctuation">.</span>TOPIC<span class="token punctuation">,</span> <span class="token string">"product"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">AlterConfigOp</span> op <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AlterConfigOp</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ConfigEntry</span><span class="token punctuation">(</span><span class="token string">"min.insync.replicas"</span><span class="token punctuation">,</span> <span class="token string">"0"</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token class-name">AlterConfigOp<span class="token punctuation">.</span>OpType</span><span class="token punctuation">.</span>SET<span class="token punctuation">)</span><span class="token punctuation">;</span> configs<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>configResource<span class="token punctuation">,</span> <span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singleton</span><span class="token punctuation">(</span>op<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>adminClient<span class="token punctuation">.</span><span class="token function">incrementalAlterConfigs</span><span class="token punctuation">(</span>configs<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="修改其他配置"><a href="#修改其他配置" class="headerlink" title="修改其他配置"></a>修改其他配置</h3><p>支持修改其他配置,具体查看<code>ConfigResource.Type</code>:</p><p><img src="/images/image-20210809144258428.png"></p><h3 id="其他API"><a href="#其他API" class="headerlink" title="其他API"></a>其他API</h3><p>adminClient还有很多API,比如</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">adminClient<span class="token punctuation">.</span><span class="token function">listPartitionReassignments</span><span class="token punctuation">(</span><span class="token punctuation">)</span>adminClient<span class="token punctuation">.</span><span class="token function">listOffsets</span><span class="token punctuation">(</span><span class="token punctuation">)</span>adminClient<span class="token punctuation">.</span><span class="token function">createPartitions</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>不一一列举,具体查看adminClient源码</p><h2 id="Producer-API"><a href="#Producer-API" class="headerlink" title="Producer API"></a>Producer API</h2><h3 id="生产数据"><a href="#生产数据" class="headerlink" title="生产数据"></a>生产数据</h3><p>这是生产者API,用于生产数据</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testProducer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span> <span class="token class-name">Properties</span> properties <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Properties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ProducerConfig</span><span class="token punctuation">.</span>BOOTSTRAP_SERVERS_CONFIG<span class="token punctuation">,</span> <span class="token string">"10.211.55.3:9092"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// key和value的序列化方法</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ProducerConfig</span><span class="token punctuation">.</span>KEY_SERIALIZER_CLASS_CONFIG<span class="token punctuation">,</span> <span class="token class-name">StringSerializer</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ProducerConfig</span><span class="token punctuation">.</span>VALUE_SERIALIZER_CLASS_CONFIG<span class="token punctuation">,</span> <span class="token class-name">StringSerializer</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Producer</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> producer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">KafkaProducer</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>properties<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 构造kafka消息,key可以不指定。</span> <span class="token class-name">ProducerRecord</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> <span class="token keyword">record</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProducerRecord</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">"hello word"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 发送消息</span> <span class="token class-name">RecordMetadata</span> recordMetadata <span class="token operator">=</span> producer<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token keyword">record</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"partition: "</span> <span class="token operator">+</span> recordMetadata<span class="token punctuation">.</span><span class="token function">partition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"offset: "</span> <span class="token operator">+</span> recordMetadata<span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>构造kafka消息时,key可以不传。</p><p>如果有传,在多个partition情况下,将会用key进行hash来决定这条消息应该存放在哪一个partition中。</p><h3 id="指定分区"><a href="#指定分区" class="headerlink" title="指定分区"></a>指定分区</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 指定0分区</span>new ProducerRecord<span class="token operator"><></span><span class="token punctuation">(</span><span class="token string">"mytopic"</span>, <span class="token number">0</span>, null, <span class="token string">"hello word"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="异步发送"><a href="#异步发送" class="headerlink" title="异步发送"></a>异步发送</h3><p>上面的例子,<code>producer.send(record).get()</code>会同步等待发送结果,为了效率,我们也可以异步发送:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 异步发送消息</span>producer<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span><span class="token keyword">record</span><span class="token punctuation">,</span> <span class="token punctuation">(</span>metadata<span class="token punctuation">,</span> exception<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"发送消息失败:"</span> <span class="token operator">+</span> exception<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"partition: "</span> <span class="token operator">+</span> metadata<span class="token punctuation">.</span><span class="token function">partition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"offset: "</span> <span class="token operator">+</span> metadata<span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="ack参数配置"><a href="#ack参数配置" class="headerlink" title="ack参数配置"></a>ack参数配置</h3><p>在生产者中,ack参数是一个重要参数,配置方法如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ProducerConfig</span><span class="token punctuation">.</span>ACKS_CONFIG<span class="token punctuation">,</span> <span class="token string">"1"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>该配置含义如下:</p><table><thead><tr><th>配置值</th><th>说明</th><th>优缺点</th></tr></thead><tbody><tr><td>acks=0</td><td>不用等待broker确认收到消息</td><td>性能高,但容易丢数据</td></tr><tr><td>acks=1</td><td>等待leader成功将数据写入磁盘,不等待follower</td><td>如果follower备份数据失败,同时leader又刚好挂了,就会丢数据</td></tr><tr><td>acks=-1或all</td><td>等待一定数量的broker都成功写入数据。可以使用<code>min.insync.replicas</code>来配置数量。</td><td>可靠性高,但性能较低。比较少用,除非对数据可靠性有很高要求,例如与钱相关的业务。</td></tr></tbody></table><h2 id="Consumer-API"><a href="#Consumer-API" class="headerlink" title="Consumer API"></a>Consumer API</h2><h3 id="消费数据"><a href="#消费数据" class="headerlink" title="消费数据"></a>消费数据</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Test</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">testConsumer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Properties</span> properties <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Properties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>BOOTSTRAP_SERVERS_CONFIG<span class="token punctuation">,</span> <span class="token string">"10.211.55.3:9092"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// key和value的反序列化方法</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>KEY_DESERIALIZER_CLASS_CONFIG<span class="token punctuation">,</span> <span class="token class-name">StringDeserializer</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>VALUE_DESERIALIZER_CLASS_CONFIG<span class="token punctuation">,</span> <span class="token class-name">StringDeserializer</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 消费策略</span> properties<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>AUTO_OFFSET_RESET_CONFIG<span class="token punctuation">,</span> <span class="token string">"earliest"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">//消费组</span> properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>GROUP_ID_CONFIG<span class="token punctuation">,</span> <span class="token string">"testgroup"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">KafkaConsumer</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> consumer <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">KafkaConsumer</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>properties<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 订阅主题,动态分配分区</span> consumer<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 消费数据</span> <span class="token class-name">ConsumerRecords</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> records <span class="token operator">=</span> consumer<span class="token punctuation">.</span><span class="token function">poll</span><span class="token punctuation">(</span><span class="token class-name">Duration</span><span class="token punctuation">.</span><span class="token function">ofMillis</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> records<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">record</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"消费到消息:"</span> <span class="token operator">+</span> <span class="token keyword">record</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>示例代码中<code>ConsumerConfig.AUTO_OFFSET_RESET_CONFIG</code>这个参数需要注意。</p><p>当消费者启动时,如果所在消费组已经有offset(之前消费过),那么就按offset继续消费。</p><p>如果没有offset,则按<code>ConsumerConfig.AUTO_OFFSET_RESET_CONFIG</code>配置消费,它有两个取值:</p><ul><li>latest(默认):旧数据不消费,只消费启动之后才产生的新数据</li><li>earliest:第一次从头消费,以后按照offset消费</li></ul><h3 id="指定分区消费"><a href="#指定分区消费" class="headerlink" title="指定分区消费"></a>指定分区消费</h3><p>我们要先将原来的订阅方法注释掉,<code>consumer.subscribe</code>其实就是动态分配分区进行消费。改用<code>consumer.assign</code>手动分配分区。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"># 原来的订阅方法注释掉。# consumer<span class="token punctuation">.</span><span class="token function">subscribe</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span># 指定<span class="token number">0</span>分区消费consumer<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="自动提交offset"><a href="#自动提交offset" class="headerlink" title="自动提交offset"></a>自动提交offset</h3><p>自动提交offset就是consumer在poll到数据时就自动提交offset到<code>__consumer_offset</code>这个topic中,但这可能导致数据丢失。</p><p>例如consumer poll到消息并提交offset了,但是此时服务挂了,业务未处理,重启consumer以后,上一条未处理的消息就无法就消费不到了。</p><h3 id="手动提交offset"><a href="#手动提交offset" class="headerlink" title="手动提交offset"></a>手动提交offset</h3><p>默认是自动提交,为了解决可能丢数据的情况,我们可以使用以下配置关闭自动提交:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">properties<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token class-name">ConsumerConfig</span><span class="token punctuation">.</span>ENABLE_AUTO_COMMIT_CONFIG<span class="token punctuation">,</span> <span class="token string">"false"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>然后我们在消费数据之后,进行手动提交offset,这样就可以保证每条消息都被处理:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 消费数据</span> <span class="token class-name">ConsumerRecords</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">String</span><span class="token punctuation">></span></span> records <span class="token operator">=</span> consumer<span class="token punctuation">.</span><span class="token function">poll</span><span class="token punctuation">(</span><span class="token class-name">Duration</span><span class="token punctuation">.</span><span class="token function">ofMillis</span><span class="token punctuation">(</span><span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> records<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">record</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"消费到消息:"</span> <span class="token operator">+</span> <span class="token keyword">record</span><span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>records<span class="token punctuation">.</span><span class="token function">count</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 同步提交,阻塞</span> consumer<span class="token punctuation">.</span><span class="token function">commitSync</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"提交失败:"</span> <span class="token operator">+</span> e<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>也可以异步提交:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java">consumer<span class="token punctuation">.</span><span class="token function">commitAsync</span><span class="token punctuation">(</span><span class="token punctuation">(</span>offsets<span class="token punctuation">,</span> exception<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exception <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"提交失败:"</span> <span class="token operator">+</span> exception<span class="token punctuation">.</span><span class="token function">getMessage</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"提交成功"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>一般情况同步提交即可</p><h3 id="指定分区消费-1"><a href="#指定分区消费-1" class="headerlink" title="指定分区消费"></a>指定分区消费</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java">consumer<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="指定offset消费:"><a href="#指定offset消费:" class="headerlink" title="指定offset消费:"></a>指定offset消费:</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"># 注意,要先指定分区consumer<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span># 指定分区,从offset <span class="token number">5</span>开始消费consumer<span class="token punctuation">.</span><span class="token function">seek</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token number">0</span> <span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">5</span> <span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>由于每个分区都有各自的offset,因此指定offset时必须指定分区。</p><p>由于要指定分区,所以seek之前也需要使用<code>consumer.assign</code>手动分配指定分区到当前消费者。</p><h3 id="从头消费"><a href="#从头消费" class="headerlink" title="从头消费"></a>从头消费</h3><p>和指定offset消费同理:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"># 注意,手动指定分区consumer<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span># 指定分区,从头开始消费consumer<span class="token punctuation">.</span><span class="token function">seekToBeginning</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span><span class="token number">0</span> <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="根据时间消费"><a href="#根据时间消费" class="headerlink" title="根据时间消费"></a>根据时间消费</h3><p>根据时间消费的原理很简单,就是先使用<code>consumer.offsetsForTimes()</code>根据时间找到对应的offset,然后再根据offset进行消费:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 获取所有分区</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">PartitionInfo</span><span class="token punctuation">></span></span> partitions <span class="token operator">=</span> consumer<span class="token punctuation">.</span><span class="token function">partitionsFor</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TopicPartition</span><span class="token punctuation">,</span> <span class="token class-name">Long</span><span class="token punctuation">></span></span> offsetMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 从10分钟前开始消费</span><span class="token class-name">Long</span> consumeTimestamp <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token number">10</span> <span class="token operator">*</span> <span class="token number">60</span> <span class="token operator">*</span> <span class="token number">1000</span><span class="token punctuation">;</span>partitions<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span>partitionInfo <span class="token operator">-></span> <span class="token punctuation">{</span> offsetMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">TopicPartition</span><span class="token punctuation">(</span><span class="token string">"mytopic"</span><span class="token punctuation">,</span> partitionInfo<span class="token punctuation">.</span><span class="token function">partition</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> consumeTimestamp<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 根据时间找到对应的offset</span><span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">TopicPartition</span><span class="token punctuation">,</span> <span class="token class-name">OffsetAndTimestamp</span><span class="token punctuation">></span></span> offsetPartMap <span class="token operator">=</span> consumer<span class="token punctuation">.</span><span class="token function">offsetsForTimes</span><span class="token punctuation">(</span>offsetMap<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 根据offset消费(所有分区)</span>offsetPartMap<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span>partition<span class="token punctuation">,</span> offsetTimestamp<span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> consumer<span class="token punctuation">.</span><span class="token function">assign</span><span class="token punctuation">(</span><span class="token class-name">Collections</span><span class="token punctuation">.</span><span class="token function">singletonList</span><span class="token punctuation">(</span>partition<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> consumer<span class="token punctuation">.</span><span class="token function">seek</span><span class="token punctuation">(</span>partition<span class="token punctuation">,</span> offsetTimestamp<span class="token punctuation">.</span><span class="token function">offset</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>副本</title>
<link href="posts/26943/"/>
<url>posts/26943/</url>
<content type="html"><![CDATA[<p>前篇提过一个主题可以有多个分区,分布在多个broker上,但如果某个broker挂了,这个borker上的数据不就丢了吗?</p><p>为了解决这个问题,就有了副本的概念。</p><p>副本其实就是备份,我们可以在创建主题时指定副本数量。假设我们有三个broker,我们创建一个2分区,3副本的主题:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-topics.sh --bootstrap-server <span class="token number">10.211</span>.55.3:9092 --create --topic test2 <span class="token punctuation">\</span>--partitions <span class="token number">2</span> <span class="token punctuation">\</span>--replication-factor <span class="token number">3</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>查看主题详情:</p><p><img src="/images/image-20220805142226610.png"></p><p>首先我们先看<code>partition-0</code>分区,<code>replicas</code>的值为<code>2,1,0</code>,意味着这个分区的数据分布在<code>broker-2</code>,<code>broker-1</code>,<code>broker-0</code>上,而<code>Leader</code>等于2,表示<code>broker-2</code>上的这一块数据是主数据,其他borker上的数据都是副本:</p><p><img src="/images/image-20220805142819272.png"></p><p>此时,无论是生产者还是消费者来读写消息时,都是从Leader上读写,Leader会将数据同步到其他两个Broker上作为来备份,且仅仅是备份(与Leader对应,副本被称为Follower)。</p><p>当Leader挂了之后,会经过选举重新为分区选一个Leader。</p><p>分区1也是同理:</p><p><img src="/images/image-20220805143525735.png"></p>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>主题与分区</title>
<link href="posts/57824/"/>
<url>posts/57824/</url>
<content type="html"><![CDATA[<p>kafka主题是以分区(Partition)的形式来存储的,什么意思呢,我们看下kafka的数据目录:</p><p><img src="/images/image-20220804182648685.png"></p><p>其中有一个目录是<code>mytopic-0</code>,这是我们之前创建的主题,这里的<code>-0</code>就是分区编号,目录中存放了数据文件:</p><p><img src="/images/image-20220804182821438.png"></p><p>我们使用<code>--partitions 2</code>参数创建一个2个分区的主题:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 集群情况下,--bootstrap-server参数把所有节点地址都配进来</span>./kafka-topics.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092,10.211.55.4:9092,10.211.55.5:9092 --create --topic <span class="token builtin class-name">test</span> --partitions <span class="token number">2</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>再看数据目录,发现了<code>test</code>主题有<code>test-0</code>和<code>test-1</code>两个目录:</p><p><img src="/images/image-20220804231720488.png"></p><p>所以所谓分区,从存储的角度来说,其实就是分成多个目录来存。</p><p>那么有什么用呢?</p><p>假设我们有一个3个台机器组成的kafka集群,然后创建一个4分区的主题,那么数据的存储有可能是这样的:</p><p><img src="/images/image-20220804232109051.png"></p><p>多节点多分区的情况下,分区是可以分布在不同节点上的,这可以带来比原来高数倍的吞吐。</p><p>因为消息的消费是按offset顺序消费的(单分区),写入和读取都要按顺序来,这意味着性能瓶颈。但有了分区之后,无论是生产者还是消费者,都可以并发的读写4个分区:</p><p><img src="/images/image-20220804232604153.png"></p><p>而且从数据目录中可以看到,有一个kafka自己创建的主题<code>__consumer_offsets</code>,足足有50个分区。</p><p>这个主题从命名上不难猜出,它是用来保存消费者offset的。当消费者消费了一条数据时,就要向broker提交offset,告诉broker我成功消费到这里了,broker就会将offset保存到<code>__consumer_offsets</code>主题中。</p><p>如果此时消费者重启了,就可以从<code>__consumer_offsets</code>中找到上次消费到哪里了,然后继续消费。</p><p>之所谓要有50个分区这么多,因为这个主题要面对所有的消费者(可能有几个甚至几十上百个消费者),分区数不足的话难以支持高并发。</p><p>当一个主题有了多个分区之后,两个消费者如果使用同样的消费组id来消费,和单分区时不一样,此时两个消费者都可以收到消息,且收到的消息是不重复的。如下图所示:</p><p><img src="/images/image-20220804233653616.png"></p><p>这也印证了上面说的并发读写,一个消费者来不及消费的时候,可以使用多个消费者来并发消费。</p><p>但消费者数量并不是可以无限增加的,在同一个消费组中,每一个分区,只能被一个消费者消费(为了保证分区消费顺序),也就是说,消费者数量要小等于分区数,多了也没用。</p><p>我们来总结一下消费者数量与分区数的关系:</p><ol><li><p>消费者数量等于分区数时,每一个消费者分别消费一个分区</p><p> <img src="/images/image-20220807205752257.png"></p></li><li><p>消费者数量大于分区数时,多出来的消费者消费不到数据</p><p> <img src="/images/image-20220807205821217.png"></p></li><li><p>消费者数量小于分区数时,部分消费者会消费多个分区</p><p> <img src="/images/image-20220807205849164.png"></p></li></ol><p>注意:同一个分区中的消息是顺序被消费的,但不同分区之间是没有顺序关系的。</p>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>生产者与消费者</title>
<link href="posts/25147/"/>
<url>posts/25147/</url>
<content type="html"><![CDATA[<h2 id="生产消费模型"><a href="#生产消费模型" class="headerlink" title="生产消费模型"></a>生产消费模型</h2><p>消息队列的使用者主要有两个角色,一个是生产者,一个是消费者。</p><p>顾名思义,生产者是生产数据的,而消费者是使用数据的。</p><p><img src="/images/image-20220804154226191.png"></p><h2 id="创建主题"><a href="#创建主题" class="headerlink" title="创建主题"></a>创建主题</h2><p>一个kafka队列,可能有很多的生产者和消费者,为了能让不同的使用者能够区分出自己所需要的数据,因此有一个主题的概念。</p><p>生产者在生产数据时把数据推送到指定的主题中,而消费者就可以从该主题中拉取数据。</p><p>创建主题只需使用以下命令即可:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 在kafka_2.13-3.2.0/bin目录下执行</span>./kafka-topics.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --create --topic mytopic<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><ul><li><code>--bootstrap-server</code>参数指定了kafka服务地址</li><li><code>--create</code>表示要创建</li><li><code>--topic</code>要创建的主题名</li></ul><p>查看主题列表:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-topics.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --list<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看主题详情:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-topics.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --describe --topic mytopic<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="生产者"><a href="#生产者" class="headerlink" title="生产者"></a>生产者</h2><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-console-producer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>执行以上命令后,就可以输入数据:</p><p><img src="/images/image-20220804160358763.png"></p><h2 id="消费者"><a href="#消费者" class="headerlink" title="消费者"></a>消费者</h2><h3 id="消费数据"><a href="#消费数据" class="headerlink" title="消费数据"></a>消费数据</h3><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic --from-beginninghello worldhello kafka<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>其中<code>--from-beginning</code>表示从头开始消费</p><h3 id="偏移量"><a href="#偏移量" class="headerlink" title="偏移量"></a>偏移量</h3><p>生产者生产的消息是有序的,我们可以把数据想象成是数组上的每一个元素,例如:</p><p><img src="/images/image-20220804161751767.png"></p><p>所谓偏移量,可以理解为这个数组的下标。</p><p>当消费者用<code>--from-beginning</code>参数时,表示从头开始消费,也就是从数组下标为0开始消费。</p><p>如果没有指定这个参数,只会消费最新的消息,也就是说,只有有新数据产生才能消费到</p><p><img src="/images/image-20220804162149536.png"></p><p>看到这里,你很容易想到,我们也可以指定偏移量来进行消费:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># --offset指定偏移量 --partition指定分区</span>./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic --offset <span class="token number">1</span> --partition <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>值得一提的是,指定偏移量时,必须指定分区。</p><p>不了解分区没关系,后面会专门介绍分区的概念,默认分区数量是1个,这里我们指定0分区进行消费</p><h3 id="消费组"><a href="#消费组" class="headerlink" title="消费组"></a>消费组</h3><p>消费数据时可以通过<code>--group</code>参数指定消费组,下面说两个场景:</p><p><strong>场景1:消费者A和消费者B在消费数据时都指定了同一个消费组<code>--group group1</code>:</strong></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic --group group1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>那么生产者在生产数据后,会被A或者B消费到,并且只会被A或B其中一个消费到,这种情况也叫做<strong>单播</strong>,如下图:</p><p><img src="/images/image-20220804165059207.png"></p><p><strong>场景2:消费者A和消费者B在消费数据时指定了不同的消费组:</strong></p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 消费者A,group1</span>./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic --group group1<span class="token comment"># 消费者B,group2</span>./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --topic mytopic --group group2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>生产者生产数据后,消费者A和消费者B都会收到数据,这种情况叫做<strong>多播</strong>,如下图:</p><p><img src="/images/image-20220804165424854.png"></p><p>可以使用以下命令查看消费者组列表:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-consumer-groups.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --list<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>查看消费者组详情:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">./kafka-consumer-groups.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.3:9092 --describe --group group1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="/images/image-20220804172653273.png"></p><p>其中有几个字段比较重要:</p><ul><li>current-offset: 已消费偏移量</li><li>Log-end-offset: 最大偏移量</li><li>lag:未消费数</li></ul><p>如果我现在关掉所有消费者,然后生产数据,消费者组详情就会如下:</p><p><img src="/images/image-20220804173047076.png"></p><p>可以看到最大偏移量为29,已消费到偏移量27,lag=2表示还有2条消息未消费</p>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>使用Minio-Operator搭建MinIO集群</title>
<link href="posts/35914/"/>
<url>posts/35914/</url>
<content type="html"><![CDATA[<h2 id="服务器准备"><a href="#服务器准备" class="headerlink" title="服务器准备"></a>服务器准备</h2><ol><li>准备4台服务器</li><li>安装ubuntu18</li><li>设置hostname分别为node1~node4</li><li>装好k8s(3个master节点,1个node节点)</li></ol><h2 id="磁盘准备"><a href="#磁盘准备" class="headerlink" title="磁盘准备"></a>磁盘准备</h2><p>每台服务器准备n块数据盘,例如4块,格式化为xfs,每一块盘都要执行:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">mkfs.xfs /dev/sdb -L MINIO-DISK1mkfs.xfs /dev/sdc -L MINIO-DISK2mkfs.xfs /dev/sdd -L MINIO-DISK3mkfs.xfs /dev/sde -L MINIO-DISK4<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><code>vim /etc/fstab</code>:</p><pre class="line-numbers language-none"><code class="language-none">LABEL=MINIO-DISK1 /data/minio/data-1 xfs defaults,noatime 0 2LABEL=MINIO-DISK2 /data/minio/data-2 xfs defaults,noatime 0 2LABEL=MINIO-DISK3 /data/minio/data-3 xfs defaults,noatime 0 2LABEL=MINIO-DISK4 /data/minio/data-4 xfs defaults,noatime 0 2<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>挂载</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">mount</span> -a<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>可以考虑下inode数量,使用<code>df -i</code>查看磁盘最大inodes数,将来如果不够用了,可以使用以下命令动态扩容:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 使用10%的容量用于存放inodes</span><span class="token function">sudo</span> xfs_growfs -m <span class="token number">10</span> /data/minio/data-1/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="创建minio存储pv"><a href="#创建minio存储pv" class="headerlink" title="创建minio存储pv"></a>创建minio存储pv</h2><p>给每一个磁盘创建一个pv,假设我们有4台服务器,每一台服务器有4个硬盘。</p><p>创建pv有两种方式,一种是使用官方提供的<a href="https://github.com/minio/directpv">minio/directpv</a>,它会自动识别硬盘并创建pv,本人没用过,就不提了。</p><p>另一种是手动创建pv,具体步骤如下:</p><p>创建<code>minio-storageclass.yaml</code>,并执行<code>kubectl apply -f minio-storageclass.yaml</code></p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> storage.k8s.io/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> StorageClass<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>local<span class="token punctuation">-</span>storage<span class="token key atrule">provisioner</span><span class="token punctuation">:</span> kubernetes.io/no<span class="token punctuation">-</span>provisioner<span class="token key atrule">volumeBindingMode</span><span class="token punctuation">:</span> WaitForFirstConsumer<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>前面已经将硬盘都挂载到4台服务器上了,目录分别为</p><ol><li>/data/minio/data-1</li><li>/data/minio/data-2</li><li>/data/minio/data-3</li><li>/data/minio/data-4</li></ol><p>给16个硬盘创建16个pv的yaml文件,并执行<code>kubectl apply -f pv-xxx.yaml</code>,其中node1的第一个pv内容如下:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> PersistentVolume<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>pv<span class="token punctuation">-</span>node1<span class="token punctuation">-</span><span class="token number">1</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">capacity</span><span class="token punctuation">:</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> 1Ti <span class="token key atrule">volumeMode</span><span class="token punctuation">:</span> Filesystem <span class="token key atrule">accessModes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ReadWriteOnce <span class="token key atrule">persistentVolumeReclaimPolicy</span><span class="token punctuation">:</span> Retain <span class="token comment"># 删除持久卷时保留数据</span> <span class="token key atrule">storageClassName</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>local<span class="token punctuation">-</span>storage <span class="token key atrule">local</span><span class="token punctuation">:</span> <span class="token key atrule">path</span><span class="token punctuation">:</span> /data/minio/data<span class="token punctuation">-</span><span class="token number">1</span> <span class="token key atrule">nodeAffinity</span><span class="token punctuation">:</span> <span class="token key atrule">required</span><span class="token punctuation">:</span> <span class="token key atrule">nodeSelectorTerms</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">matchExpressions</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">key</span><span class="token punctuation">:</span> kubernetes.io/hostname <span class="token key atrule">operator</span><span class="token punctuation">:</span> In <span class="token key atrule">values</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> node1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其他15个按照以上模板,其中需要修改的字段有:</p><ol><li>metadata.name:pv名称,格式为minio-pv-{node hostname}-{1-4}</li><li>spec.capacity.storage: 磁盘大小</li><li>local.path:对应的磁盘挂载路径,格式为/data/minio-data-{1-4}</li><li>最后一行的node1,当前服务器hostname</li></ol><p>由于pv数量可以比较多,需要修改的部分都是比较有规律的,可以自行写个脚本快速生成。</p><h2 id="创建local-path-pv"><a href="#创建local-path-pv" class="headerlink" title="创建local-path pv"></a>创建local-path pv</h2><p>已经为minio存储创建了pv,为啥还需要local-path pv呢?因为集群部署minio默认会开启审计功能以及prometheus监控,这两个模块也是需要存储的。</p><p>minio数据存储一般都是比较大的硬盘,如果也单独为这两个模块准备大硬盘实在不划算,所以需要为他们单独准备存储。</p><p>如果你有额外的数据盘就再好不过,如果没有,系统盘足够大也可以。</p><p>具体步骤参考:<a href="/posts/16358">使用Pocal-Path-Provisioner动态创建本地磁盘pv</a></p><h2 id="集群部署"><a href="#集群部署" class="headerlink" title="集群部署"></a>集群部署</h2><h3 id="安装Operator"><a href="#安装Operator" class="headerlink" title="安装Operator"></a>安装Operator</h3><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">wget</span> https://github.com/minio/operator/releases/download/v4.4.28/kubectl-minio_4.4.28_linux_amd64 -O kubectl-minio<span class="token function">chmod</span> +x kubectl-minio<span class="token function">mv</span> kubectl-minio /usr/local/bin/<span class="token comment"># 检查安装</span>kubectl minio version<span class="token comment"># 初始化,默认初始到minio-operator这个命名空间</span>kubectl minio init<span class="token comment"># 查看,等服务都Running</span>kubectl get all --namespace minio-operator<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>临时代理一个端口访问operator控制台</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl minio proxy<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>该命令会打印出访问控制台所需要的JWT:</p><p><img src="/images/image-20220828162116334.png"></p><h3 id="创建租户"><a href="#创建租户" class="headerlink" title="创建租户"></a>创建租户</h3><p>我们可以在operator控制台页面上创建租户,基本上是傻瓜式操作,自己看一眼就知道怎么操作了。</p><p>也可以使用命令行方式,但是实测发现命令行方式功能不全,可配置项没有页面上多。</p><p>要用哪种方式自行决定,这里两种方式都介绍一下</p><h4 id="页面方式"><a href="#页面方式" class="headerlink" title="页面方式"></a>页面方式</h4><p>使用上面拿到的地址和JWT登录operator页面,点击右上角<code>create tenant</code>:</p><p><img src="/images/image-20220902214852407.png"></p><p>下图Expose是指用loadbalance方式暴露服务,我没有云负载均衡,所以不勾选:</p><p><img src="/images/image-20220902215052894.png"></p><p>配置镜像版本,可以使用默认:</p><p><img src="/images/image-20220902215122501.png"></p><p>pod调度相关配置:</p><p><img src="/images/image-20220902215146858.png"></p><p>用户相关,可以把这里默认的用户的账号密码记下来,之后登录控制台或s3/sdk访问minio都可以用这个账号。</p><p><img src="/images/image-20220902215223245.png"></p><p>是否启用tls,根据你自己需要选择:</p><p><img src="/images/image-20220902215407842.png"></p><p>是否加密:</p><p><img src="/images/image-20220902215423473.png"></p><p>审计日志,你可以选择关闭,如果启用,storage class选一个别的存储,不要用minio专用磁盘。</p><p>我本地创建了一个local-path的存储,可以参考:<a href="/posts/16358">使用local-path-provisioner动态创建本地磁盘pv</a></p><p><img src="/images/image-20220902215845114.png"></p><p>prometheus监控,与审计日志一样,可以不用,用的话选一个存储:</p><p><img src="/images/image-20220902220022762.png"></p><p>点击右下角创建,记下租户的<code>Access Key</code>和<code>Secret Key</code>,控制台登录或SDK访问就用这个账号密码。</p><p><img src="/images/image-20220902220251699.png"></p><p>这就创建好了,可以通过命令行查看pod状态。</p><h4 id="命令行方式"><a href="#命令行方式" class="headerlink" title="命令行方式"></a>命令行方式</h4><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># 给租户创建单独的namespace</span>kubectl create namespace minio-tenant-1<span class="token comment"># 创建租户</span>kubectl minio tenant create minio-tenant-1 <span class="token punctuation">\</span> --servers <span class="token number">4</span> <span class="token punctuation">\</span> --volumes <span class="token number">16</span> <span class="token punctuation">\</span> --capacity 16Ti <span class="token punctuation">\</span> --storage-class minio-local-storage <span class="token punctuation">\</span> --namespace minio-tenant-1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>常用参数:</p><table><thead><tr><th><strong>参数</strong></th><th><strong>含义</strong></th></tr></thead><tbody><tr><td>–disable-tls</td><td>关闭tls</td></tr><tr><td>enable-audit-logs</td><td>是否启用审计,默认开启。启用审计时,需要准备额外存储给审计用。<br>这里有个奇怪的bug,必须用<code>--enable-audit-logs=false</code>,不能用<code>enable-audit-logs false</code></td></tr><tr><td>servers</td><td>服务器总数</td></tr><tr><td>volumes</td><td>硬盘总数(4台服务器,每台4块盘)</td></tr><tr><td>capacity</td><td>容量总数(假设一块磁盘1T,共16块)</td></tr><tr><td>storage-class</td><td>存储类,使用刚才创建的minio-local-storage</td></tr><tr><td>namespace</td><td>租户命名空间</td></tr></tbody></table><p>其他参数:</p><p><img src="/images/image-20220828170137175.png"></p><p>可以从命令行参数列表中看出命令行工具其实功能是不全的,因此建议使用页面操作。</p><p>创建成功后,会打印出租户的用户名密码,拿个小本本记一下,控制台登录或SDK访问就用这个账号密码。</p><p><img src="/images/image-20220828152633567.png"></p><p>刚执行完create,使用<code>kubectl get pods -n minio-tenant-1</code>可能看不到minio相关pod,需要稍等一小会,minio-operator会自动把相关pod创建出来。</p><h3 id="访问控制台"><a href="#访问控制台" class="headerlink" title="访问控制台"></a>访问控制台</h3><p>minio租户默认情况只能在集群内部访问:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl get svc --namespace minio-tenant-1NAME TYPE CLUSTER-IP EXTERNAL-IP PORT<span class="token punctuation">(</span>S<span class="token punctuation">)</span> AGEminio ClusterIP <span class="token number">10.109</span>.88.X <span class="token operator"><</span>none<span class="token operator">></span> <span class="token number">443</span>/TCP 137mminio-tenant-1-console ClusterIP <span class="token number">10.97</span>.87.X <span class="token operator"><</span>none<span class="token operator">></span> <span class="token number">9090</span>/TCP,9443/TCP 129mminio-tenant-1-hl ClusterIP None <span class="token operator"><</span>none<span class="token operator">></span> <span class="token number">9000</span>/TCP 137m<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>临时访问控制台:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl port-forward --address <span class="token number">0.0</span>.0.0 svc/minio-tenant-1-console -n minio-tenant-1 <span class="token number">9443</span>:9443<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>使用<code>https://ip:9443</code>访问minio控制台,使用之前拿到的账号密码即可登录。</p><p>如果需要永久外部访问控制台,需要手动创建nodePort类型的service(开启了tls):</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Service<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span>1<span class="token punctuation">-</span>console<span class="token punctuation">-</span>external <span class="token key atrule">namespace</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> NodePort <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> https<span class="token punctuation">-</span>console<span class="token punctuation">-</span>external <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">32443</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9443</span> <span class="token key atrule">protocol</span><span class="token punctuation">:</span> TCP <span class="token key atrule">targetPort</span><span class="token punctuation">:</span> <span class="token number">9443</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">v1.min.io/tenant</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用<code>https://ip:32443</code>访问minio控制台</p><p>同理,如果需要外部访问<code>Minio Server</code>,需要创建如下service:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> Service<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>external <span class="token key atrule">namespace</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> NodePort <span class="token key atrule">ports</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> https<span class="token punctuation">-</span>external <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">31900</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9000</span> <span class="token key atrule">protocol</span><span class="token punctuation">:</span> TCP <span class="token key atrule">targetPort</span><span class="token punctuation">:</span> <span class="token number">9000</span> <span class="token key atrule">selector</span><span class="token punctuation">:</span> <span class="token key atrule">v1.min.io/tenant</span><span class="token punctuation">:</span> minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Root账号密码"><a href="#Root账号密码" class="headerlink" title="Root账号密码"></a>Root账号密码</h2><p>前面用的账号密码是创建租户时默认创建的,如果你需要root账号密码,可以用如下方式获取:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl get secret minio-tenant-1-env-configuration -n minio-tenant-1 -o yaml<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>需要base64 decode一下:</p><p><img src="/images/image-20220828180841935.png"></p><h2 id="客户端"><a href="#客户端" class="headerlink" title="客户端"></a>客户端</h2><h3 id="命令行客户端mc"><a href="#命令行客户端mc" class="headerlink" title="命令行客户端mc"></a>命令行客户端mc</h3><p>安装mc</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">wget</span> https://dl.min.io/client/mc/release/linux-amd64/mc<span class="token function">chmod</span> +x <span class="token function">mc</span><span class="token function">sudo</span> <span class="token function">mv</span> <span class="token function">mc</span> /usr/local/bin<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>设置mc连接到当前集群的minio</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">mc</span> <span class="token builtin class-name">alias</span> <span class="token builtin class-name">set</span> <span class="token builtin class-name">local</span> https://10.211.55.3:31900 414F9JEUWCKVK3ZYBTB3 VAobHPxAYebixuGHPzgCtpbtsECcIua5NB2mxcNl --insecure<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>命令中的local只是个名字,你可以改成任意你喜欢的名称,以后mc的任意命令都用这个名称来代指我们部署的这个minio集群。</p><p>这也意味着你可以通过<code>mc alias set</code>配置多个minio集群。</p><p>最后3个参数:</p><ul><li>用户名</li><li>密码</li><li>跳过https证书认证</li></ul><p>然后就可以使用mc命令管理minio了,例如:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 查看local集群中test这个bucket的对象列表</span><span class="token function">mc</span> <span class="token function">ls</span> local/test --insecure<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="SDK"><a href="#SDK" class="headerlink" title="SDK"></a>SDK</h3><p>minio提供了部分语言的SDK客户端,以Java为例,参考官方文档:<a href="https://docs.min.io/minio/baremetal/sdk/java/minio-java.html">Java Client API Reference — MinIO Baremetal Documentation</a></p><h2 id="扩容"><a href="#扩容" class="headerlink" title="扩容"></a>扩容</h2><p>要求扩容的机器和硬盘是原来的倍数。</p><p>假设新增4台服务器,每台4块硬盘,每块硬盘1T,给新的服务器的每一个硬盘创建pv,参考部署步骤。</p><p>可以用operator页面上扩容(推荐):</p><p><img src="/images/image-20220902221644693.png"></p><p>也可以用扩容命令:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml">kubectl minio tenant expand minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span>1 \ <span class="token punctuation">-</span><span class="token punctuation">-</span>servers 4 \ <span class="token punctuation">-</span><span class="token punctuation">-</span>volumes 16 \ <span class="token punctuation">-</span><span class="token punctuation">-</span>capacity 16Ti \ <span class="token punctuation">-</span><span class="token punctuation">-</span>storage<span class="token punctuation">-</span>class minio<span class="token punctuation">-</span>local<span class="token punctuation">-</span>storage \ <span class="token punctuation">-</span><span class="token punctuation">-</span>namespace minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span> <span class="token comment"># 查看扩容后的情况</span> kubectl minio tenant info minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span>1 \ <span class="token punctuation">-</span><span class="token punctuation">-</span>namespace minio<span class="token punctuation">-</span>tenant<span class="token punctuation">-</span><span class="token number">1</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="数据恢复"><a href="#数据恢复" class="headerlink" title="数据恢复"></a>数据恢复</h2><h3 id="磁盘损坏"><a href="#磁盘损坏" class="headerlink" title="磁盘损坏"></a>磁盘损坏</h3><p>当磁盘损坏时,只需更换新磁盘,minio会自动修复,具体步骤如下:</p><p>卸载损坏的磁盘</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 假设损坏的磁盘是/dev/sdb</span><span class="token function">umount</span> /dev/sdb<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>换上新的磁盘,格式化并挂载</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token comment"># 根据实际情况修改命令</span>mkfs.xfs /dev/sdb -L MINIO-DISK1<span class="token comment"># vim /etc/fstab</span><span class="token assign-left variable">LABEL</span><span class="token operator">=</span>MINIO-DISK1 /data/minio-data-1 xfs defaults,noatime <span class="token number">0</span> <span class="token number">2</span><span class="token comment"># 挂载</span><span class="token function">mount</span> -a<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>使用<code>mc admin console</code>观察日志,minio会自动发现新的磁盘并修复数据</p><p>使用以下命令监控修复:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash"><span class="token function">mc</span> admin heal<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="节点损坏"><a href="#节点损坏" class="headerlink" title="节点损坏"></a>节点损坏</h3><p>节点损坏是指整个服务器损坏无法使用,只需给该节点装好系统,将节点加入原k8s集群,然后按照修复磁盘的步骤修复即可</p><h2 id="升级minio"><a href="#升级minio" class="headerlink" title="升级minio"></a>升级minio</h2><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl minio tenant upgrade minio-tenant-1 <span class="token punctuation">\</span>--namespace minio-tenant-1 <span class="token punctuation">\</span>--image minio/minio:RELEASE.2022-08-25T07-17-05Z<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>执行完你可能看不到从pod上看不到什么反应,不要慌,它背后在偷偷升级呢,过一会你会看到pod被重新创建了,然后describe pod可以看到使用了新镜像</p><h2 id="删除租户"><a href="#删除租户" class="headerlink" title="删除租户"></a>删除租户</h2><p>如果使用的pv是recycle或delete策略,删除租户也会删除数据。</p><p>我上面的例子用的是retain策略,也就是保留数据。</p><p>删除命令如下:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">kubectl minio tenant delete minio-tenant-1 --namespace minio-tenant-1<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>]]></content>
<categories>
<category> 云原生 </category>
</categories>
<tags>
<tag> 云原生 </tag>
<tag> k8s </tag>
</tags>
</entry>
<entry>
<title>使用Prometheus-Operator搭建监控集群</title>
<link href="posts/5019/"/>
<url>posts/5019/</url>
<content type="html"><![CDATA[<h2 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h2><h3 id="下载配置清单"><a href="#下载配置清单" class="headerlink" title="下载配置清单"></a>下载配置清单</h3><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">git</span> clone https://github.com/prometheus-operator/kube-prometheus<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>根据你K8S版本,<code>git checkout</code>到匹配的分支</p><p><img src="/images/image-20220729185942085.png"></p><h3 id="修改service"><a href="#修改service" class="headerlink" title="修改service"></a>修改service</h3><p>修改<code>manifests/prometheus-service.yaml</code>,将service类型改成nodeport,不改的话默认是clusterIP类型,集群外无法访问</p><p><img src="/images/image-20220730162054007.png"></p><p>同样修改manifests/grafana-service.yaml:</p><p><img src="/images/image-20220730161930168.png"></p><p>找到<code>grafana-networkPolicy.yaml</code>和<code>prometheus-networkPolicy.yaml</code>,可以考虑删除这两个文件。</p><p>我在这里踩了一个坑:由于我用的是云服务器,用公网ip一直无法访问nodeport暴露的grafana和prometheus,最后发现是这两个文件中的网络策略限制了。如果不是云服务器应该就没这个问题。</p><h3 id="修改存储"><a href="#修改存储" class="headerlink" title="修改存储"></a>修改存储</h3><p>Grafana和Prometheus默认未持久化存储,重启pod数据就丢失了。</p><p>修改<code>prometheus-prometheus.yaml</code>,在spec下增加pvc:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> <span class="token key atrule">volumeClaimTemplate</span><span class="token punctuation">:</span> <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">storageClassName</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token key atrule">resources</span><span class="token punctuation">:</span> <span class="token key atrule">requests</span><span class="token punctuation">:</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> 100Gi<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其中<code>local-path</code>是我创建的一个本地存储的storageClass,具体可以参考<a href="/posts/16358">使用local-path-provisioner动态创建本地磁盘pv</a></p><p>Grafana是deployment方式部署的,无法像Prometheus那样直接添加pvc,需要我们手动创建,我们把它放在manifests/setup目录下</p><p><code>setup/grafana-pvc.yaml</code>:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">kind</span><span class="token punctuation">:</span> PersistentVolumeClaim<span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> grafana <span class="token key atrule">namespace</span><span class="token punctuation">:</span> monitoring<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">storageClassName</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token key atrule">accessModes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> ReadWriteOnce <span class="token key atrule">resources</span><span class="token punctuation">:</span> <span class="token key atrule">requests</span><span class="token punctuation">:</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> 10Gi<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>修改<code>grafana-deployment.yaml</code>,找到grafana-storage,将emptyDir改成如下内容:</p><p><img src="/images/image-20220730174403704.png"></p><h3 id="修改权限"><a href="#修改权限" class="headerlink" title="修改权限"></a>修改权限</h3><p>这是一个坑,<code>prometheus-clusterRole.yaml</code>中默认给的权限不足,导致只能无法获取pod列表,某些场景下会有问题。</p><p>例如我在部署kafka集群时,使用pod-monitor方式让prometheus自动发现kafka要监控的一些pod,但是prometheus没有list pods权限,就无法监控了。</p><p>修改完如下:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> rbac.authorization.k8s.io/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> ClusterRole<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app.kubernetes.io/component</span><span class="token punctuation">:</span> prometheus <span class="token key atrule">app.kubernetes.io/instance</span><span class="token punctuation">:</span> k8s <span class="token key atrule">app.kubernetes.io/name</span><span class="token punctuation">:</span> prometheus <span class="token key atrule">app.kubernetes.io/part-of</span><span class="token punctuation">:</span> kube<span class="token punctuation">-</span>prometheus <span class="token key atrule">app.kubernetes.io/version</span><span class="token punctuation">:</span> 2.37.0 <span class="token key atrule">name</span><span class="token punctuation">:</span> prometheus<span class="token punctuation">-</span>k8s<span class="token key atrule">rules</span><span class="token punctuation">:</span><span class="token punctuation">-</span> <span class="token key atrule">apiGroups</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token string">""</span> <span class="token key atrule">resources</span><span class="token punctuation">:</span> <span class="token comment"># 这里要改</span> <span class="token punctuation">-</span> nodes/metrics <span class="token punctuation">-</span> services <span class="token punctuation">-</span> endpoints <span class="token punctuation">-</span> pods <span class="token key atrule">verbs</span><span class="token punctuation">:</span> <span class="token comment"># 这里要改</span> <span class="token punctuation">-</span> get <span class="token punctuation">-</span> list <span class="token punctuation">-</span> watch<span class="token punctuation">-</span> <span class="token key atrule">nonResourceURLs</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> /metrics <span class="token key atrule">verbs</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> get<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># 安装</span>kubectl apply --server-side -f manifests/setup<span class="token comment"># 等待上一步自定义的资源都创建完成</span><span class="token keyword">until</span> kubectl get servicemonitors --all-namespaces <span class="token punctuation">;</span> <span class="token keyword">do</span> <span class="token function">date</span><span class="token punctuation">;</span> <span class="token function">sleep</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token builtin class-name">echo</span> <span class="token string">""</span><span class="token punctuation">;</span> <span class="token keyword">done</span><span class="token comment"># 启动相关组件</span>kubectl apply -f manifests/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="启停服务"><a href="#启停服务" class="headerlink" title="启停服务"></a>启停服务</h2><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># 启动服务</span>kubectl apply -f manifests/<span class="token comment"># 停止服务</span>kubectl delete -f manifests/<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 云原生 </category>
</categories>
<tags>
<tag> 云原生 </tag>
<tag> k8s </tag>
</tags>
</entry>
<entry>
<title>使用Strimzi-Kafka-Operator搭建kafka集群</title>
<link href="posts/55588/"/>
<url>posts/55588/</url>
<content type="html"><![CDATA[<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><h3 id="安装Kafka-Operator"><a href="#安装Kafka-Operator" class="headerlink" title="安装Kafka-Operator"></a>安装Kafka-Operator</h3><p>创建命名空间</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl create namespace kafka<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>下载<code>https://strimzi.io/install/latest?namespace=kafka</code>并重命名为install.yaml,执行安装</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl create -f install.yaml -n kafka<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="创建kafka集群"><a href="#创建kafka集群" class="headerlink" title="创建kafka集群"></a>创建kafka集群</h3><p>下载<code>https://strimzi.io/examples/latest/kafka/kafka-persistent-single.yaml</code>,修改内容:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> kafka.strimzi.io/v1beta2<span class="token key atrule">kind</span><span class="token punctuation">:</span> Kafka<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> mykafka <span class="token comment"># 集群名称</span> <span class="token key atrule">namespace</span><span class="token punctuation">:</span> kafka <span class="token comment"># 增加一行命名空间</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">kafka</span><span class="token punctuation">:</span> <span class="token key atrule">version</span><span class="token punctuation">:</span> 3.2.0 <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token comment"># kafka节点数</span> <span class="token key atrule">listeners</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> plain <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9092</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> tls <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9093</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">config</span><span class="token punctuation">:</span> <span class="token comment"># 根据情况修改</span> <span class="token key atrule">offsets.topic.replication.factor</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">transaction.state.log.replication.factor</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">transaction.state.log.min.isr</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">default.replication.factor</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">min.insync.replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">inter.broker.protocol.version</span><span class="token punctuation">:</span> <span class="token string">"3.2"</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> jbod <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> <span class="token number">0</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> persistent<span class="token punctuation">-</span>claim <span class="token key atrule">class</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token comment"># 使用local-path这个storageclass</span> <span class="token key atrule">size</span><span class="token punctuation">:</span> 100Gi <span class="token key atrule">deleteClaim</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">zookeeper</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token comment"># zk节点数</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> persistent<span class="token punctuation">-</span>claim <span class="token key atrule">class</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token comment"># 使用local-path这个storageclass</span> <span class="token key atrule">size</span><span class="token punctuation">:</span> 100Gi <span class="token key atrule">deleteClaim</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">entityOperator</span><span class="token punctuation">:</span> <span class="token key atrule">topicOperator</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token key atrule">userOperator</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>注意用#注释的部分,视情况修改。其中namespace尤其重要,一定要填</p><p>创建集群:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl apply -f kafka-persistent-single.yaml<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>不要在意<code>kafka-persistent-single.yaml</code>这个文件的命名,官方给的example就是这个名,你可以更改为任意名字。</p><p>查看pods:</p><p><img src="/images/image-20220728232158853.png"></p><p>查看svc:</p><p><img src="/images/image-20220728232233016.png"></p><p>集群内使用连接kafka只需只用<code>mykafka-kafka-bootstrap:9092</code>,例如生产数据:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl -n kafka run kafka-producer -ti <span class="token punctuation">\</span> --image<span class="token operator">=</span>quay.io/strimzi/kafka:0.30.0-kafka-3.2.0 <span class="token punctuation">\</span> --rm<span class="token operator">=</span>true --restart<span class="token operator">=</span>Never -- bin/kafka-console-producer.sh <span class="token punctuation">\</span> --bootstrap-server mykafka-kafka-bootstrap:9092 <span class="token punctuation">\</span> --topic my-topic<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>消费数据:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl -n kafka run kafka-consumer -ti <span class="token punctuation">\</span> --image<span class="token operator">=</span>quay.io/strimzi/kafka:0.30.0-kafka-3.2.0 <span class="token punctuation">\</span> --rm<span class="token operator">=</span>true --restart<span class="token operator">=</span>Never -- bin/kafka-console-consumer.sh <span class="token punctuation">\</span> --bootstrap-server mykafka-kafka-bootstrap:9092 <span class="token punctuation">\</span> --topic my-topic --from-beginning<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="对外暴露服务"><a href="#对外暴露服务" class="headerlink" title="对外暴露服务"></a>对外暴露服务</h3><p>安装好集群默认只能k8s集群内部访问,如果需要对外暴露,修改配置文件listeners,如下:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">listeners</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> plain <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9092</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> tls <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9093</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> external <span class="token comment"># 增加外部访问用的linstener</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9094</span> <span class="token comment">#端口</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> nodeport <span class="token comment"># nodeport类型</span> <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">configuration</span><span class="token punctuation">:</span> <span class="token key atrule">bootstrap</span><span class="token punctuation">:</span> <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">32094</span> <span class="token comment"># 指定nodeport端口,不指定会随机分配</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行<code>kubectl apply -f kafka-persistent-single.yaml</code>使配置生效,kafka operator会自动帮你重启kafka。</p><p>查看svc:</p><p><img src="/images/image-20220729143920528.png"></p><p>也可以使用<code>kubectl get kafka -n kafka -o yaml</code>看到监听信息:</p><p><img src="/images/image-20220729144142604.png"></p><p>上图中的<code>10.211.55.5</code>等三个ip是我服务器的ip。为了验证外部可访问,我另外找了一台机器,下载了一个原生的kafka包,消费一下:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">./kafka-console-consumer.sh --bootstrap-server<span class="token operator">=</span><span class="token number">10.211</span>.55.5:32094,10.211.55.4:32094,10.211.55.3:32094 <span class="token punctuation">\</span>--topic my-topic --from-beginning<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>消费成功:</p><p><img src="/images/image-20220729144914260.png"></p><p>但如果你是用云服务,有一点需要注意:以上对外暴露的方法,只能使用云服务器的内网ip进行访问,无法使用公网ip访问,因为对于云服务器本身来说,它是感知不到公网ip的,而且安装k8s时用的也是内网ip进行安装。</p><p>为了能够用公网ip进行访问,可以按如下方法配置:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">listeners</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> plain <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9092</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> tls <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9093</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> external <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9094</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> nodeport <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">configuration</span><span class="token punctuation">:</span> <span class="token key atrule">bootstrap</span><span class="token punctuation">:</span> <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">32094</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> pubexternal <span class="token comment"># 公网访问的</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9095</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> nodeport <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">configuration</span><span class="token punctuation">:</span> <span class="token key atrule">bootstrap</span><span class="token punctuation">:</span> <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">32095</span> <span class="token comment"># nodeport端口</span> <span class="token key atrule">brokers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">broker</span><span class="token punctuation">:</span> <span class="token number">0</span> <span class="token key atrule">advertisedHost</span><span class="token punctuation">:</span> 10.10.10.1 <span class="token comment"># 公网ip</span> <span class="token punctuation">-</span> <span class="token key atrule">broker</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">advertisedHost</span><span class="token punctuation">:</span> 10.10.10.2 <span class="token comment"># 公网ip</span> <span class="token punctuation">-</span> <span class="token key atrule">broker</span><span class="token punctuation">:</span> <span class="token number">2</span> <span class="token key atrule">advertisedHost</span><span class="token punctuation">:</span> 10.10.10.3 <span class="token comment"># 公网ip</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>kubectl get kafka -n kafka -o yaml</code>看到监听信息:</p><p><img src="/images/image-20220729150338610.png"></p><h3 id="Broker配置"><a href="#Broker配置" class="headerlink" title="Broker配置"></a>Broker配置</h3><p>安装集群时用的yaml中就包含了kafka broker的配置:</p><p><img src="/images/image-20220729151519036.png"></p><p>具体配置项直接参考kafka官网:<a href="https://kafka.apache.org/documentation/#brokerconfigs">Apache Kafka</a></p><p>不过有小部分配置项是不支持在这里修改:</p><p><img src="/images/image-20220729151652203.png"></p><p>为了验证配置,我们将“自动创建topic”设置为false:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">config</span><span class="token punctuation">:</span> <span class="token key atrule">auto.create.topics.enable</span><span class="token punctuation">:</span> <span class="token boolean important">false</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>kafka启动时日志会打印配置,可以查看日志:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl logs mykafka-kafka-0 -n kafka<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>使用不存在的topic来生产数据,发现报<code>UNKNOWN_TOPIC_OR_PARTITION</code>错误,可见配置生效</p><p><img src="/images/image-20220729152800426.png"></p><h2 id="Operator"><a href="#Operator" class="headerlink" title="Operator"></a>Operator</h2><p>我们安装了Operator之后,通过<code>kubectl get pods -n kafka</code>可以看到有两个operator的pod:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">AME READY STATUS RESTARTS AGEmykafka-entity-operator-df697469d-lmwtq <span class="token number">3</span>/3 Running <span class="token number">0</span> 11hmykafka-kafka-0 <span class="token number">1</span>/1 Running <span class="token number">0</span> 11hmykafka-kafka-exporter-5db54494d6-f2mm8 <span class="token number">1</span>/1 Running <span class="token number">0</span> 11hmykafka-zookeeper-0 <span class="token number">1</span>/1 Running <span class="token number">0</span> 11hstrimzi-cluster-operator-8677464d48-7dgf7 <span class="token number">1</span>/1 Running <span class="token number">0</span> 32h<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其中cluster-operator就是帮助我们创建集群的,而entity-operator中包含了topic operator和user operator</p><h3 id="TopicOperator"><a href="#TopicOperator" class="headerlink" title="TopicOperator"></a>TopicOperator</h3><p>topic operator的架构图如下:</p><p><img src="/images/image-20220731085721038.png">我们可以通过创建一个KafkaTopic类型的K8S资源,来让TopicOperator帮你自动创建主题:</p><p><code>topic.yaml</code>:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> kafka.strimzi.io/v1beta2<span class="token key atrule">kind</span><span class="token punctuation">:</span> KafkaTopic<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> test<span class="token punctuation">-</span>topic <span class="token comment">#主题名称</span> <span class="token key atrule">namespace</span><span class="token punctuation">:</span> kafka <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">strimzi.io/cluster</span><span class="token punctuation">:</span> <span class="token string">"mykafka"</span> <span class="token comment"># 集群名</span><span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">partitions</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token comment"># 分区数</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token comment"># 副本数</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl apply -f topic.yaml<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>比较牛逼的是,如果你以后想增加主题的分区数,只需要修改topic.yaml中的<code>spec.partitions</code>再apply即可。注意分区数只能增加,不能减少,replicas不能修改。</p><p>要小心的是,如果你执行了<code>kubectl delete -f topic.yaml</code>,这个主题就被删掉了,因此如果你使用这种方式来管理topic,一定要小心。</p><h3 id="UserOperator"><a href="#UserOperator" class="headerlink" title="UserOperator"></a>UserOperator</h3><p>同理,user operator可以帮助你管理kafka用户,可以参考官方给的例子:<a href="https://github.com/strimzi/strimzi-kafka-operator/blob/main/examples/user/kafka-user.yaml">strimzi-kafka-operator/kafka-user.yaml</a></p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> kafka.strimzi.io/v1beta2<span class="token key atrule">kind</span><span class="token punctuation">:</span> KafkaUser<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>user <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">strimzi.io/cluster</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>cluster<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">authentication</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> tls <span class="token key atrule">authorization</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> simple <span class="token key atrule">acls</span><span class="token punctuation">:</span> <span class="token comment"># Example consumer Acls for topic my-topic using consumer group my-group</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> topic <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>topic <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Read <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> topic <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>topic <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Describe <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> group <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>group <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Read <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span> <span class="token comment"># Example Producer Acls for topic my-topic</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> topic <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>topic <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Write <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> topic <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>topic <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Create <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span> <span class="token punctuation">-</span> <span class="token key atrule">resource</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> topic <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>topic <span class="token key atrule">patternType</span><span class="token punctuation">:</span> literal <span class="token key atrule">operation</span><span class="token punctuation">:</span> Describe <span class="token key atrule">host</span><span class="token punctuation">:</span> <span class="token string">"*"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="Kafka-Bridge"><a href="#Kafka-Bridge" class="headerlink" title="Kafka Bridge"></a>Kafka Bridge</h2><p>这是一个通过http来使用kafka的桥接组件,我们可以创建一个bridge:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">kind</span><span class="token punctuation">:</span> KafkaBridge<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> my<span class="token punctuation">-</span>bridge <span class="token key atrule">namespace</span><span class="token punctuation">:</span> kafka<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">bootstrapServers</span><span class="token punctuation">:</span> mykafka<span class="token punctuation">-</span>kafka<span class="token punctuation">-</span>bootstrap<span class="token punctuation">:</span><span class="token number">9092</span> <span class="token key atrule">http</span><span class="token punctuation">:</span> <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">8080</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后会多一个bridge的pod,为了方便测试,我们直接使用<code>kubectl get pods -n kafka -o wide</code>查看pod的ip(你也可以创建service)。</p><p>然后使用http接口查看bridge支持哪些api:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">curl</span> <span class="token number">100.72</span>.162.179:8080/openapi<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这个接口会返回一个JSON格式的openapi,从中可以看到所有api定义。</p><p>例如查看topic列表:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">curl</span> -s <span class="token number">100.72</span>.162.179:8080/topics <span class="token operator">|</span> jq <span class="token builtin class-name">.</span><span class="token punctuation">[</span> <span class="token string">"__strimzi_store_topic"</span>, <span class="token string">"test-topic"</span>, <span class="token string">"__strimzi-topic-operator-kstreams-topic-store-changelog"</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>查看指定topic:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">curl</span> -s <span class="token number">100.72</span>.162.179:8080/topics/test-topic <span class="token operator">|</span> jq <span class="token builtin class-name">.</span><span class="token punctuation">{</span> <span class="token string">"name"</span><span class="token builtin class-name">:</span> <span class="token string">"test-topic"</span>, <span class="token string">"configs"</span><span class="token builtin class-name">:</span> <span class="token punctuation">{</span> <span class="token string">"compression.type"</span><span class="token builtin class-name">:</span> <span class="token string">"producer"</span>, <span class="token string">"leader.replication.throttled.replicas"</span><span class="token builtin class-name">:</span> <span class="token string">""</span>, <span class="token string">"message.downconversion.enable"</span><span class="token builtin class-name">:</span> <span class="token string">"true"</span>, <span class="token string">"min.insync.replicas"</span><span class="token builtin class-name">:</span> <span class="token string">"1"</span>, <span class="token string">"segment.jitter.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"0"</span>, <span class="token string">"cleanup.policy"</span><span class="token builtin class-name">:</span> <span class="token string">"delete"</span>, <span class="token string">"flush.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"9223372036854775807"</span>, <span class="token string">"follower.replication.throttled.replicas"</span><span class="token builtin class-name">:</span> <span class="token string">""</span>, <span class="token string">"segment.bytes"</span><span class="token builtin class-name">:</span> <span class="token string">"1073741824"</span>, <span class="token string">"retention.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"604800000"</span>, <span class="token string">"flush.messages"</span><span class="token builtin class-name">:</span> <span class="token string">"9223372036854775807"</span>, <span class="token string">"message.format.version"</span><span class="token builtin class-name">:</span> <span class="token string">"3.0-IV1"</span>, <span class="token string">"max.compaction.lag.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"9223372036854775807"</span>, <span class="token string">"file.delete.delay.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"60000"</span>, <span class="token string">"max.message.bytes"</span><span class="token builtin class-name">:</span> <span class="token string">"1048588"</span>, <span class="token string">"min.compaction.lag.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"0"</span>, <span class="token string">"message.timestamp.type"</span><span class="token builtin class-name">:</span> <span class="token string">"CreateTime"</span>, <span class="token string">"preallocate"</span><span class="token builtin class-name">:</span> <span class="token string">"false"</span>, <span class="token string">"min.cleanable.dirty.ratio"</span><span class="token builtin class-name">:</span> <span class="token string">"0.5"</span>, <span class="token string">"index.interval.bytes"</span><span class="token builtin class-name">:</span> <span class="token string">"4096"</span>, <span class="token string">"unclean.leader.election.enable"</span><span class="token builtin class-name">:</span> <span class="token string">"false"</span>, <span class="token string">"retention.bytes"</span><span class="token builtin class-name">:</span> <span class="token string">"-1"</span>, <span class="token string">"delete.retention.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"86400000"</span>, <span class="token string">"segment.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"604800000"</span>, <span class="token string">"message.timestamp.difference.max.ms"</span><span class="token builtin class-name">:</span> <span class="token string">"9223372036854775807"</span>, <span class="token string">"segment.index.bytes"</span><span class="token builtin class-name">:</span> <span class="token string">"10485760"</span> <span class="token punctuation">}</span>, <span class="token string">"partitions"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"partition"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"replicas"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"broker"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> true, <span class="token string">"in_sync"</span><span class="token builtin class-name">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span>, <span class="token punctuation">{</span> <span class="token string">"partition"</span><span class="token builtin class-name">:</span> <span class="token number">1</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"replicas"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"broker"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> true, <span class="token string">"in_sync"</span><span class="token builtin class-name">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span>, <span class="token punctuation">{</span> <span class="token string">"partition"</span><span class="token builtin class-name">:</span> <span class="token number">2</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"replicas"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"broker"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> true, <span class="token string">"in_sync"</span><span class="token builtin class-name">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span>, <span class="token punctuation">{</span> <span class="token string">"partition"</span><span class="token builtin class-name">:</span> <span class="token number">3</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"replicas"</span><span class="token builtin class-name">:</span> <span class="token punctuation">[</span> <span class="token punctuation">{</span> <span class="token string">"broker"</span><span class="token builtin class-name">:</span> <span class="token number">0</span>, <span class="token string">"leader"</span><span class="token builtin class-name">:</span> true, <span class="token string">"in_sync"</span><span class="token builtin class-name">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>更多信息参考官网:<a href="https://strimzi.io/docs/bridge/latest/">Strimzi Kafka Bridge Documentation (0.21.6)</a></p><h2 id="监控"><a href="#监控" class="headerlink" title="监控"></a>监控</h2><h3 id="部署监控系统"><a href="#部署监控系统" class="headerlink" title="部署监控系统"></a>部署监控系统</h3><p>首先按照<a href="/posts/5019">使用Prometheus-Operator搭建监控集群</a>先部署好一套监控系统</p><h3 id="修改配置"><a href="#修改配置" class="headerlink" title="修改配置"></a>修改配置</h3><p>根据官方提供的<a href="https://github.com/strimzi/strimzi-kafka-operator/blob/main/examples/metrics/kafka-metrics.yaml">kafka-metrics.yaml</a>修改之前创建kafka集群时用的<code>kafka-persistent-single.yaml</code>,修改后如下(注意看注释的部分,其他部分和文章前半部分有所不同,因为我的试验环境换了):</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> kafka.strimzi.io/v1beta2<span class="token key atrule">kind</span><span class="token punctuation">:</span> Kafka<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> real <span class="token key atrule">namespace</span><span class="token punctuation">:</span> kafka<span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">kafka</span><span class="token punctuation">:</span> <span class="token key atrule">version</span><span class="token punctuation">:</span> 3.2.0 <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">listeners</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> plain <span class="token key atrule">type</span><span class="token punctuation">:</span> internal <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9092</span> <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> external <span class="token key atrule">type</span><span class="token punctuation">:</span> nodeport <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">32094</span> <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">configuration</span><span class="token punctuation">:</span> <span class="token key atrule">bootstrap</span><span class="token punctuation">:</span> <span class="token key atrule">nodePort</span><span class="token punctuation">:</span> <span class="token number">32094</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> tls <span class="token key atrule">port</span><span class="token punctuation">:</span> <span class="token number">9093</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> nodeport <span class="token key atrule">tls</span><span class="token punctuation">:</span> <span class="token boolean important">true</span> <span class="token key atrule">config</span><span class="token punctuation">:</span> <span class="token key atrule">offsets.topic.replication.factor</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">transaction.state.log.replication.factor</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">transaction.state.log.min.isr</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">default.replication.factor</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">min.insync.replicas</span><span class="token punctuation">:</span> <span class="token number">1</span> <span class="token key atrule">inter.broker.protocol.version</span><span class="token punctuation">:</span> <span class="token string">"3.2"</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> jbod <span class="token key atrule">volumes</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">id</span><span class="token punctuation">:</span> <span class="token number">0</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> persistent<span class="token punctuation">-</span>claim <span class="token key atrule">size</span><span class="token punctuation">:</span> 100Gi <span class="token key atrule">class</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token key atrule">deleteClaim</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">metricsConfig</span><span class="token punctuation">:</span> <span class="token comment"># kafka监控</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> jmxPrometheusExporter <span class="token key atrule">valueFrom</span><span class="token punctuation">:</span> <span class="token key atrule">configMapKeyRef</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> kafka<span class="token punctuation">-</span>metrics <span class="token key atrule">key</span><span class="token punctuation">:</span> kafka<span class="token punctuation">-</span>metrics<span class="token punctuation">-</span>config.yml <span class="token key atrule">zookeeper</span><span class="token punctuation">:</span> <span class="token key atrule">replicas</span><span class="token punctuation">:</span> <span class="token number">3</span> <span class="token key atrule">storage</span><span class="token punctuation">:</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> persistent<span class="token punctuation">-</span>claim <span class="token key atrule">size</span><span class="token punctuation">:</span> 100Gi <span class="token key atrule">class</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token key atrule">deleteClaim</span><span class="token punctuation">:</span> <span class="token boolean important">false</span> <span class="token key atrule">metricsConfig</span><span class="token punctuation">:</span> <span class="token comment"># zk监控</span> <span class="token key atrule">type</span><span class="token punctuation">:</span> jmxPrometheusExporter <span class="token key atrule">valueFrom</span><span class="token punctuation">:</span> <span class="token key atrule">configMapKeyRef</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> kafka<span class="token punctuation">-</span>metrics <span class="token key atrule">key</span><span class="token punctuation">:</span> zookeeper<span class="token punctuation">-</span>metrics<span class="token punctuation">-</span>config.yml <span class="token key atrule">entityOperator</span><span class="token punctuation">:</span> <span class="token key atrule">topicOperator</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token key atrule">userOperator</span><span class="token punctuation">:</span> <span class="token punctuation">{</span><span class="token punctuation">}</span> <span class="token key atrule">kafkaExporter</span><span class="token punctuation">:</span> <span class="token comment"># 增加kafka exporter监控</span> <span class="token key atrule">topicRegex</span><span class="token punctuation">:</span> <span class="token string">".*"</span> <span class="token key atrule">groupRegex</span><span class="token punctuation">:</span> <span class="token string">".*"</span><span class="token comment"># 以下都是新增的</span><span class="token punctuation">---</span><span class="token key atrule">kind</span><span class="token punctuation">:</span> ConfigMap<span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> kafka<span class="token punctuation">-</span>metrics <span class="token key atrule">namespace</span><span class="token punctuation">:</span> kafka <span class="token comment"># 这里要加上命名空间</span> <span class="token key atrule">labels</span><span class="token punctuation">:</span> <span class="token key atrule">app</span><span class="token punctuation">:</span> strimzi<span class="token key atrule">data</span><span class="token punctuation">:</span> <span class="token key atrule">kafka-metrics-config.yml</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token scalar string"> # See https://github.com/prometheus/jmx_exporter for more info about JMX Prometheus Exporter metrics lowercaseOutputName: true rules: # Special cases and very specific rules - pattern: kafka.server<type=(.+), name=(.+), clientId=(.+), topic=(.+), partition=(.*)><>Value name: kafka_server_$1_$2 type: GAUGE labels: clientId: "$3" topic: "$4" partition: "$5" - pattern: kafka.server<type=(.+), name=(.+), clientId=(.+), brokerHost=(.+), brokerPort=(.+)><>Value name: kafka_server_$1_$2 type: GAUGE labels: clientId: "$3" broker: "$4:$5" - pattern: kafka.server<type=(.+), cipher=(.+), protocol=(.+), listener=(.+), networkProcessor=(.+)><>connections name: kafka_server_$1_connections_tls_info type: GAUGE labels: cipher: "$2" protocol: "$3" listener: "$4" networkProcessor: "$5" - pattern: kafka.server<type=(.+), clientSoftwareName=(.+), clientSoftwareVersion=(.+), listener=(.+), networkProcessor=(.+)><>connections name: kafka_server_$1_connections_software type: GAUGE labels: clientSoftwareName: "$2" clientSoftwareVersion: "$3" listener: "$4" networkProcessor: "$5" - pattern: "kafka.server<type=(.+), listener=(.+), networkProcessor=(.+)><>(.+):" name: kafka_server_$1_$4 type: GAUGE labels: listener: "$2" networkProcessor: "$3" - pattern: kafka.server<type=(.+), listener=(.+), networkProcessor=(.+)><>(.+) name: kafka_server_$1_$4 type: GAUGE labels: listener: "$2" networkProcessor: "$3" # Some percent metrics use MeanRate attribute # Ex) kafka.server<type=(KafkaRequestHandlerPool), name=(RequestHandlerAvgIdlePercent)><>MeanRate - pattern: kafka.(\w+)<type=(.+), name=(.+)Percent\w*><>MeanRate name: kafka_$1_$2_$3_percent type: GAUGE # Generic gauges for percents - pattern: kafka.(\w+)<type=(.+), name=(.+)Percent\w*><>Value name: kafka_$1_$2_$3_percent type: GAUGE - pattern: kafka.(\w+)<type=(.+), name=(.+)Percent\w*, (.+)=(.+)><>Value name: kafka_$1_$2_$3_percent type: GAUGE labels: "$4": "$5" # Generic per-second counters with 0-2 key/value pairs - pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*, (.+)=(.+), (.+)=(.+)><>Count name: kafka_$1_$2_$3_total type: COUNTER labels: "$4": "$5" "$6": "$7" - pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*, (.+)=(.+)><>Count name: kafka_$1_$2_$3_total type: COUNTER labels: "$4": "$5" - pattern: kafka.(\w+)<type=(.+), name=(.+)PerSec\w*><>Count name: kafka_$1_$2_$3_total type: COUNTER # Generic gauges with 0-2 key/value pairs - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+), (.+)=(.+)><>Value name: kafka_$1_$2_$3 type: GAUGE labels: "$4": "$5" "$6": "$7" - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+)><>Value name: kafka_$1_$2_$3 type: GAUGE labels: "$4": "$5" - pattern: kafka.(\w+)<type=(.+), name=(.+)><>Value name: kafka_$1_$2_$3 type: GAUGE # Emulate Prometheus 'Summary' metrics for the exported 'Histogram's. # Note that these are missing the '_sum' metric! - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+), (.+)=(.+)><>Count name: kafka_$1_$2_$3_count type: COUNTER labels: "$4": "$5" "$6": "$7" - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.*), (.+)=(.+)><>(\d+)thPercentile name: kafka_$1_$2_$3 type: GAUGE labels: "$4": "$5" "$6": "$7" quantile: "0.$8" - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.+)><>Count name: kafka_$1_$2_$3_count type: COUNTER labels: "$4": "$5" - pattern: kafka.(\w+)<type=(.+), name=(.+), (.+)=(.*)><>(\d+)thPercentile name: kafka_$1_$2_$3 type: GAUGE labels: "$4": "$5" quantile: "0.$6" - pattern: kafka.(\w+)<type=(.+), name=(.+)><>Count name: kafka_$1_$2_$3_count type: COUNTER - pattern: kafka.(\w+)<type=(.+), name=(.+)><>(\d+)thPercentile name: kafka_$1_$2_$3 type: GAUGE labels: quantile: "0.$4"</span> <span class="token key atrule">zookeeper-metrics-config.yml</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token scalar string"> # See https://github.com/prometheus/jmx_exporter for more info about JMX Prometheus Exporter metrics lowercaseOutputName: true rules: # replicated Zookeeper - pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+)><>(\\w+)" name: "zookeeper_$2" type: GAUGE - pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+)><>(\\w+)" name: "zookeeper_$3" type: GAUGE labels: replicaId: "$2" - pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+)><>(Packets\\w+)" name: "zookeeper_$4" type: COUNTER labels: replicaId: "$2" memberType: "$3" - pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+)><>(\\w+)" name: "zookeeper_$4" type: GAUGE labels: replicaId: "$2" memberType: "$3" - pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+), name3=(\\w+)><>(\\w+)" name: "zookeeper_$4_$5" type: GAUGE labels: replicaId: "$2" memberType: "$3"</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>执行<code>kubectl apply -f kafka-persistent-single.yaml </code> 自动重启kafka,这样kafka监控接口就暴露出来了。</p><p>下载<a href="https://github.com/strimzi/strimzi-kafka-operator/blob/main/examples/metrics/prometheus-install/strimzi-pod-monitor.yaml">strimzi-pod-monitor.yaml</a>,让prometheus自动发现并拉取kafka监控数据:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl apply -f strimzi-pod-monitor.yaml<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h3 id="Grafana-Dashboard"><a href="#Grafana-Dashboard" class="headerlink" title="Grafana Dashboard"></a>Grafana Dashboard</h3><p>打开grafana页面,导入官方提供的dashboard:<a href="https://github.com/strimzi/strimzi-kafka-operator/tree/main/examples/metrics/grafana-dashboards">strimzi-kafka-operator/examples/metrics/grafana-dashboards at main · strimzi/strimzi-kafka-operator (github.com)</a></p><p>我只导入了我需要的这几个</p><p><img src="/images/image-20220730210443912.png"></p><p>预览效果如下:</p><p><img src="/images/image-20220730212342769.png"></p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://strimzi.io/docs/operators/in-development/configuring.html">Configuring Strimzi (In Development)</a></p>]]></content>
<categories>
<category> 云原生 </category>
</categories>
<tags>
<tag> 云原生 </tag>
<tag> k8s </tag>
</tags>
</entry>
<entry>
<title>使用Pocal-Path-Provisioner动态创建本地磁盘pv</title>
<link href="posts/16358/"/>
<url>posts/16358/</url>
<content type="html"><![CDATA[<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>使用k8s最麻烦的就是提供pv存储,很多情况下我们需要使用本地磁盘作为存储。</p><p>但是k8s默认不支持通过storageClass的方式自动创建以本地磁盘为存储的pv。</p><p>好在有开源的<a href="https://github.com/rancher/local-path-provisioner">rancher/local-path-provisioner</a></p><p>使用方法很简单,下载yaml文件 <a href="https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.22/deploy/local-path-storage.yaml">https://raw.githubusercontent.com/rancher/local-path-provisioner/v0.0.22/deploy/local-path-storage.yaml</a></p><p>有3处地方一般情况下需要修改:</p><p>第一,删除–debug</p><p><img src="/images/image-20220727212341480.png"></p><p>第二,搜索reclaimPolicy,默认值是Delete,可以改成Retain,避免误删pv造成数据丢失</p><p>第三,搜索StorageClass,增加annotations,使local-path storageclass成为默认storageclass:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> storage.k8s.io/v1<span class="token key atrule">kind</span><span class="token punctuation">:</span> StorageClass<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path <span class="token key atrule">annotations</span><span class="token punctuation">:</span> <span class="token key atrule">storageclass.kubernetes.io/is-default-class</span><span class="token punctuation">:</span> <span class="token string">"true"</span> <span class="token comment"># 默认storageclass</span><span class="token key atrule">provisioner</span><span class="token punctuation">:</span> rancher.io/local<span class="token punctuation">-</span>path<span class="token key atrule">volumeBindingMode</span><span class="token punctuation">:</span> WaitForFirstConsumer<span class="token key atrule">reclaimPolicy</span><span class="token punctuation">:</span> Retain<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>第四,修改最后面的configMap:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml"><span class="token key atrule">kind</span><span class="token punctuation">:</span> ConfigMap<span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1<span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path<span class="token punctuation">-</span>config <span class="token key atrule">namespace</span><span class="token punctuation">:</span> local<span class="token punctuation">-</span>path<span class="token punctuation">-</span>storage<span class="token key atrule">data</span><span class="token punctuation">:</span> <span class="token key atrule">config.json</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token punctuation">-</span> <span class="token punctuation">{</span> "nodePathMap"<span class="token punctuation">:</span><span class="token punctuation">[</span> <span class="token punctuation">{</span> "node"<span class="token punctuation">:</span><span class="token string">"DEFAULT_PATH_FOR_NON_LISTED_NODES"</span><span class="token punctuation">,</span> "paths"<span class="token punctuation">:</span><span class="token punctuation">[</span><span class="token string">"/opt/local-path-provisioner"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token punctuation">]</span> <span class="token punctuation">}</span> <span class="token key atrule">setup</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token punctuation">-</span> <span class="token comment">#!/bin/sh</span> set <span class="token punctuation">-</span>eu mkdir <span class="token punctuation">-</span>m 0777 <span class="token punctuation">-</span>p "$VOL_DIR" <span class="token key atrule">teardown</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token punctuation">-</span> <span class="token comment">#!/bin/sh</span> set <span class="token punctuation">-</span>eu rm <span class="token punctuation">-</span>rf "$VOL_DIR" <span class="token key atrule">helperPod.yaml</span><span class="token punctuation">:</span> <span class="token punctuation">|</span><span class="token punctuation">-</span> <span class="token key atrule">apiVersion</span><span class="token punctuation">:</span> v1 <span class="token key atrule">kind</span><span class="token punctuation">:</span> Pod <span class="token key atrule">metadata</span><span class="token punctuation">:</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> helper<span class="token punctuation">-</span>pod <span class="token key atrule">spec</span><span class="token punctuation">:</span> <span class="token key atrule">containers</span><span class="token punctuation">:</span> <span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> helper<span class="token punctuation">-</span>pod <span class="token key atrule">image</span><span class="token punctuation">:</span> busybox <span class="token key atrule">imagePullPolicy</span><span class="token punctuation">:</span> IfNotPresent<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其中<code>/opt/local-path-provisioner</code>就是你本地数据存储目录,根据情况自由修改。</p><p>执行<code>kubectl apply -f local-path-storage.yaml </code>即可。</p><p>要使用这个动态pv时,storageClass设置为<code>local-path</code>就OK了</p><h2 id="个性化配置"><a href="#个性化配置" class="headerlink" title="个性化配置"></a>个性化配置</h2><p>我们也可以个性化配置上面的配置文件</p><h3 id="nodePathMap"><a href="#nodePathMap" class="headerlink" title="nodePathMap"></a>nodePathMap</h3><p>我们也可以个性化定制每个节点的存储路径,示例如下:</p><pre class="line-numbers language-yaml" data-language="yaml"><code class="language-yaml">"nodePathMap"<span class="token punctuation">:</span><span class="token punctuation">[</span> <span class="token punctuation">{</span> "node"<span class="token punctuation">:</span><span class="token string">"DEFAULT_PATH_FOR_NON_LISTED_NODES"</span><span class="token punctuation">,</span> "paths"<span class="token punctuation">:</span><span class="token punctuation">[</span><span class="token string">"/opt/local-path-provisioner"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> "node"<span class="token punctuation">:</span><span class="token string">"node-1"</span><span class="token punctuation">,</span> "paths"<span class="token punctuation">:</span><span class="token punctuation">[</span><span class="token string">"/opt/local-path-provisioner"</span><span class="token punctuation">,</span> <span class="token string">"/data1"</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> "node"<span class="token punctuation">:</span><span class="token string">"node-2"</span><span class="token punctuation">,</span> "paths"<span class="token punctuation">:</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li><p><code>DEFAULT_PATH_FOR_NON_LISTED_NODES</code>表示默认存储位置,所有节点如果没有特别指定,都将使用这个路径。</p></li><li><p>node-1节点指定了两个目录,在分配pv时将随机指定一个目录创建pv。</p></li><li><p>node-2没有指定任何路径,那么该节点不会被分配pv</p></li></ul><h3 id="setup"><a href="#setup" class="headerlink" title="setup"></a>setup</h3><p>setup脚本在volume创建前调用,用于创建存储目录</p><h3 id="teardown"><a href="#teardown" class="headerlink" title="teardown"></a>teardown</h3><p>teardown脚本在volume被删除时调用,用于清除数据</p><h3 id="helperPod-yaml"><a href="#helperPod-yaml" class="headerlink" title="helperPod.yaml"></a>helperPod.yaml</h3><p>helperPod.yaml是pod模板,运行一个busybox的pod,setup和teardown脚本都是在这个pod中执行</p><h2 id="自动刷新配置"><a href="#自动刷新配置" class="headerlink" title="自动刷新配置"></a>自动刷新配置</h2><p>我们可以直接使用kubectl edit去修改上面的configmap,或者修改local-path-provisioner.yaml文件然后执行kubectl apply,local-path-provisioner会自动刷新配置</p>]]></content>
<categories>
<category> 云原生 </category>
</categories>
<tags>
<tag> 云原生 </tag>
<tag> k8s </tag>
</tags>
</entry>
<entry>
<title>使用Sealos一键安装K8S</title>
<link href="posts/112225/"/>
<url>posts/112225/</url>
<content type="html"><![CDATA[<p>sealos应该是我用过的安装k8s最方便的工具了,没有之一。</p><p>官网:<a href="https://www.sealyun.com/">https://www.sealyun.com/</a></p><h2 id="环境"><a href="#环境" class="headerlink" title="环境"></a>环境</h2><ul><li>Ubuntu18.04</li><li>内核4.15.0-180-generic</li></ul><h2 id="安装sealos"><a href="#安装sealos" class="headerlink" title="安装sealos"></a>安装sealos</h2><p>sealos是go写的二进制包,直接下载放到/usr/bin目录即可</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">wget</span> https://github.com/labring/sealos/releases/download/v4.1.3/sealos_4.1.3_linux_amd64.tar.gz <span class="token punctuation">\</span> <span class="token operator">&&</span> <span class="token function">tar</span> zxvf sealos_4.1.3_linux_amd64.tar.gz sealos <span class="token operator">&&</span> <span class="token function">chmod</span> +x sealos <span class="token operator">&&</span> <span class="token function">mv</span> sealos /usr/bin<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>可以从<a href="https://github.com/labring/sealos/releases%E6%89%BE%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC%E4%B8%8B%E8%BD%BD">https://github.com/labring/sealos/releases找最新版本下载</a></p><h2 id="创建K8S集群"><a href="#创建K8S集群" class="headerlink" title="创建K8S集群"></a>创建K8S集群</h2><p>由于容器日志默认是保存在<code>/var/log/pods</code>下的,为了防止系统盘被日志打满,可以事先创建好一个软链,链接到数据盘,例:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># 所有机器上执行</span><span class="token function">mkdir</span> -p /data/k8s/log/pods<span class="token function">ln</span> -s /data/k8s/log/pods /var/log/pods<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>使用root用户创建集群:(不能用sudo)</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">sealos run labring/kubernetes:v1.24.3 labring/calico:v3.22.1 <span class="token punctuation">\</span> --env <span class="token assign-left variable">criData</span><span class="token operator">=</span>/data/k8s/containerd <span class="token punctuation">\</span> --masters <span class="token number">192.168</span>.64.2,192.168.64.22,192.168.64.20 <span class="token punctuation">\</span> --nodes <span class="token number">192.168</span>.64.21,192.168.64.19 -p <span class="token punctuation">[</span>your-ssh-passwd<span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><ol><li>k8s版本可以从这里找:<a href="https://hub.docker.com/r/labring/kubernetes/tags">labring/kubernetes Tags | Docker Hub</a></li><li>–masters参数是master节点ip列表</li><li>–nodes参数是node节点ip列表</li><li>-p是ssh密码</li></ol><p><code>--env</code>是环境变量,可选参数如下:</p><pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token comment"># containerd数据目录</span><span class="token attr-name">criData</span><span class="token punctuation">=</span><span class="token attr-value">/var/lib/containerd</span><span class="token comment"># 私有仓库配置</span><span class="token attr-name">registryData</span><span class="token punctuation">=</span><span class="token attr-value">/var/lib/registry</span><span class="token attr-name">registryConfig</span><span class="token punctuation">=</span><span class="token attr-value">/etc/registry</span><span class="token attr-name">registryDomain</span><span class="token punctuation">=</span><span class="token attr-value">sealos.hub</span><span class="token attr-name">registryPort</span><span class="token punctuation">=</span><span class="token attr-value">5000</span><span class="token attr-name">registryUsername</span><span class="token punctuation">=</span><span class="token attr-value">admin</span><span class="token attr-name">registryPassword</span><span class="token punctuation">=</span><span class="token attr-value">passw0rd</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>其他参数可以使用<code>sealos run -h</code>查看</p><h2 id="去除master污点"><a href="#去除master污点" class="headerlink" title="去除master污点"></a>去除master污点</h2><p>master节点去除污点后才能运行业务pod</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">kubectl taint nodes --all node-role.kubernetes.io/control-plane-kubectl taint nodes --all node-role.kubernetes.io/master-<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h2 id="nerdctl"><a href="#nerdctl" class="headerlink" title="nerdctl"></a>nerdctl</h2><p>现在k8s已经默认不使用docker,而是使用containerd,因此没有docker命令来管理镜像,可以使用nerdctl命令代替,命令基本上和docker一致。</p><p>要注意的是,containerd有命名空间的概念:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">root@VM-4-2-ubuntu:~$ nerdctl namespace <span class="token function">ls</span>NAME CONTAINERS IMAGES VOLUMESdefault <span class="token number">1</span> <span class="token number">2</span> <span class="token number">0</span>k8s.io <span class="token number">78</span> <span class="token number">94</span> <span class="token number">0</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>k8s默认使用了k8s.io这个命名空间下的镜像,如果你要手动load镜像并且希望它能被k8s使用,需要在<code>nerdctl load</code>的时候指定命名空间:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">nerdctl load -i xxx.tar --namespace k8s.io<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="非root用户"><a href="#非root用户" class="headerlink" title="非root用户"></a>非root用户</h2><p>sealos目前版本只能使用root用户安装,如果你平时不使用root用户,需要使用非root用户执行:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token comment"># 将.kube拷贝到当前用户目录下,否则无法执行kubectl命令</span><span class="token function">sudo</span> <span class="token function">cp</span> -r /root/.kube <span class="token environment constant">$HOME</span><span class="token function">sudo</span> <span class="token function">chown</span> -R <span class="token variable">${whoami}</span><span class="token builtin class-name">:</span><span class="token variable">${whoami}</span> <span class="token environment constant">$HOME</span>/.kube<span class="token comment"># 给nerdctl提高权限,否则执行nerdctl需要sudo</span><span class="token function">sudo</span> <span class="token function">chmod</span> +s /usr/bin/nerdctl<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="私有镜像仓库"><a href="#私有镜像仓库" class="headerlink" title="私有镜像仓库"></a>私有镜像仓库</h2><p>sealos自带了一个镜像仓库,部署在第一个master上。</p><p>查看registry:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">nerdctl <span class="token function">ps</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>登录:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">nerdctl login sealos.hub:5000 -u admin -p passw0rd<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>推送镜像到私有仓库:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">nerdctl tag xxx:v1.0.0 sealos.hub:5000/xxx:v1.0.0 --namespace k8s.ionerdctl push sealos.hub:5000/xxx:v1.0.0<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> 云原生 </category>
</categories>
<tags>
<tag> 云原生 </tag>
<tag> k8s </tag>
</tags>
</entry>
<entry>
<title>JDK调优命令行工具</title>
<link href="posts/59855/"/>
<url>posts/59855/</url>
<content type="html"><![CDATA[<h2 id="jps"><a href="#jps" class="headerlink" title="jps"></a>jps</h2><p>查看java进程的工具</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>-q</td><td>只输出进程ID</td></tr><tr><td>-m</td><td>输出传递给Java进程(主函数)的参数</td></tr><tr><td>-l</td><td>输出主函数的完整路径</td></tr><tr><td>-v</td><td>显示传递给Java虚拟的参数</td></tr></tbody></table><h2 id="jstat"><a href="#jstat" class="headerlink" title="jstat"></a>jstat</h2><p>查看Java程序运⾏时相关信息,可以通过它查看堆信息的相关情况</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">jstat -<span class="token operator"><</span>options<span class="token operator">></span> <span class="token punctuation">[</span>-t<span class="token punctuation">]</span> <span class="token punctuation">[</span>-h<span class="token operator"><</span>lines<span class="token operator">></span><span class="token punctuation">]</span> <span class="token operator"><</span>vmid<span class="token operator">></span> <span class="token punctuation">[</span><span class="token operator"><</span>interval<span class="token operator">></span> <span class="token punctuation">[</span><span class="token operator"><</span>count<span class="token operator">></span><span class="token punctuation">]</span><span class="token punctuation">]</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>参数如下:</p><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>-class</td><td>显示ClassLoader的相关信息</td></tr><tr><td>-compiler</td><td>显示JIT编译的相关信息</td></tr><tr><td>-gc</td><td>显示与GC相关信息</td></tr><tr><td>-gccapacity</td><td>显示各个代的容量和使⽤情况</td></tr><tr><td>-gccause</td><td>显示垃圾收集相关信息(同-gcutil),同时显示最后⼀次或当前正在发⽣的垃圾收集的诱发</td></tr><tr><td>-gcnew</td><td>原因显示新⽣代信息</td></tr><tr><td>-gcnewcapacity</td><td>显示新⽣代⼤⼩和使⽤情况</td></tr><tr><td>-gcold</td><td>显示⽼年代信息</td></tr><tr><td>-gcoldcapacity</td><td>显示⽼年代⼤⼩</td></tr><tr><td>-gcpermcapacity</td><td>显示永久代⼤⼩</td></tr><tr><td>-gcutil</td><td>显示垃圾收集信息</td></tr><tr><td>-printcompilation</td><td>输出JIT编译的⽅法信息</td></tr><tr><td>-t</td><td>在输出信息前加上⼀个Timestamp列,显示程序的运⾏时间</td></tr><tr><td>-h</td><td>可以在周期性数据输出后,输出多少⾏数据后,跟着⼀个表头信息</td></tr><tr><td>interval</td><td>⽤于指定输出统计数据的周期,单位为毫秒</td></tr><tr><td>count</td><td>⽤于指定⼀个输出多少次数据</td></tr></tbody></table><p>例:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">jstat -gc <span class="token number">60104</span> <span class="token number">500</span> <span class="token number">4</span> <span class="token comment"># 500毫秒频率,采样数为4</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p><img src="/images/image-20220117203437224.png"></p><p>字段含义:</p><ul><li><p>S0C:年轻代中第⼀个survivor(幸存区)的容量 (字节)</p></li><li><p>S1C:年轻代中第⼆个survivor(幸存区)的容量 (字节)</p></li><li><p>S0U:年轻代中第⼀个survivor(幸存区)⽬前已使⽤空间 (字节)</p></li><li><p>S1U :年轻代中第⼆个survivor(幸存区)⽬前已使⽤空间 (字节)</p></li><li><p>EC :年轻代中Eden(伊甸园)的容量 (字节)</p></li><li><p>EU :年轻代中Eden(伊甸园)⽬前已使⽤空间 (字节)</p></li><li><p>OC :Old代的容量 (字节)</p></li><li><p>OU :Old代⽬前已使⽤空间 (字节)</p></li><li><p>MC:metaspace(元空间)的容量 (字节)</p></li><li><p>MU:metaspace(元空间)⽬前已使⽤空间 (字节)</p></li><li><p>CCSC:压缩类空间⼤⼩</p></li><li><p>CCSU:压缩类空间使⽤⼤⼩</p></li><li><p>YGC :从应⽤程序启动到采样时年轻代中gc次数</p></li><li><p>YGCT :从应⽤程序启动到采样时年轻代中gc所⽤时间(s)</p></li><li><p>FGC :从应⽤程序启动到采样时old代(全gc)gc次数</p></li><li><p>FGCT :从应⽤程序启动到采样时old代(全gc)gc所⽤时间(s)</p></li><li><p>GCT:从应⽤程序启动到采样时gc⽤的总时间(s)</p></li></ul><h2 id="jinfo"><a href="#jinfo" class="headerlink" title="jinfo"></a>jinfo</h2><p>查看正在运⾏的<strong>java</strong>程序的扩展参数,甚⾄⽀持运⾏时,修改部分参数</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">jinfo <span class="token punctuation">[</span>option<span class="token punctuation">]</span> <span class="token operator"><</span>pid<span class="token operator">></span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><table><thead><tr><th>参数</th><th>说明</th></tr></thead><tbody><tr><td>-flag <code><name></code></td><td>打印参数</td></tr><tr><td>-flag `[+</td><td>-]<name>`</name></td></tr><tr><td>-flag <code><name>=<value></code></td><td>设置参数</td></tr><tr><td>-flags</td><td>打印所有参数</td></tr><tr><td>-sysprops</td><td>打印java系统参数</td></tr></tbody></table><p>例如:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">jinfo -flag MaxHeapSize <span class="token number">60104</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="jmap"><a href="#jmap" class="headerlink" title="jmap"></a>jmap</h2><p>查看堆内存情况</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>JIT编译器优化</title>
<link href="posts/63062/"/>
<url>posts/63062/</url>
<content type="html"><![CDATA[<p>虚拟机针对JIT编译器做了很多优化,其中有几项比较重要且有代表性的优化措施</p><h2 id="公共子表达式消除"><a href="#公共子表达式消除" class="headerlink" title="公共子表达式消除"></a>公共子表达式消除</h2><p>公共子表达式消除就是对一个表达式中重复计算的子表达式进行缓存替换。</p><p>例如一个表达式:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> d <span class="token operator">=</span> <span class="token punctuation">(</span>c <span class="token operator">*</span> b<span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token number">12</span> <span class="token operator">+</span> a <span class="token operator">+</span> <span class="token punctuation">(</span>a <span class="token operator">+</span> b <span class="token operator">*</span> c<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>观察这个表达式中,<code>c * b</code>出现了2次,且计算过程中b和c的值不变,<code>c * b</code>的值也就不变。此时可以将<code>c * b</code>替换为表达式E:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> d <span class="token operator">=</span> <span class="token class-name">E</span> <span class="token operator">*</span> <span class="token number">12</span> <span class="token operator">+</span> a <span class="token operator">+</span> <span class="token punctuation">(</span>a <span class="token operator">+</span> <span class="token class-name">E</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>此处的E就是公共子表达式,经过优化之后E的值只会被计算一次,这就是公共子表达式消除</p><p>有些虚拟机还会对表达式做<strong>代数简化</strong>:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> d <span class="token operator">=</span> <span class="token class-name">E</span> <span class="token operator">*</span> <span class="token number">13</span> <span class="token operator">+</span> a <span class="token operator">*</span> <span class="token number">2</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="数组边界检查消除"><a href="#数组边界检查消除" class="headerlink" title="数组边界检查消除"></a>数组边界检查消除</h2><p>有我们访问一个数组<code>A[i]</code>时,JVM会对数组下标i做一次越界检查,如果越界将抛出异常。</p><p>但如果数组很大,每次访问都进行越界检查的话无疑也是不小的性能负担,但越界检查又不能不做。</p><p>因此JIT在编译时可以根据上下文分析判断是否可以省略越界检查步骤。</p><p>比如数组长度如果固定是10,那么访问<code>A[5]</code>时,在编译期间就可以分析出肯定是不会越界的,那么在编译后的机器码中就省去了越界检查这一步骤。</p><p>再比如使用for循环访问数组:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator"><</span> <span class="token class-name">A</span><span class="token punctuation">.</span>length<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">A</span><span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>显然上面这段代码时肯定不会越界的,在编译时也可以省去越界检查。</p><p>除了数组边界检查消除,还有别的相同思想的优化手段,比如以下代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">if</span><span class="token punctuation">(</span>obj <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> obj<span class="token punctuation">.</span>value<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>这段代码每次都进行了非空的判断,JIT可以进行如下优化:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span><span class="token punctuation">{</span> obj<span class="token punctuation">.</span>value<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">catch</span><span class="token punctuation">(</span>xxx<span class="token punctuation">)</span><span class="token punctuation">{</span> xxxx<span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>优化之后调用<code>obj.value</code>时就节省了一次非空判断。但是由于异常捕获的效率比较低,所以如果obj经常为null的话,这次优化就起了反作用,还不如不优化。</p><p>好在HotSpot虚拟机比较聪明,会在运行期间收集信息自动选择最优优化方案</p><h2 id="方法内联"><a href="#方法内联" class="headerlink" title="方法内联"></a>方法内联</h2><p>比如方法<code>a()</code>中调用了方法<code>b()</code>,那么可以将<code>b()</code>对应的代码块直接替换到方法<code>a()</code>中,这就是方法内联。</p><p>比如以下代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> left <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> right <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">b</span><span class="token punctuation">(</span>left<span class="token punctuation">,</span> right<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">b</span><span class="token punctuation">(</span><span class="token keyword">int</span> left<span class="token punctuation">,</span> <span class="token keyword">int</span> right<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> left <span class="token operator">*</span> right<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>方法内联优化后:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> left <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> right <span class="token operator">=</span> <span class="token number">2</span><span class="token punctuation">;</span> <span class="token keyword">return</span> left <span class="token operator">*</span> right<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>为什么这么做呢?要知道我们调用一个方法时,要先到方法区找到该方法对应的字节码,然后在虚拟机栈上生成一个栈帧进行入栈出栈。经过方法内联之后,在方法调用过程中就可以减少了一次方法区寻址和栈帧出入栈的过程。</p><h2 id="逃逸分析"><a href="#逃逸分析" class="headerlink" title="逃逸分析"></a>逃逸分析</h2><p>逃逸分析是一种比较前沿的优化手段,但它不直接优化代码,而是为其他优化手段提供依据。</p><p>当一个对象在方法内被定义后,它如果可以在方法外被使用,就称为<strong>方法逃逸</strong>。</p><p>比如方法内new了一个对象a并return a,那么对象a就可以在该方法之外被使用,这就是方法逃逸。</p><p>逃逸分析大致有以下几种情况:</p><p><em><strong>全局变量赋值逃逸/线程逃逸</strong></em></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 其他线程或其他方法中都可以使用该对象</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Object</span> object<span class="token punctuation">;</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><em><strong>方法返回值逃逸</strong></em></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// a方法的调用者可使用此处创建的object对象</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><em><strong>实例引用发生逃逸</strong></em></p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">a</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">Object</span> obj <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token function">newInstance</span><span class="token punctuation">(</span>obj<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 这里只可以使用newInstance()中new的对象</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">newInstance</span><span class="token punctuation">(</span><span class="token class-name">Object</span> obj<span class="token punctuation">)</span><span class="token punctuation">{</span> obj <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>JDK1.7开始默认开启了逃逸分析,我们也可以使用:</p><ul><li><code>-XX:+DoEscapeAnalysis</code>参数手动开启。</li><li><code>-XX:+PrintEscapeAnalysis</code>参数查看分析结果</li></ul><h2 id="栈上分配"><a href="#栈上分配" class="headerlink" title="栈上分配"></a>栈上分配</h2><p>基于逃逸分析,可以针对Java对象做栈上分配优化。所谓栈上分配就是将对象的空间分配在栈上,而不是堆上。</p><p>我们都知道Java对象的内存空间正常是分配在堆上的,但是如果该对象没有发生方法逃逸,如:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">test</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">Object</span> o <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>o<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>从这段代码可以看出,对象o没有逃逸,随着test方法结束,栈帧出栈后,o对象也就没用了。</p><p>所以可以将对象o的内存空间分配在栈上,那么随着test方法出栈,对象o自然也就被销毁了,可以大大减少垃圾回收器的工作量</p><h2 id="标量替换"><a href="#标量替换" class="headerlink" title="标量替换"></a>标量替换</h2><p>int、long这种无法继续拆解成更小粒度的数据可以称之为<strong>标量</strong>,而对象可以拆解成更小的粒度,这种称之为<strong>聚合量</strong>。</p><p>如果一个对象确认不会逃逸,那么可以将其拆解成更小的标量,在栈上进行读写,而不用在堆中创建对象。</p><p>因此标量替换可以算是栈上分配的一种特例。</p><ul><li><code>-XX:+EliminateAllocations</code>参数开启标量替换</li><li><code>-XX:+PrintEliminateAllocations</code>查看替换情况</li></ul><h2 id="线程同步锁消除"><a href="#线程同步锁消除" class="headerlink" title="线程同步锁消除"></a>线程同步锁消除</h2><p>如果确认对象不会线程逃逸,也就是不会有多个线程使用同一个对象,那么相关的<code>synchronized</code>同步锁可以将其消除。</p><p>举个例子:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">String</span> <span class="token function">append</span><span class="token punctuation">(</span><span class="token class-name">String</span> str1<span class="token punctuation">,</span> <span class="token class-name">String</span> str2<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">StringBuffer</span> sb <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">StringBuffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> sb<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>str1<span class="token punctuation">)</span><span class="token punctuation">;</span> sb<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>str2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> sb<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个例子中,sb.toString()产生了一个新的字符串对象,因此StringBuffer对象不会发生逃逸。而sb.append()方法中使用了<code>synchronized</code>:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">synchronized</span> <span class="token class-name">StringBuffer</span> <span class="token function">append</span><span class="token punctuation">(</span><span class="token class-name">String</span> str<span class="token punctuation">)</span> <span class="token punctuation">{</span> toStringCache <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">super</span><span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span>str<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这种情况下就可以消除<code>synchronized</code>同步锁</p><ul><li><code>+XX:+EliminateLocks</code>参数开启同步锁消除</li></ul>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>解释器与编译器</title>
<link href="posts/42197/"/>
<url>posts/42197/</url>
<content type="html"><![CDATA[<h2 id="方法执行过程"><a href="#方法执行过程" class="headerlink" title="方法执行过程"></a>方法执行过程</h2><p>Java中方法的执行由解释器解释执行,所谓解释执行就是针对方法的字节码逐行解释,逐行执行。</p><p>解释器进行解释执行虽然比较快,但热点代码没必要每次都重复解释,因此即时编译器将热点代码编译成机器码做个缓存,速度可以大大提升。</p><p>比如有一个方法有50行字节码指令,如果是解释器,需要解释50次,因为它是逐行解释。但如果该方法多次调用后成为了热点代码,则可以将该方法编译成机器码进行缓存,那么下次执行的时候可以直接找到对应的机器码执行即可。</p><p>方法执行的全过程大致如下:</p><p><img src="/images/image-20220116112438916.png"></p><p>并不是所有的JVM虚拟机同时有解释器和编译器,但多数主流虚拟机都是二者共存,比如HotSpot虚拟机</p><h2 id="热点代码"><a href="#热点代码" class="headerlink" title="热点代码"></a>热点代码</h2><p><em><strong>什么样的代码可以称为热点代码呢?</strong></em></p><ul><li>多次调用的方法</li><li>多次执行的循环体</li></ul><p>无论是什么样的热点代码,编译器编译的对象都是整个方法</p><p><em><strong>那么如何判断代码是否可以成为热点代码呢?</strong></em></p><p>主要有两种探测方式:</p><ol><li>基于采样的热点探测(经常出现在栈顶的方法即认为是热点代码)</li><li>基于计数器的热点探测(HotSpot虚拟机使用这种):<ol><li>这种方法是为每个方法建立一个方法计数器,计算执行次数,达到某个阈值则认为是热点代码。</li><li>如果是针对循环体,则建立一个回边计数器(控制流向后跳转的指令称为回边)。</li><li>热点代码阈值通过<code>-XX:CompileThreshold</code>设置。</li></ol></li></ol><blockquote><p>值得一提的是,当方法调用次数达到阈值时,本次执行还是走解释执行,但同时会向编译器发起编译请求,下次执行该热点代码时才会用到编译后的缓存机器码</p></blockquote><h2 id="解释器"><a href="#解释器" class="headerlink" title="解释器"></a>解释器</h2><p>假设有这样一个方法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> <span class="token keyword">int</span> b <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span> <span class="token keyword">int</span> c <span class="token operator">=</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token keyword">return</span> c<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对应的字节码如下:</p><pre class="line-numbers language-none"><code class="language-none">0 iconst_11 istore_12 iconst_33 istore_24 iload_15 iload_26 iadd7 istore_38 iload_39 ireturn<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面字节码执行过程如下:</p><table><thead><tr><th>指令行号</th><th>指令</th><th>作用</th></tr></thead><tbody><tr><td>0</td><td>iconst_1</td><td>变量1入栈</td></tr><tr><td>1</td><td>istore_1</td><td>将栈顶元素移入本地变量表下标1位置</td></tr><tr><td>2</td><td>iconst_3</td><td>变量3入栈</td></tr><tr><td>3</td><td>istore_2</td><td>将栈顶元素移入本地变量表下标2位置</td></tr><tr><td>4</td><td>iload_1</td><td>本地变量表下标1的值入操作数栈</td></tr><tr><td>5</td><td>iload_2</td><td>本地变量表下标2的值入操作数栈</td></tr><tr><td>6</td><td>iadd</td><td>弹出操作数栈顶2个元素相加</td></tr><tr><td>7</td><td>istore_3</td><td>将执行结果放入本地变量表下标3的位置</td></tr><tr><td>8</td><td>iload_3</td><td>本地变量表下标3的值入操作数栈</td></tr><tr><td>9</td><td>ireturn</td><td>返回操作数栈栈顶int元素</td></tr></tbody></table><p>上面这个字节码执行过程其实就是解释器的执行全过程</p><h2 id="即时编译器"><a href="#即时编译器" class="headerlink" title="即时编译器"></a>即时编译器</h2><p>编译分为<strong>动态编译</strong>和<strong>静态编译</strong>,C/C++这类语言就是静态编译</p><p>即时编译器(Just In Time Compiler)也叫JIT编译器,是在运行时进行编译,也就是动态编译。它用空间换时间,加速热点代码的执行</p><blockquote><p>注意:这里所说的编译是指编译成机器码,而不是Java源码编译成class文件。</p></blockquote><p>JIT编译器分为了<code>Client Complier</code>和<code>Server Complier</code>两种,简称C1、C2编译器</p><p>HotSpot虚拟机会根据硬件情况自动选择哪一种方法运行,也可以使用<code>-client</code>、<code>-server</code>参数去强制指定。</p><p><em><strong>为什么会有两种JIT呢?</strong></em></p><p>因为这两种编译器编译的过程不同:</p><ul><li><code>Client Complier</code>专注于局部优化,拥有更快的编译速度,适合嵌入式设备等硬件条件一般的情况。</li><li><code>Server Complier</code>面向服务器端,拥有更高的编译质量,是一个充分优化过的高级编译器,参考<a href="/posts/63062">JIT编译器优化</a></li></ul>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>对象创建与内存分配</title>
<link href="posts/679/"/>
<url>posts/679/</url>
<content type="html"><![CDATA[<h2 id="对象创建过程"><a href="#对象创建过程" class="headerlink" title="对象创建过程"></a>对象创建过程</h2><p>当我们执行以下代码时:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">Apple</span> apple <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Apple</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>大致经过三个步骤:</p><ol><li>堆内存中为Apple对象开辟一块空间</li><li>初始化对象(成员变量赋值等)</li><li>apple变量指向堆内存中的Apple对象的地址</li></ol><p>其中第1、2部可以细化为:</p><ol><li>JVM到常量池中定位这个类的符号引用</li><li>确认这个类是否已加载,如未加载则进行加载</li><li>在堆中开辟对象内存区域</li><li>成员变量初始化0值</li><li>设置对象头中必要的信息(如GC年代信息、锁信息、对象哈希等)</li><li>执行<code><init></code>方法(初始化成员变量值等)</li></ol><h2 id="对象的内存布局"><a href="#对象的内存布局" class="headerlink" title="对象的内存布局"></a>对象的内存布局</h2><p>堆中的对象可以分为三个部分:</p><ul><li>对象头(Header)</li><li>实例数据(Instance Data)</li><li>对齐填充(Padding)</li></ul><p><img src="/images/image-20220115202920254.png"></p><h3 id="对象头"><a href="#对象头" class="headerlink" title="对象头"></a>对象头</h3><p>对象头中存储了两类数据:markword和类型指针。</p><p><strong>markword</strong>,在64位系统中占用8字节空间(64bit),具体包含:哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。</p><p>但markword中所包含的数据其实加起来已经超过8字节,为了节省存储空间,在不同锁状态下,markword的结构有所不同,以此达到复用存储空间的目的。</p><p>markword示意如下:</p><p><img src="/images/image-20220115210121118.png"></p><p><strong>类型指针</strong>,指向方法区中该对象的class元数据。简单来说就是用来确认当前对象的class。如果开启压缩指针(JDK1.6开始默认开启),那么占用4字节,未开启则占用8字节</p><p>如果是数组对象,除了markword和类型指针之外,还有数组长度</p><p><img src="/images/image-20220115202807489.png"></p><p>我们可以借助一个工具类打印对象头,方法如下:</p><p>Gradle:</p><pre class="line-numbers language-bash" data-language="bash"><code class="language-bash">compile group: <span class="token string">'org.openjdk.jol'</span>, name: <span class="token string">'jol-core'</span>, version: <span class="token string">'0.16'</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>Java:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Apple</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Integer</span> weight<span class="token punctuation">;</span> <span class="token keyword">private</span> <span class="token keyword">byte</span> content<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Test</span> <span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token class-name">ClassLayout</span><span class="token punctuation">.</span><span class="token function">parseInstance</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Apple</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toPrintable</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>打印结果如下:</p><p><img src="/images/image-20220115212642698.png"></p><h3 id="实例数据"><a href="#实例数据" class="headerlink" title="实例数据"></a>实例数据</h3><p>实例数据就是我们定义的类的成员变量数据,也就是我们开发者自定义的部分</p><h3 id="对齐填充"><a href="#对齐填充" class="headerlink" title="对齐填充"></a>对齐填充</h3><p>JVM的内存管理要求对象起始地址必须是8字节的整数倍,所以对象头和实例数据的内存大小加起来如果不是8字节的整数倍,就用对齐填充来补全。</p><h2 id="对象的访问定位"><a href="#对象的访问定位" class="headerlink" title="对象的访问定位"></a>对象的访问定位</h2><p>在<a href="/posts/49553">运行时数据区</a>中介绍过栈中局部变量表reference类型的数据是用于指向堆中的对象。</p><p>实际上具体的实现方式分为两种:</p><ol><li>使用句柄访问</li><li>直接指针访问</li></ol><h3 id="句柄访问"><a href="#句柄访问" class="headerlink" title="句柄访问"></a>句柄访问</h3><p>使用这种方式的话,堆中将有一个句柄池,存放了对象实例所在的地址以及对象类型地址。</p><p>示意图如下:</p><p><img src="/images/image-20220110104303673.png"></p><p>优点:reference指针是稳定地指向句柄池,如果垃圾回收器移动了对象实例,只需要改变句柄即可。</p><p>缺点:每次访问对象都需要经过句柄池,也就是需要两次地址访问才能访问到真正的对象实例</p><h3 id="直接指针访问"><a href="#直接指针访问" class="headerlink" title="直接指针访问"></a>直接指针访问</h3><p>这种方式就是reference直接指向了对象实例,我们常用的HotSpot虚拟机主要使用这种方式</p><p><img src="/images/image-20220110105925465.png"></p><p>优点:比句柄方式少了一次指针定位</p><p>缺点:如果移动了实例对象,reference需要改变</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>常量池与String.intern()方法</title>
<link href="posts/35447/"/>
<url>posts/35447/</url>
<content type="html"><![CDATA[<p>常量池其实是一个统称,具体可以分为:</p><ol><li>class文件常量池,参考<a href="/posts/26123">精通JVM(一):class文件详解</a></li><li>运行时常量池</li><li>字符串常量池</li></ol><h3 id="运行时常量池"><a href="#运行时常量池" class="headerlink" title="运行时常量池"></a>运行时常量池</h3><p>JDK1.7版本开始运行时常量池从方法区迁移到了堆中。</p><p>要了解运行时常量池,首先得从class文件说起。</p><p>每一个class文件中,都有一个常量池,大体分为字面量和符号引用:</p><ol><li>字面量有int,float等数值型常量,还有双引号引起来的字符串值。</li><li>符号引用有Class、Method、Feild等。</li></ol><p>当class文件被类加载器加载之后,运行时常量池中,每一个Class对象都有一块自己的常量池,用于存储class文件中的常量。</p><p>唯一有变化的是,JVM为双引号引起来的字符串单独开辟了一块“字符串常量池”,且这块区域是共享的,全局只有一个字符串常量池</p><p>大致示意如下:</p><p><img src="/images/image-20211221135414298.png"></p><h2 id="字符串常量池的结构"><a href="#字符串常量池的结构" class="headerlink" title="字符串常量池的结构"></a>字符串常量池的结构</h2><p>JVM之所以设计一个全局的字符串常量池,是因为字符串在Java中使用的频率非常高。字符串常量池让我们能够重复利用字符串,一定程度上减少内存消耗。</p><p>字符串常量池在JVM中的实现其实就是hashtable,是一个定长的数组。</p><p>当要把某字符串加入到字符串常量池中时,会用字符串的hashcode,对数组长度取余,得到一个数组下标。该字符串将放到这个下标的位置上。</p><p>但哈希存在碰撞的可能,也就是不同的字符串,计算得出的数组下标可能一样,因此数组中使用了一个链表来存放所有下标一样的字符串。</p><p>值得注意的是,string table中存放的是字符串的引用,而不是真实的字符串。</p><p>大致结构如下图所示:</p><p><img src="/images/image-20211218233559609.png"></p><p>类比我们常用的hashmap,hashtable,我们可以简单的把字符串常量池理解成一个kv结构,v存的就是字符串引用。如下图:</p><p><img src="/images/image-20211221153014548.png"></p><h2 id="intern-方法的作用"><a href="#intern-方法的作用" class="headerlink" title="intern()方法的作用"></a>intern()方法的作用</h2><p>intern()方法是查找字符串常量池中是否由该字符串:</p><ul><li>如果有,直接返回字符串的引用。</li><li>如果没有,把当前字符串对象放到常量池中,并返回引用</li></ul><h2 id="字符串对象的内存分布"><a href="#字符串对象的内存分布" class="headerlink" title="字符串对象的内存分布"></a>字符串对象的内存分布</h2><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token string">"abc"</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>上面这行代码,当JVM执行到它时,对应的字节码指令是<code>ldc abc</code>,ldc指令将会去字符串常量池中找是否有abc,如果有,则直接返回。</p><p>因此内存结构是这样的:</p><p><img src="/images/image-20211221153812278.png"></p><p>简单改改:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"abc"</span><span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>此时常量池中有“abc”,堆中还会有一个String对象,且String对象的value是一个char数组,指向内存中真正的“abc”。</p><p>内存结构是这样的:</p><p><img src="/images/image-20211221153751987.png"></p><p>如果执行intern方法,可以得到常量池中的abc的引用</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"abc"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>s1 <span class="token operator">=</span> s1<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>内存结构如下:</p><p><img src="/images/image-20211221154011273.png"></p><p>如果是拼接new String,拼接之后的字符串默认并不在常量池中,如:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"ab"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"c"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>该例子中,ab和c都在常量池中,s1对应的abc并不在常量中:</p><p><img src="/images/image-20211221154518705.png"></p><p>如果s1执行intern(),那么abc将进入常量池。</p><p>不过这里有一个点非常重要,字符串常量池kv结构中,v存的并不是”abc”的引用,而是String(“abc”)的引用(JDK1.7开始)。如果是JDK1.6版本,v存的仍然是“abc”的引用</p><p><img src="/images/image-20211221154738854.png"></p><p>为了证明上面的观点,看下面这个例子:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"ab"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"c"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>s1<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"abc"</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s1 <span class="token operator">==</span> s2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 输出true</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>当第一行和第二行执行完之后,内存结构如上图所示。</p><p>当执行<code>String s2 = "abc"</code>时,ldc指令执行时去常量池中找abc,发现此时常量池中有abc,则直接返回引用。</p><p>由于kv结构中的v存的是String(“abc”)的引用,所以此时s1和s2都是指向String(“abc”)</p><p>内存结构见下图:</p><p><img src="/images/image-20211221155735146.png"></p><p>如果将intern()放到<code>String s2 = "abc"</code>后面:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"ab"</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">"c"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"abc"</span><span class="token punctuation">;</span>s1<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s1 <span class="token operator">==</span> s2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 输出false</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>当第二行执行完毕之后,字符串常量池中已经有”ab”,”c”,”abc”。此时s1执行intern()没有意义,因为abc已经在常量池中。</p><p>所以此时s1指向的是字符串对象String(“abc”),而s2指向的是常量池中的“abc”,如下图:</p><p><img src="/images/image-20211221155622341.png"></p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>运行时数据区</title>
<link href="posts/49553/"/>
<url>posts/49553/</url>
<content type="html"><![CDATA[<h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>这部分是JVM中最为重要的部分。我们研究JVM主要的目的就是为了对JVM进行调优,本质上就是针对运行时数据区进行调优。</p><p>所谓运行时数据区,其实就是JVM对内存的使用</p><p>运行时数据区大致分为:</p><ol><li>线程共享区域<ol><li>方法区</li><li>堆</li></ol></li><li>线程独享区域<ol><li>栈</li><li>程序计数器</li><li>本地方法栈</li></ol></li></ol><p>示意图大致如下:</p><p><img src="/images/image-20211218204354077.png"></p><p>线程共享空间就是所有线程共同使用的内存空间,而独享空间,则是创建线程才有的空间,且每一个线程都有自己的这么一块空间。</p><h2 id="方法区"><a href="#方法区" class="headerlink" title="方法区"></a>方法区</h2><p>方法区主要存放以下内容:</p><table><thead><tr><th>存储内容</th><th>说明</th></tr></thead><tbody><tr><td>类型信息</td><td>Class信息</td></tr><tr><td>方法信息</td><td>如Method,含方法名称,参数列表,方法返回值等</td></tr><tr><td>字段信息</td><td>如Field,含字段类型,名称</td></tr><tr><td>Code区</td><td>存储方法执行对应的字节码指令</td></tr><tr><td>方法表</td><td>例如调用A类的某个method时,是根据A类的方法表找到对应的方法</td></tr><tr><td>静态变量</td><td>JDK1.7之后,移到堆中</td></tr><tr><td>运行时常量池<br>(字符串常量池最为重要)</td><td>从class文件中的常量池加载,JDK1.7之后移到堆中。<br>参考<a href="/posts/26123">精通JVM(一):class文件详解</a></td></tr><tr><td>JIT编译器编译之后的代码缓存</td><td>JIT编译器将字节码编译成系统能够识别的机器码,为了效率进行了缓存</td></tr></tbody></table><h3 id="永久代与元空间"><a href="#永久代与元空间" class="headerlink" title="永久代与元空间"></a>永久代与元空间</h3><p>方法区其实只是一个统称,或者说是一种抽象,一种规范。而永久代和元空间则是方法区的具体实现</p><p>在JDK1.8之前使用的是永久代,JDK1.8开始使用的是元空间</p><p>两者之间的区别如下:</p><ol><li><p>存储位置不同:</p><ol><li>永久代占用的是JVM进程所使用的内存空间,大小受JVM空间限制。</li><li>元空间使用的是物理内存区域,只受机器物理内存大小限制</li></ol></li><li><p>存储内容不同:</p><ol><li>永久代存储如上面的表格所示的内容。</li><li>元空间只存储类的元信息。静态变量和运行时常量池都已经挪到了堆中</li></ol></li></ol><p> 永久代和元空间的历史变成过程大致如下:</p><p><img src="/images/image-20211218211939501.png"></p><h3 id="方法区OOM异常"><a href="#方法区OOM异常" class="headerlink" title="方法区OOM异常"></a>方法区OOM异常</h3><p>方法区能够导致OOM异常的情况主要两种:</p><ol><li>加载太多的类,超出方法区空间大小。<ol><li>JDK1.8之前将抛出<code>java.lang.OutOfMemoryError: PerGem space</code></li><li>JDK1.8之后将抛出<code>java.lang.OutOfMemoryError: Metaspace</code></li></ol></li><li>字符串OOM异常:当我们创建太多的字符串时,将发生OOM。<ol><li>JDK1.6及之前版本,字符串常量还在永久代中,因此异常为<code>java.lang.OutOfMemoryError: PerGem space</code></li><li>JDK1.7开始,字符串常量池在堆中,因此异常为<code>java.lang.OutOfMemoryError:Java heap space</code></li></ol></li></ol><h2 id="堆内存"><a href="#堆内存" class="headerlink" title="堆内存"></a>堆内存</h2><p>Java堆是Java应用中最大的一块内存,几乎所有的对象都在堆上,例如我们new出来的对象,字符串常量等等。</p><p>堆内存是由垃圾收集器分配和管理,因此也叫做GC堆。</p><p>堆内存的分配与垃圾收集器息息相关,并不是固定不变的,不同的垃圾收集器堆内存可能是不一样的。</p><p>最为经典的是分代设计:主要划分为年轻代(Young)和老年代(Old),二者之间默认大小比例为1:2。</p><p>其中年轻代又具体划分为Eden,Survivor From, Survivor To三个区域,三者之间默认大小比例为8:1:1。</p><p>具体示意图如下:</p><p><img src="/images/image-20211222151408762.png"></p><p>由于不同的垃圾收集器的堆内存设计是不一样的,因此想要深入理解堆内存,建议参考垃圾收集器部分</p><h2 id="程序计数器"><a href="#程序计数器" class="headerlink" title="程序计数器"></a>程序计数器</h2><p>程序计数器是线程私有的,也就是说每一次线程都会有一个程序计数器,它的作用是用来记录当前线程下一个要执行的字节码的行号。</p><p>比如有这样一段代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">int</span> a <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span><span class="token keyword">int</span> b <span class="token operator">=</span> <span class="token number">3</span><span class="token punctuation">;</span><span class="token keyword">int</span> c <span class="token operator">=</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>字节码指令如下:(使用IDEA插件jclasslib bytecode viewer查看)</p><p><img src="/images/image-20211222183255224.png"></p><p>左侧红色数字就是字节码的行号。</p><p>正是由于程序计数器记录了行号,当线程发生切换时才知道应该从哪里继续执行。</p><p>if,while等控制流语句的完成也依赖程序计数器来完成</p><p>如果正在执行的方法是native方法,那么程序计数器记录的值为空。</p><h2 id="虚拟机栈"><a href="#虚拟机栈" class="headerlink" title="虚拟机栈"></a>虚拟机栈</h2><p>Java中方法是虚拟机执行的最基本单元,栈帧则是对应了一个方法。</p><p>当某一个方法A要执行时,对应的栈帧将入栈,如果方法A中又调用了方法B,那么执行到方法B时,B对应的栈帧也将入栈,位于栈顶。</p><p>也就是说栈顶的那一个栈帧就是当前正在执行的方法,因此称之为“当前栈帧”</p><p>一个栈帧包含以下内容:</p><ol><li>局部变量表</li><li>操作数栈</li><li>动态链接</li><li>方法返回地址</li></ol><h3 id="局部变量表"><a href="#局部变量表" class="headerlink" title="局部变量表"></a>局部变量表</h3><p>局部变量表用于存放当前方法的局部变量,如方法入参,方法内部定义的局部变量等,可以保存<code>boolean,byte,char,short,int,reference,returnAddress</code>类型的数据。</p><p>其中reference是对一个对象实例的引用,returnAddress比较少见,它指向了一条字节码指令。</p><p>局部变量表中变量顺序为:</p><ol><li>this引用</li><li>方法参数</li><li>方法内声明的局部变量</li></ol><p>在局部变量表中,存储变量是以变量槽(slot)为单位,像boolean,int这种占用四个字节的变量都占用一个slot,而double/long这种8字节的变量需要两个slot</p><h3 id="操作数栈"><a href="#操作数栈" class="headerlink" title="操作数栈"></a>操作数栈</h3><p>操作数栈的基本数据结构是一个先进先出、后进后出的栈结构,它是JVM用于计算用的。</p><p>比如有一个方法如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">int</span> a<span class="token punctuation">,</span> <span class="token keyword">int</span> b<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>查看该方法的字节码如下:</p><p><img src="/images/image-20220108142036820.png"></p><p>其中<code>iload</code>指令就是将加载int类型的变量到操作数栈中,查看<a href="https://docs.oracle.com/javase/specs/jvms/se16/html/jvms-6.html#jvms-6.5.iload_n">JVM规范文档)</a>可以证实这一点:</p><p><img src="/images/image-20220108142219615.png"></p><p>将a和b变量iload入栈之后,然后<code>iadd</code>指令会将栈顶的两个变量出栈并相加,然后把结果入栈,<code>ireturn</code>从操作数栈的栈顶元素出栈用作返回结果。</p><p>这就是操作数栈的作用,不过操作数栈可不仅仅是计算基本数据类型,操作数栈中的元素可以是任意Java数据类型,一个32位的数据栈1个栈容量,64位的数据栈2个容量</p><h3 id="动态链接"><a href="#动态链接" class="headerlink" title="动态链接"></a>动态链接</h3><p>动态链接就是将<strong>符号引用</strong>转换为内存地址中的<strong>直接引用</strong>,这个过程有一部份是在类加载阶段或第一次使用的时候执行(参考<a href="/posts/52809">类加载过程</a>中的解析阶段)。比如静态方法和私有方法,这两者在编译期间就已经确定下来,因此适合在类加载阶段就进行连接。</p><p>而另一部分就是在运行期间执行,也就是这里说到的动态链接,比如public方法,protect方法,可能被子类继承重写,在类加载阶段无法确定到底要调用哪个版本的方法,因此适合在运行期间再进行情况进行动态链接</p><h3 id="方法返回地址"><a href="#方法返回地址" class="headerlink" title="方法返回地址"></a>方法返回地址</h3><p>一个方法执行结果无非就两种:</p><ol><li>正常返回(return)</li><li>异常返回(throw new exception)</li></ol><p>无法哪种方法返回,方法返回之后都需要回到方法调用的地方,程序才能继续往后执行,因此方法返回时需要在栈帧中保存一些信息,用于帮助调用者恢复状态继续向后执行</p><p>为什么这里用到了“可能”这个模糊的字眼呢?因为JVM仅仅是提供了一个规范,具体的实现还得依靠各个JVM厂商,不同的实现方法,不同的退出方式,可能都会有不一样的过程</p><h2 id="本地方法栈"><a href="#本地方法栈" class="headerlink" title="本地方法栈"></a>本地方法栈</h2><p>本地方法就是native关键字修饰的方法,它是由C/C++实现的。Java方法执行的时候需要栈,同样的C++方法执行的时候也是需要栈的。</p><p>本地方法栈就是native方法执行时所用的栈空间</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>类加载器与双亲委派机制</title>
<link href="posts/52202/"/>
<url>posts/52202/</url>
<content type="html"><![CDATA[<h2 id="类加载器分类"><a href="#类加载器分类" class="headerlink" title="类加载器分类"></a>类加载器分类</h2><p>类加载器主要有以下几种:</p><table><thead><tr><th>类加载器</th><th>加载范围</th></tr></thead><tbody><tr><td>启动类加载器(Bootstrap ClassLoader)</td><td>1. 加载<code>%JAVA_HOME%/lib</code>下的jar包<br>2. _Xbootclasspath参数指定路径的jar(不常用)<br>注意:由C++实现,不是ClassLoader的子类,只加载虚拟机认可的类,手动把包放到lib目录下是不管用的</td></tr><tr><td>扩展类加载器(Extension ClassLoader)</td><td>1. 加载<code>%JAVA_HOME%/lib/ext</code>下的jar包<br>2. java.ext.dirs系统变量指定的路径的jar</td></tr><tr><td>应用类加载器(App ClassLoader)</td><td>用户classpath指定的类,也就是我们日常开发的类</td></tr><tr><td>自定义类加载器(User ClassLoader)</td><td>上面的类加载器无法满足需求时,就自定义类加载器</td></tr></tbody></table><h2 id="确定两个类是否相同"><a href="#确定两个类是否相同" class="headerlink" title="确定两个类是否相同"></a>确定两个类是否相同</h2><p>在确定两个类是否相同时,有一个大坑需要我们注意。</p><p>每一个类加载器都有各自的类名称空间,例如我们编写的某个类A,正常情况会被<code>App ClassLoader</code>加载,此时如果我们自定义一个类加载器,也去加载A的class文件,也能加载成功。</p><p>如果用<code>instanceof</code>去判断这两个类是否是同一个类,你将得到一个false的结果,即使这两个类其实是一模一样的。</p><p>也就是说,判断两个类是否是同一个类,前提是这两个类必须被同一个类加载进行加载,否则没有意义。</p><h2 id="双亲委派机制"><a href="#双亲委派机制" class="headerlink" title="双亲委派机制"></a>双亲委派机制</h2><p>从JDK1.2开始,引入了双亲委派机制,这是JAVA设计者推荐给开发者的一种类加载器的实现方式。</p><p>双亲委派机制的大致过程是:</p><ul><li>当一个类加载器要加载类时,不会直接进行加载,而是把这个请求交给它的父类加载器。同样的,父类加载器同样也会将请求往上传递,层层传递直到到达顶层。</li><li>顶层类加载器会尝试加载这个类,如果成功,则结束,如果失败,则让下层类加载器尝试加载,如果再失败,继续层层往下传递,直到没有成功或者没有下层</li></ul><p>整个过程大致如下图所示:</p><p><img src="/images/image-20211127224735269.png"></p><p>为什么要用这种机制去加载一个类呢?</p><p>例如类<code>java.lang.Object</code>,是在rt.jar包中,使用双亲委派机制,可以确保Object类一定会被最顶层的启动类加载器加载,因为下层即使收到加载Object类的请求,也会把请求往上委托,最终被启动类加载器加载完成。</p><p>否则程序中就可能出现多个<code>java.lang.Object</code>,将一片混乱</p><h2 id="破坏双亲委派机制"><a href="#破坏双亲委派机制" class="headerlink" title="破坏双亲委派机制"></a>破坏双亲委派机制</h2><p>双亲委派机制并不是一种强制性的模型要求,因此在JDK发展历史上出现了三次较大规模的破坏</p><h3 id="第一次破坏"><a href="#第一次破坏" class="headerlink" title="第一次破坏"></a>第一次破坏</h3><p>双亲委派机制是在JDK1.2开始出现,其核心代码在<code>ClassLoader.loadClass()</code>方法中:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> <span class="token function">loadClass</span><span class="token punctuation">(</span><span class="token class-name">String</span> name<span class="token punctuation">,</span> <span class="token keyword">boolean</span> resolve<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">ClassNotFoundException</span> <span class="token punctuation">{</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token function">getClassLoadingLock</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 查看该类是否已加载</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> c <span class="token operator">=</span> <span class="token function">findLoadedClass</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>c <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> t0 <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>parent <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 父加载器存在,则让父加载器处理</span> c <span class="token operator">=</span> parent<span class="token punctuation">.</span><span class="token function">loadClass</span><span class="token punctuation">(</span>name<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> c <span class="token operator">=</span> <span class="token function">findBootstrapClassOrNull</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>c <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">long</span> t1 <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 加载类</span> c <span class="token operator">=</span> <span class="token function">findClass</span><span class="token punctuation">(</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// this is the defining class loader; record the stats</span> <span class="token comment">// 删除部分代码</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolve<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">resolveClass</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> c<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在JDK1.2之前,就已经有自定义类加载器的能力,开发者们继承<code>ClassLoader</code>类并重写<code>loadClass()</code>方法来自定义类加载器。</p><p>而双亲委派的核心代码都在<code>loadClass()</code>方法中,一旦被开发者重写,双亲委派机制就被破坏了。</p><p>因此在双亲委派机制出现之后,增加了<code>findClass()</code>方法,建议开发者们在自定义类加载时,选择重写<code>findClass()</code>而不是<code>loadClass()</code>,避免对双亲委派进行破坏。</p><p>因此,可以说双亲委派历史上第一次较大规模的破坏,发生在双亲委派出现之前</p><h3 id="第二次破坏"><a href="#第二次破坏" class="headerlink" title="第二次破坏"></a>第二次破坏</h3><p>第二次破坏的出现,是由于双亲委派机制的缺陷导致。</p><p>有些情况下,顶层的<code>BootstrapClassLoader</code>,需要使用到classpath下的类,也就是需要调用<code>AppClassLoader</code>去加载类,也就破坏了双亲委派模型。</p><p>示意图如下:</p><p><img src="/images/image-20211127224634570.png"></p><p>什么时候会出现这种情况呢?</p><p>以JDBC为例,<code>rt.jar</code>中有一个接口<code>java.sql.Driver</code>,该接口由<code>BootstrapClassLoader</code>进行加载。</p><p>但SQL的驱动的实现类是由服务提供商提供(如MySQL服务商),而提供商提供的实现类<code>com.mysql.jdbc.Driver</code>是在classpath下的,由<code>AppClassLoader</code>进行加载。</p><p>Java设计了SPI(Service Provider Interface)机制,约定在jar包的<code>META-INF/services/</code>目录下的文本文件中,存放了服务实现类的全限定类名,也就是<code>com.mysql.jdbc.Driver</code>。</p><p>如下图:</p><p><img src="/images/image-20211218193912493.png"></p><p>有了SPI机制之后,<code>BootstrapClassLoader</code>就能够通过<code>AppClassLoader</code>加载<code>com.mysql.jdbc.Driver</code>类,但这种行为破坏了双亲委派模型。</p><p>那么<code>BootstrapClassLoader</code>是如何拿到<code>AppClassLoader</code>的呢?</p><p>其实很简单,Java启动时,会将<code>AppClassLoader</code>放到当前线程的上下文中,<code>BootstrapClassLoader</code>就可以直接从线程上下文中拿到了。</p><p>代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">try</span> <span class="token punctuation">{</span> loader <span class="token operator">=</span> <span class="token class-name">AppClassLoader</span><span class="token punctuation">.</span><span class="token function">getAppClassLoader</span><span class="token punctuation">(</span>extcl<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">IOException</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">InternalError</span><span class="token punctuation">(</span> <span class="token string">"Could not create application class loader"</span><span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">// Also set the context class loader for the primordial thread.</span><span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">setContextClassLoader</span><span class="token punctuation">(</span>loader<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="第三次破坏"><a href="#第三次破坏" class="headerlink" title="第三次破坏"></a>第三次破坏</h3><p>第三次破坏是因为开发对“动态性”的追求,比如热部署。简单来说就是希望JVM能够再不重启的前提下,动态更新代码。</p><p>OSGi(Open Service Gateway Initiative) 技术是 Java 动态化模块化系统的一系列规范,该技术自定了类加载器,有着自己的类加载机制,它是一种网状结构的类加载方式,破坏了双亲委派机制,可以实现热部署。</p><p>鼎鼎大名的Eclipse就是基于OSGi开发的</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>类加载机制</title>
<link href="posts/52809/"/>
<url>posts/52809/</url>
<content type="html"><![CDATA[<h2 id="类什么时候加载?"><a href="#类什么时候加载?" class="headerlink" title="类什么时候加载?"></a>类什么时候加载?</h2><p>当虚拟机启动时,会先加载main方法所在的主类</p><p>除此之外,以下几种情况将触发类加载:</p><ol><li>调用类的静态方法</li><li>new对象</li><li>使用反射实例化对象</li></ol><p>简单来说,就是需要用到类时就加载类</p><p>加载一个类时,如果其父类尚未加载,则会先加载父类</p><h2 id="类加载过程"><a href="#类加载过程" class="headerlink" title="类加载过程"></a>类加载过程</h2><p>类加载主要有五个过程:</p><p><img src="/images/image-20211125212905466.png"></p><h3 id="加载"><a href="#加载" class="headerlink" title="加载"></a>加载</h3><p>加载其实就是将class文件转换为类对象的过程,共分为三个步骤:</p><ol><li>根据类的全限定名加载对应的class文件</li><li>将其转换为方法区的运行时数据结构(JDK1.7之前),JDK1.7之后是放在堆中</li><li>在内存中生成一个java.lang.Class对象</li></ol><p>值得一提的两点:</p><ul><li>class文件的来源并不一定是本地的class文件,只要是符合class文件结构的字节流都可以,比如来源于网络的二进制流,来源于数据库的二进制流,War包,运行时生成等</li><li>数组不由类加载器加载,而是JVM直接创建。</li></ul><h3 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h3><p>验证就是校验加载的类是否符合虚拟机规范,是否有安全问题。</p><p>该阶段非常重要,决定了JVM是否能够承受恶意代码的攻击。</p><p>如果你足够信任所运行的代码,可以使用<code>-Xverify:none</code>参数关闭,关闭后可以一定程度上加快类加载过程</p><p>验证的主要过程分为以下几步:</p><table><thead><tr><th>步骤</th><th>验证内容</th></tr></thead><tbody><tr><td><strong>文件格式验证</strong></td><td>验证class文件是否符合规范,例如:<br>1. 魔数校验:文件开头是否是CAFA BABE<br>2. 版本校验:当前JVM是否支持该版本的class<br>3. 常量池中的常量是否有不被支持的常量<br>4. 等等</td></tr><tr><td><strong>元数据验证</strong></td><td>class描述的信息进行校验,确保符合虚拟机规范,例如:<br>1. 是否对final的类进行了继承<br>2. 是否有父类<br>3. 抽象方法是否都已实现<br>4. 等等</td></tr><tr><td><strong>字节码验证</strong></td><td>验证程序语法语义是否合法,即操作指令是否合规合法</td></tr><tr><td><strong>符号引用验证</strong></td><td>该动作发生在将符号引用转换为直接引用时,对常量池中各种信息进行校验,如:<br>1. 符号引用中引用的类是否存在<br>2. 符号引用中的类、字段、方法是否可以被当前类访问<br>3. 等等</td></tr></tbody></table><h3 id="准备"><a href="#准备" class="headerlink" title="准备"></a>准备</h3><p>在准备阶段,JVM将为static修饰的变量分配内存,并初始化0值。</p><p>比如有一个静态变量为:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">int</span> value <span class="token operator">=</span> <span class="token number">10</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>在准备阶段,value的值是0,而不是10</p><p>注意:<strong>JDK1.7之前,此处分配内存在方法区,1.7之后在堆</strong></p><h3 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h3><p>解析就是将<strong>符号引用</strong>替换为<strong>直接引用</strong>,这里不得不重点提一下二者之间的区别:</p><table><thead><tr><th>符号引用</th><th>直接引用</th></tr></thead><tbody><tr><td>class文件中,用一组符号来描述一个目标。<br>例如常量池中<code>CONSTANT_Class_info</code>,描述了一个类的全限定名(如<code>java/lang/Object</code>)</td><td>指向内存某个位置的指针</td></tr></tbody></table><p>那么解析到底是怎么一回事呢?</p><p>比如现在有两个类,A和B,其中A中引用了B,在A的常量池中有B的符号引用,如下图:</p><p><img src="/images/image-20211125222950279.png"></p><p>当JVM的类加载器将A和B加载到内存的方法区(JDK1.7)中时,它们俩都各自有一个内存地址</p><p>A中B的符号引用替换为B的内存地址,以便将来使用B时,能够正确找到B</p><p><img src="/images/image-20211125223550341.png"></p><h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>当一个类中有<strong>初始化变量需要赋值</strong>或<strong>静态语句块</strong>时,JVM会为其自动生成一个<code><clinit></code>初始化方法。</p><p>初始化就是执行该方法,这是一个线程安全的方法,父类比子类先执行</p><p>接口没有静态语句块,但由于会有变量赋值,因此也会生成<code><clinit></code>方法</p><p><code><clinit></code>方法就是执行赋值操作以及静态语句块</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>class文件详解</title>
<link href="posts/26123/"/>
<url>posts/26123/</url>
<content type="html"><![CDATA[<h2 id="class文件结构"><a href="#class文件结构" class="headerlink" title="class文件结构"></a>class文件结构</h2><p>使用sublime text等可以查看16进制文件的工具,打开任意一个.class文件,可以看到如下内容:</p><p><img src="/images/image-20211120114041548.png"></p><p>其中大部分内容都是看不懂的,唯一能看懂的就是最开始的8个字母<code>cafe babe</code>,这个叫做“魔数”。</p><p>魔数是用来标记这个文件是什么类型的文件的。</p><p>很多类型的文件都使用了魔数,例如:</p><p><img src="/images/image-20211120114337577.png"></p><p>魔数之后才是class文件真正的内容。整个class文件结构大致如下:</p><p><img src="/images/image-20211120121501344.png"></p><p>含义解析:</p><table><thead><tr><th>结构</th><th>含义</th></tr></thead><tbody><tr><td>CA FE BA BE</td><td>魔数(咖啡宝贝),标记该文件是一个class文件</td></tr><tr><td>副版本号</td><td>编译的JDK副版本号,比如1.8.0,副版本就是0</td></tr><tr><td>主版本号</td><td>编译的JDK主版本号,比如1.8.0,主版本号是1.8</td></tr><tr><td>常量计数器</td><td>该类中的常量数量</td></tr><tr><td>常量池数据</td><td>常量的数据,常量的数量由“常量计数器”决定</td></tr><tr><td>访问标志</td><td>类的访问权限,如public</td></tr><tr><td>类索引</td><td>本类的引用信息</td></tr><tr><td>父类索引</td><td>父类的引用信息</td></tr><tr><td>接口计数器</td><td>该类中接口数量</td></tr><tr><td>接口信息数据</td><td>接口数据</td></tr><tr><td>字段计数器</td><td>该类中的字段数量</td></tr><tr><td>字段信息数据</td><td>字段详细信息</td></tr><tr><td>方法计数器</td><td>该类中的方法数量</td></tr><tr><td>方法信息数据</td><td>方法信息</td></tr><tr><td>属性计数器</td><td>属性数量</td></tr><tr><td>属性信息数据</td><td>属性信息,是对前面的数据的一个补充。例如方法信息区中无法保存方法体内容,则放在属性信息数据中保存</td></tr></tbody></table><p>工欲善其事必先利其器,为了更好的学习class文件,推荐安装IDEA的class解析插件,叫<code>jclasslib Bytecode Viewer</code>,安装好后,选中一个类文件,点击view->show bytecode with jclasslib 即可查看class文件信息</p><p><img src="/images/image-20211117215611482.png"></p><p>如果发现提示<code>class root not found</code>,只需要执行一下main方法,让代码编译一下即可。</p><h2 id="常量池结构"><a href="#常量池结构" class="headerlink" title="常量池结构"></a>常量池结构</h2><p>类中常量的数量是不一定的,因此有一个常量计数值(constant_pool_count)来表示常量的数量</p><p>这个数值有点特殊,它是从1开始计数,假设这个值是20,那么实际常量的数量其实是19</p><p>常量池中主要有两大类常量:</p><ol><li>字面量:Java语言层面的常量概念,比如final修饰的常量,字符串等</li><li>符号引用:类、接口、字段、方法引用等</li></ol><p>常量的数据结构类型主要有:</p><table><thead><tr><th>类型</th><th>标志</th><th>描述</th></tr></thead><tbody><tr><td>CONSTANT_Utf8_info</td><td>1</td><td>UTF-8字符串</td></tr><tr><td>CONSTANT_Integer_info</td><td>3</td><td>int常量</td></tr><tr><td>CONSTANT_Float_info</td><td>4</td><td>float常量</td></tr><tr><td>CONSTANT_Long_info</td><td>5</td><td>long常量</td></tr><tr><td>CONSTANT_Double_info</td><td>6</td><td>double常量</td></tr><tr><td>CONSTANT_Class_info</td><td>7</td><td>类或接口引用</td></tr><tr><td>CONSTANT_String_info</td><td>8</td><td>字符串常量</td></tr><tr><td>CONSTANT_Fieldref_info</td><td>9</td><td>字段引用</td></tr><tr><td>CONSTANT_Methodref_info</td><td>10</td><td>方法引用</td></tr><tr><td>CONSTANT_InterfaceMethodref_info</td><td>11</td><td>接口中方法引用</td></tr><tr><td>CONSTANT_NameAndType_info</td><td>12</td><td>字段或方法的名称和类型</td></tr><tr><td>CONSTANT_MethodHandle_info</td><td>15</td><td>方法句柄</td></tr><tr><td>CONSTANT_MethodType_info</td><td>16</td><td>方法类型</td></tr><tr><td>CONSTANT_InvokeDynamic_info</td><td>18</td><td>动态方法调用点</td></tr></tbody></table><p>以上类型的常量,都有各自不同的数据结构,使用标志tag来标记是哪一种常量,举几个例子:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_Integer_info</span><span class="token punctuation">{</span>u1 tag<span class="token operator">=</span><span class="token number">3</span><span class="token punctuation">;</span> u4 bytes<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>该结构中:</p><ul><li>u1中的1,表示无符号的1个字节,u4则表示4个字节</li><li>tag=3,对照上面的表格,说明这是int常量</li><li>bytes则是int的值</li></ul><p>类似,<code>CONSTANT_Float_info</code>有着和<code>CONSTANT_Integer_info</code>几乎一样的结构</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_Float_info</span><span class="token punctuation">{</span>u1 tag<span class="token operator">=</span><span class="token number">4</span><span class="token punctuation">;</span> u4 bytes<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>double和long因为占用8个字节,因此和int、float有些许不同,例如:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_Long_info</span><span class="token punctuation">{</span> u1 tag<span class="token operator">=</span><span class="token number">6</span><span class="token punctuation">;</span> u4 high_bytes<span class="token punctuation">;</span> u4 low_bytes<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>long和double的数据有高位字节和低位字节</p><p><code>CONSTANT_String_info</code>的结构也不一样:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_String_info</span><span class="token punctuation">{</span> u1 tag<span class="token operator">=</span><span class="token number">8</span><span class="token punctuation">;</span> u2 string_index<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>String类型常量结构并不直接存储字符串,而是保存了一个索引,指向<code>CONSTANT_Utf8_info</code>:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_Utf8_info</span><span class="token punctuation">{</span> u1 tag<span class="token operator">=</span><span class="token number">1</span><span class="token punctuation">;</span> u2 length<span class="token punctuation">;</span> u1 bytes<span class="token punctuation">[</span>length<span class="token punctuation">]</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p> 其中<code>length</code>表示字符串的字节数,<code>bytes[length]</code>则是字符串的字节数组</p><p>类似的,class类型的常量也是保存了一个引用:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">CONSTANT_Class_info</span><span class="token punctuation">{</span> u1 tag<span class="token operator">=</span><span class="token number">7</span><span class="token punctuation">;</span> u2 name_index<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p><code>name_index</code>指向了一个字符串<code>CONSTANT_Utf8_info</code>,该字符串保存了类的全限定名,即含包名的完整类名,如<code>java/lang/Object</code>。</p><p>与之类似的,<strong>类中的方法和字段也使用了这种方法来表示</strong></p><blockquote><p>那么哪些字面量类型的值会进入常量池呢?</p></blockquote><ol><li>final修饰的基本数据类型会进入常量池</li><li>非final类型的值,只有double,float,long类型的才会进入常量池</li><li>常量池中包含的字符串类型字面量会进入常量池(双引号的字符串值)</li></ol><p>可以使用<code>jclasslib Bytecode Viewer</code>查看常量池情况,例如下方的类,可以和上面提到的三种情况一一对照</p><p><img src="/images/image-20211124214649396.png"></p><h2 id="访问标记"><a href="#访问标记" class="headerlink" title="访问标记"></a>访问标记</h2><p>访问标记比较简单,有以下几种:</p><table><thead><tr><th>名称</th><th>含义</th><th>名称</th><th>含义</th></tr></thead><tbody><tr><td><strong>ACC_PUBLIC</strong></td><td>是否为public</td><td><strong>ACC_ABSTRACT</strong></td><td>是否抽象</td></tr><tr><td><strong>ACC_FINAL</strong></td><td>是否为final</td><td><strong>ACC_SYNTHETIC</strong></td><td>不是用户代码生成</td></tr><tr><td><strong>ACC_SUPER</strong></td><td>始终为true,没啥用</td><td><strong>ACC_ANNOTATION</strong></td><td>是否是注解</td></tr><tr><td><strong>ACC_INTERFACE</strong></td><td>是否是接口</td><td><strong>ACC_ENUM</strong></td><td>是否是枚举</td></tr></tbody></table><h2 id="类索引"><a href="#类索引" class="headerlink" title="类索引"></a>类索引</h2><p>类索引包含本类索引,父类索引,接口索引,它们都是一种符号引用,指向了常量池中对应的<code>CONSTANT_Class_info</code></p><h2 id="字段"><a href="#字段" class="headerlink" title="字段"></a>字段</h2><p>字段包含几个部分:</p><ol><li>访问标记,如public,private</li><li>字段名</li><li>描述符</li></ol><p><strong>字段访问标记:</strong></p><table><thead><tr><th>标记</th><th>含义</th><th>标记</th><th>含义</th></tr></thead><tbody><tr><td><strong>ACC_PUBLIC</strong></td><td>是否public</td><td><strong>ACC_VOLATILE</strong></td><td>是否volatile</td></tr><tr><td><strong>ACC_PRIVATE</strong></td><td>是否private</td><td><strong>ACC_TRANSIENT</strong></td><td>是否transient</td></tr><tr><td><strong>ACC_PROTECTED</strong></td><td>是否protected</td><td><strong>ACC_SYNTHETIC</strong></td><td>是否由编译器自动产生</td></tr><tr><td><strong>ACC_STATIC</strong></td><td>是否static</td><td><strong>ACC_ENUM</strong></td><td>是否是枚举</td></tr><tr><td><strong>ACC_FINAL</strong></td><td>是否final</td><td></td><td></td></tr></tbody></table><p><strong>字段名</strong>:一种符号引用,指向常量池</p><p><strong>描述符</strong>,用于表示字段的类型:</p><table><thead><tr><th>描述符标志</th><th>含义</th><th>描述符标志</th><th>含义</th></tr></thead><tbody><tr><td><strong>B</strong></td><td>byte</td><td><strong>J</strong></td><td>long</td></tr><tr><td><strong>C</strong></td><td>char</td><td><strong>S</strong></td><td>short</td></tr><tr><td><strong>D</strong></td><td>double</td><td><strong>Z</strong></td><td>boolean</td></tr><tr><td><strong>F</strong></td><td>float</td><td><strong>V</strong></td><td>void</td></tr><tr><td><strong>I</strong></td><td>int</td><td><strong>L</strong></td><td>对象类型,如<code>Ljava/lang/Object</code></td></tr></tbody></table><p>数组类型的,则使用符号<code>[</code>表示,n维数组就有n个<code>[</code>,例如:</p><ul><li><p><code>String[]</code>的描述符为<code>[Ljava/lang/Object</code></p></li><li><p><code>float[][]</code>的描述符为<code>[[F</code></p></li></ul><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><p>方法和字段的结构一致,也是包含访问标记、名称、描述符</p><p><strong>方法访问标记</strong></p><table><thead><tr><th>标记</th><th>含义</th><th>标记</th><th>含义</th></tr></thead><tbody><tr><td><strong>ACC_PUBLIC</strong></td><td>是否public</td><td><strong>ACC_BRIDGE</strong></td><td>是否由编译器产生的桥接方法</td></tr><tr><td><strong>ACC_PRIVATE</strong></td><td>是否private</td><td><strong>ACC_VARARGS</strong></td><td>是否接受不定参数</td></tr><tr><td><strong>ACC_PROTECTED</strong></td><td>是否protected</td><td><strong>ACC_NATIVE</strong></td><td>是否为native</td></tr><tr><td><strong>ACC_STATIC</strong></td><td>是否static</td><td><strong>ACC_ABSTRACT</strong></td><td>是否abstract</td></tr><tr><td><strong>ACC_FINAL</strong></td><td>是否final</td><td><strong>ACC_STRICTFP</strong></td><td>是否stricfp</td></tr><tr><td><strong>ACC_SYNCHRONIZED</strong></td><td>是否synchronized</td><td><strong>ACC_SYNTHETIC</strong></td><td>是否由编译器自动产生</td></tr></tbody></table><p><strong>方法名</strong>:一个符号引用,指向常量池字符串</p><p><strong>描述符</strong>:方法的描述符,基本格式为<code>(方法参数1;方法参数2)返回值</code>,例如以下方法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">private</span> <span class="token keyword">int</span> <span class="token function">myMethod</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">,</span> <span class="token keyword">int</span> args2<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token number">0</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>对应的描述符为<code>([java/lang/String;I)I</code></p><p>奇怪的是,方法区里只有方法的签名,却没有方法的具体代码。</p><p>其实方法的代码,被编译成字节码之后保存在属性信息数据中了</p><h2 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h2><p>字段、方法等数据,都可以携带自己的属性,例如方法的源码,则是编译成字节码保存在名为<code>Code</code>的属性中</p>]]></content>
<categories>
<category> JVM </category>
</categories>
<tags>
<tag> jvm </tag>
</tags>
</entry>
<entry>
<title>Kafka安装</title>
<link href="posts/30673/"/>
<url>posts/30673/</url>
<content type="html"><![CDATA[<h2 id="单机部署"><a href="#单机部署" class="headerlink" title="单机部署"></a>单机部署</h2><h3 id="安装JDK"><a href="#安装JDK" class="headerlink" title="安装JDK"></a>安装JDK</h3><p>安装kafka依赖jdk和zookeeper,jdk的安装自行百度,如果是ubuntu,可以直接使用:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">sudo</span> <span class="token function">apt-get</span> <span class="token function">install</span> -y openjdk-8-jdk<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>安装成功后可以使用<code>java -version</code>查看:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">➜ softs java -versionopenjdk version <span class="token string">"1.8.0_292"</span>OpenJDK Runtime Environment <span class="token punctuation">(</span>build <span class="token number">1.8</span>.0_292-8u292-b10-0ubuntu1~18.04-b10<span class="token punctuation">)</span>OpenJDK <span class="token number">64</span>-Bit Server VM <span class="token punctuation">(</span>build <span class="token number">25.292</span>-b10, mixed mode<span class="token punctuation">)</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><h3 id="安装zookeeper"><a href="#安装zookeeper" class="headerlink" title="安装zookeeper"></a>安装zookeeper</h3><p>下载zookeeper包:<a href="https://zookeeper.apache.org/releases.html">Apache ZooKeeper Download</a></p><p>解压:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">tar</span> -xvf apache-zookeeper-3.8.0-bin.tar.gz<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>修改配置:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token builtin class-name">cd</span> apache-zookeeper-3.8.0-bin/conf<span class="token function">cp</span> zoo_sample.cfg zoo.cfg<span class="token function">vim</span> zoo.cfg<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>主要修改<code>dataDir</code>,默认是<code>/tmp/zookeeper</code>,建议修改为硬盘空间比较大的目录</p><p>在<code>apache-zookeeper-3.7.0-bin/bin</code>目录下启动zk:</p><p><img src="/images/image-20210807151316928.png"></p><h3 id="安装kafka"><a href="#安装kafka" class="headerlink" title="安装kafka"></a>安装kafka</h3><p>下载kafka包:<a href="https://kafka.apache.org/downloads">Apache Kafka Download</a></p><p>解压:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">tar</span> -xvf kafka_2.13-3.2.1.tgz<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>修改配置:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token builtin class-name">cd</span> kafka_2.13-3.2.1<span class="token function">vim</span> config/server.properties<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>主要有以下几项重要配置:</p><pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token comment"># 节点id</span><span class="token attr-name">broker.id</span><span class="token punctuation">=</span><span class="token attr-value">0</span><span class="token comment"># ip改为你本机ip</span><span class="token attr-name">listeners</span><span class="token punctuation">=</span><span class="token attr-value">PLAINTEXT://10.211.55.3:9092 </span><span class="token comment"># ip改为你本机ip</span><span class="token attr-name">advertised.listeners</span><span class="token punctuation">=</span><span class="token attr-value">PLAINTEXT://10.211.55.3:9092</span><span class="token comment"># 虽然叫log,实际是数据目录,建议修改为硬盘空间比较大的目录</span><span class="token attr-name">log.dirs</span><span class="token punctuation">=</span><span class="token attr-value">/tmp/kafka-logs</span><span class="token comment"># 修改为zk的地址,本次演示zk就在同一台机器上,所以使用localhost</span><span class="token attr-name">zookeeper.connect</span><span class="token punctuation">=</span><span class="token attr-value">localhost:2181</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>启动kafka</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">bin/kafka-server-start.sh -daemon config/server.properties<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>停止kafka:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell">bin/kafka-server-stop.sh<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><h2 id="集群部署"><a href="#集群部署" class="headerlink" title="集群部署"></a>集群部署</h2><h3 id="zookeeper集群"><a href="#zookeeper集群" class="headerlink" title="zookeeper集群"></a>zookeeper集群</h3><p>准备三台服务器,分别下载解压好zookeeper,与安装单机zookeeper时一样,修改配置文件<code>conf/zoo.cfg</code>,关键在于增加三行配置:</p><pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token attr-name">server.1</span><span class="token punctuation">=</span><span class="token attr-value">服务器1-ip:2888:3888</span><span class="token attr-name">server.2</span><span class="token punctuation">=</span><span class="token attr-value">服务器2-ip:2888:3888</span><span class="token attr-name">server.3</span><span class="token punctuation">=</span><span class="token attr-value">服务器3-ip:2888:3888</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>三台服务器都用相同的配置,然后启动服务即可。三个zookeeper之间会用2888和3888端口进行通讯</p><h3 id="kafka集群"><a href="#kafka集群" class="headerlink" title="kafka集群"></a>kafka集群</h3><p>和zk同理,找三台服务器,解压修改配置:</p><pre class="line-numbers language-properties" data-language="properties"><code class="language-properties"><span class="token comment"># 三个kafka的id必须不同,如0,1,2</span><span class="token attr-name">broker.id</span><span class="token punctuation">=</span><span class="token attr-value">0 </span><span class="token comment"># 把三台zookeeper地址都配进来</span><span class="token attr-name">zookeeper.connect</span><span class="token punctuation">=</span><span class="token attr-value">zk1-ip:2181,zk2-ip:2181,zk3-ip:2181</span><span class="token comment"># 其他配置和单机时一样</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>启动服务即可</p><h2 id="K8s部署Kafka集群"><a href="#K8s部署Kafka集群" class="headerlink" title="K8s部署Kafka集群"></a>K8s部署Kafka集群</h2><p>参考<a href="/posts/55588">使用Strimzi-Kafka-Operator搭建kafka集群</a></p>]]></content>
<categories>
<category> kafka详解 </category>
</categories>
<tags>
<tag> kafka </tag>
</tags>
</entry>
<entry>
<title>AOP的原理与实现</title>
<link href="posts/6446/"/>
<url>posts/6446/</url>
<content type="html"><![CDATA[<h2 id="AOP基本原理"><a href="#AOP基本原理" class="headerlink" title="AOP基本原理"></a>AOP基本原理</h2><p>在上一篇文章<a href="/posts/21443/">Bean的创建与循环依赖</a>的最后,提到了一个<code>BeanPostProcessor</code>,它的其中一个实现类是<code>AbstractAutoProxyCreator</code>,AOP就是通过这个后置处理器来实现的</p><p>当有循环依赖时,通过三级缓存的lambda表达式,调用<code>getEarlyBeanReference</code>获取AOP代理对象,代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span><span class="token class-name">Object</span> bean<span class="token punctuation">,</span> <span class="token class-name">String</span> beanName<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Object</span> cacheKey <span class="token operator">=</span> <span class="token function">getCacheKey</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlyProxyReferences<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">wrapIfNecessary</span><span class="token punctuation">(</span>bean<span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> cacheKey<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>如果没有循环依赖,则是按照常规流程,在bean初始化之后调用<code>AbstractAutoProxyCreator</code>的<code>postProcessAfterInitialization</code>,代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">postProcessAfterInitialization</span><span class="token punctuation">(</span><span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Object</span> bean<span class="token punctuation">,</span> <span class="token class-name">String</span> beanName<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bean <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Object</span> cacheKey <span class="token operator">=</span> <span class="token function">getCacheKey</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>earlyProxyReferences<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">)</span> <span class="token operator">!=</span> bean<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">wrapIfNecessary</span><span class="token punctuation">(</span>bean<span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> cacheKey<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以发现,这两个路径,最终都是调用了<code>wrapIfNecessary</code>:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Object</span> <span class="token function">wrapIfNecessary</span><span class="token punctuation">(</span><span class="token class-name">Object</span> bean<span class="token punctuation">,</span> <span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">Object</span> cacheKey<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">StringUtils</span><span class="token punctuation">.</span><span class="token function">hasLength</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token keyword">this</span><span class="token punctuation">.</span>targetSourcedBeans<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">Boolean</span><span class="token punctuation">.</span>FALSE<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">isInfrastructureClass</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token function">shouldSkip</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> <span class="token class-name">Boolean</span><span class="token punctuation">.</span>FALSE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Create proxy if we have advice.</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> specificInterceptors <span class="token operator">=</span> <span class="token function">getAdvicesAndAdvisorsForBean</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>specificInterceptors <span class="token operator">!=</span> DO_NOT_PROXY<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> <span class="token class-name">Boolean</span><span class="token punctuation">.</span>TRUE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 创建代理</span> <span class="token class-name">Object</span> proxy <span class="token operator">=</span> <span class="token function">createProxy</span><span class="token punctuation">(</span> bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> specificInterceptors<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">SingletonTargetSource</span><span class="token punctuation">(</span>bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>proxyTypes<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> proxy<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> proxy<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> <span class="token class-name">Boolean</span><span class="token punctuation">.</span>FALSE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>wrapIfNecessary</code>中调用了<code>createProxy</code>创建代理对象,继续往下追踪,能找到如下图所示的代码:</p><p><img src="/images/image-20210611095851666.png"></p><p>从图中可以看到,这里有两个实现,一个是JDK动态代理,一个是Cglib代理。</p><p>那么到底应该用哪种呢?请看<code>createAopProxy</code>方法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">AopProxy</span> <span class="token function">createAopProxy</span><span class="token punctuation">(</span><span class="token class-name">AdvisedSupport</span> config<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">AopConfigException</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>config<span class="token punctuation">.</span><span class="token function">isOptimize</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> config<span class="token punctuation">.</span><span class="token function">isProxyTargetClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token function">hasNoUserSuppliedProxyInterfaces</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> targetClass <span class="token operator">=</span> config<span class="token punctuation">.</span><span class="token function">getTargetClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>targetClass <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AopConfigException</span><span class="token punctuation">(</span><span class="token string">"TargetSource cannot determine target class: "</span> <span class="token operator">+</span> <span class="token string">"Either an interface or a target is required for proxy creation."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 如果是接口,或者是代理类</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>targetClass<span class="token punctuation">.</span><span class="token function">isInterface</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token class-name">Proxy</span><span class="token punctuation">.</span><span class="token function">isProxyClass</span><span class="token punctuation">(</span>targetClass<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">JdkDynamicAopProxy</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ObjenesisCglibAopProxy</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">JdkDynamicAopProxy</span><span class="token punctuation">(</span>config<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>当被代理对象是接口,或者是代理类的时候,将会使用JDK代理。</p><p>接下来以<code>JdkDynamicAopProxy</code>为例,这个类实现了<code>InvocationHandler</code>接口,熟悉JDK动态代理的应该对这个接口不陌生</p>]]></content>
<categories>
<category> Spring5源码解析 </category>
</categories>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>Bean的创建与循环依赖</title>
<link href="posts/21443/"/>
<url>posts/21443/</url>
<content type="html"><![CDATA[<h2 id="Bean创建过程"><a href="#Bean创建过程" class="headerlink" title="Bean创建过程"></a>Bean创建过程</h2><p>Bean的创建过程是从beanFactory的getBean方法开始的。以单例bean为例,当从单例池中获取不到bean时,会调用<code>createBean</code>进行创建。</p><p>先看<code>AbstractBeanFactory.doGetBean()</code>中的其中一段代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// Create bean instance.</span><span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> sharedInstance <span class="token operator">=</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">createBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">BeansException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Explicitly remove instance from singleton cache: It might have been put there</span> <span class="token comment">// eagerly by the creation process, to allow for circular reference resolution.</span> <span class="token comment">// Also remove any beans that received a temporary reference to the bean.</span> <span class="token function">destroySingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">throw</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> bean <span class="token operator">=</span> <span class="token function">getObjectForBeanInstance</span><span class="token punctuation">(</span>sharedInstance<span class="token punctuation">,</span> name<span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这个方法中调用了<code>getSingleton</code>,第二个参数是一个lambda表达式。</p><p>在<code>getSingleton</code>方法中:</p><ol><li>会先尝试从单例池中找bean,找到则直接返回。</li><li>找不到先调用<code>beforeSingletonCreation()</code></li><li>然后调用上面的lambda表达式进行创建bean</li><li>接着调用<code>afterSingletonCreation()</code></li><li>最后<code>addSingleton()</code>将bean添加到单例池中</li></ol><p>代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">ObjectFactory</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> singletonFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token string">"Bean name must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 从单例池中找</span> <span class="token class-name">Object</span> singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>singletonsCurrentlyInDestruction<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationNotAllowedException</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token string">"Singleton bean creation not allowed while singletons of this factory are in destruction "</span> <span class="token operator">+</span> <span class="token string">"(Do not request a bean from a BeanFactory in a destroy method implementation!)"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Creating shared instance of singleton bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"'"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 准备开始创建bean,将beanName存到singletonsCurrentlyInCreation集合中,标记该bean正在创建</span> <span class="token function">beforeSingletonCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">boolean</span> newSingleton <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">boolean</span> recordSuppressedExceptions <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>suppressedExceptions <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>recordSuppressedExceptions<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>suppressedExceptions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 调用lambda表达式,实际是调用了createBean</span> singletonObject <span class="token operator">=</span> singletonFactory<span class="token punctuation">.</span><span class="token function">getObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> newSingleton <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 此处省略了部分catch异常的代码</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>recordSuppressedExceptions<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>suppressedExceptions <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 创建bean结束,将其从singletonsCurrentlyInCreation集合中移除</span> <span class="token function">afterSingletonCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>newSingleton<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 将bean添加到单例池</span> <span class="token function">addSingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> singletonObject<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> singletonObject<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>所以,真正创建bean的过程还得看<code>createBean()</code>方法,<code>createBean()</code>中又调用了<code>doCreateBean()</code>,这个方法比较长,摘出重要的部分如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Object</span> <span class="token function">doCreateBean</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">RootBeanDefinition</span> mbd<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">BeanCreationException</span> <span class="token punctuation">{</span> <span class="token comment">// Instantiate the bean.</span> <span class="token class-name">BeanWrapper</span> instanceWrapper <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> instanceWrapper <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>factoryBeanInstanceCache<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 实例化bean,通过反射new</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instanceWrapper <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> instanceWrapper <span class="token operator">=</span> <span class="token function">createBeanInstance</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">Object</span> bean <span class="token operator">=</span> instanceWrapper<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> beanType <span class="token operator">=</span> instanceWrapper<span class="token punctuation">.</span><span class="token function">getWrappedClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanType <span class="token operator">!=</span> <span class="token class-name">NullBean</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> mbd<span class="token punctuation">.</span>resolvedTargetType <span class="token operator">=</span> beanType<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Allow post-processors to modify the merged bean definition.</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>postProcessingLock<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mbd<span class="token punctuation">.</span>postProcessed<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token function">applyMergedBeanDefinitionPostProcessors</span><span class="token punctuation">(</span>mbd<span class="token punctuation">,</span> beanType<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Post-processing of merged bean definition failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> mbd<span class="token punctuation">.</span>postProcessed <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Eagerly cache singletons to be able to resolve circular references</span> <span class="token comment">// even when triggered by lifecycle interfaces like BeanFactoryAware.</span> <span class="token comment">// 解决循环依赖,提前暴露单例对象</span> <span class="token keyword">boolean</span> earlySingletonExposure <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">isSingleton</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token keyword">this</span><span class="token punctuation">.</span>allowCircularReferences <span class="token operator">&&</span> <span class="token function">isSingletonCurrentlyInCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonExposure<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Eagerly caching bean '"</span> <span class="token operator">+</span> beanName <span class="token operator">+</span> <span class="token string">"' to allow for resolving potential circular references"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 将lambda表达式添加到三级缓存中</span> <span class="token function">addSingletonFactory</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Initialize the bean instance.</span> <span class="token class-name">Object</span> exposedObject <span class="token operator">=</span> bean<span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 属性填充</span> <span class="token function">populateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> instanceWrapper<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 初始化,执行Aware,执行beanPostProcessor</span> exposedObject <span class="token operator">=</span> <span class="token function">initializeBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> exposedObject<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 省略catch代码块</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonExposure<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Object</span> earlySingletonReference <span class="token operator">=</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>earlySingletonReference <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>exposedObject <span class="token operator">==</span> bean<span class="token punctuation">)</span> <span class="token punctuation">{</span> exposedObject <span class="token operator">=</span> earlySingletonReference<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 省略</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Register bean as disposable.</span> <span class="token comment">// 省略</span> <span class="token keyword">return</span> exposedObject<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>从上面代码中,可以看到创建bean的大致过程:</p><ol><li>实例化bean:createBeanInstance</li><li>属性填充:populateBean</li><li>初始化bean:initializeBean,包含执行Aware,执行beanPostProcessor</li></ol><p>下面看看这三个步骤的细节</p><h3 id="createBeanInstance"><a href="#createBeanInstance" class="headerlink" title="createBeanInstance"></a>createBeanInstance</h3><p>其实即使不看createBeanInstance的代码,也大致能猜到,无非就是通过反射的机制来实例化对象,不过实际上要更复杂一下,别忘了构造函数的依赖注入,就是在这里实现的。</p><p>我们可以先看下这个方法的注释:</p><p><img src="/images/image-20210603154251756.png"></p><p>该方法中,其实就是使用这四种方式实例化对象的:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">BeanWrapper</span> <span class="token function">createBeanInstance</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">RootBeanDefinition</span> mbd<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">Object</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Make sure bean class is actually resolved at this point.</span> <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> beanClass <span class="token operator">=</span> <span class="token function">resolveBeanClass</span><span class="token punctuation">(</span>mbd<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanClass <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token operator">!</span><span class="token class-name">Modifier</span><span class="token punctuation">.</span><span class="token function">isPublic</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">.</span><span class="token function">getModifiers</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isNonPublicAccessAllowed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Bean class isn't public, and non-public access not allowed: "</span> <span class="token operator">+</span> beanClass<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token class-name">Supplier</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> instanceSupplier <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getInstanceSupplier</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>instanceSupplier <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 实例化方式一</span> <span class="token keyword">return</span> <span class="token function">obtainFromSupplier</span><span class="token punctuation">(</span>instanceSupplier<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getFactoryMethodName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 实例化方式二</span> <span class="token keyword">return</span> <span class="token function">instantiateUsingFactoryMethod</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Shortcut when re-creating the same bean...</span> <span class="token keyword">boolean</span> resolved <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">boolean</span> autowireNecessary <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>args <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>constructorArgumentLock<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span>resolvedConstructorOrFactoryMethod <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> resolved <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> autowireNecessary <span class="token operator">=</span> mbd<span class="token punctuation">.</span>constructorArgumentsResolved<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolved<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>autowireNecessary<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 实例化方式三</span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// 实例化方式四</span> <span class="token keyword">return</span> <span class="token function">instantiateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Candidate constructors for autowiring?</span> <span class="token class-name">Constructor</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span><span class="token punctuation">[</span><span class="token punctuation">]</span> ctors <span class="token operator">=</span> <span class="token function">determineConstructorsFromBeanPostProcessors</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ctors <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">||</span> mbd<span class="token punctuation">.</span><span class="token function">getResolvedAutowireMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> AUTOWIRE_CONSTRUCTOR <span class="token operator">||</span> mbd<span class="token punctuation">.</span><span class="token function">hasConstructorArgumentValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token operator">!</span><span class="token class-name">ObjectUtils</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>args<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> ctors<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Preferred constructors for default construction?</span> ctors <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getPreferredConstructors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>ctors <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">autowireConstructor</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> ctors<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// No special handling: simply use no-arg constructor.</span> <span class="token keyword">return</span> <span class="token function">instantiateBean</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>obtainFromSupplier</strong></p><p>这种方法,是直接调用<code>Supplier</code>接口的<code>get()</code>方法拿到实例化好的对象,也就是说具体如何实例化不归当前方法管</p><p><strong>instantiateUsingFactoryMethod</strong></p><p>如果开发者实现了<code>FactoryBean</code>接口,则使用该方式进行实例化对象。<code>FactoryBean</code>接口是让开发者定制实例化过程的接口</p><p><strong>autowireConstructor</strong></p><p>从命名就可以看出,当需要自动注入的时候,就会调用这种方式进行实例化</p><p><strong>instantiateBean</strong></p><p>使用默认构造函数实例化</p><h3 id="populateBean"><a href="#populateBean" class="headerlink" title="populateBean"></a>populateBean</h3><p>属性填充就是在实例化之后,为bean的属性注入值。</p><p>例如<code>UserService</code>中有个属性<code>OrderService orderService</code>,且该属性使用了<code>@Autowire</code>注解,那么在属性填充时,就会去获取orderService这个bean</p><p>如果OrderService这个bean中刚好也依赖了UserService,那么就形成了死循环,这就是面试常问的循环依赖。</p><p>至于如何解决循环依赖问题,这里先不展开,请往后看。</p><p>现在先看看populateBean源码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">populateBean</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">RootBeanDefinition</span> mbd<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">BeanWrapper</span> bw<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bw <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">hasPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span> mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Cannot apply property values to null instance"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Skip property population phase for null instance.</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the</span> <span class="token comment">// state of the bean before properties are set. This can be used, for example,</span> <span class="token comment">// to support styles of field injection.</span> <span class="token comment">// 执行InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isSynthetic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">BeanPostProcessor</span> bp <span class="token operator">:</span> <span class="token function">getBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bp <span class="token keyword">instanceof</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span> ibp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> bp<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>ibp<span class="token punctuation">.</span><span class="token function">postProcessAfterInstantiation</span><span class="token punctuation">(</span>bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token class-name">PropertyValues</span> pvs <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">hasPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">?</span> mbd<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> resolvedAutowireMode <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getResolvedAutowireMode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_NAME <span class="token operator">||</span> resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_TYPE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">MutablePropertyValues</span> newPvs <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MutablePropertyValues</span><span class="token punctuation">(</span>pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Add property values based on autowire by name if applicable.</span> <span class="token comment">// 根据名称自动注入</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_NAME<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">autowireByName</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> newPvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Add property values based on autowire by type if applicable.</span> <span class="token comment">// 根据类型自动注入</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>resolvedAutowireMode <span class="token operator">==</span> AUTOWIRE_BY_TYPE<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">autowireByType</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> newPvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pvs <span class="token operator">=</span> newPvs<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">boolean</span> hasInstAwareBpps <span class="token operator">=</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">boolean</span> needsDepCheck <span class="token operator">=</span> <span class="token punctuation">(</span>mbd<span class="token punctuation">.</span><span class="token function">getDependencyCheck</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token class-name">AbstractBeanDefinition</span><span class="token punctuation">.</span>DEPENDENCY_CHECK_NONE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">PropertyDescriptor</span><span class="token punctuation">[</span><span class="token punctuation">]</span> filteredPds <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>hasInstAwareBpps<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvs <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> pvs <span class="token operator">=</span> mbd<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 执行InstantiationAwareBeanPostProcessor的postProcessPropertyValues</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">BeanPostProcessor</span> bp <span class="token operator">:</span> <span class="token function">getBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bp <span class="token keyword">instanceof</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">InstantiationAwareBeanPostProcessor</span> ibp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">InstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> bp<span class="token punctuation">;</span> <span class="token class-name">PropertyValues</span> pvsToUse <span class="token operator">=</span> ibp<span class="token punctuation">.</span><span class="token function">postProcessProperties</span><span class="token punctuation">(</span>pvs<span class="token punctuation">,</span> bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvsToUse <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>filteredPds <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> filteredPds <span class="token operator">=</span> <span class="token function">filterPropertyDescriptorsForDependencyCheck</span><span class="token punctuation">(</span>bw<span class="token punctuation">,</span> mbd<span class="token punctuation">.</span>allowCaching<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> pvsToUse <span class="token operator">=</span> ibp<span class="token punctuation">.</span><span class="token function">postProcessPropertyValues</span><span class="token punctuation">(</span>pvs<span class="token punctuation">,</span> filteredPds<span class="token punctuation">,</span> bw<span class="token punctuation">.</span><span class="token function">getWrappedInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvsToUse <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> pvs <span class="token operator">=</span> pvsToUse<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>needsDepCheck<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>filteredPds <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> filteredPds <span class="token operator">=</span> <span class="token function">filterPropertyDescriptorsForDependencyCheck</span><span class="token punctuation">(</span>bw<span class="token punctuation">,</span> mbd<span class="token punctuation">.</span>allowCaching<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token function">checkDependencies</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> filteredPds<span class="token punctuation">,</span> pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pvs <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">applyPropertyValues</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> mbd<span class="token punctuation">,</span> bw<span class="token punctuation">,</span> pvs<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看到,populateBean中调用了InstantiationAwareBeanPostProcessor后置处理器,通过debug发现,是<code>AutowiredAnnotationBeanPostProcessor</code>这个后置处理器为UserService填充了OrderService这个属性:</p><p><img src="/images/image-20210603162652592.png"></p><p>继续跟踪,最终找到设置属性的代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">inject</span><span class="token punctuation">(</span><span class="token class-name">Object</span> bean<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">PropertyValues</span> pvs<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Throwable</span> <span class="token punctuation">{</span> <span class="token class-name">Field</span> field <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">Field</span><span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>member<span class="token punctuation">;</span> <span class="token class-name">Object</span> value<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>cached<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token function">resolvedCachedArgument</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>cachedFieldValue<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">NoSuchBeanDefinitionException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Unexpected removal of target bean for cached argument -> re-resolve</span> value <span class="token operator">=</span> <span class="token function">resolveFieldValue</span><span class="token punctuation">(</span>field<span class="token punctuation">,</span> bean<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> value <span class="token operator">=</span> <span class="token function">resolveFieldValue</span><span class="token punctuation">(</span>field<span class="token punctuation">,</span> bean<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>value <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">ReflectionUtils</span><span class="token punctuation">.</span><span class="token function">makeAccessible</span><span class="token punctuation">(</span>field<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 设置属性值</span> field<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>bean<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="initializeBean"><a href="#initializeBean" class="headerlink" title="initializeBean"></a>initializeBean</h3><p>该方法结构很简单:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Object</span> <span class="token function">initializeBean</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">Object</span> bean<span class="token punctuation">,</span> <span class="token annotation punctuation">@Nullable</span> <span class="token class-name">RootBeanDefinition</span> mbd<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 执行aware扩展点</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">getSecurityManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">AccessController</span><span class="token punctuation">.</span><span class="token function">doPrivileged</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">PrivilegedAction</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">Object</span><span class="token punctuation">></span></span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-></span> <span class="token punctuation">{</span> <span class="token function">invokeAwareMethods</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function">getAccessControlContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token function">invokeAwareMethods</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 执行postProcess Before</span> <span class="token comment">// @PostConstruct就是在这里实现的</span> <span class="token class-name">Object</span> wrappedBean <span class="token operator">=</span> bean<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isSynthetic</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> wrappedBean <span class="token operator">=</span> <span class="token function">applyBeanPostProcessorsBeforeInitialization</span><span class="token punctuation">(</span>wrappedBean<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// 执行init方法</span> <span class="token function">invokeInitMethods</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> wrappedBean<span class="token punctuation">,</span> mbd<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">BeanCreationException</span><span class="token punctuation">(</span> <span class="token punctuation">(</span>mbd <span class="token operator">!=</span> <span class="token keyword">null</span> <span class="token operator">?</span> mbd<span class="token punctuation">.</span><span class="token function">getResourceDescription</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> <span class="token string">"Invocation of init method failed"</span><span class="token punctuation">,</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 执行BeanPostProcessors after</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>mbd <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isSynthetic</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> wrappedBean <span class="token operator">=</span> <span class="token function">applyBeanPostProcessorsAfterInitialization</span><span class="token punctuation">(</span>wrappedBean<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> wrappedBean<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="循环依赖"><a href="#循环依赖" class="headerlink" title="循环依赖"></a>循环依赖</h2><p>循环依赖就是A依赖B,B又依赖A。</p><p>先抛开Spring来谈,两个普通的Java对象,如何解决循环依赖?</p><p>很简单,假设有UserService和OrderService:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">UserService</span> <span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">OrderService</span> orderService<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token class-name">OrderService</span> <span class="token function">getOrderService</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> orderService<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setOrderService</span><span class="token punctuation">(</span><span class="token class-name">OrderService</span> orderService<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>orderService <span class="token operator">=</span> orderService<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">OrderService</span> <span class="token punctuation">{</span><span class="token keyword">private</span> <span class="token class-name">UserService</span> userService<span class="token punctuation">;</span><span class="token keyword">public</span> <span class="token class-name">UserService</span> <span class="token function">getUserService</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">return</span> userService<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">setUserService</span><span class="token punctuation">(</span><span class="token class-name">UserService</span> userService<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">this</span><span class="token punctuation">.</span>userService <span class="token operator">=</span> userService<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>循环依赖:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">UserService</span> userService <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">UserService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">OrderService</span> orderService <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">OrderService</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>userService<span class="token punctuation">.</span><span class="token function">setOrderService</span><span class="token punctuation">(</span>orderService<span class="token punctuation">)</span><span class="token punctuation">;</span>orderService<span class="token punctuation">.</span><span class="token function">setUserService</span><span class="token punctuation">(</span>userService<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>是不是很简单?其实Spring解决循环依赖也是这个流程,只不过Spring Bean不是一个普通的Java对象,它需要经过属性填充,初始化,各种后置处理器处理等生命周期后,才能称之为bean。</p><p>因此为了解决循环依赖问题,Spring有一级缓存,二级缓存,三级缓存。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/** Cache of singleton objects: bean name to bean instance. */</span><span class="token comment">// 一级缓存</span><span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> singletonObjects <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentHashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token number">256</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/** Cache of singleton factories: bean name to ObjectFactory. */</span><span class="token comment">// 三级缓存</span><span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">ObjectFactory</span><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> singletonFactories <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">/** Cache of early singleton objects: bean name to bean instance. */</span><span class="token comment">// 二级缓存</span><span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">Map</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">,</span> <span class="token class-name">Object</span><span class="token punctuation">></span></span> earlySingletonObjects <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentHashMap</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token number">16</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><ul><li>一级缓存其实就是单例池</li><li>二级缓存可以认为是一个半成品bean,也叫提前暴露的单例bean</li><li>三级缓存是一个存放ObjectFactory接口的集合</li></ul><p>当我们从单例池中寻找bean时,会先查一级缓存,再查二级缓存,最后找三级缓存:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Object</span> <span class="token function">getSingleton</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token keyword">boolean</span> allowEarlyReference<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Quick check for existing instance without full singleton lock</span> <span class="token comment">// 从一级缓存中取</span> <span class="token class-name">Object</span> singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 如果bean正在创建中</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> <span class="token function">isSingletonCurrentlyInCreation</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 从二级缓存中取</span> singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlySingletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">&&</span> allowEarlyReference<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Consistent creation of early reference within full singleton lock</span> singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> singletonObject <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlySingletonObjects<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 二级缓存中没有,则从三级缓存中取出lambda</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonObject <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">ObjectFactory</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> singletonFactory <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonFactories<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>singletonFactory <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 调用lambda,得到bean,并放入二级缓存</span> singletonObject <span class="token operator">=</span> singletonFactory<span class="token punctuation">.</span><span class="token function">getObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlySingletonObjects<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> singletonObject<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>singletonFactories<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> singletonObject<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里有一点很重要的:<strong>二级缓存中的bean,是调用三级缓存的<code>ObjectFactory.getObject()</code>得到的并放入二级缓存的</strong></p><p><strong>解决循环依赖过程:</strong></p><ol><li>创建UserService bean</li><li>把lambda表达式添加到三级缓存:<code>() -> getEarlyBeanReference(beanName, mbd, bean)</code></li><li>给UserService 填充属性,发现依赖OrderService</li><li>开始创建OrderService bean</li><li>给OrderService 填充属性,发现依赖UserService</li><li>寻找UserService bean,发现UserService正在创建中,且有三级缓存,于是调用三级缓存,得到半成品UserService bean,并放入二级缓存</li><li><code>orderService.setUserService(半成品bean)</code></li><li>OrderService Bean创建完成,放入一级缓存(单例池)</li><li><code>userService.setOrderService(orderService)</code></li><li>UserService 创建完成,放入一级缓存(单例池)</li></ol><p>示意图大致如下,请按箭头序号看</p><p><img src="/images/image-20210604102027480.png"></p><p>循环依赖过程其实并不复杂,关键在于为什么需要三级缓存。这也是面试常问的问题。</p><p>想知道为什么需要三级缓存,我们就需要看看三级缓存中的lambda表达式到底做了什么。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Object</span> <span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span><span class="token class-name">String</span> beanName<span class="token punctuation">,</span> <span class="token class-name">RootBeanDefinition</span> mbd<span class="token punctuation">,</span> <span class="token class-name">Object</span> bean<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Object</span> exposedObject <span class="token operator">=</span> bean<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>mbd<span class="token punctuation">.</span><span class="token function">isSynthetic</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token function">hasInstantiationAwareBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">BeanPostProcessor</span> bp <span class="token operator">:</span> <span class="token function">getBeanPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>bp <span class="token keyword">instanceof</span> <span class="token class-name">SmartInstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">SmartInstantiationAwareBeanPostProcessor</span> ibp <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">SmartInstantiationAwareBeanPostProcessor</span><span class="token punctuation">)</span> bp<span class="token punctuation">;</span> exposedObject <span class="token operator">=</span> ibp<span class="token punctuation">.</span><span class="token function">getEarlyBeanReference</span><span class="token punctuation">(</span>exposedObject<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> exposedObject<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>从源码中看到,这个方法中执行了一个<code>BeanPostProcessor</code>后置处理器,其中一个实现居然是<code>AbstractAutoProxyCreator</code>,如下图:</p><p><img src="/images/image-20210604102531042.png"></p><p>这个类看着眼熟吗?其实这就是AOP。</p><p>当循环依赖发生时,调用三级缓存中的lambda表达式,得到一个半成品bean,活着叫提前暴露的bean,并放入二级缓存。</p><p>如果需要AOP,则这个半成品就是AOP代理对象。</p><p>我们知道AOP基本大致原理是给原来的对象创建一个代理对象,通过代理对象调用目标方法才能达到切面的目的。</p><p>因此三级缓存就是为了创建AOP代理对象(如果有使用AOP)。</p><p>二级缓存用来临时存放三级缓存产生的对象。</p>]]></content>
<categories>
<category> Spring5源码解析 </category>
</categories>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>Spring容器创建过程</title>
<link href="posts/64660/"/>
<url>posts/64660/</url>
<content type="html"><![CDATA[<h2 id="Spring启动入口"><a href="#Spring启动入口" class="headerlink" title="Spring启动入口"></a>Spring启动入口</h2><p>用多了SpringBoot,也许很多人已经忘了如何手动启动一个Spring容器,以下是一个最简单的启动方法:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Application</span> <span class="token punctuation">{</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token class-name">ApplicationContext</span> applicationContext <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AnnotationConfigApplicationContext</span><span class="token punctuation">(</span><span class="token string">"net.javajun"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">Object</span> userService <span class="token operator">=</span> applicationContext<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span><span class="token string">"userService"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>userService<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这里我们使用了AnnotationConfigApplicationContext这个实现。</p><p>很多博客教程中常用基于xml的ClassPathXmlApplicationContext,但如今SpringBoot时代,已经很少有人用xml了,且本文也想写一些和别人不一样的东西,因此使用了基于注解的Spring容器AnnotationConfigApplicationContext</p><p>从上面两三行代码可以看出,启动一个Spring容器,只需<code>new AnnotationConfigApplicationContext</code>即可,因此,Spring的整个启动过程就在AnnotationConfigApplicationContext的构造函数中。</p><p>常用的两个构造函数如下所示,可以传入包名,也可以传入具体的class:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">AnnotationConfigApplicationContext</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> componentClasses<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">register</span><span class="token punctuation">(</span>componentClasses<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token class-name">AnnotationConfigApplicationContext</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> basePackages<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">scan</span><span class="token punctuation">(</span>basePackages<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>以传入包名的构造方法为例,Spring启动包含两个步骤:</p><ol><li>扫描bean</li><li>refresh()</li></ol><h2 id="扫描bean"><a href="#扫描bean" class="headerlink" title="扫描bean"></a>扫描bean</h2><p>扫描bean,就是根据传入的包名,扫描所有的类,并封装到BeanDefinition中,它是调用了<code>ClassPathBeanDefinitionScanner</code>这个scanner进行扫描的,且在扫描过程中,将beanDefinition注册给beanFactory</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">AnnotationConfigApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>reader <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AnnotatedBeanDefinitionReader</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>scanner <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassPathBeanDefinitionScanner</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>BeanDefinition是一个接口,从下图可以看出,BeanDefinition大致包含以下信息:</p><table><thead><tr><th>信息</th><th>说明</th></tr></thead><tbody><tr><td>beanClass</td><td>bean是哪个类</td></tr><tr><td>scope</td><td>单例 singleton,原型 prototype</td></tr><tr><td>lazyinit</td><td>是否懒加载</td></tr><tr><td>dependsOn</td><td>依赖的bean,不是指@autowire注入的bean</td></tr><tr><td>isPrimary</td><td>有多个实现时,指定优先使用的bean</td></tr><tr><td>其他</td><td>…</td></tr></tbody></table><p><img src="/images/image-20210601162440449.png"></p><p>我们来看看<code>ClassPathBeanDefinitionScanner</code>具体是如何扫描bean的,找到doScan方法,代码中给出了注释,请仔细阅读</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanDefinitionHolder</span><span class="token punctuation">></span></span> <span class="token function">doScan</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> basePackages<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">Assert</span><span class="token punctuation">.</span><span class="token function">notEmpty</span><span class="token punctuation">(</span>basePackages<span class="token punctuation">,</span> <span class="token string">"At least one base package must be specified"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanDefinitionHolder</span><span class="token punctuation">></span></span> beanDefinitions <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 遍历所有包名</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> basePackage <span class="token operator">:</span> basePackages<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 找到包中候选的component</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanDefinition</span><span class="token punctuation">></span></span> candidates <span class="token operator">=</span> <span class="token function">findCandidateComponents</span><span class="token punctuation">(</span>basePackage<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">BeanDefinition</span> candidate <span class="token operator">:</span> candidates<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 解析scope</span> <span class="token class-name">ScopeMetadata</span> scopeMetadata <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>scopeMetadataResolver<span class="token punctuation">.</span><span class="token function">resolveScopeMetadata</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span><span class="token punctuation">;</span> candidate<span class="token punctuation">.</span><span class="token function">setScope</span><span class="token punctuation">(</span>scopeMetadata<span class="token punctuation">.</span><span class="token function">getScopeName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 解析beanName</span> <span class="token class-name">String</span> beanName <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanNameGenerator<span class="token punctuation">.</span><span class="token function">generateBeanName</span><span class="token punctuation">(</span>candidate<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>candidate <span class="token keyword">instanceof</span> <span class="token class-name">AbstractBeanDefinition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 为BeanDefinition设置一些默认属性</span> <span class="token comment">// 不要被postProcess这个命名给骗了,这不是一个后置处理器</span> <span class="token function">postProcessBeanDefinition</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">AbstractBeanDefinition</span><span class="token punctuation">)</span> candidate<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>candidate <span class="token keyword">instanceof</span> <span class="token class-name">AnnotatedBeanDefinition</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 处理bean上的一些注解,如@Primary,如@Lazy</span> <span class="token class-name">AnnotationConfigUtils</span><span class="token punctuation">.</span><span class="token function">processCommonDefinitionAnnotations</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token class-name">AnnotatedBeanDefinition</span><span class="token punctuation">)</span> candidate<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 校验候选bean,如是否已存在,名称是否冲突</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">checkCandidate</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> candidate<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// BeanDefinition外层封装一层BeanDefinitionHolder</span> <span class="token class-name">BeanDefinitionHolder</span> definitionHolder <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BeanDefinitionHolder</span><span class="token punctuation">(</span>candidate<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">;</span> definitionHolder <span class="token operator">=</span> <span class="token class-name">AnnotationConfigUtils</span><span class="token punctuation">.</span><span class="token function">applyScopedProxyMode</span><span class="token punctuation">(</span>scopeMetadata<span class="token punctuation">,</span> definitionHolder<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span> beanDefinitions<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>definitionHolder<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 将BeanDefinitionHolder注册给registry</span> <span class="token function">registerBeanDefinition</span><span class="token punctuation">(</span>definitionHolder<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> beanDefinitions<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>从上面的代码可以看出,扫描的过程大致分为三步骤:</p><ol><li><p>先找出所有候选的Component</p></li><li><p>解析出beanName,isPrimary等属性封装到BeanDefinition中</p></li><li><p>注册到registry中,此处的registry其实就是<code>ApplicationContext</code>,在创建scanner时传入的:</p><p><img src="/images/image-20210601172220685.png"></p></li></ol><p>这里分享两个阅读源码的小技巧:</p><p><strong>第一、看一个方法,要先看整体,根据命名猜测作用,不要一直深入从而陷入到细节</strong></p><p><strong>第二、善用debug功能</strong></p><p>比如<code>ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);</code>这一行代码,你可能没法一眼看出它的作用。</p><p>点进去你会发现<code>ScopeMetadataResolver</code>是一个接口,接下去又得去找它的实现类,如果实现类很多,咱也不知道到底具体用的是哪个,这就陷入了细节。</p><p>此时正确的做法是设一个断点,如:</p><p><img src="/images/image-20210601172926910.png"></p><p>从截图中可以看到,那一行代码传入一个候选component,返回一个scope,那我们就知道了它的作用是解析scope,至于它具体使用的是哪个实现类根本不重要。</p><p>了解了扫描的的大致步骤,再带着好奇心进一步了解一些细节,比如<code>findCandidateComponents</code>,是如何找到候选component的。</p><p>找到该方法的具体实现,通过debug发现,该方法内部找到了我们所定义的类.class文件, 如下图所示:</p><p><img src="/images/image-20210601174445675.png"></p><p>值得一提的是,找出的所有.class文件,是包含没有@Component注解的普通类的,然后在<code>isCandidateComponent()</code>中通过判断是否有@Component注解来决定是否真正成为候选bean</p><p><img src="/images/image-20210601181247870.png"></p><p>至此,我们已经基本上了解的Spring扫描Bean的全过程</p><h2 id="refresh"><a href="#refresh" class="headerlink" title="refresh"></a>refresh</h2><p>refresh是真正的进行bean的整个生命周期,该方法代码是由父类<code>AbstractApplicationContext</code>实现的,结构也十分清晰,具体请看注释:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">BeansException</span><span class="token punctuation">,</span> <span class="token class-name">IllegalStateException</span> <span class="token punctuation">{</span> <span class="token keyword">synchronized</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>startupShutdownMonitor<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Prepare this context for refreshing.</span> <span class="token comment">// 准备工作,打印日志,标记一些状态</span> <span class="token function">prepareRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Tell the subclass to refresh the internal bean factory.</span> <span class="token comment">// 获得bean工厂</span> <span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory <span class="token operator">=</span> <span class="token function">obtainFreshBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Prepare the bean factory for use in this context.</span> <span class="token comment">// 准备bean工厂,设置一些beanFactory属性</span> <span class="token function">prepareBeanFactory</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{</span> <span class="token comment">// Allows post-processing of the bean factory in context subclasses.</span> <span class="token comment">// 提供给子类的扩展点</span> <span class="token function">postProcessBeanFactory</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Invoke factory processors registered as beans in the context.</span> <span class="token comment">// 执行BeanFactoryPostProcessor实现类的方法</span> <span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Register bean processors that intercept bean creation.</span> <span class="token comment">// 注册BeanPostProcessors</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initialize message source for this context.</span> <span class="token comment">// 国际化,不重要</span> <span class="token function">initMessageSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initialize event multicaster for this context.</span> <span class="token comment">// 初始化事件多播器</span> <span class="token function">initApplicationEventMulticaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initialize other special beans in specific context subclasses.</span> <span class="token comment">// 提供给子类的扩展点</span> <span class="token function">onRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Check for listener beans and register them.</span> <span class="token comment">// 注册事件监听器-ApplicationListener</span> <span class="token function">registerListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Instantiate all remaining (non-lazy-init) singletons.</span> <span class="token comment">// 初始化所有非懒加载的bean</span> <span class="token comment">// bean的创建过程都在该方法中</span> <span class="token function">finishBeanFactoryInitialization</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Last step: publish corresponding event.</span> <span class="token comment">// 广播结束事件,容器创建完成</span> <span class="token function">finishRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">BeansException</span> ex<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isWarnEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token string">"Exception encountered during context initialization - "</span> <span class="token operator">+</span> <span class="token string">"cancelling refresh attempt: "</span> <span class="token operator">+</span> ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Destroy already created singletons to avoid dangling resources.</span> <span class="token comment">// 销毁bean</span> <span class="token function">destroyBeans</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Reset 'active' flag.</span> <span class="token comment">// 重置状态</span> <span class="token function">cancelRefresh</span><span class="token punctuation">(</span>ex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Propagate exception to caller.</span> <span class="token keyword">throw</span> ex<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span> <span class="token comment">// Reset common introspection caches in Spring's core, since we</span> <span class="token comment">// might not ever need metadata for singleton beans anymore...</span> <span class="token function">resetCommonCaches</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>下面将依次解析上述的所有方法</p><h3 id="prepareRefresh"><a href="#prepareRefresh" class="headerlink" title="prepareRefresh"></a>prepareRefresh</h3><p>这个方法比较简单,设置一下closed和active的状态,这两个属性都是AtomicBoolean类型,然后打印一些日志,初始化一些集合等</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">prepareRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Switch to active.</span> <span class="token keyword">this</span><span class="token punctuation">.</span>startupDate <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>closed<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>active<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isDebugEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Refreshing "</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">"Refreshing "</span> <span class="token operator">+</span> <span class="token function">getDisplayName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// Initialize any placeholder property sources in the context environment.</span> <span class="token function">initPropertySources</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Validate that all properties marked as required are resolvable:</span> <span class="token comment">// see ConfigurablePropertyResolver#setRequiredProperties</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">validateRequiredProperties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Store pre-refresh ApplicationListeners...</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationListeners <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationListeners <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>applicationListeners<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// Reset local application listeners to pre-refresh state.</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationListeners<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationListeners<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationListeners<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Allow for the collection of early ApplicationEvents,</span> <span class="token comment">// to be published once the multicaster is available...</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationEvents <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedHashSet</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="obtainFreshBeanFactory"><a href="#obtainFreshBeanFactory" class="headerlink" title="obtainFreshBeanFactory"></a>obtainFreshBeanFactory</h3><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token class-name">ConfigurableListableBeanFactory</span> <span class="token function">obtainFreshBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">refreshBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> <span class="token function">getBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>该方法很简单,第一行<code>refreshBeanFactory()</code>中,使用AtomicBoolean避免了refresh操作被多次调用</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span><span class="token keyword">protected</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">refreshBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IllegalStateException</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>refreshed<span class="token punctuation">.</span><span class="token function">compareAndSet</span><span class="token punctuation">(</span><span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalStateException</span><span class="token punctuation">(</span> <span class="token string">"GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">.</span><span class="token function">setSerializationId</span><span class="token punctuation">(</span><span class="token function">getId</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><code>getBeanFactory()</code>则是直接返回了事先创建好的ConfigurableListableBeanFactory</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token class-name">ConfigurableListableBeanFactory</span> <span class="token function">getBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>ConfigurableListableBeanFactory的创建是在父类GenericApplicationContext中创建,子类在被创建时,会自动调用父类的构造方法</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">GenericApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DefaultListableBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="prepareBeanFactory"><a href="#prepareBeanFactory" class="headerlink" title="prepareBeanFactory"></a>prepareBeanFactory</h3><p>该方法中主要是给bean工厂设置一些属性,快速瞄一眼即可</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">prepareBeanFactory</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Tell the internal bean factory to use the context's class loader etc.</span> beanFactory<span class="token punctuation">.</span><span class="token function">setBeanClassLoader</span><span class="token punctuation">(</span><span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">setBeanExpressionResolver</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">StandardBeanExpressionResolver</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBeanClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">addPropertyEditorRegistrar</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ResourceEditorRegistrar</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Configure the bean factory with context callbacks.</span> beanFactory<span class="token punctuation">.</span><span class="token function">addBeanPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ApplicationContextAwareProcessor</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">EnvironmentAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">EmbeddedValueResolverAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">ResourceLoaderAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">ApplicationEventPublisherAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">MessageSourceAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">ignoreDependencyInterface</span><span class="token punctuation">(</span><span class="token class-name">ApplicationContextAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// BeanFactory interface not registered as resolvable type in a plain factory.</span> <span class="token comment">// MessageSource registered (and found for autowiring) as a bean.</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerResolvableDependency</span><span class="token punctuation">(</span><span class="token class-name">BeanFactory</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerResolvableDependency</span><span class="token punctuation">(</span><span class="token class-name">ResourceLoader</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerResolvableDependency</span><span class="token punctuation">(</span><span class="token class-name">ApplicationEventPublisher</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerResolvableDependency</span><span class="token punctuation">(</span><span class="token class-name">ApplicationContext</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Register early post-processor for detecting inner beans as ApplicationListeners.</span> beanFactory<span class="token punctuation">.</span><span class="token function">addBeanPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ApplicationListenerDetector</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Detect a LoadTimeWeaver and prepare for weaving, if found.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsBean</span><span class="token punctuation">(</span>LOAD_TIME_WEAVER_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">addBeanPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">LoadTimeWeaverAwareProcessor</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Set a temporary ClassLoader for type matching.</span> beanFactory<span class="token punctuation">.</span><span class="token function">setTempClassLoader</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ContextTypeMatchClassLoader</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBeanClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Register default environment beans.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsLocalBean</span><span class="token punctuation">(</span>ENVIRONMENT_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerSingleton</span><span class="token punctuation">(</span>ENVIRONMENT_BEAN_NAME<span class="token punctuation">,</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsLocalBean</span><span class="token punctuation">(</span>SYSTEM_PROPERTIES_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerSingleton</span><span class="token punctuation">(</span>SYSTEM_PROPERTIES_BEAN_NAME<span class="token punctuation">,</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSystemProperties</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsLocalBean</span><span class="token punctuation">(</span>SYSTEM_ENVIRONMENT_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerSingleton</span><span class="token punctuation">(</span>SYSTEM_ENVIRONMENT_BEAN_NAME<span class="token punctuation">,</span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSystemEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="postProcessBeanFactory"><a href="#postProcessBeanFactory" class="headerlink" title="postProcessBeanFactory"></a>postProcessBeanFactory</h3><p>钩子方法,是一个空实现,子类可以重写该方法已达到扩展的目的</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">postProcessBeanFactory</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><h3 id="invokeBeanFactoryPostProcessors"><a href="#invokeBeanFactoryPostProcessors" class="headerlink" title="invokeBeanFactoryPostProcessors"></a>invokeBeanFactoryPostProcessors</h3><p>从命名上就可以看到它的作用,执行beanFactory的后置处理器,其中重要代码就一行:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">PostProcessorRegistrationDelegate</span><span class="token punctuation">.</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> <span class="token function">getBeanFactoryPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>这行代码中,通过<code>getBeanFactoryPostProcessors()</code>拿到所有beanFactory的后置处理器,点进去发现它是直接返回:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">></span></span> <span class="token function">getBeanFactoryPostProcessors</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactoryPostProcessors<span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>然后我们继续找是什么时候添加后置处理器的,一顿操作猛如虎,最后居然发现,<code>getBeanFactoryPostProcessors()</code>根本没人调用,调用的地方都是测试类:</p><p><img src="/images/image-20210602103306136.png"></p><p>由此可见,<code>addBeanFactoryPostProcessor()</code>这个方法是给开发者调用的,用于手动添加beanFactory后置处理器。</p><p>大致使用方法如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">MyBeanFactoryPostProcessor</span> <span class="token keyword">implements</span> <span class="token class-name">BeanFactoryPostProcessor</span> <span class="token punctuation">{</span><span class="token annotation punctuation">@Override</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">postProcessBeanFactory</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">BeansException</span> <span class="token punctuation">{</span><span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"postProcessBeanFactory"</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Application</span> <span class="token punctuation">{</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token class-name">AnnotationConfigApplicationContext</span> applicationContext <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">AnnotationConfigApplicationContext</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>applicationContext<span class="token punctuation">.</span><span class="token function">addBeanFactoryPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">MyBeanFactoryPostProcessor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>applicationContext<span class="token punctuation">.</span><span class="token function">scan</span><span class="token punctuation">(</span><span class="token string">"net.javajun"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>applicationContext<span class="token punctuation">.</span><span class="token function">refresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><blockquote><p>注意,这里自定义的MyBeanFactoryPostProcessor是没有@Component注解的</p></blockquote><p>接下来我们重点看<code>PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors</code>的内部实现,这个方法代码比较多,我们分多段来看。</p><p>第一段,是执行BeanDefinition后置处理器<code>BeanDefinitionRegistryPostProcessor</code>,它是<code>BeanFactoryPostProcessor</code>的子类:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory <span class="token keyword">instanceof</span> <span class="token class-name">BeanDefinitionRegistry</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">BeanDefinitionRegistry</span> registry <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">BeanDefinitionRegistry</span><span class="token punctuation">)</span> beanFactory<span class="token punctuation">;</span> <span class="token comment">// BeanFactory后置处理器</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">></span></span> regularPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// BeanDefinition 后置处理器</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">></span></span> registryProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">BeanFactoryPostProcessor</span> postProcessor <span class="token operator">:</span> beanFactoryPostProcessors<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>postProcessor <span class="token keyword">instanceof</span> <span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">BeanDefinitionRegistryPostProcessor</span> registryProcessor <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">)</span> postProcessor<span class="token punctuation">;</span> registryProcessor<span class="token punctuation">.</span><span class="token function">postProcessBeanDefinitionRegistry</span><span class="token punctuation">(</span>registry<span class="token punctuation">)</span><span class="token punctuation">;</span> registryProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>registryProcessor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> regularPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>postProcessor<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token comment">//...</span><span class="token comment">//...</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面这段代码中,从手动添加的后置处理器中,找到BeanDefinition后置处理器和BeanFactory后置处理器,先保存到集合中,暂时不用。</p><p>第二段,从容器中找到BeanDefinition后置处理器,进行排序并调用</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">></span></span> currentRegistryProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> postProcessorNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 找到实现了PriorityOrdered接口的BeanDefinition后置处理器</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> postProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">PriorityOrdered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> processedBeans<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 后置处理器排序</span><span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span>registryProcessors<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 执行BeanDefinition后置处理器,内部是遍历调用postProcessBeanDefinitionRegistry()</span><span class="token function">invokeBeanDefinitionRegistryPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 找到实现了Ordered接口的BeanDefinition后置处理器</span>postProcessorNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> postProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>processedBeans<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span> <span class="token operator">&&</span> beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">Ordered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> processedBeans<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// 排序并执行</span><span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span>registryProcessors<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">invokeBeanDefinitionRegistryPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span>currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>可以看出,实现了<code>PriorityOrdered</code>接口的BeanDefinition后置处理器优先被执行,然后执行实现了<code>Ordered</code>接口的后置处理器</p><p>最后继续执行其他的BeanDefinition后置处理器,代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 继续其他的BeanDefinition后置处理器</span><span class="token keyword">boolean</span> reiterate <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span><span class="token keyword">while</span> <span class="token punctuation">(</span>reiterate<span class="token punctuation">)</span> <span class="token punctuation">{</span> reiterate <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> postProcessorNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> postProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 过滤前面已经执行过的后置处理器</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>processedBeans<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanDefinitionRegistryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> processedBeans<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> reiterate <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 排序并执行</span> <span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> registryProcessors<span class="token punctuation">.</span><span class="token function">addAll</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">invokeBeanDefinitionRegistryPostProcessors</span><span class="token punctuation">(</span>currentRegistryProcessors<span class="token punctuation">,</span> registry<span class="token punctuation">)</span><span class="token punctuation">;</span> currentRegistryProcessors<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token comment">// Now, invoke the postProcessBeanFactory callback of all processors handled so far.</span><span class="token comment">// 执行BeanDefinition后置处理器</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>registryProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 执行BeanFactory后置处理器</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>regularPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>注意:上面这段代码中,最后两行执行的是手动<code>addBeanFactoryPostProcessor</code>添加的BeanDefinition后置处理器和BeanFactory后置处理器。也就是前面第一段代码中,保存到集合里暂时没用的后置处理器</p><p>第三段,接下去要执行bean的BeanFactory后置处理器。下面的代码其实和上面的没有太大区别</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 找到BeanFactoryPostProcessor的bean name</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> postProcessorNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// Separate between BeanFactoryPostProcessors that implement PriorityOrdered,</span><span class="token comment">// Ordered, and the rest.</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">></span></span> priorityOrderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> orderedPostProcessorNames <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> nonOrderedPostProcessorNames <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// 分别找出PriorityOrdered和Ordered的后置处理器</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> postProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>processedBeans<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// skip - already processed in first phase above</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">PriorityOrdered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> priorityOrderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">Ordered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> orderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> nonOrderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token comment">// First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.</span><span class="token comment">// 排序并执行实现了PriorityOrdered的BeanFactory后置处理器</span><span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>priorityOrderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>priorityOrderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// Next, invoke the BeanFactoryPostProcessors that implement Ordered.</span><span class="token comment">// 排序并执行实现了Ordered的BeanFactory后置处理器</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">></span></span> orderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>orderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> postProcessorName <span class="token operator">:</span> orderedPostProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> orderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>postProcessorName<span class="token punctuation">,</span> <span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>orderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>orderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// Finally, invoke all other BeanFactoryPostProcessors.</span><span class="token comment">// 执行其他的BeanFactory后置处理器</span><span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">></span></span> nonOrderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>nonOrderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> postProcessorName <span class="token operator">:</span> nonOrderedPostProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> nonOrderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>postProcessorName<span class="token punctuation">,</span> <span class="token class-name">BeanFactoryPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token function">invokeBeanFactoryPostProcessors</span><span class="token punctuation">(</span>nonOrderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span>beanFactory<span class="token punctuation">.</span><span class="token function">clearMetadataCache</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p><strong>现在,咱们重新理一下整个流程:</strong></p><ol><li>遍历手动添加的后置处理器,保存到集合里</li><li>执行容器中的BeanDefinition后置处理器<ol><li>实现了PriorityOrdered接口的优先进行排序并执行</li><li>然后排序并执行实现了Ordered接口的</li><li>最后执行其他的</li></ol></li><li>执行第一步的BeanDefinition后置处理器和BeanFactory后置处理器</li><li>执行容器中的BeanFactory后置处理器<ol><li>实现了PriorityOrdered接口的优先进行排序并执行</li><li>然后排序并执行实现了Ordered接口的</li><li>最后执行其他的</li></ol></li></ol><h3 id="registerBeanPostProcessors"><a href="#registerBeanPostProcessors" class="headerlink" title="registerBeanPostProcessors"></a>registerBeanPostProcessors</h3><p>这个方法中,直接使用了PostProcessorRegistrationDelegate:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">PostProcessorRegistrationDelegate</span><span class="token punctuation">.</span><span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>从命名上看,是注册BeanPostProcessor,姑且大胆猜测一下,这个方法中只是找到所有的BeanPostProcessor,仅仅是注册,并没有执行。</p><p>且参考上面的BeanFactoryProcessor,是否也是按照<code>PriorityOrdered -> Ordered -> 其他</code>这个流程去走呢?</p><p>带着疑问继续看下去:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span> <span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">,</span> <span class="token class-name">AbstractApplicationContext</span> applicationContext<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// 找到所有BeanPostProcessor bean name</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> postProcessorNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">BeanPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Register BeanPostProcessorChecker that logs an info message when</span> <span class="token comment">// a bean is created during BeanPostProcessor instantiation, i.e. when</span> <span class="token comment">// a bean is not eligible for getting processed by all BeanPostProcessors.</span> <span class="token keyword">int</span> beanProcessorTargetCount <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanPostProcessorCount</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token number">1</span> <span class="token operator">+</span> postProcessorNames<span class="token punctuation">.</span>length<span class="token punctuation">;</span> <span class="token comment">// 用于记录日志的后置处理器</span> beanFactory<span class="token punctuation">.</span><span class="token function">addBeanPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">BeanPostProcessorChecker</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> beanProcessorTargetCount<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Separate between BeanPostProcessors that implement PriorityOrdered,</span> <span class="token comment">// Ordered, and the rest.</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanPostProcessor</span><span class="token punctuation">></span></span> priorityOrderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanPostProcessor</span><span class="token punctuation">></span></span> internalPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> orderedPostProcessorNames <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">String</span><span class="token punctuation">></span></span> nonOrderedPostProcessorNames <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// PriorityOrdered, Ordered, 其他</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> postProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">PriorityOrdered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">BeanPostProcessor</span> pp <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> priorityOrderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pp <span class="token keyword">instanceof</span> <span class="token class-name">MergedBeanDefinitionPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> internalPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">Ordered</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> orderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> nonOrderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>ppName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// First, register the BeanPostProcessors that implement PriorityOrdered.</span> <span class="token comment">// 排序</span> <span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>priorityOrderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 遍历调用beanFactory.addBeanPostProcessor</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> priorityOrderedPostProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Next, register the BeanPostProcessors that implement Ordered.</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanPostProcessor</span><span class="token punctuation">></span></span> orderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>orderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> orderedPostProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">BeanPostProcessor</span> pp <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> orderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pp <span class="token keyword">instanceof</span> <span class="token class-name">MergedBeanDefinitionPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> internalPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 排序</span> <span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>orderedPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 遍历调用beanFactory.addBeanPostProcessor</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> orderedPostProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Now, register all regular BeanPostProcessors.</span> <span class="token comment">// 其他未排序的后置处理器</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">BeanPostProcessor</span><span class="token punctuation">></span></span> nonOrderedPostProcessors <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation"><</span><span class="token punctuation">></span></span><span class="token punctuation">(</span>nonOrderedPostProcessorNames<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> ppName <span class="token operator">:</span> nonOrderedPostProcessorNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">BeanPostProcessor</span> pp <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>ppName<span class="token punctuation">,</span> <span class="token class-name">BeanPostProcessor</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> nonOrderedPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>pp <span class="token keyword">instanceof</span> <span class="token class-name">MergedBeanDefinitionPostProcessor</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> internalPostProcessors<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>pp<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> nonOrderedPostProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Finally, re-register all internal BeanPostProcessors.</span> <span class="token comment">// 排序</span> <span class="token function">sortPostProcessors</span><span class="token punctuation">(</span>internalPostProcessors<span class="token punctuation">,</span> beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 遍历调用beanFactory.addBeanPostProcessor</span> <span class="token function">registerBeanPostProcessors</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">,</span> internalPostProcessors<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Re-register post-processor for detecting inner beans as ApplicationListeners,</span> <span class="token comment">// moving it to the end of the processor chain (for picking up proxies etc).</span> <span class="token comment">// ApplicationListenerDetector后置处理器</span> <span class="token comment">// 该处理器用于处理ApplicationListener监听器</span> beanFactory<span class="token punctuation">.</span><span class="token function">addBeanPostProcessor</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ApplicationListenerDetector</span><span class="token punctuation">(</span>applicationContext<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>与刚刚猜测的一致,BeanPostProcessor和之前的BeanFactoryPostProcessor十分相似,同样支持PriorityOrdered接口和Ordered接口。</p><p>且从最后一行可以发现,原来ApplicationListener也和BeanPostProcessor有关</p><h3 id="initMessageSource"><a href="#initMessageSource" class="headerlink" title="initMessageSource"></a>initMessageSource</h3><p>国际化相关,可以不看</p><h3 id="initApplicationEventMulticaster"><a href="#initApplicationEventMulticaster" class="headerlink" title="initApplicationEventMulticaster"></a>initApplicationEventMulticaster</h3><p>这个方法里比较简单,如果没有定义多播器,则默认使用<code>SimpleApplicationEventMulticaster</code>,且将该bean直接添加到单例池中</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">initApplicationEventMulticaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory <span class="token operator">=</span> <span class="token function">getBeanFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsLocalBean</span><span class="token punctuation">(</span>APPLICATION_EVENT_MULTICASTER_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationEventMulticaster <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>APPLICATION_EVENT_MULTICASTER_BEAN_NAME<span class="token punctuation">,</span> <span class="token class-name">ApplicationEventMulticaster</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"Using ApplicationEventMulticaster ["</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationEventMulticaster <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationEventMulticaster <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">SimpleApplicationEventMulticaster</span><span class="token punctuation">(</span>beanFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 直接添加到单例池中</span> beanFactory<span class="token punctuation">.</span><span class="token function">registerSingleton</span><span class="token punctuation">(</span>APPLICATION_EVENT_MULTICASTER_BEAN_NAME<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationEventMulticaster<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>logger<span class="token punctuation">.</span><span class="token function">isTraceEnabled</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> logger<span class="token punctuation">.</span><span class="token function">trace</span><span class="token punctuation">(</span><span class="token string">"No '"</span> <span class="token operator">+</span> APPLICATION_EVENT_MULTICASTER_BEAN_NAME <span class="token operator">+</span> <span class="token string">"' bean, using "</span> <span class="token operator">+</span> <span class="token string">"["</span> <span class="token operator">+</span> <span class="token keyword">this</span><span class="token punctuation">.</span>applicationEventMulticaster<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">"]"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="onRefresh"><a href="#onRefresh" class="headerlink" title="onRefresh"></a>onRefresh</h3><p>钩子方法,留给子类去扩展</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">onRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">BeansException</span> <span class="token punctuation">{</span> <span class="token comment">// For subclasses: do nothing by default.</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><h3 id="registerListeners"><a href="#registerListeners" class="headerlink" title="registerListeners"></a>registerListeners</h3><p>把ApplicationListener注册给多播器</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">registerListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Register statically specified listeners first.</span> <span class="token comment">// 获取ApplicationListener,注册给事件多播器</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">ApplicationListener</span><span class="token generics"><span class="token punctuation"><</span><span class="token operator">?</span><span class="token punctuation">></span></span> listener <span class="token operator">:</span> <span class="token function">getApplicationListeners</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">getApplicationEventMulticaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addApplicationListener</span><span class="token punctuation">(</span>listener<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Do not initialize FactoryBeans here: We need to leave all regular beans</span> <span class="token comment">// uninitialized to let post-processors apply to them!</span> <span class="token comment">// 获取ApplicationListener,注册给事件多播器</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> listenerBeanNames <span class="token operator">=</span> <span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">ApplicationListener</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> listenerBeanName <span class="token operator">:</span> listenerBeanNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">getApplicationEventMulticaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">addApplicationListenerBean</span><span class="token punctuation">(</span>listenerBeanName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Publish early application events now that we finally have a multicaster...</span> <span class="token comment">// 发布事件</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation"><</span><span class="token class-name">ApplicationEvent</span><span class="token punctuation">></span></span> earlyEventsToProcess <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationEvents<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>earlyApplicationEvents <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token class-name">CollectionUtils</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>earlyEventsToProcess<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">ApplicationEvent</span> earlyEvent <span class="token operator">:</span> earlyEventsToProcess<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">getApplicationEventMulticaster</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">multicastEvent</span><span class="token punctuation">(</span>earlyEvent<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h3 id="finishBeanFactoryInitialization"><a href="#finishBeanFactoryInitialization" class="headerlink" title="finishBeanFactoryInitialization"></a>finishBeanFactoryInitialization</h3><p><strong>这个方法是重中之重,</strong>大部分bean都在这个方法中被创建。为什么是大部分呢?因为在前面的几个步骤中,有一些后置处理的bean其实已经的调用<code>beanFactory.getBean()</code>时就已经创建好了</p><p>该方法中,最重要的是最后一行<code>beanFactory.preInstantiateSingletons()</code>,其他不重要,大致瞄一眼即可:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">finishBeanFactoryInitialization</span><span class="token punctuation">(</span><span class="token class-name">ConfigurableListableBeanFactory</span> beanFactory<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Initialize conversion service for this context.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>beanFactory<span class="token punctuation">.</span><span class="token function">containsBean</span><span class="token punctuation">(</span>CONVERSION_SERVICE_BEAN_NAME<span class="token punctuation">)</span> <span class="token operator">&&</span> beanFactory<span class="token punctuation">.</span><span class="token function">isTypeMatch</span><span class="token punctuation">(</span>CONVERSION_SERVICE_BEAN_NAME<span class="token punctuation">,</span> <span class="token class-name">ConversionService</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">setConversionService</span><span class="token punctuation">(</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>CONVERSION_SERVICE_BEAN_NAME<span class="token punctuation">,</span> <span class="token class-name">ConversionService</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Register a default embedded value resolver if no BeanFactoryPostProcessor</span> <span class="token comment">// (such as a PropertySourcesPlaceholderConfigurer bean) registered any before:</span> <span class="token comment">// at this point, primarily for resolution in annotation attribute values.</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>beanFactory<span class="token punctuation">.</span><span class="token function">hasEmbeddedValueResolver</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> beanFactory<span class="token punctuation">.</span><span class="token function">addEmbeddedValueResolver</span><span class="token punctuation">(</span>strVal <span class="token operator">-></span> <span class="token function">getEnvironment</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">resolvePlaceholders</span><span class="token punctuation">(</span>strVal<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.</span> <span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> weaverAwareNames <span class="token operator">=</span> beanFactory<span class="token punctuation">.</span><span class="token function">getBeanNamesForType</span><span class="token punctuation">(</span><span class="token class-name">LoadTimeWeaverAware</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token boolean">false</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">String</span> weaverAwareName <span class="token operator">:</span> weaverAwareNames<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">getBean</span><span class="token punctuation">(</span>weaverAwareName<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Stop using the temporary ClassLoader for type matching.</span> beanFactory<span class="token punctuation">.</span><span class="token function">setTempClassLoader</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Allow for caching all bean definition metadata, not expecting further changes.</span> beanFactory<span class="token punctuation">.</span><span class="token function">freezeConfiguration</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Instantiate all remaining (non-lazy-init) singletons.</span> beanFactory<span class="token punctuation">.</span><span class="token function">preInstantiateSingletons</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>在<code>beanFactory.preInstantiateSingletons();</code>中,会遍历所有的beanNames,然后挨个调用getBean尝试去获取bean。</p><p><img src="/images/image-20210602153558106.png"></p><p>bean的一切就始于<code>getBean</code>方法,当单例池中有这个bean,直接返回,如果没有,则进行创建。</p><p>如果是原型类型的bean,则会当场创建一个并返回</p><p>之前注册的<code>BeanPostProcessor</code>就是在这个过程中被调用</p><p>创建bean的过程比较复杂,涉及到了循环依赖,一级缓存,二级缓存,三级缓存等,因此单独写了一篇来讲解,请参考 <a href="/posts/21443">Spring5源码(四):Bean的创建与循环依赖</a></p><h3 id="finishRefresh"><a href="#finishRefresh" class="headerlink" title="finishRefresh"></a>finishRefresh</h3><p>初始化LifecycleProcessor并执行onFresh,发布ContextRefreshedEvent事件。该方法不是很重要</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">protected</span> <span class="token keyword">void</span> <span class="token function">finishRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// Clear context-level resource caches (such as ASM metadata from scanning).</span> <span class="token function">clearResourceCaches</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Initialize lifecycle processor for this context.</span> <span class="token function">initLifecycleProcessor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Propagate refresh to lifecycle processor first.</span> <span class="token function">getLifecycleProcessor</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">onRefresh</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Publish the final event.</span> <span class="token function">publishEvent</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">ContextRefreshedEvent</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Participate in LiveBeansView MBean, if active.</span> <span class="token class-name">LiveBeansView</span><span class="token punctuation">.</span><span class="token function">registerApplicationContext</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>Spring容器启动流程大致如下:</p><ol><li>扫描bean,封装到BeanDefinition</li><li>准备环境</li><li>执行BeanDefinition后置处理器</li><li>执行BeanFactory后置处理器</li><li>初始化多播器</li><li>注册监听器</li><li>遍历beanName,创建bean</li></ol>]]></content>
<categories>
<category> Spring5源码解析 </category>
</categories>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>纵览全局-Bean的生命周期</title>
<link href="posts/39850/"/>
<url>posts/39850/</url>
<content type="html"><![CDATA[<h2 id="Bean从生到死"><a href="#Bean从生到死" class="headerlink" title="Bean从生到死"></a>Bean从生到死</h2><p>阅读开源框架源码,最忌讳一开始就陷入到代码细节中。</p><p>比较推荐的做法是,先了解整个框架的大致结构,主线的主要流程,然后再从源码中找到对应的执行路线,相互验证。</p><p>Spring中最核心的就是IOC容器,而主线路就是Bean从定义到创建,再到销毁的全过程,即Bean的生命周期。</p><p>AOP其实也是在bean的生命周期过程中实现的。</p><p>可以说,Spring IOC是整个Spring的基石。</p><p>因此,我们首先必须了解Bean的生命周期,再深入研究源码</p><p>先抛开spring,如果让我们实现一个类似IOC容器的功能,我们会如何实现?</p><p>最简单的就是直接实例化对象,然后放到map中,需要的时候直接从map中取出:</p><p><img src="/images/image-20210601111851160.png"></p><p>我们一般使用xml文件或者注解的方式告诉容器哪些类是需要作为bean放入容器中管理,因此需要定义beanDefinition来保存bean的信息,包括bean的名称,bean的类,是否是primary等。</p><p>是否可以不要beanDefinition呢?答案是不可以。</p><p>因为bean的来源可能是xml,可能是注解,甚至可能是json配置文件,因此需要一层抽象,容器内部在构建bean时,只需对接beanDefinition,而不需要去对接具体的bean来源。</p><p><img src="/images/image-20210601112223423.png"></p><p>在实际开发中,bean和bean之间往往存在着依赖关系,因此在实例化之后,需要为bean自动注入所需的依赖的bean,于是就有了属性填充这一步骤:</p><p><img src="/images/image-20210601140937358.png"></p><p>然后进行初始化:</p><p><img src="/images/image-20210610140306837.png"></p><p>当bean的生命走到尽头,就需要销毁:(为了图精简好看,暂时去除单例池)</p><p><img src="/images/image-20210610140341405.png"></p><h2 id="扩展点"><a href="#扩展点" class="headerlink" title="扩展点"></a>扩展点</h2><p>不过Spring容器就这么简单吗?当然不是。</p><p>Spring是一个扩展性极高的框架,因为它在bean的从生到死的过程中,插入了很多扩展点</p><h3 id="BeanFactoryPostProcessor"><a href="#BeanFactoryPostProcessor" class="headerlink" title="BeanFactoryPostProcessor"></a>BeanFactoryPostProcessor</h3><p>第一个要说的扩展点就是BeanFactoryPostProcessor,这是一个bean工厂的后置处理器,在bean工厂创建出来后,bean实例化之前调用。</p><p>开发者可以利用这个扩展点对bean工厂进行一定程度的改造</p><p><img src="/images/image-20210610140608101.png"></p><h3 id="BeanDefinitionRegistryPostProcessor"><a href="#BeanDefinitionRegistryPostProcessor" class="headerlink" title="BeanDefinitionRegistryPostProcessor"></a>BeanDefinitionRegistryPostProcessor</h3><p><code>BeanDefinitionRegistryPostProcessor</code>其实是<code>BeanFactoryPostProcessor</code>的子接口,从命名上可以看出,这个是针对BeanDefinition的后置处理器,可以让我们修改BeanDefinition。</p><p>从执行顺序上,是先执行<code>BeanDefinitionRegistryPostProcessor</code>,再执行<code>BeanFactoryPostProcessor</code></p><p><img src="/images/image-20210610140644899.png"></p><h3 id="InstantiationAwareBeanPostProcessor"><a href="#InstantiationAwareBeanPostProcessor" class="headerlink" title="InstantiationAwareBeanPostProcessor"></a>InstantiationAwareBeanPostProcessor</h3><p>在实例化bean前后,会调用这个扩展点的<code>postProcessBeforeInstantiation</code>和<code>postProcessAfterInstantiation</code>方法</p><p><img src="/images/image-20210610140838275.png"></p><h3 id="BeanPostProcessor"><a href="#BeanPostProcessor" class="headerlink" title="BeanPostProcessor"></a>BeanPostProcessor</h3><p>在初始化前后,会调用<code>BeanPostProcessor</code>的<code>postProcessBeforeInitialization</code>和<code>postProcessAfterInitialization</code>方法</p><p>大名鼎鼎的AOP就是在该过程中实现的</p><p><img src="/images/image-20210610141052947.png"></p><p>以上就是bean的完整的生命周期</p>]]></content>
<categories>
<category> Spring5源码解析 </category>
</categories>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>编译Spring5源码</title>
<link href="posts/55427/"/>
<url>posts/55427/</url>
<content type="html"><![CDATA[<p>先将Spring源码克隆到本地:</p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token function">git</span> clone https://github.com/spring-projects/spring-framework.git<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre><p>选择一个最近的release版本:</p><p><img src="/images/image-20210531165532145.png"></p><pre class="line-numbers language-shell" data-language="shell"><code class="language-shell"><span class="token builtin class-name">cd</span> spring-framework<span class="token function">git</span> checkout v5.2.15.RELEASE<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span></span></code></pre><p>官方也给出了编译的步骤:</p><ol><li>编译spring-oxm: <code>./gradlew :spring-oxm:compileTestJava</code></li><li>将Spring工程导入到IDEA中,IDEA自动构建gradle工程,此过程中,建议使用Gradle Wrapper,不要使用你本地安装的gradle,因为容易出现版本不匹配问题</li><li>排除spring-aspects模块:右键spring-aspects,点击Load/Unload Modules,将模块unload</li></ol><p>顺利的话工程就编译好了,不顺利的话也会遇到一些奇奇怪怪的问题,可以参考官方文档:</p><ol><li><a href="https://github.com/spring-projects/spring-framework/wiki/Build-from-Source">https://github.com/spring-projects/spring-framework/wiki/Build-from-Source</a></li><li><a href="https://github.com/spring-projects/spring-framework/blob/main/import-into-idea.md">https://github.com/spring-projects/spring-framework/blob/main/import-into-idea.md</a></li></ol><p>然后创建一个spring-debug模块用于编写测试代码,并在gradle.build中引入spring-context:</p><pre class="line-numbers language-groovy" data-language="groovy"><code class="language-groovy">dependencies <span class="token punctuation">{</span> <span class="token function">compile</span><span class="token punctuation">(</span><span class="token function">project</span><span class="token punctuation">(</span><span class="token string gstring">":spring-context"</span><span class="token punctuation">)</span><span class="token punctuation">)</span> testCompile group<span class="token punctuation">:</span> <span class="token string">'junit'</span><span class="token punctuation">,</span> name<span class="token punctuation">:</span> <span class="token string">'junit'</span><span class="token punctuation">,</span> version<span class="token punctuation">:</span> <span class="token string">'4.12'</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>]]></content>
<categories>
<category> Spring5源码解析 </category>
</categories>
<tags>
<tag> Spring </tag>
</tags>
</entry>
<entry>
<title>B树与B+树</title>
<link href="posts/18710/"/>
<url>posts/18710/</url>
<content type="html"><![CDATA[<h2 id="B树"><a href="#B树" class="headerlink" title="B树"></a>B树</h2><p>B树也叫B-树,与二叉树相比,B树其实就是多叉树。</p><p>二叉树上,每个节点中只有1个元素,2个子节点,而一棵N阶的B树中,每个节点最多N-1个元素,N个子节点,且B树每条路径的高度是一样的,最底层的节点称之为叶子节点</p><p>如下图就是一个4阶的B树:</p><p><img src="/images/image-20210525154318387.png"></p><h2 id="B-树"><a href="#B-树" class="headerlink" title="B+树"></a>B+树</h2><p>B+树是B树的一个变种,它与B树最大的区别是:</p><ol><li>B+树的叶子节点拥有所有的数据,非叶子节点仅仅起到一个索引的作用</li><li>B+树的叶子节点通过指针连接</li></ol><p>例如将<code>[1,2,3,4,5,6,7,12,23]</code>分别插入B树和B+树,分别如下:</p><p><img src="/images/image-20210525155530332.png"></p><p>从上图可以看出,B树的每个节点都存有数据,且不重复,而B+树的非叶子作为索引,叶子节点存数据,是可能重复的</p><p>MySQL数据库就是使用了B+树作为数据存储的结构,由于叶子节点形成了一个链表,因此做范围查询也十分的方便</p><p>关于MySQL可以参考:<a href="/posts/51731">MySQL数据与索引</a></p>]]></content>
<categories>
<category> 数据结构与算法 </category>
</categories>
<tags>
<tag> B树与B+树 </tag>
<tag> 数据结构 </tag>
</tags>
</entry>
<entry>
<title>红黑树</title>
<link href="posts/28769/"/>
<url>posts/28769/</url>
<content type="html"><![CDATA[<h2 id="红黑树规则"><a href="#红黑树规则" class="headerlink" title="红黑树规则"></a>红黑树规则</h2><p>红黑树和平衡二叉树一样,都是一种自平衡的二叉树,且红黑树有它自己的规则:</p><ol><li>每个节点都有颜色,红色或黑色</li><li>根节点是黑色</li><li>叶子节点是黑色,且是NIL</li><li>两个红色节点不能相连</li><li>每个节点到叶子节点(NIL)的路径上,黑色节点的数量都一样</li></ol><p>说起来有点抽象,下图是一棵红黑树的示例:</p><p><img src="/images/image-20210524151533889.png"></p><p>红黑树中,最长路径不会超过最短路径的2倍,为什么呢?</p><p>在红黑树中,最短路径是全是黑色节点的路径,而最长路径是红黑交替的路径。</p><p>根据红黑树的规则,每个路径上黑色节点数量必须一致,因此最长路径最多是最短路径的2倍,如下图</p><p><img src="/images/image-20210524154416089.png"></p><h2 id="插入新节点"><a href="#插入新节点" class="headerlink" title="插入新节点"></a>插入新节点</h2><p>当红黑树插入后删除节点时,红黑树可能失衡,此时则需要通过变更节点颜色和左旋右旋让红黑树恢复平衡。</p><p>新插入的节点的颜色可以是红色或黑色,但是红黑树规则中,任一路径上的黑色节点数量是相同的,为了不破坏这个规则,因此我们新插入一般都定为红色</p><p>红黑树的左旋与右旋是和平衡二叉树一模一样的,参考 <a href="/posts/20058">平衡二叉树(AVL)</a></p><p>当插入节点后没有破坏红黑树的规则,那么则无需变动。若破坏了规则,则需要变色或旋转。</p><p>从某网看到一个顺口溜,可以参考一下:</p><blockquote><p>根节点必黑,新增是红色,只能黑连黑,不能红连红;</p><p>爸叔通红就变色,爸红叔黑就旋转,哪边黑往哪边转</p></blockquote><p>根据这个顺口溜,做点练习题</p><p><strong>41、38、31、12、19、8 连续地插入一棵初始化为空的红黑树之后,画出该结果树</strong></p><p><img src="/images/image-20210524170140683.png"></p>]]></content>
<categories>
<category> 数据结构与算法 </category>
</categories>
<tags>
<tag> 数据结构 </tag>
<tag> 红黑树 </tag>
</tags>
</entry>
<entry>
<title>平衡二叉树(AVL)</title>
<link href="posts/20058/"/>
<url>posts/20058/</url>
<content type="html"><![CDATA[<h2 id="平衡二叉树"><a href="#平衡二叉树" class="headerlink" title="平衡二叉树"></a>平衡二叉树</h2><p>平衡二叉树英文叫Balanced Binary Tree,简称AVL,是为了解决二叉树搜索树极端情况下退化为线性结构的问题。</p><p>如何解决呢?</p><p>在插入或删除元素时,重新调整节点的排列顺序,使每个节点的左右两棵子树的高度相差不超过1</p><p>例如下图中,当插入新节点20时,红色节点28的左子树高度为2,右子树高度为0,那么这就不是一颗平衡二叉树</p><p><img src="/images/image-20210522152935989.png"></p><p>因此在插入新节点时,如果当前二叉树不平衡了,则需要调整节点的位置使其始终保持平衡</p><p><img src="/images/image-20210522153222791.png"></p><h2 id="左旋"><a href="#左旋" class="headerlink" title="左旋"></a>左旋</h2><p>当插入一个新节点后,导致二叉树失衡,那么就需要通过旋转的方式使二叉树重新恢复平衡</p><p>当右子树比左子树高时,使用左旋,反之使用右旋</p><p>左旋的方法如下(假设要旋转的节点为A):</p><ol><li>A节点的右子节点替代A节点</li><li>右子节点的左子树变成A节点的右子树</li><li>A节点变成右子节点的左子树</li></ol><p>例如一个平衡二叉树插入了一个新节点:</p><p><img src="/images/image-20210524102114476.png"></p><p>上图中,红色节点69为新插入的节点,导致二叉树失衡,紫色节点60的左子树高度为1,右子树高度为3,右比左高,需要左旋。</p><p>紫色节点60就是所谓的节点A,按照左旋的步骤依次执行:</p><p><img src="/images/image-20210524103000929.png"></p><h2 id="右旋"><a href="#右旋" class="headerlink" title="右旋"></a>右旋</h2><p>右旋的步骤其实和左旋一模一样,只不过把右变成左而已:</p><ol><li>A节点的左子节点替代A节点</li><li>左子节点的右子树变成A节点的左子树</li><li>A节点变成左子节点的右子树</li></ol><h2 id="两次旋转"><a href="#两次旋转" class="headerlink" title="两次旋转"></a>两次旋转</h2><p>有一种特殊情况,进行了一次左旋或右旋之后,二叉树仍然不平衡,例如:</p><p><img src="/images/image-20210524104743943.png"></p><p>观察上图可以发现,紫色节点进行左旋之后,二叉树仍然不平衡,这种情况下应先对60节点进行旋转,得到如下图:</p><p><img src="/images/image-20210524105023886.png"></p><p>然后再给紫色节点做一次左旋即可</p>]]></content>
<categories>
<category> 数据结构与算法 </category>
</categories>
<tags>
<tag> 数据结构 </tag>
<tag> 平衡二叉树 </tag>
</tags>