-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
963 lines (462 loc) · 765 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
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
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>Spring cloud 06GateWay网关</title>
<link href="/2024/03/22/SpringCloud06/"/>
<url>/2024/03/22/SpringCloud06/</url>
<content type="html"><![CDATA[<p>Spring cloud GateWay网关</p><span id="more"></span><h1 id="GateWay网关"><a href="#GateWay网关" class="headerlink" title="GateWay网关"></a>GateWay网关</h1><p>Gateway是在Spring生态系统之上构建的API网关服务,基于Spring6,Spring Boot 3和Project Reactor等技术。它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式,并为它们提供跨领域的关注点,例如:安全性、监控/度量和恢复能力。</p><blockquote><p>Cloud全家桶中有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;</p><p>但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关SpringCloud Gateway替代Zuul,</p><p>那就是SpringCloud Gateway一句话:gateway是原zuul1.x版的替代</p></blockquote><p><strong>网关介于负载均衡和微服务之间</strong>,从服务注册中心拉取服务然后向微服务模块发起请求</p><p><strong>作用</strong>:</p><ul><li>反向代理</li><li>鉴权</li><li>流量控制</li><li>熔断</li><li>日志监控</li></ul><h2 id="三大核心"><a href="#三大核心" class="headerlink" title="三大核心"></a>三大核心</h2><h3 id="Route路由"><a href="#Route路由" class="headerlink" title="Route路由"></a>Route路由</h3><p> 路由是构建网关的基本模块,<strong>它由一个 ID、一个目标 URI、一组断言和一组过滤器</strong>定义。当请求到达网关时,网关会检查所有的路由,并找到第一个匹配断言的路由,然后将请求转发到该路由配置的目标 URI。如果该路由配置了过滤器,那么请求在转发之前或之后会经过这些过滤器。</p><p><strong>路由的主要作用是定义请求如何被转发到后端服务,并且可以附加一系列的过滤器来修改请求或响应,如添加响应头、参数重写、增加安全性等</strong></p><h3 id="Predicate"><a href="#Predicate" class="headerlink" title="Predicate"></a>Predicate</h3><p>断言是 Java 8 函数式接口 <code>Predicate</code> 的实例,用于匹配 HTTP 请求的任何属性,是一个布尔值,例如请求头或参数。在 Spring Cloud Gateway 中,这些断言会用于判断传入的请求是否符合路由规则的条件。如果请求满足断言条件,则该请求会被发送到对应的路由。<strong>断言的主要作用是决定哪些请求应该被路由到对应的后端服务</strong>。</p><blockquote><p>例如,一个断言可能会检查请求的路径是否以 <code>/api</code> 开头,或者请求的 header 中是否包含某个特定的值。Spring Cloud Gateway 提供了许多内置的断言工厂,比如 <code>AfterRoutePredicateFactory</code>、<code>BeforeRoutePredicateFactory</code>、<code>PathRoutePredicateFactory</code> 等,使得可以很容易地根据需求定义断言。</p></blockquote><h3 id="过滤器"><a href="#过滤器" class="headerlink" title="过滤器"></a>过滤器</h3><p>置于请求转发之前或之后,对请求进行过滤,增强功能</p><blockquote><p>请求进入服务–>找到对应路由—>匹配断言规则—>转发(经过过滤器)</p><p>客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。</p><p>过滤器之间用虚线分开是因为过滤器可能会在发送**代理请求之前(Pre)或之后(Post)**执行业务逻辑。</p><p>在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等;</p><p>在“post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。</p></blockquote><h2 id="入门配置"><a href="#入门配置" class="headerlink" title="入门配置"></a>入门配置</h2><p><strong>GateWay作为一个微服务入驻微服务注册中心</strong></p><p>然后将GateWay将不同微服务之间隔离起来,访问必须经过GateWay</p><h3 id="创建GateWay服务模块"><a href="#创建GateWay服务模块" class="headerlink" title="创建GateWay服务模块"></a>创建GateWay服务模块</h3><ul><li>Pom文件</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span><br> <span class="hljs-comment"><!--gateway--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-gateway<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-comment"><!--服务注册发现consul discovery,网关也要注册进服务注册中心统一管控--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-consul-discovery<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br> <span class="hljs-comment"><!-- 指标监控健康检查的actuator,网关是响应式编程删除掉spring-boot-starter-web dependency--></span><br> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-actuator<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span><br></code></pre></td></tr></table></figure><ul><li>yml文件</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">9527</span><br><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">cloud-gateway</span> <span class="hljs-comment">#以微服务注册进consul或nacos服务列表内</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">consul:</span> <span class="hljs-comment">#配置consul地址</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">8500</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">prefer-ip-address:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">service-name:</span> <span class="hljs-string">${spring.application.name}</span><br></code></pre></td></tr></table></figure><ul><li>观察consul是否加入了gateway</li></ul><h3 id="将服务路由映射到gateway"><a href="#将服务路由映射到gateway" class="headerlink" title="将服务路由映射到gateway"></a>将服务路由映射到gateway</h3><p> 新建PayGateWayController (服务端8001建立)(用于测试)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PayGateWayController</span><br>{<br> <span class="hljs-meta">@Resource</span><br> PayService payService;<br><br> <span class="hljs-meta">@GetMapping(value = "/pay/gateway/get/{id}")</span><br> <span class="hljs-keyword">public</span> ResultData<Pay> <span class="hljs-title function_">getById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br> {<br> <span class="hljs-type">Pay</span> <span class="hljs-variable">pay</span> <span class="hljs-operator">=</span> payService.getById(id);<br> <span class="hljs-keyword">return</span> ResultData.success(pay);<br> }<br><br> <span class="hljs-meta">@GetMapping(value = "/pay/gateway/info")</span><br> <span class="hljs-keyword">public</span> ResultData<String> <span class="hljs-title function_">getGatewayInfo</span><span class="hljs-params">()</span><br> {<br> <span class="hljs-keyword">return</span> ResultData.success(<span class="hljs-string">"gateway info test:"</span>+ IdUtil.simpleUUID());<br> }<br>}<br></code></pre></td></tr></table></figure><p>新增gateway网关yml配置(配置路由)</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">9527</span><br><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">cloud-gateway</span> <span class="hljs-comment">#以微服务注册进consul或nacos服务列表内</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">consul:</span> <span class="hljs-comment">#配置consul地址</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">8500</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">prefer-ip-address:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">service-name:</span> <span class="hljs-string">${spring.application.name}</span><br> <span class="hljs-attr">gateway:</span><br> <span class="hljs-attr">routes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">pay_routh1</span> <span class="hljs-comment">#pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">http://localhost:8001</span> <span class="hljs-comment">#匹配后提供服务的路由地址</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/pay/gateway/get/**</span> <span class="hljs-comment"># 断言,路径相匹配的进行路由</span><br><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">pay_routh2</span> <span class="hljs-comment">#pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">http://localhost:8001</span> <span class="hljs-comment">#匹配后提供服务的路由地址</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/pay/gateway/info/**</span> <span class="hljs-comment"># 断言,路径相匹配的进行路由</span><br></code></pre></td></tr></table></figure><p>映射前:<a href="http://localhost:8001/pay/gateway/get/1%E8%AE%BF%E9%97%AE">http://localhost:8001/pay/gateway/get/1访问</a></p><p>映射后:<a href="http://localhost:9527/pay/gateway/get/1%E4%B9%9F%E8%83%BD%E8%AE%BF%E9%97%AE%EF%BC%8C%E7%9B%B8%E5%BD%93%E4%BA%8E%E8%BF%9B%E8%A1%8C%E4%BA%86%E6%8E%A9%E7%9B%96">http://localhost:9527/pay/gateway/get/1也能访问,相当于进行了掩盖</a></p><p><strong>如和让80调用端通过Feign访问Gateway然后访问8001?</strong></p><p>配置Feign的对外暴露接口,服务名称改为服务名</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@FeignClient(value ="cloud-gateway")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">PayFeignApi</span> {<br></code></pre></td></tr></table></figure><h2 id="高级特性"><a href="#高级特性" class="headerlink" title="高级特性"></a>高级特性</h2><h3 id="动态路由uri"><a href="#动态路由uri" class="headerlink" title="动态路由uri"></a>动态路由uri</h3><p>原本的路由端口写死</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">9527</span><br><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">cloud-gateway</span> <span class="hljs-comment">#以微服务注册进consul或nacos服务列表内</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">consul:</span> <span class="hljs-comment">#配置consul地址</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">8500</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">prefer-ip-address:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">service-name:</span> <span class="hljs-string">${spring.application.name}</span><br> <span class="hljs-attr">gateway:</span><br> <span class="hljs-attr">routes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">pay_routh1</span> <span class="hljs-comment">#pay_routh1 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名</span><br> <span class="hljs-comment">#uri: http://localhost:8001 #匹配后提供服务的路由地址</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://cloud-payment-service</span> <span class="hljs-comment">#匹配后提供服务的路由地址</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/pay/gateway/get/**</span> <span class="hljs-comment"># 断言,路径相匹配的进行路由</span><br><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">id:</span> <span class="hljs-string">pay_routh2</span> <span class="hljs-comment">#pay_routh2 #路由的ID(类似mysql主键ID),没有固定规则但要求唯一,建议配合服务名</span><br> <span class="hljs-comment">#uri: http://localhost:8001 #匹配后提供服务的路由地址</span><br> <span class="hljs-attr">uri:</span> <span class="hljs-string">lb://cloud-payment-service</span> <span class="hljs-comment">#匹配后提供服务的路由地址</span><br> <span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/pay/gateway/info/**</span> <span class="hljs-comment"># 断言,路径相匹配的进行路由</span><br></code></pre></td></tr></table></figure><p>将8001服务端修改为8007依然能访问,服务端的端口已经解耦了,consul已经将此解耦</p><h3 id="Predicate断言配置"><a href="#Predicate断言配置" class="headerlink" title="Predicate断言配置"></a>Predicate断言配置</h3><p>断言种类高达十几种,对请求进行多种验证</p><p>内置了十几种断言工厂用于处理</p><p>配置有shortcut和full expanded两种配置</p><p><strong>short cut</strong>:使用等号进行bool判断</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">Path=/pay/gateway/info/**</span> <br></code></pre></td></tr></table></figure><p><strong>full expanded</strong>: </p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">predicates:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cookie</span><br> <span class="hljs-attr">args:</span> <br> <span class="hljs-attr">name:</span> <span class="hljs-string">mycookie</span><br> <span class="hljs-attr">regexp:</span> <span class="hljs-string">mycookievalue</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title>Spring cloud 05分布式链路追踪</title>
<link href="/2024/03/20/SpringCloud05/"/>
<url>/2024/03/20/SpringCloud05/</url>
<content type="html"><![CDATA[<p>Spring cloud 分布式链路追踪</p><span id="more"></span><h1 id="分布式链路追踪"><a href="#分布式链路追踪" class="headerlink" title="分布式链路追踪"></a>分布式链路追踪</h1><p>Spring Cloud 分布式链路追踪是微服务架构中用于跟踪分布式系统中请求的处理路径的技术。在微服务环境中,一个客户端请求可能会经过多个服务,每个服务可能还会调用其他服务。为了能够理解整个请求的处理过程、分析性能瓶颈、快速定位问题,就需要一种机制来跟踪这些跨服务的请求</p><p>分布式链路追踪(Distributed Tracing),就是将一次分布式请求还原成调用链路,进行日志记录,性能监控并将一次分布式请求的调用情况集中展示。比如各个服务节点上的耗时、请求具体到达哪台机器上、每个服务节点的请求状态等等</p><h2 id="sleuth与micrometer"><a href="#sleuth与micrometer" class="headerlink" title="sleuth与micrometer"></a>sleuth与micrometer</h2><p>sleuth停止更新,只支持SpringBoot 2.x版本</p><p>micrometer是sleuth更好的替代品,完美的提供了一套完整的分布式链路追踪的解决办法且兼容支持了zipkin的展现</p><blockquote><p>micrometer负责收集链路追踪的数据,上传给zipkin展现</p></blockquote><p>其他比较成熟的技术:Cat,Zipkin,Prinpoint,Skywalking</p><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><p>在不同服务互相调用的时候,一条链路追踪会在每个服务调用的时候加上Trace ID 和 Span ID</p><p>链路通过<strong>TraceId唯一标识</strong>,<strong>Span标识发起的请求信息</strong>,各span通过parent id 关联起来 (Span:表示调用链路来源,通俗的理解span就是一次请求信息)</p><blockquote><p>A(SpanID=A,Parent ID=null)——>B(SpanID=B,Parent ID=A)—>C(SpanID=C,Parent ID=B)</p><p>所有的TranceID都一致</p></blockquote><p>还包括请求的发送和响应时间</p><h2 id="Zipkin"><a href="#Zipkin" class="headerlink" title="Zipkin"></a>Zipkin</h2><p>Zipkin 是一个开源的分布式跟踪系统,由 Twitter 公司开发。它可以帮助收集微服务架构中各个服务之间的调用链路数据,并提供了一个友好的用户界面来展示和分析这些跟踪数据。Zipkin 允许开发者监控和排查分布式系统中的延迟问题,从而提高系统的性能和稳定性。</p><p><strong>Zipkin 的核心概念包括</strong>:</p><ul><li>Span:基本的工作单元,代表系统中发生的一个操作,如一个 HTTP 请求。Span 包含了操作的开始时间和结束时间,还包含了其他元数据,如标签(Tags)、注释(Annotations)等。</li><li>Trace:由一组 Span 组成的树状结构,表示一个请求的完整生命周期。一个 Trace 可以包含多个 Span,因为一个请求可能会经过多个服务。</li><li>Annotation:用于及时记录事件,通常包含时间戳。常见的注解有:<ul><li>cs(Client Sent):客户端发起请求。</li><li>sr(Server Received):服务端接收到请求。</li><li>ss(Server Sent):服务端发送响应。</li><li>cr(Client Received):客户端接收到响应。</li></ul></li><li>Collector:Zipkin 的一个组件,用于收集各个服务上报的跟踪数据。</li><li>Storage:Zipkin 的一个组件,用于存储跟踪数据。Zipkin 支持多种存储方式,如内存、MySQL、Cassandra、Elasticsearch 等。</li><li>Query Service:Zipkin 的一个组件,提供查询接口,用于检索和展示跟踪数据。</li><li>Web UI:Zipkin 提供的一个用户界面,用于展示跟踪数据和分析调用链路。</li></ul><p>在微服务架构中,每个服务都会在自己的代码中埋入跟踪代码,这些代码会在服务间调用时生成和传播跟踪数据。这些数据最终会被发送到 Zipkin 进行聚合和分析。通过 Zipkin 的 Web UI,开发者可以查看每个请求的详细调用链路,包括每个服务调用的时间、顺序和状态,从而帮助开发者理解服务之间的依赖关系,快速定位和解决性能问题。</p><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>默认端口 9411</p><p>必须JDK17</p><p>启动之后才可查看,使用java命令行启动即可</p><h2 id="Micrometer"><a href="#Micrometer" class="headerlink" title="Micrometer"></a>Micrometer</h2><h3 id="引入依赖(服务端和调用端)"><a href="#引入依赖(服务端和调用端)" class="headerlink" title="引入依赖(服务端和调用端)"></a>引入依赖(服务端和调用端)</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--micrometer-tracing指标追踪 1--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.micrometer<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>micrometer-tracing<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!--micrometer-tracing-bridge-brave适配zipkin的桥接包 2--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.micrometer<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>micrometer-tracing-bridge-brave<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!--micrometer-observation 3--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.micrometer<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>micrometer-observation<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!--feign-micrometer 4--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.openfeign<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>feign-micrometer<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!--zipkin-reporter-brave 5--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.zipkin.reporter2<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>zipkin-reporter-brave<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>由于Micrometer Tracing是一个门面工具自身并没有实现完整的链路追踪系统,具体的链路追踪另外需要引入的是第三方链路追踪系统的依赖:</p><table><thead><tr><th>1</th><th>micrometer-tracing-bom</th><th>导入链路追踪版本中心,体系化说明</th></tr></thead><tbody><tr><td><strong>2</strong></td><td><strong>micrometer-tracing</strong></td><td><strong>指标追踪</strong></td></tr><tr><td><strong>3</strong></td><td><strong>micrometer-tracing-bridge-brave</strong></td><td><strong>一个Micrometer模块,用于与分布式跟踪工具 Brave 集成,以收集应用程序的分布式跟踪数据。Brave是一个开源的分布式跟踪工具,它可以帮助用户在分布式系统中跟踪请求的流转,它使用一种称为”跟踪上下文”的机制,将请求的跟踪信息存储在请求的头部,然后将请求传递给下一个服务。在整个请求链中,Brave会将每个服务处理请求的时间和其他信息存储到跟踪数据中,以便用户可以了解整个请求的路径和性能。</strong></td></tr><tr><td><strong>4</strong></td><td><strong>micrometer-observation</strong></td><td><strong>一个基于度量库 Micrometer的观测模块,用于收集应用程序的度量数据。</strong></td></tr><tr><td><strong>5</strong></td><td>xxxxxxxxxx5 1predicates:2 - name: Cookie3 args: 4 name: mycookie5 regexp: mycookievalueyaml</td><td><strong>一个Feign HTTP客户端的Micrometer模块,用于收集客户端请求的度量数据。</strong></td></tr><tr><td><strong>6</strong></td><td><strong>zipkin-reporter-brave</strong></td><td><strong>一个用于将 Brave 跟踪数据报告到Zipkin 跟踪系统的库。</strong></td></tr></tbody></table><p>补充包:spring-boot-starter-actuator SpringBoot框架的一个模块用于监视和管理应用程序</p><h3 id="yaml配置"><a href="#yaml配置" class="headerlink" title="yaml配置"></a>yaml配置</h3><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># zipkin图形展现地址和采样率设置</span><br><span class="hljs-attr">management:</span><br> <span class="hljs-attr">zipkin:</span><br> <span class="hljs-attr">tracing:</span><br> <span class="hljs-attr">endpoint:</span> <span class="hljs-string">http://localhost:9411/api/v2/spans</span><br> <span class="hljs-attr">tracing:</span><br> <span class="hljs-attr">sampling:</span><br> <span class="hljs-attr">probability:</span> <span class="hljs-number">1.0</span> <span class="hljs-comment">#采样率默认为0.1(0.1就是10次只能有一次被记录下来),值越大收集越及时。</span><br></code></pre></td></tr></table></figure><h3 id="接口测试"><a href="#接口测试" class="headerlink" title="接口测试"></a>接口测试</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//80调用端</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderMicrometerController</span><br>{<br> <span class="hljs-meta">@Resource</span><br> <span class="hljs-keyword">private</span> PayFeignApi payFeignApi;<br><br> <span class="hljs-meta">@GetMapping(value = "/feign/micrometer/{id}")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">myMicrometer</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br> {<br> <span class="hljs-keyword">return</span> payFeignApi.myMicrometer(id);<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//8001服务端</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">PayMicrometerController</span><br>{<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * Micrometer(Sleuth)进行链路监控的例子</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> id</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@GetMapping(value = "/pay/micrometer/{id}")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">myMicrometer</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br> {<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, 欢迎到来myMicrometer inputId: "</span>+id+<span class="hljs-string">" \t 服务返回:"</span> + IdUtil.simpleUUID();<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title>Java进阶</title>
<link href="/2024/03/20/Java%E8%BF%9B%E9%98%B6/"/>
<url>/2024/03/20/Java%E8%BF%9B%E9%98%B6/</url>
<content type="html"><![CDATA[<p>Java进阶</p><span id="more"></span><h1 id="Java进阶"><a href="#Java进阶" class="headerlink" title="Java进阶"></a>Java进阶</h1><h2 id="JDK动态代理"><a href="#JDK动态代理" class="headerlink" title="JDK动态代理"></a>JDK动态代理</h2><h3 id="是什么?"><a href="#是什么?" class="headerlink" title="是什么?"></a>是什么?</h3><p>当你写了一个接口,里面有方法,然后写了实现类实现方法,完成方法逻辑,然后有一天,想对这个方法进行修改,但是不想改源码,所以有两种方式可以实现。第一种是静态代理,创建一个类继承实现类,然后对方法进行修改,这样太局限,因为只能针对特定的类增强方法,有100个实现类就要创建100个子类去实现。第二种方式是动态代理,可以动态地生成代理类,这是可以接受的</p><h3 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h3><ul><li>异常处理类</li><li>mybatis</li><li>SpringBoot aop</li><li>日志框架</li></ul><h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><p>JDK动态代理是Java提供的一种代理模式,允许程序在运行时动态创建代理类和对象,以实现接口中定义的方法。这种代理方式不需要我们手动编写代理类,Java运行时环境会为我们自动生成代理类。</p><p>JDK动态代理的原理主要涉及Java的反射机制和Java.lang.reflect包下的以下类:</p><ul><li><p><strong>java.lang.reflect.Proxy</strong>:这是生成代理类的主类,它提供了用于创建代理对象的静态方法newProxyInstance</p></li><li><p><strong>java.lang.reflect.InvocationHandler</strong>:这是一个接口,用于自定义代理对象的方法调用处理逻辑。每一个代理对象的调用处理程序都需要实现这个接口。</p></li></ul><h4 id="JDK动态代理的基本步骤如下:"><a href="#JDK动态代理的基本步骤如下:" class="headerlink" title="JDK动态代理的基本步骤如下:"></a>JDK动态代理的基本步骤如下:</h4><p>1.定义一个或多个接口,并实现接口,创建委托类(目标对象)。</p><p>2.创建一个实现了InvocationHandler接口的调用处理器,在实现的invoke方法中加入代理逻辑。</p><p>3.通过Proxy类的newProxyInstance静态方法创建代理对象,这个方法需要三个参数:</p><ul><li>类加载器(ClassLoader):用于加载代理类。</li><li>一个Class数组,表示代理类需要实现的接口列表。</li><li>一个InvocationHandler实例,表示代理对象的调用处理程序。</li></ul><p>当客户端通过代理对象调用一个方法时,这个调用会被转发到实现了InvocationHandler接口的invoke方法。在invoke方法里,可以通过反射机制来调用目标对象的方法,同时可以添加额外的处理逻辑,例如日志记录、权限校验等。</p><h4 id="JDK动态代理的特点是:"><a href="#JDK动态代理的特点是:" class="headerlink" title="JDK动态代理的特点是:"></a>JDK动态代理的特点是:</h4><ul><li><p>只能为接口创建代理,代理类必须实现一个或多个接口。</p></li><li><p>代理类的字节码在程序运行时由Java反射API动态生成,无需手动编写代理类。</p></li><li><p>代理对象的方法调用最终都会被转发到InvocationHandler对象的invoke方法,进行统一处理。</p><blockquote><p>由于JDK动态代理要求代理的类必须实现接口,因此对于那些没有实现接口的类,JDK动态代理就无法使用,这时可以考虑使用CGLIB等其他的代理方式。</p></blockquote></li></ul><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><ul><li>people接口类 及其实现类 User</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JavaSE.Proxy;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">people</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">shop</span><span class="hljs-params">()</span>;<br>}<br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JavaSE.Proxy;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">people</span>{<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> money;<br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">User</span><span class="hljs-params">(<span class="hljs-type">int</span> money)</span>{<br> <span class="hljs-built_in">this</span>.money=money;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">shop</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">if</span>(<span class="hljs-built_in">this</span>.money><span class="hljs-number">0</span>){<br> <span class="hljs-built_in">this</span>.money--;<br> System.out.println(<span class="hljs-string">"花费一块钱"</span>);<br> }<br> System.out.println(<span class="hljs-string">"剩余"</span>+money+<span class="hljs-string">"元钱"</span>);<br> }<br> <br>}<br></code></pre></td></tr></table></figure><ul><li><strong>User产生代理工具类(核心)</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JavaSE.Proxy;<br><br><span class="hljs-keyword">import</span> java.lang.reflect.InvocationHandler;<br><span class="hljs-keyword">import</span> java.lang.reflect.Method;<br><span class="hljs-keyword">import</span> java.lang.reflect.Proxy;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserProxyUtil</span> {<br> <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> people <span class="hljs-title function_">creatUser</span><span class="hljs-params">(User user)</span>{<br> <span class="hljs-comment">/* newProxyInstance参数</span><br><span class="hljs-comment"> * ClassLoader loader,</span><br><span class="hljs-comment"> * Class<?>[] interfaces,</span><br><span class="hljs-comment"> * InvocationHandler h</span><br><span class="hljs-comment"> * 参数一:类加载器,将代理类加载进JVM,一般固定以当前代理产生类的类加载器</span><br><span class="hljs-comment"> * 参数二:指定生成的代理的方法,一般是一个接口类的数组</span><br><span class="hljs-comment"> * 参数三:指定生成的代理对象干什么事情,是主要修改区域</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-comment">//这里直接new一个接口的匿名内部类对象来指定代理对象干什么</span><br> <span class="hljs-comment">//invoke方法决定代理对象干什么,invoke也是一个回调方法,当代理对象调用相关方法的时候会回调此函数</span><br> <span class="hljs-type">people</span> <span class="hljs-variable">userProxy</span> <span class="hljs-operator">=</span> (people) Proxy.newProxyInstance(UserProxyUtil.class.getClassLoader(), <span class="hljs-keyword">new</span> <span class="hljs-title class_">Class</span>[]{people.class}, <span class="hljs-keyword">new</span> <span class="hljs-title class_">InvocationHandler</span>() {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">invoke</span><span class="hljs-params">(Object proxy, Method method, Object[] args)</span> <span class="hljs-keyword">throws</span> Throwable {<br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> proxy 参数代表代理对象本身。</span><br><span class="hljs-comment"> method 参数代表被调用的方法。</span><br><span class="hljs-comment"> args 参数代表被调用的方法的参数。</span><br><span class="hljs-comment"> */</span> <br> <span class="hljs-keyword">if</span>(method.getName().equals(<span class="hljs-string">"shop"</span>)){<br> System.out.println(<span class="hljs-string">"准备购物"</span>);<br> <span class="hljs-comment">//反射调用方法的invoke,并将结果返回,如果想在调用原本方法后进行操作只要先将返回值存储起来就行</span><br> <span class="hljs-keyword">return</span> method.invoke(user, args);<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br> }<br> });<br> <span class="hljs-keyword">return</span> userProxy;<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>main函数及其输出</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> JavaSE.Proxy;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">main</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-number">10</span>);<br> <span class="hljs-type">people</span> <span class="hljs-variable">userProxy</span> <span class="hljs-operator">=</span> UserProxyUtil.creatUser(user);<br> userProxy.shop();<br> System.out.println(<span class="hljs-string">"消费完成"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs cmd">准备购物<br>花费一块钱<br>剩余<span class="hljs-number">9</span>元钱<br>消费完成<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>Spring cloud 04服务熔断与降级</title>
<link href="/2024/03/18/SpringCloud04/"/>
<url>/2024/03/18/SpringCloud04/</url>
<content type="html"><![CDATA[<p>Spring cloud 服务熔断与降级</p><span id="more"></span><h1 id="服务熔断与降级"><a href="#服务熔断与降级" class="headerlink" title="服务熔断与降级"></a>服务熔断与降级</h1><h2 id="什么是服务熔断与降级?"><a href="#什么是服务熔断与降级?" class="headerlink" title="什么是服务熔断与降级?"></a>什么是服务熔断与降级?</h2><ul><li>服务熔断: 服务熔断是一种自我保护机制。当服务提供者因为某些原因(如下游服务延迟、服务过载、服务提供者自身发生故障等)无法正常处理请求时,为了防止整个系统的雪崩效应,暂时切断对下游服务的调用,而不是持续不断地重试,这样可以避免服务调用方因为服务提供者的故障而导致自身资源耗尽甚至崩溃。一旦服务提供者恢复正常,服务熔断器会关闭断路,恢复服务调用。</li><li>服务降级: 服务降级是指当系统面临高压时,为了保持核心功能的可用性,主动关闭一些非核心功能或者降低某些服务的服务质量。比如,在电商平台的购物高峰期,如果系统负载过高,可以关闭一些不那么重要的功能如用户评论、商品推荐等,保证用户的下单、支付等核心功能的顺畅进行。</li></ul><blockquote><p>服务雪崩:当分布式系统中的某个模块失效,进而会牵连其他所有相关模块进而导致整个系统崩溃</p></blockquote><p>为了防止服务雪崩,我们会进行服务熔断,降级,限流等措施</p><h2 id="Hystrix"><a href="#Hystrix" class="headerlink" title="Hystrix"></a>Hystrix</h2><p>Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性</p><blockquote><p>停更了</p></blockquote><h1 id="Spring-Cloud-Circuit-Breaker断路器"><a href="#Spring-Cloud-Circuit-Breaker断路器" class="headerlink" title="Spring Cloud Circuit Breaker断路器"></a>Spring Cloud Circuit Breaker断路器</h1><h2 id="是什么?"><a href="#是什么?" class="headerlink" title="是什么?"></a>是什么?</h2><p>提供了跨不同断路器实现的抽象,只是一个接口和规范</p><p>CircuitBreaker的目的是保护分布式系统免受故障和异常,提高系统的可用性和健壮性。</p><p>当一个组件或服务出现故障时,CircuitBreaker会迅速切换到开放OPEN状态(保险丝跳闸断电),阻止请求发送到该组件或服务从而避免更多的请求发送到该组件或服务。这可以减少对该组件或服务的负载,防止该组件或服务进一步崩溃,并使整个系统能够继续正常运行。同时,CircuitBreaker还可以提高系统的可用性和健壮性,因为它可以在分布式系统的各个组件之间自动切换,从而避免单点故障的问题</p><p><strong>主要实现类</strong></p><ul><li><strong>Resilience4j</strong></li><li>Spring Retry</li></ul><h2 id="Resilience4j"><a href="#Resilience4j" class="headerlink" title="Resilience4j"></a>Resilience4j</h2><h3 id="是什么"><a href="#是什么" class="headerlink" title="是什么"></a>是什么</h3><p>Resilience4j 是一个轻量级的容错库,专为函数式编程而设计。Resilience4j 提供了高阶函数(装饰器),用以增强任何函数接口、lambda 表达式或方法引用的功能,包括熔断器、限流器、重试或隔板等。你可以在任何函数接口、lambda 表达式或方法引用上堆叠多个装饰器。这样做的好处是,你可以根据需要选择所需的装饰器,而无需其他多余的功能。</p><p>Resilience4j 2 需要 Java 17。</p><p><strong>Resilience4j 提供了几个核心模块</strong>:</p><ul><li><strong>resilience4j-circuitbreaker: 熔断器</strong></li><li><strong>resilience4j-ratelimiter: 限流器</strong></li><li><strong>resilience4j-bulkhead: 隔板</strong></li><li>resilience4j-retry: 自动重试(同步和异步)</li><li>resilience4j-timelimiter: 超时处理</li><li>resilience4j-cache: 结果缓存<br>此外,还有用于指标、Feign、Kotlin、Spring、Ratpack、Vertx、RxJava2 等的附加模块。</li></ul><h3 id="服务熔断"><a href="#服务熔断" class="headerlink" title="服务熔断"></a>服务熔断</h3><p>断路器的三个状态</p><ul><li>关闭 CLOSED</li><li>开启 OPEN</li><li>半开 HALF_OPEN</li></ul><p><strong>当熔断器关闭的时候,所有的请求都要通过熔断器</strong></p><ul><li>当失败率超过设定的阈值,熔断器就会关闭状态转换别到打开状态,这时所有请求都会被拒绝</li><li>经过一段时间后,熔断器就会从打开状态转换到半开状态,这时仅有一定数量的请求会被放入,重新计算失败率</li><li>如果失败率超过阈值,则恢复为打开状态,如果失败率低于阈值,则恢复为关闭状态</li></ul><p><strong>短路器使用滑动窗口来存储和统计调用结果,你可以选择基于调用数量的滑动窗离口或者基于时间的滑动窗口</strong></p><ul><li>基于访问数连个的滑动窗口统计了最近N次调用的返回结果,居于时间的滑动窗口统计了最近N秒的调用返回结果</li></ul><p>除此以外,熔断器还会有两种特殊状态:DISABLED(始终允许访问),FORCED_OPEN(始终拒绝访问)</p><ul><li>这两个状态不会生成熔断器事件,并且不会记录事件的成功失败与否</li><li>要想退出这两个状态,唯一方法是触发状态转换或者重置熔断器</li></ul><h4 id="熔断器参数"><a href="#熔断器参数" class="headerlink" title="熔断器参数"></a>熔断器参数</h4><table><thead><tr><th><strong>failure-rate-threshold</strong></th><th><strong>以百分比配置失败率峰值</strong></th></tr></thead><tbody><tr><td><strong>sliding-window-type</strong></td><td><strong>断路器的滑动窗口期类型,可以基于“次数”(COUNT_BASED)或者“时间”(TIME_BASED)进行熔断,默认是COUNT_BASED。</strong></td></tr><tr><td><strong>sliding-window-size</strong></td><td><strong>若COUNT_BASED,则10次调用中有50%失败(即5次)打开熔断断路器;若为TIME_BASED则,此时还有额外的两个设置属性,含义为:在N秒内(sliding-window-size)100%(slow-call-rate-threshold)的请求超过N秒(slow-call-duration-threshold)打开断路器</strong></td></tr><tr><td><strong>slowCallRateThreshold</strong></td><td><strong>以百分比的方式配置,断路器把调用时间大于slowCallDurationThreshold的调用视为慢调用,当慢调用比例大于等于峰值时,断路器开启,并进入服务降级。</strong></td></tr><tr><td><strong>slowCallDurationThreshold</strong></td><td><strong>配置调用时间的峰值,高于该峰值的视为慢调用。</strong></td></tr><tr><td><strong>permitted-number-of-calls-in-half-open-state</strong></td><td><strong>运行断路器在HALF_OPEN状态下时进行N次调用,如果故障或慢速调用仍然高于阈值,断路器再次进入打开状态。</strong></td></tr><tr><td><strong>minimum-number-of-calls</strong></td><td><strong>在每个滑动窗口期样本数,配置断路器计算错误率或者慢调用率的最小调用数。比如设置为5意味着,在计算故障率之前,必须至少调用5次。如果只记录了4次,即使4次都失败了,断路器也不会进入到打开状态。</strong></td></tr><tr><td><strong>wait-duration-in-open-state</strong></td><td><strong>从OPEN到HALF_OPEN状态需要等待的时间</strong></td></tr></tbody></table><p>熔断器设置在消费端</p><h4 id="熔断配置"><a href="#熔断配置" class="headerlink" title="熔断配置"></a>熔断配置</h4><h4 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h4><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-circuitbreaker-resilience4j<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- 由于断路保护等需要AOP实现,所以必须导入AOP包 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-aop<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h4 id="修改yml"><a href="#修改yml" class="headerlink" title="修改yml"></a>修改yml</h4><ul><li>COUNT_BASED</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs yaml"> <span class="hljs-comment"># 开启circuitbreaker和分组激活 spring.cloud.openfeign.circuitbreaker.enabled,一定要开启这个兼容!</span><br> <span class="hljs-attr">circuitbreaker:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span><br> <span class="hljs-attr">group:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment">#没开分组永远不用分组的配置。精确优先、分组次之(开了分组)、默认最后</span><br> <br><span class="hljs-comment"># Resilience4j CircuitBreaker 按照次数:COUNT_BASED 的例子</span><br><span class="hljs-comment"># 6次访问中当执行方法的失败率达到50%时CircuitBreaker将进入开启OPEN状态(保险丝跳闸断电)拒绝所有请求。</span><br><span class="hljs-comment"># 等待5秒后,CircuitBreaker 将自动从开启OPEN状态过渡到半开HALF_OPEN状态,允许一些请求通过以测试服务是否恢复正常。</span><br><span class="hljs-comment"># 如还是异常CircuitBreaker 将重新进入开启OPEN状态;如正常将进入关闭CLOSE闭合状态恢复正常处理请求。</span><br><span class="hljs-attr">resilience4j:</span><br> <span class="hljs-attr">circuitbreaker:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">failureRateThreshold:</span> <span class="hljs-number">50</span> <span class="hljs-comment">#设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。</span><br> <span class="hljs-attr">slidingWindowType:</span> <span class="hljs-string">COUNT_BASED</span> <span class="hljs-comment"># 滑动窗口的类型</span><br> <span class="hljs-attr">slidingWindowSize:</span> <span class="hljs-number">6</span> <span class="hljs-comment">#滑动窗⼝的⼤⼩配置COUNT_BASED表示6个请求,配置TIME_BASED表示6秒</span><br> <span class="hljs-attr">minimumNumberOfCalls:</span> <span class="hljs-number">6</span> <span class="hljs-comment">#断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。如果minimumNumberOfCalls为10,则必须最少记录10个样本,然后才能计算失败率。如果只记录了9次调用,即使所有9次调用都失败,断路器也不会开启。</span><br> <span class="hljs-attr">automaticTransitionFromOpenToHalfOpenEnabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># 是否启用自动从开启状态过渡到半开状态,默认值为true。如果启用,CircuitBreaker将自动从开启状态过渡到半开状态,并允许一些请求通过以测试服务是否恢复正常</span><br> <span class="hljs-attr">waitDurationInOpenState:</span> <span class="hljs-string">5s</span> <span class="hljs-comment">#从OPEN到HALF_OPEN状态需要等待的时间</span><br> <span class="hljs-attr">permittedNumberOfCallsInHalfOpenState:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#半开状态允许的最大请求数,默认值为10。在半开状态下,CircuitBreaker将允许最多permittedNumberOfCallsInHalfOpenState个请求通过,如果其中有任何一个请求失败,CircuitBreaker将重新进入开启状态。</span><br> <span class="hljs-attr">recordExceptions:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">java.lang.Exception</span><br> <span class="hljs-attr">instances:</span><br> <span class="hljs-attr">cloud-payment-service:</span> <span class="hljs-comment">#与实例名字保持一致</span><br> <span class="hljs-attr">baseConfig:</span> <span class="hljs-string">default</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>circuitbreaker: enabled: true:</strong>这个配置项用于开启或关闭Spring Cloud Circuit Breaker的功能。当设置为 true 时,Spring Cloud Circuit Breaker会生效,框架会监控调用远程服务的方法,并在检测到异常或超时时自动熔断,避免应用程序因为下游服务的问题而崩溃。<br><strong>circuitbreaker: group: enabled: true:</strong>这个配置项用于开启或关闭Spring Cloud Circuit Breaker的分组功能。在Spring Cloud中,Circuit Breaker支持分组,这意味着可以针对不同的服务或方法设置不同的熔断策略。例如,你可能希望对某些关键业务流程实施更严格的熔断策略,而其他不那么关键的流程则可以有更宽松的策略。通过设置 group.enabled 为 true,你可以激活分组熔断功能,从而可以针对不同的服务分组独立配置熔断策略。</p></blockquote><ul><li>时间滑动窗口</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Resilience4j CircuitBreaker 按照时间:TIME_BASED 的例子</span><br><span class="hljs-attr">resilience4j:</span><br> <span class="hljs-attr">timelimiter:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">timeout-duration:</span> <span class="hljs-string">10s</span> <span class="hljs-comment">#神坑的位置,timelimiter 默认限制远程1s,超于1s就超时异常,配置了降级,就走降级逻辑</span><br> <span class="hljs-attr">circuitbreaker:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">failureRateThreshold:</span> <span class="hljs-number">50</span> <span class="hljs-comment">#设置50%的调用失败时打开断路器,超过失败请求百分⽐CircuitBreaker变为OPEN状态。</span><br> <span class="hljs-attr">slowCallDurationThreshold:</span> <span class="hljs-string">2s</span> <span class="hljs-comment">#慢调用时间阈值,高于这个阈值的视为慢调用并增加慢调用比例。</span><br> <span class="hljs-attr">slowCallRateThreshold:</span> <span class="hljs-number">30</span> <span class="hljs-comment">#慢调用百分比峰值,断路器把调用时间⼤于slowCallDurationThreshold,视为慢调用,当慢调用比例高于阈值,断路器打开,并开启服务降级</span><br> <span class="hljs-attr">slidingWindowType:</span> <span class="hljs-string">TIME_BASED</span> <span class="hljs-comment"># 滑动窗口的类型</span><br> <span class="hljs-attr">slidingWindowSize:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#滑动窗口的大小配置,配置TIME_BASED表示2秒</span><br> <span class="hljs-attr">minimumNumberOfCalls:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#断路器计算失败率或慢调用率之前所需的最小样本(每个滑动窗口周期)。</span><br> <span class="hljs-attr">permittedNumberOfCallsInHalfOpenState:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#半开状态允许的最大请求数,默认值为10。</span><br> <span class="hljs-attr">waitDurationInOpenState:</span> <span class="hljs-string">5s</span> <span class="hljs-comment">#从OPEN到HALF_OPEN状态需要等待的时间</span><br> <span class="hljs-attr">recordExceptions:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">java.lang.Exception</span><br> <span class="hljs-attr">instances:</span><br> <span class="hljs-attr">cloud-payment-service:</span><br> <span class="hljs-attr">baseConfig:</span> <span class="hljs-string">default</span> <br></code></pre></td></tr></table></figure><h3 id="服务隔离"><a href="#服务隔离" class="headerlink" title="服务隔离"></a>服务隔离</h3><p>BulkHead</p><p><strong>作用</strong>:依赖隔离和负载保护</p><p>用于限制对于下游的最大并发数量的限制</p><p><strong>方法</strong>:</p><h4 id="1-实现SemaphoreBulkhead信号量舱壁"><a href="#1-实现SemaphoreBulkhead信号量舱壁" class="headerlink" title="1.实现SemaphoreBulkhead信号量舱壁"></a>1.实现SemaphoreBulkhead信号量舱壁</h4><blockquote><p>当信号量有空闲的时候,系统的请求会直接获取信号量并开始业务处理,</p><p>当信号量全被占用的时候,接下来的请求将会进入阻塞状态,Semaphorehead提供了一个阻塞计时器,</p><p>如果阻塞的请求在阻塞计时器内无法获取到信号量系统则会拒绝此次请求,反之响应处理</p></blockquote><ul><li>引入依赖</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--resilience4j-bulkhead--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.resilience4j<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>resilience4j-bulkhead<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>yml配置</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">####resilience4j bulkhead 的例子,同时需要开启OpenFeign分组激活</span><br><span class="hljs-attr">resilience4j:</span><br> <span class="hljs-attr">bulkhead:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">maxConcurrentCalls:</span> <span class="hljs-number">2</span> <span class="hljs-comment"># 隔离允许并发线程执行的最大数量,默认25</span><br> <span class="hljs-attr">maxWaitDuration:</span> <span class="hljs-string">1s</span> <span class="hljs-comment"># 当达到并发调用数量时,新的线程的阻塞时间,我只愿意等待1秒,过时不候进舱壁兜底fallback,默认值为0</span><br> <span class="hljs-attr">instances:</span><br> <span class="hljs-attr">cloud-payment-service:</span><br> <span class="hljs-attr">baseConfig:</span> <span class="hljs-string">default</span><br> <span class="hljs-attr">timelimiter:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">timeout-duration:</span> <span class="hljs-string">20s</span><br></code></pre></td></tr></table></figure><ul><li>调用方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">*(船的)舱壁,隔离,形式与熔断方法一样,都是定义fallback方法以及通过OpenFeign访问服务</span><br><span class="hljs-comment">* <span class="hljs-doctag">@param</span> id</span><br><span class="hljs-comment">* <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">*/</span><br><span class="hljs-meta">@GetMapping(value = "/feign/pay/bulkhead/{id}")</span><br><span class="hljs-comment">//指定此方法调用服务名,服务降级处理,以及舱壁类型为Bulkhead.Type.SEMAPHORE</span><br><span class="hljs-meta">@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadFallback",type = Bulkhead.Type.SEMAPHORE)</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">myBulkhead</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br>{<br> <span class="hljs-keyword">return</span> payFeignApi.myBulkhead(id);<br>}<br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">myBulkheadFallback</span><span class="hljs-params">(Throwable t)</span><br>{<br> <span class="hljs-comment">//降级方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"myBulkheadFallback,隔板超出最大数量限制,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~"</span>;<br>}<br></code></pre></td></tr></table></figure><h4 id="2-实现固定线程池舱壁"><a href="#2-实现固定线程池舱壁" class="headerlink" title="2.实现固定线程池舱壁"></a>2.实现固定线程池舱壁</h4><p><strong>固定线程池舱壁(FixedThreadPoolBulkhead)</strong></p><p>FixedThreadPoolBulkhead的功能与SemaphoreBulkhead一样也是<strong>用于限制并发执行的次数</strong>的,但是二者的实现原理存在差别而且表现效果也存在细微的差别。FixedThreadPoolBulkhead使用一个固定线程池和一个等待队列来实现舱壁。</p><ul><li>当线程池中存在空闲时,则此时进入系统的请求将直接进入线程池开启新线程或使用空闲线程来处理请求。</li><li>当线程池中无空闲时时,接下来的请求将进入等待队列</li><li>若等待队列仍然无剩余空间时接下来的请求将直接被拒绝</li><li>在队列中的请求等待线程池出现空闲时,将进入线程池进行业务处理</li></ul><blockquote><p> 另外:ThreadPoolBulkhead只对CompletableFuture方法有效,所以我们必创建返回CompletableFuture类型的方法、</p></blockquote><p><strong>方法</strong></p><ul><li>引入依赖</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--resilience4j-bulkhead--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.resilience4j<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>resilience4j-bulkhead<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>yaml</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">####resilience4j bulkhead -THREADPOOL的例子</span><br><span class="hljs-attr">resilience4j:</span><br> <span class="hljs-attr">timelimiter:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">timeout-duration:</span> <span class="hljs-string">10s</span> <span class="hljs-comment">#timelimiter默认限制远程1s,超过报错不好演示效果所以加上10秒</span><br> <span class="hljs-attr">thread-pool-bulkhead:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">core-thread-pool-size:</span> <span class="hljs-number">1</span><br> <span class="hljs-attr">max-thread-pool-size:</span> <span class="hljs-number">1</span><br> <span class="hljs-attr">queue-capacity:</span> <span class="hljs-number">1</span><br> <span class="hljs-attr">instances:</span><br> <span class="hljs-attr">cloud-payment-service:</span><br> <span class="hljs-attr">baseConfig:</span> <span class="hljs-string">default</span><br><span class="hljs-comment"># spring.cloud.openfeign.circuitbreaker.group.enabled 请设置为false 新启线程和原来主线程脱离</span><br></code></pre></td></tr></table></figure><p>不要配置OpenFeign的group!</p><ul><li>调用方法,必须构造CompletableFuture<String>方法返回</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * (船的)舱壁,隔离,THREADPOOL</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> id</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@GetMapping(value = "/feign/pay/bulkhead/{id}")</span><br><span class="hljs-meta">@Bulkhead(name = "cloud-payment-service",fallbackMethod = "myBulkheadPoolFallback",type = Bulkhead.Type.THREADPOOL)</span><br><span class="hljs-keyword">public</span> CompletableFuture<String> <span class="hljs-title function_">myBulkheadTHREADPOOL</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br>{<br> System.out.println(Thread.currentThread().getName()+<span class="hljs-string">"\t"</span>+<span class="hljs-string">"enter the method!!!"</span>);<br> <span class="hljs-keyword">try</span> { TimeUnit.SECONDS.sleep(<span class="hljs-number">3</span>); } <span class="hljs-keyword">catch</span> (InterruptedException e) { e.printStackTrace(); }<br> System.out.println(Thread.currentThread().getName()+<span class="hljs-string">"\t"</span>+<span class="hljs-string">"exist the method!!!"</span>);<br><br> <span class="hljs-keyword">return</span> CompletableFuture.supplyAsync(() -> payFeignApi.myBulkhead(id) + <span class="hljs-string">"\t"</span> + <span class="hljs-string">" Bulkhead.Type.THREADPOOL"</span>);<br>}<br><span class="hljs-keyword">public</span> CompletableFuture<String> <span class="hljs-title function_">myBulkheadPoolFallback</span><span class="hljs-params">(Integer id,Throwable t)</span><br>{<br> <span class="hljs-keyword">return</span> CompletableFuture.supplyAsync(() -> <span class="hljs-string">"Bulkhead.Type.THREADPOOL,系统繁忙,请稍后再试-----/(ㄒoㄒ)/~~"</span>);<br>}<br></code></pre></td></tr></table></figure><h3 id="服务限流"><a href="#服务限流" class="headerlink" title="服务限流"></a>服务限流</h3><p>对并发访问进行限速,也就是频率控制</p><h4 id="常见限流算法"><a href="#常见限流算法" class="headerlink" title="常见限流算法"></a>常见限流算法</h4><ul><li><p><strong>漏斗算法</strong>:一个固定的漏桶,按照设定固定的速率流出水滴,当漏斗满的时候抛弃新的需求,但是对于突发特性的流量来说缺乏效率</p></li><li><p><strong>令牌桶算法(默认算法)</strong>:类似于信号量的机制,往一个桶内匀速添加令牌,当有请求来时,有令牌则拿走令牌,没有则抛弃,当处理完请求后将令牌丢掉,令牌相当于一个队列</p></li><li><p><strong>滚动实现窗</strong>:允许固定请求进入,比如一秒使四个数据相加,超过25就over,超过数量就拒绝或者排队,等待下一个时间段队列,但是,间隔临界的的一段时间内请求超过系统限制,可能导致系统被压垮</p></li><li><p><strong>滑动时间窗口</strong>:需要定义滑动窗口的大小,区别于滚动,不同时间段窗口实际上有重叠,也就是当前的时间窗口的前面半段可以是上一秒的后半段</p></li></ul><p><strong>方法</strong></p><ul><li>引入依赖</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--resilience4j-ratelimiter--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.resilience4j<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>resilience4j-ratelimiter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>yaml配置</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">####resilience4j ratelimiter 限流的例子</span><br><span class="hljs-attr">resilience4j:</span><br> <span class="hljs-attr">ratelimiter:</span><br> <span class="hljs-attr">configs:</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-attr">limitForPeriod:</span> <span class="hljs-number">2</span> <span class="hljs-comment">#在一次刷新周期内,允许执行的最大请求数</span><br> <span class="hljs-attr">limitRefreshPeriod:</span> <span class="hljs-string">1s</span> <span class="hljs-comment"># 限流器每隔limitRefreshPeriod刷新一次,将允许处理的最大请求数量重置为limitForPeriod</span><br> <span class="hljs-attr">timeout-duration:</span> <span class="hljs-number">1</span> <span class="hljs-comment"># 线程等待权限的默认等待时间</span><br> <span class="hljs-attr">instances:</span><br> <span class="hljs-attr">cloud-payment-service:</span><br> <span class="hljs-attr">baseConfig:</span> <span class="hljs-string">default</span><br></code></pre></td></tr></table></figure><ul><li>调用方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@GetMapping(value = "/feign/pay/ratelimit/{id}")</span><br><span class="hljs-meta">@RateLimiter(name = "cloud-payment-service",fallbackMethod = "myRatelimitFallback")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">myBulkhead</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span><br>{<br> <span class="hljs-keyword">return</span> payFeignApi.myRatelimit(id);<br>}<br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">myRatelimitFallback</span><span class="hljs-params">(Integer id,Throwable t)</span><br>{<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"你被限流了,禁止访问/(ㄒoㄒ)/~~"</span>;<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
<tag>Resilience4j</tag>
</tags>
</entry>
<entry>
<title>Spring cloud 03负载均衡(二)</title>
<link href="/2024/03/17/SpringCloud03/"/>
<url>/2024/03/17/SpringCloud03/</url>
<content type="html"><![CDATA[<p>Spring cloud 负载均衡(二)</p><span id="more"></span><h1 id="负载均衡(二):OpenFeign"><a href="#负载均衡(二):OpenFeign" class="headerlink" title="负载均衡(二):OpenFeign"></a>负载均衡(二):OpenFeign</h1><p>与LoadBalance相同的是在客户端的负载均衡技术</p><h2 id="与LoadBalance的区别"><a href="#与LoadBalance的区别" class="headerlink" title="与LoadBalance的区别"></a>与LoadBalance的区别</h2><p>LoadBalance+RestTemplate的方式,在更复杂的系统的时候显得不规范,因为一个微服务在实际开发中往往有着多个客户端来调用,而每个客户端都要写一套LoadBalance+RestTemplate来指向固定的某个服务接口,难以统一规范与维护(譬如接口变动的时候,所有调用者都要修改),OpenFeign往往可以简化这开发流程</p><p>OpenFeign加到微服务接口上可以对外暴露该微服务的接口,通过@FeignClient(标注在服务端接口上),可以自动完成客户端和服务端的接口绑定,大大简化了开发流程,此外,OpenFeign还支持继承LoadBalance等及其它强大组件,自带负载均衡</p><p>OpenFeign的原理是将所有的服务API都暴露出来统一到一起,然后由调用者去这个API中找到对应的api,然后由OpenFeign去注册中心寻找服务提供者(一个微服务模块)具体流程如下:</p><blockquote><p>服务消费者 -> 调用含有@FeignClient注解的Api服务接口-> 服务提供者</p></blockquote><p>服务调用者将不用管服务来自哪个模块而是只用关心调用接口,服务层解耦</p><h2 id="集成OpenFeign"><a href="#集成OpenFeign" class="headerlink" title="集成OpenFeign"></a>集成OpenFeign</h2><h3 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--openfeign--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-openfeign<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h3 id="启动类注解(接口调用端)"><a href="#启动类注解(接口调用端)" class="headerlink" title="启动类注解(接口调用端)"></a>启动类注解(接口调用端)</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootApplication</span><br><span class="hljs-meta">@EnableDiscoveryClient</span><span class="hljs-comment">//开启consul服务发现</span><br><span class="hljs-meta">@EnableFeignClients</span><span class="hljs-comment">//启用Feign客户端,以声明式的方法简单的实现服务调用</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MainOpenFeign80</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> SpringApplication.run(MainOpenFeign80.class,args);<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="通过payFeign获取接口"><a href="#通过payFeign获取接口" class="headerlink" title="通过payFeign获取接口"></a>通过payFeign获取接口</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@FeignClient(value ="cloud-payment-service")</span><span class="hljs-comment">//要与consul上服务名称相同</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">PayFeignApi</span> {<br> <span class="hljs-meta">@PostMapping(value = "/pay/add")</span><br> <span class="hljs-keyword">public</span> ResultData <span class="hljs-title function_">addPay</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> PayDTO payDTO)</span>;<br><br> <span class="hljs-meta">@GetMapping(value = "/pay/get/{id}")</span><br> <span class="hljs-keyword">public</span> ResultData <span class="hljs-title function_">getPayById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span>;<br><br> <span class="hljs-meta">@GetMapping(value ="/pay/get/info")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">mylb</span><span class="hljs-params">()</span>;<br>}<br></code></pre></td></tr></table></figure><h3 id="直接通过PayFeignApi发送请求"><a href="#直接通过PayFeignApi发送请求" class="headerlink" title="直接通过PayFeignApi发送请求"></a>直接通过PayFeignApi发送请求</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@Slf4j</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">OrderController</span>{<br> <span class="hljs-meta">@Resource</span><br> <span class="hljs-keyword">private</span> PayFeignApi payFeignApi;<br><br> <span class="hljs-meta">@PostMapping(value = "/feign/pay/add")</span><br> <span class="hljs-keyword">public</span> ResultData <span class="hljs-title function_">addOrder</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> PayDTO payDTO)</span>{<br> log.info(<span class="hljs-string">"新增订单:{}"</span>,payDTO);<br> <span class="hljs-type">ResultData</span> <span class="hljs-variable">resultData</span> <span class="hljs-operator">=</span> payFeignApi.addPay(payDTO);<br> <span class="hljs-keyword">return</span> resultData;<br> }<br><br> <span class="hljs-meta">@GetMapping(value = "/feign/pay/get/{id}")</span><br> <span class="hljs-keyword">public</span> ResultData <span class="hljs-title function_">getOrderById</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable("id")</span> Integer id)</span>{<br> log.info(<span class="hljs-string">"查询订单:{}"</span>,id);<br> <span class="hljs-type">ResultData</span> <span class="hljs-variable">resultData</span> <span class="hljs-operator">=</span> payFeignApi.getPayById(id);<br> <span class="hljs-keyword">return</span> resultData;<br> }<br><br> <span class="hljs-meta">@GetMapping(value = "/feign/pay/mylb")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">mylb</span><span class="hljs-params">()</span>{<br> log.info(<span class="hljs-string">"展示负载均衡"</span>);<br> <span class="hljs-keyword">return</span> payFeignApi.mylb();<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="高级特性"><a href="#高级特性" class="headerlink" title="高级特性"></a>高级特性</h2><h3 id="超时控制"><a href="#超时控制" class="headerlink" title="超时控制"></a>超时控制</h3><p>实际服务过程之中可能存在网络阻塞,业务复杂等各种原因,因此总是存在超时错误</p><p><strong>OpenFeign默认超时时间为60s,超过报错</strong></p><h4 id="自定义超时处理"><a href="#自定义超时处理" class="headerlink" title="自定义超时处理"></a>自定义超时处理</h4><p>yml文件中开启配置:</p><p>connectTimeout 连接超时时间</p><p>readTimeout 请求处理超时时间</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">openfeign:</span><br> <span class="hljs-attr">client:</span><br> <span class="hljs-attr">config:</span><br> <span class="hljs-comment">#对所有服务设置超时</span><br> <span class="hljs-attr">default:</span><br> <span class="hljs-comment">#连接超时时间,单位为毫秒,这里为3s</span><br> <span class="hljs-attr">connectTimeout:</span> <span class="hljs-number">3000</span><br> <span class="hljs-comment">#读取超时时间</span><br> <span class="hljs-attr">readTimeout:</span> <span class="hljs-number">3000</span><br> <span class="hljs-comment">#指定特殊的的某个服务来配置超时,default改为服务名,特殊设置优先级更高 </span><br> <span class="hljs-attr">cloud-payment-service:</span><br> <span class="hljs-attr">connectTimeout:</span> <span class="hljs-number">8000</span> <span class="hljs-comment">#连接超时时间</span><br> <span class="hljs-attr">readTimeout:</span> <span class="hljs-number">8000</span> <span class="hljs-comment">#读取超时时间 </span><br></code></pre></td></tr></table></figure><h3 id="重试机制"><a href="#重试机制" class="headerlink" title="重试机制"></a>重试机制</h3><p>默认重试机制是关闭的,只会调用一次</p><h4 id="开启重试机制"><a href="#开启重试机制" class="headerlink" title="开启重试机制"></a>开启重试机制</h4><p>新增配置类FeignConfg重写Retryer</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">FeignConfig</span> {<br> <br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> Retryer <span class="hljs-title function_">myRetryer</span><span class="hljs-params">()</span>{<br> <span class="hljs-comment">//默认返回是 return Retryer.NEVER_RETRY;</span><br> <br> <span class="hljs-comment">//最大请求次数为3(1+2),初始时间间隔为100ms,重试最大间隔时间为1s</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Retryer</span>.Default(<span class="hljs-number">100</span>,<span class="hljs-number">1</span>,<span class="hljs-number">3</span>);<br> }<br><br>}<br></code></pre></td></tr></table></figure><h3 id="HttpCilent修改与替换"><a href="#HttpCilent修改与替换" class="headerlink" title="HttpCilent修改与替换"></a>HttpCilent修改与替换</h3><p>如果不做特殊配置,默认使用JDK自带的HttpURLConnection发送Http请求</p><p>HttpURLConnection没有连接池、性能和效率比较低,更高级的配置可以做出性能优化</p><h4 id="修改为Apache-HttpClient-5"><a href="#修改为Apache-HttpClient-5" class="headerlink" title="修改为Apache HttpClient 5"></a>修改为Apache HttpClient 5</h4><h5 id="引入依赖-1"><a href="#引入依赖-1" class="headerlink" title="引入依赖"></a>引入依赖</h5><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!-- httpclient5--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.httpcomponents.client5<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>httpclient5<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>5.3<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- feign-hc5--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.github.openfeign<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>feign-hc5<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>13.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h5 id="配置开启"><a href="#配置开启" class="headerlink" title="配置开启"></a>配置开启</h5><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># Apache HttpClient5 配置开启</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">openfeign:</span><br> <span class="hljs-attr">httpclient:</span><br> <span class="hljs-attr">hc5:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span><br></code></pre></td></tr></table></figure><h3 id="请求响应压缩"><a href="#请求响应压缩" class="headerlink" title="请求响应压缩"></a>请求响应压缩</h3><p>默认不开启,开启压缩为gzip</p><h4 id="开启"><a href="#开启" class="headerlink" title="开启"></a>开启</h4><p>yml配置</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">openfeign:</span><br> <span class="hljs-attr">compression:</span><br> <span class="hljs-attr">request:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment">#开启请求压缩</span><br> <span class="hljs-attr">min-request-size:</span> <span class="hljs-number">2048</span> <span class="hljs-comment">#最小触发压缩的大小</span><br> <span class="hljs-attr">mime-types:</span> <span class="hljs-string">text/xml,application/xml,application/json</span> <span class="hljs-comment">#触发压缩数据类型</span><br> <span class="hljs-attr">response:</span><br> <span class="hljs-attr">enabled:</span> <span class="hljs-literal">true</span> <span class="hljs-comment">#开启响应压缩</span><br></code></pre></td></tr></table></figure><h3 id="日志打印"><a href="#日志打印" class="headerlink" title="日志打印"></a>日志打印</h3><h4 id="日志级别"><a href="#日志级别" class="headerlink" title="日志级别"></a>日志级别</h4><ul><li>NONE:不显示</li><li>BASIC:只记录请求方法,url,执行时间</li><li>HEADERS:还有请求和响应的头信息</li><li>FULL:还有请求和响应的正文及元数据</li></ul><h4 id="开启调试日志"><a href="#开启调试日志" class="headerlink" title="开启调试日志"></a>开启调试日志</h4><h5 id="配置日志Bean"><a href="#配置日志Bean" class="headerlink" title="配置日志Bean"></a>配置日志Bean</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//在Feign中改变日志级别</span><br><span class="hljs-meta">@Bean</span><br>Logger.Level <span class="hljs-title function_">feignLoggerLevel</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">return</span> Logger.Level.FULL;<br>}<br></code></pre></td></tr></table></figure><h5 id="yml文件开启日志客户端指定日志输出哪个接口"><a href="#yml文件开启日志客户端指定日志输出哪个接口" class="headerlink" title="yml文件开启日志客户端指定日志输出哪个接口"></a>yml文件开启日志客户端指定日志输出哪个接口</h5><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment"># feign日志客户端打印</span><br><span class="hljs-attr">logging:</span><br> <span class="hljs-attr">level:</span><br> <span class="hljs-attr">com.atguigu.cloud.apis.PayFeignApi:</span> <span class="hljs-string">debug</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
<tag>LoadBalance</tag>
</tags>
</entry>
<entry>
<title>Spring cloud 02负载均衡(二)</title>
<link href="/2024/03/15/SpringCloud02/"/>
<url>/2024/03/15/SpringCloud02/</url>
<content type="html"><![CDATA[<p>Spring cloud 负载均衡(二)</p><span id="more"></span><h1 id="负载均衡(一):Spring-cloud-LoadBalance"><a href="#负载均衡(一):Spring-cloud-LoadBalance" class="headerlink" title="负载均衡(一):Spring-cloud-LoadBalance"></a>负载均衡(一):Spring-cloud-LoadBalance</h1><h2 id="什么是负载均衡?"><a href="#什么是负载均衡?" class="headerlink" title="什么是负载均衡?"></a>什么是负载均衡?</h2><p>将用户的请求平均分摊到各个服务之上,从而达到系统的高可用,常见的有Nginx,LVS,硬件的F5</p><h2 id="什么是Spring-cloud-loadbalance?"><a href="#什么是Spring-cloud-loadbalance?" class="headerlink" title="什么是Spring-cloud-loadbalance?"></a>什么是Spring-cloud-loadbalance?</h2><p>是SpingCloud官方提供的一个开源的简单易用的客户端负载均衡器,用于替换以前的Ribbon组件,相对于Ribbon,LoadBalance不仅支持RestTemplate,还支持WebClient(Spring Web Flux中提供的功能,可以实现响应式异步请求)</p><blockquote><p> Ribbon</p><p>也叫spring-cloud-ribbon,是一种客户端软件负载均衡工具,提供软件负载均衡算法和服务调用</p><p>因为不再维护替换为spring-cloud-loadbalance</p></blockquote><h3 id="Nginx服务端负载均衡和LoadBalance客户端均衡区别"><a href="#Nginx服务端负载均衡和LoadBalance客户端均衡区别" class="headerlink" title="Nginx服务端负载均衡和LoadBalance客户端均衡区别"></a>Nginx服务端负载均衡和LoadBalance客户端均衡区别</h3><p>Nginx服务器负载均衡,客户段所有请求都会交给Nginx,由Nginx实现转发请求</p><p>LoadBalance本地负载均衡,在调用微服务接口的时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术</p><h2 id="设置本地负载均衡"><a href="#设置本地负载均衡" class="headerlink" title="设置本地负载均衡"></a>设置本地负载均衡</h2><h3 id="引入依赖(在用户模块上设置)"><a href="#引入依赖(在用户模块上设置)" class="headerlink" title="引入依赖(在用户模块上设置)"></a>引入依赖(在用户模块上设置)</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--loadbalancer--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-loadbalancer<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h3 id="通过RestTemplate设置-LoadBalanced"><a href="#通过RestTemplate设置-LoadBalanced" class="headerlink" title="通过RestTemplate设置@LoadBalanced"></a>通过RestTemplate设置@LoadBalanced</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RestTemplateConfig</span> {<br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-meta">@LoadBalanced</span><br> <span class="hljs-keyword">public</span> RestTemplate <span class="hljs-title function_">restTemplate</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RestTemplate</span>();<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h2><h3 id="客户端从服务中心拉取服务列表"><a href="#客户端从服务中心拉取服务列表" class="headerlink" title="客户端从服务中心拉取服务列表"></a>客户端从服务中心拉取服务列表</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Resource</span><br> <span class="hljs-keyword">private</span> DiscoveryClient discoveryClient;<br> <span class="hljs-meta">@GetMapping("/consumer/discovery")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">discovery</span><span class="hljs-params">()</span><br> {<br> List<String> services = discoveryClient.getServices();<br> <span class="hljs-keyword">for</span> (String element : services) {<br> System.out.println(element);<br> }<br><br> System.out.println(<span class="hljs-string">"==================================="</span>);<br><br> List<ServiceInstance> instances = discoveryClient.getInstances(<span class="hljs-string">"cloud-payment-service"</span>);<br> <span class="hljs-keyword">for</span> (ServiceInstance element : instances) {<br> System.out.println(element.getServiceId()+<span class="hljs-string">"\t"</span>+element.getHost()+<span class="hljs-string">"\t"</span>+element.getPort()+<span class="hljs-string">"\t"</span>+element.getUri());<br> }<br><br> <span class="hljs-keyword">return</span> instances.get(<span class="hljs-number">0</span>).getServiceId()+<span class="hljs-string">":"</span>+instances.get(<span class="hljs-number">0</span>).getPort();<br> }<br></code></pre></td></tr></table></figure><ul><li>效果</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java">===================================<br>cloud-payment-servicee<span class="hljs-number">8001</span>http:<span class="hljs-comment">//e:8001</span><br>cloud-payment-servicee<span class="hljs-number">8002</span>http:<span class="hljs-comment">//e:8002</span><br>cloud-consumer-order<br>cloud-payment-service<br>consul<br></code></pre></td></tr></table></figure><blockquote><p>LoadBalance 默认是轮询算法,内置一个计数器j计算总请求数,倘若,同一个服务中模块数量大于1,将总请求数对模块数量取余,例如上面,1%2=1,2%2=0;0和1对应服务模块下标位置</p></blockquote><h3 id="负载均衡算法"><a href="#负载均衡算法" class="headerlink" title="负载均衡算法"></a>负载均衡算法</h3><p> <strong>默认算法是轮询以及随机算法</strong></p><h4 id="改变算法"><a href="#改变算法" class="headerlink" title="改变算法"></a>改变算法</h4><ul><li>切换随机算法,在原有的RestTemplate组件上设置</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@LoadBalancerClient(</span><br><span class="hljs-meta"> //下面的value值大小写一定要和consul里面的名字一样,必须一样</span><br><span class="hljs-meta"> value = "cloud-payment-service",configuration = RestTemplateConfig.class)</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RestTemplateConfig</span><br>{<br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-meta">@LoadBalanced</span> <span class="hljs-comment">//使用@LoadBalanced注解赋予RestTemplate负载均衡的能力</span><br> <span class="hljs-keyword">public</span> RestTemplate <span class="hljs-title function_">restTemplate</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RestTemplate</span>();<br> }<br><br> <span class="hljs-meta">@Bean</span><br> ReactorLoadBalancer<ServiceInstance> <span class="hljs-title function_">randomLoadBalancer</span><span class="hljs-params">(Environment environment,</span><br><span class="hljs-params"> LoadBalancerClientFactory loadBalancerClientFactory)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);<br><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RandomLoadBalancer</span>(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>自定义算法</li></ul><p>通过查看官方文档和代码可以发现默认的两种算法都继承了ReactorServiceInstanceLoadBalancer</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">RoundRobinLoadBalancer <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ReactorServiceInstanceLoadBalancer</span><br>RandomLoadBalancer <span class="hljs-keyword">implements</span> <span class="hljs-title class_">ReactorServiceInstanceLoadBalancer</span> <br></code></pre></td></tr></table></figure><p>因此实现这个方法可以改写算法</p>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
<tag>LoadBalance</tag>
</tags>
</entry>
<entry>
<title>Spring cloud 01服务注册与发现</title>
<link href="/2024/03/13/SpringCloud01/"/>
<url>/2024/03/13/SpringCloud01/</url>
<content type="html"><![CDATA[<p>Spring cloud consul 服务注册与发现</p><span id="more"></span><h1 id="服务注册与发现"><a href="#服务注册与发现" class="headerlink" title="服务注册与发现"></a>服务注册与发现</h1><h2 id="CAP定理"><a href="#CAP定理" class="headerlink" title="CAP定理"></a>CAP定理</h2><ul><li>C: Consistency (强一致性)</li><li>A: Availability (可用性)</li><li>P: Partition tolenrance (分区容错性)</li></ul><p>CAP定理(也称为Brewer定理)是分布式系统设计中的一个重要概念,它指出在一个分布式计算机系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个特性不可能同时完全满足,最多只能同时满足其中两个</p><p>CAP定理的核心在于,当网络分区发生时(即系统中的节点无法完全相互通信),系统必须在一致性和可用性之间做出选择:</p><ul><li>如果选择一致性,那么系统在面对网络分区时会牺牲可用性,即系统可能会选择拒绝请求或者返回错误,以确保数据的一致性。</li><li>如果选择可用性,那么系统在面对网络分区时会牺牲一致性,即系统可能会返回旧的或者不一致的数据,以确保系统能够继续响应请求。</li></ul><blockquote><p>Euraka 属于AP:保证系统可用性,但是当数据出现不一致时候(这是分区的必然结果,不同模块通信出现延迟),虽然两个节点注册信息不一致,为了保证每个节点依然对外可用,会出现两个查询结果不一样的情况,这样违背了一致性的原则</p><p>Zookeeper和consul属于CP:与AP相反</p></blockquote><h1 id="Consul"><a href="#Consul" class="headerlink" title="Consul"></a>Consul</h1><blockquote><p>为什么不再使用Euraka?</p><ul><li>不再更新</li><li>自我保护机制,不友好</li><li>服务注册中心不独立,不解耦,Eurata自身也是作为微服务模块</li><li>阿里巴巴Nacos与consul都拥有更强大的功能,还能完成分布式配置中心</li></ul></blockquote><p>官网:<a href="https://www.consul.io/">Consul</a></p><h2 id="什么是consul?"><a href="#什么是consul?" class="headerlink" title="什么是consul?"></a>什么是consul?</h2><p>Consul 是一套开源的分布式服务发现和配置管理系统,由 HashiCorp 公司用 Go 语言开发。</p><p>提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。它具有很多优点。包括: 基于 raft 协议,比较简洁; 支持健康检查, 同时支持 HTTP 和 DNS 协议 支持跨数据中心的 WAN 集群 提供图形界面 跨平台,支持 Linux、Mac、Windows</p><h3 id="作用"><a href="#作用" class="headerlink" title="作用"></a>作用</h3><ul><li>服务发现</li><li>健康检测,支持多种方式HTTP,TCP</li><li>KV存储,Key/Value存储方式</li><li>多数据中心</li><li>可视化Web界面</li></ul><h2 id="Spring-Cloud-Consul"><a href="#Spring-Cloud-Consul" class="headerlink" title="Spring Cloud Consul"></a>Spring Cloud Consul</h2><ul><li>Spring Cloud Consul 是 Spring Cloud 项目的一部分,它为 Spring 应用程序提供了与 Consul 的集成。</li><li>它允许 Spring 应用程序轻松地利用 Consul 的服务发现和配置管理功能。</li><li>Spring Cloud Consul 提供了Spring应用程序中的抽象,使得服务注册、服务发现和配置管理更加简单和无缝。</li></ul><h2 id="下载与安装"><a href="#下载与安装" class="headerlink" title="下载与安装"></a>下载与安装</h2><p>官网下载AMD64版本</p><blockquote><p>consul –version 查看版本路径</p><p>consul agent -dev 启动 默认端口8500,<a href="http://localhost:8500/">http://localhost:8500/</a></p></blockquote><h2 id="服务注册"><a href="#服务注册" class="headerlink" title="服务注册"></a>服务注册</h2><h3 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-consul-discovery<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>配置:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs yml"><span class="hljs-comment">#注册微服务</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">cloud-payment-service</span><br><br><span class="hljs-comment"># 配置consul 将服务注册到consul中去</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">consul:</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">8500</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">service-name:</span> <span class="hljs-string">${spring.application.name}</span><br></code></pre></td></tr></table></figure><h3 id="EnableDiscoveryClient注解"><a href="#EnableDiscoveryClient注解" class="headerlink" title="@EnableDiscoveryClient注解"></a>@EnableDiscoveryClient注解</h3><p>注解于启动类</p><p>这个注解的作用是告诉Spring Cloud应用程序,它是一个可被发现的服务实例,并且它想要向服务发现服务器注册自己,以便其他服务可以使用它。<strong>这样做的好处是,其他服务可以通过服务发现机制来查找和调用这个服务,而不需要硬编码服务的具体地址</strong>。</p><blockquote><p>@EnableDiscoveryClient 注解在某些情况下是必要的,但大多数情况下,如果你使用的是Spring Cloud的最新版本并且已经正确添加了服务发现依赖,那么自动配置应该足以让你的服务被Consul发现和注册。</p></blockquote><h3 id="服务注册的效果"><a href="#服务注册的效果" class="headerlink" title="服务注册的效果"></a>服务注册的效果</h3><p>注册以前对于服务a的调用:<a href="http://localhost:8001/pay/get/1%EF%BC%8C%E6%B3%A8%E5%86%8C%E6%9C%8D%E5%8A%A1%E5%90%8D%E4%B8%BA">http://localhost:8001/pay/get/1,注册服务名为</a> cloud-payment-service</p><p>注册之后:<a href="http://cloud-payment-service/pay/get/1">http://cloud-payment-service/pay/get/1</a> </p><p>当服务的端口号变动之后无需考虑修改配置</p><p><strong>但是仅仅做出以上操作之后,按服务名字调用仍然出错</strong></p><p>原因是consul本身支持负载均衡,当通过服务名称调用的时候,默认名称后面拥有多个</p><ul><li><p>解决办法:@LoadBalance注解于RestTemplate之上</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RestTemplateConfig</span> {<br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-meta">@LoadBalanced</span><br> <span class="hljs-keyword">public</span> RestTemplate <span class="hljs-title function_">restTemplate</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RestTemplate</span>();<br> }<br>}<br></code></pre></td></tr></table></figure></li></ul><blockquote><p>@LoadBalanced 是Spring Cloud中的一个注解,用于修饰Spring RestTemplate bean,以启用客户端负载均衡功能。当你在Spring Cloud应用程序中使用RestTemplate来调用其他服务时,如果你在RestTemplate上标注了@LoadBalanced,那么Spring Cloud会自动为它提供一个负载均衡拦截器,<strong>这样RestTemplate在发起HTTP请求时就会使用服务名而不是具体的URL,并且会通过服务发现机制来选择一个服务实例进行调用</strong>。</p></blockquote><h2 id="分布式配置中心"><a href="#分布式配置中心" class="headerlink" title="分布式配置中心"></a>分布式配置中心</h2><ul><li><p>通过全局配置信息,直接注册进Consul服务器,从Consul获取</p></li><li><p>通过consul的KV存储以及bootstrap实现</p><blockquote><p>Consul提供了一个键-值(Key-Value,KV)存储系统,可以用来存储配置信息、动态配置、特征标志、协调数据等。Consul的KV存储是一种分布式的、高度可用的、一致性的存储系统,它可以在整个Consul集群中复制和同步数据。</p></blockquote></li></ul><h3 id="引入依赖-1"><a href="#引入依赖-1" class="headerlink" title="引入依赖"></a>引入依赖</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--consul分布配置中心--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-consul-config<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.cloud<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-cloud-starter-bootstrap<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><blockquote><p>spring-cloud-starter-bootstrap 是Spring Cloud的一个启动器依赖,它提供了Spring Cloud应用启动时的配置引导功能。这个依赖的主要作用是在Spring Cloud应用启动时,从配置服务器(如Spring Cloud Config Server)加载外部配置,并将这些配置应用到应用程序中。简单理解就是相对于application用户级别的配置,bootstrap是系统级的</p></blockquote><blockquote><p>Spring Cloud会创建一个“Bootstrap Context”,作为Spring应用的<code>Application Context</code>的父上下文。初始化的时候,<code>Bootstrap Context</code>负责从外部源加载配置属性并解析配置。这两个上下文共享一个从外部获取的<code>Environment</code>。</p><p><code>Bootstrap</code>属性有高优先级,默认情况下,它们不会被本地配置覆盖。 <code>Bootstrap context</code>和<code>Application Context</code>有着不同的约定,所以新增了一个<code>bootstrap.yml</code>文件,保证<code>Bootstrap Context</code>和<code>Application Context</code>配置的分离。</p><p> application.yml文件改为bootstrap.yml,这是很关键的或者两者共存</p></blockquote><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><ul><li>bootstrap.yml配置</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">application:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-string">cloud-payment-service</span><br> <span class="hljs-comment">####Spring Cloud Consul for Service Discovery</span><br> <span class="hljs-attr">cloud:</span><br> <span class="hljs-attr">consul:</span><br> <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">8500</span><br> <span class="hljs-attr">discovery:</span><br> <span class="hljs-attr">service-name:</span> <span class="hljs-string">${spring.application.name}</span><br> <span class="hljs-attr">config:</span><br> <span class="hljs-attr">profile-separator:</span> <span class="hljs-string">'-'</span> <span class="hljs-comment"># default value is ",",we update '-'</span><br> <span class="hljs-attr">format:</span> <span class="hljs-string">YAML</span><br> <span class="hljs-attr">watch:</span><br> <span class="hljs-attr">wait-time:</span> <span class="hljs-number">1</span> <span class="hljs-comment">#动态刷新时间:也就是服务器数据修改之后多久同步到各个模块,官方推荐以及默认是5s</span><br> <span class="hljs-comment"># 对应consul KV存储的信息 </span><br> <span class="hljs-comment">#config/cloud-payment-service/data</span><br> <span class="hljs-comment"># /cloud-payment-service-dev/data</span><br> <span class="hljs-comment"># /cloud-payment-service-prod/data</span><br></code></pre></td></tr></table></figure><blockquote><ul><li>profile-separator: 这个配置项定义了在配置文件名称中使用哪个字符来分隔配置文件的环境 profile。例如,如果你有一个名为 application-dev.yml 的配置文件,其中 dev 是环境 profile,那么默认情况下,分隔符是逗号(,)。通过将 profile-separator 设置为 ‘-‘,你可以使用连字符作为环境 profile 的分隔符,例如 application-dev.yml</li><li>format: 这个配置项指定了配置文件的格式。默认情况下,Spring Cloud Config 使用的是 properties 文件格式。通过将 format 设置为 YAML,你可以告诉 Spring Cloud Config 客户端使用 YAML 格式的配置文件。YAML(Yet Another Markup Language)是一种直观的数据序列化格式,被用于配置文件、数据交换等场景。</li></ul></blockquote><ul><li>application.yml配置:</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">profiles:</span><br> <span class="hljs-attr">active:</span> <span class="hljs-string">dev</span> <span class="hljs-comment"># 多环境配置加载内容dev/prod,不写就是默认default配置</span><br><span class="hljs-comment"># 配置使用的是consul库中哪个环境的配置,放在application之中用于随时更改</span><br><span class="hljs-comment">#consul不同环境目录:</span><br><span class="hljs-comment">#config/cloud-payment-service/data</span><br><span class="hljs-comment"># /cloud-payment-service-dev/data</span><br><span class="hljs-comment"># /cloud-payment-service-prod/data</span><br></code></pre></td></tr></table></figure><h3 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Value("${server.port}")</span><br><span class="hljs-keyword">private</span> String port;<br><br><span class="hljs-meta">@GetMapping("/pay/get/version")</span><br><span class="hljs-keyword">public</span> ResultData<String> <span class="hljs-title function_">getconsul</span><span class="hljs-params">(<span class="hljs-meta">@Value("${atguigu.info}")</span> String atguiguInfo)</span>{<br> <span class="hljs-comment">//获取当前环境目录下data文件内atguigu.info项的信息</span><br> <span class="hljs-keyword">return</span> ResultData.success(atguiguInfo+<span class="hljs-string">" port:"</span>+port);<br>}<br></code></pre></td></tr></table></figure><h3 id="数据配置持久化"><a href="#数据配置持久化" class="headerlink" title="数据配置持久化"></a>数据配置持久化</h3><p>因为consul每次都要重新启动且存储配置数据会丢失,因此通过bat脚本(也可以手动设置service)将consul设置为开机自启并且将数据保存在文件里</p>]]></content>
<categories>
<category>Cloud</category>
</categories>
<tags>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title>Java入门02</title>
<link href="/2024/03/13/Mysql%E5%8E%9F%E7%90%86/"/>
<url>/2024/03/13/Mysql%E5%8E%9F%E7%90%86/</url>
<content type="html"><![CDATA[<p>Mysql原理学习</p><span id="more"></span><h1 id="Mysql原理"><a href="#Mysql原理" class="headerlink" title="Mysql原理"></a>Mysql原理</h1><h2 id="Mysql架构图"><a href="#Mysql架构图" class="headerlink" title="Mysql架构图"></a>Mysql架构图</h2><ul><li><strong>连接层</strong></li></ul><p>最上层是一些客户端和连接服务,主要完成一些类似于连接处理,授权认证及其相关安全方案</p><p>在该层上引入了线程池的概念,为通过认证安全接入的客户端提供线程,同样在该层上可以实现基于SSL的安全连接</p><blockquote><p>SSL使用公钥和私钥对数据进行加密和解密,确保数据在传输中的安全性,没有私钥(私钥只有自己知道)无法加密</p></blockquote><p>服务器也会为安全接入的每个客户端验证它所具有的操作权限</p><ul><li><strong>服务层</strong></li></ul><p>第二层服务层,主要完成大部分核心服务功能,包括<strong>查询解析,分析,优化,缓存以及所有的内置函数</strong></p><p>所有的跨存储引擎的功能也在这一层实现,包括<strong>触发器,存储过程,视图</strong>等</p><ul><li><strong>引擎层</strong></li></ul><p>第三层引擎层,存储引擎真正负责了Mysql中的<strong>数据存储和提取</strong></p><p>服务器通过API和存储引擎进行通信,不同的存储引擎具有不同的功能,可以自行选取</p><ul><li><strong>存储层</strong></li></ul><p>第四层为数据存储层,主要是将数据存储在运行于该设备的文件系统之上,并完成与存储引擎的交互</p><h4 id="SQL语句执行流程"><a href="#SQL语句执行流程" class="headerlink" title="SQL语句执行流程"></a><strong>SQL语句执行流程</strong></h4><blockquote><p>客户端请求<br>—> 连接器(验证用户身份,给予权限)<br>—> 查询缓存(存在缓存则直接返回,不存在则执行后续操作) 默认关闭<br>—> 分析器(对SQL进行词法分析和语法分析操作)<br>—> 优化器(主要对执行的sql优化选择最优的执行方案方法)<br>—> 执行器(执行时会先看用户是否有执行权限,有才去使用这个引擎提供的接口)<br>—> 去引擎层获取数据返回(如果开启查询缓存则会缓存查询结果</p></blockquote><h2 id="存储引擎"><a href="#存储引擎" class="headerlink" title="存储引擎"></a>存储引擎</h2><blockquote><p>常见的存储引擎有 InnoDB、MyISAM、Memory、NDB。</p></blockquote><p>InnoDB 现在是 MySQL 默认的存储引擎,支持事务、行级锁定和外键</p><h3 id="InnoDB"><a href="#InnoDB" class="headerlink" title="InnoDB"></a>InnoDB</h3><p><strong>InnoDB 物理文件结构为:</strong></p><ul><li>.frm 文件:与表相关的元数据信息,包括表结构的定义信息等 ;</li><li>.ibd 文件(或 .ibdata 文件):这两种文件都是存放 InnoDB 数据的文件;</li></ul><blockquote><p>之所以有两种文件形式存放InnoDB 的数据,是因为 InnoDB 的数据存储方式能够通过配置来决定是使用共享表空间存放存储数据,还是用独享表空间存放存储数据。</p><p>独享表空间存储方式使用.ibd文件,并且每个表一个.ibd文件<br>共享表空间存储方式使用.ibdata文件,所有表共同使用一个.ibdata文件(或多个,可自己配置)</p></blockquote><h3 id="InnoDB-与-MyISAM-的比较"><a href="#InnoDB-与-MyISAM-的比较" class="headerlink" title="InnoDB 与 MyISAM 的比较"></a>InnoDB 与 MyISAM 的比较</h3><table><thead><tr><th>对比项</th><th>MyISAM</th><th>InnoDB</th></tr></thead><tbody><tr><td>主外键</td><td>不支持</td><td>支持</td></tr><tr><td>事务</td><td>不支持</td><td>支持</td></tr><tr><td>行表锁</td><td>表锁,即使操作一条记录也会锁住整个表,不适合高并发的操作</td><td>行锁,操作时只锁某一行,不对其它行有影响,适合高并发的操作</td></tr><tr><td>缓存</td><td>只缓存索引,不缓存真实数据 (<strong>非聚集索引</strong>)</td><td>不仅缓存索引还要缓存真实数据(<strong>聚集索引</strong>),对内存要求较高,而且内存大小对性能有决定性的影响</td></tr><tr><td>表空间</td><td>小</td><td>大</td></tr><tr><td>关注点</td><td>性能</td><td>事务</td></tr></tbody></table><ul><li><strong>InnoDB 支持事务</strong>,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;</li><li><strong>InnoDB 支持外键</strong>,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;</li><li><strong>InnoDB是聚簇索引,MyISAM 是非聚簇索引</strong>。聚簇索引的文件存放在主键索引的叶子节点上,因此 InnoDB 必须要有主键,通过主键索引效率很高。但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此,主键不应该过大,因为主键太大,其他索引也都会很大。而 MyISAM 是非聚集索引,数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。</li><li>InnoDB 不保存表的具体行数,执行select count(*) from table 时需要全表扫描。而 MyISAM 用一个变量保存了整个表的行数,执行上述语句时只需要读出该变量即可,速度很快;</li><li>InnoDB 最小的锁粒度是行锁,MyISAM 最小的锁粒度是表锁。一个更新语句会锁住整张表,导致其他查询和更新都会被阻塞,因此并发访问受限。这也是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;</li></ul><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><h3 id="CHAR-和-VARCHAR-的区别"><a href="#CHAR-和-VARCHAR-的区别" class="headerlink" title="CHAR 和 VARCHAR 的区别"></a>CHAR 和 VARCHAR 的区别</h3><p>char是固定长度,varchar长度可变:</p><p>char(n) 和 varchar(n) 中括号中 n 代表字符的个数,并不代表字节个数,比如 CHAR(30) 就可以存储 30 个字符。</p><p><strong>char 存储时,不管实际存储数据的长度,直接按 char 规定的长度分配存储空间;</strong><br><strong>varchar 存储时,会根据实际存储的数据分配最终的存储空间;</strong></p><h3 id="相同点:"><a href="#相同点:" class="headerlink" title="相同点:"></a>相同点:</h3><p>char(n),varchar(n)中的 n 都代表字符的个数<br>超过char,varchar最大长度 n 的限制后,字符串会被截断。</p><h3 id="不同点:"><a href="#不同点:" class="headerlink" title="不同点:"></a>不同点:</h3><p>char不论实际存储的字符数<strong>都会占用n个字符的空间</strong>;</p><p>varchar只会<strong>占用实际字符</strong>应该占用的字节空间+1(实际长度length,0<=length<255)或加2(length>255)。</p><p>因为varchar保存数据时除了要保存字符串之外还会加<strong>一个字节来记录长度</strong>(如果列声明长度大于255则使用两个字节来保存长度)。</p><p>能存储的<strong>最大空间限制不一样</strong>:char的存储上限为255字节;varchar可配置。</p><p>char在存储时会<strong>截断尾部的空格</strong>,而varchar不会。</p><p><strong>char适合存储很短的</strong>、一般固定长度的字符串。例如,char非常适合存储密码的MD5值,因为这是一个定长的值。对于非常短的列,char比varchar在存储空间上也更有效率。</p><h3 id="BLOB和TEXT区别"><a href="#BLOB和TEXT区别" class="headerlink" title="BLOB和TEXT区别"></a>BLOB和TEXT区别</h3><p>BLOB是一个二进制对象,可以容纳可变数量的数据。有四种类型的BLOB:TINYBLOB、BLOB、MEDIUMBLO和 LONGBLOB</p><p>TEXT是一个不区分大小写的BLOB。四种TEXT类型:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。</p><p><strong>BLOB 保存二进制数据,TEXT 保存字符数据。</strong></p><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><p>索引(Index)是帮助MySQL高效获取数据的数据结构,所以说索引的本质是:数据结构</p><p>索引的目的在于提高查询效率,可以类比图书的目录</p><p>数据本身之外,数据库还维护者一个满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引</p><h3 id="索引的优缺点"><a href="#索引的优缺点" class="headerlink" title="索引的优缺点"></a>索引的优缺点</h3><p>优点</p><ul><li>提高数据检索效率,<strong>降低数据库IO成本</strong>;</li><li>降低数据排序的成本,<strong>降低CPU的消耗</strong>;</li></ul><p>缺点</p><ul><li><p>索引也是一张表,保存了主键和索引字段,并指向实体表的记录,所以也需要<strong>占用内存</strong>。</p></li><li><p>虽然索引大大提高了查询速度,同时却会降低更新表的速度,如对表进行INSERT、UPDATE和DELETE。因为更新表时,MySQL不</p><p>仅要保存数据,还要<strong>保存一下索引文件每次更新添加了索引列的字段</strong>,都会调整因为更新所带来的键值变化后的索引信息。</p></li></ul><h3 id="索引分类"><a href="#索引分类" class="headerlink" title="索引分类"></a>索引分类</h3><h4 id="数据结构角度"><a href="#数据结构角度" class="headerlink" title="数据结构角度"></a>数据结构角度</h4><ul><li>B+ 树索引</li><li>Hash 索引</li><li>Full-Text 全文索引</li><li>R-Tree 索引</li></ul><h4 id="从物理存储角度"><a href="#从物理存储角度" class="headerlink" title="从物理存储角度"></a>从物理存储角度</h4><ul><li><p>聚集索引(clustered index)</p></li><li><p>非聚集索引(non-clustered index),也叫辅助索引(secondary index)</p><blockquote><p>聚集索引和非聚集索引都是B+树结构</p></blockquote></li></ul><h4 id="从逻辑角度"><a href="#从逻辑角度" class="headerlink" title="从逻辑角度"></a>从逻辑角度</h4><ul><li><p>主键索引:主键索引是一种特殊的<strong>唯一索引</strong>,不允许有空值 ;</p></li><li><p>普通索引(单列索引):每个索引只包含单个列,一个表可以有<strong>多个单列索引</strong>;</p></li><li><p>多列索引(复合索引、联合索引):复合索引指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才</p><p>会被使用。使用复合索引时遵循 “最左前缀原则</p></li><li><p>唯一索引 或 非唯一索引</p></li><li><p>空间索引:空间索引是<strong>对空间数据类型的字段建立的索引</strong>,MYSQL中的空间数据类型有4种,分别是GEOMETRY、POINT、LINESTRING、POLYGON。MYSQL使用SPATIAL关键字进行扩展,使得能够用于创建正规索引类型的语法创建空间索引。创建空间索引的列,必须将其声明为 NOT NULL,空间索引只能在存储引擎为MYISAM的表中创建</p></li></ul><h3 id="B-Tree-和-B-Tree-的区别"><a href="#B-Tree-和-B-Tree-的区别" class="headerlink" title="B-Tree 和 B+Tree 的区别"></a>B-Tree 和 B+Tree 的区别</h3><h4 id="B-Tree"><a href="#B-Tree" class="headerlink" title="B-Tree"></a>B-Tree</h4><p>B-Tree是为磁盘等外存储设备设计的一种平衡查找树。</p><p>系统从磁盘读取数据到内存时是以<strong>磁盘块</strong>(block)为基本单位的,位于同一个磁盘块中的数据会被<strong>一次性读取</strong>出来,而不是需要什么取什么。</p><p>InnoDB 存储引擎中有页(Page)的概念,<strong>页是其磁盘管理的最小单位</strong>。InnoDB 存储引擎中<strong>默认每个页的大小为16KB</strong>,可通过参数 innodb_page_size 将页的大小设置为 4K、8K、16K,在 MySQL 中可通过如下命令查看页的大小:show variables like ‘innodb_page_size’;</p><p>而系统一个磁盘块的存储空间往往没有这么大,因此 InnoDB <strong>每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小 16KB</strong>。<strong>InnoDB 在把磁盘数据读入到磁盘时会以页为基本单位</strong>,在查询数据时如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。</p><p><strong>特性</strong>:</p><ul><li>每个节点最多有m个孩子</li><li>除了根节点和叶子节点外,其它每个节点至少有Ceil(m/2)个孩子。</li><li>若根节点不是叶子节点,则至少有2个孩子</li><li>所有叶子节点都在同一层,且不包含其它关键字信息 每个非终端节点包含n个关键字信息(P0,P1,…Pn, k1,…kn)</li><li>关键字的个数n满足:ceil(m/2)-1 <= n <= m-1 ki(i=1,…n)为关键字,且关键字升序排序<br>Pi(i=1,…n)为指向子树根节点的指针。P(i-1)指向的子树的所有节点关键字均小于ki,但都大于k(i-1)</li></ul><blockquote><p>B-Tree 结构的缺点:</p><ul><li>不支持<strong>范围查询的快速查找,需要回到根节点重新遍历查找</strong>,需要从根节点进行多次遍历。</li><li>每个节点中不仅包含数据的key值,还有data值;每一个页的存储空间是有限的,<strong>如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小</strong>;会<strong>导致B-Tree的深度较大,增大查询时的磁盘I/O次数</strong>,进而影响查询效率</li></ul></blockquote><h4 id="B-Tree-1"><a href="#B-Tree-1" class="headerlink" title="B+Tree"></a>B+Tree</h4><p>B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,InnoDB 存储引擎就是用 B+Tree 实现其索引结构。</p><p>在B+Tree中:所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,<strong>降低 B+Tree 的高度</strong>。</p><p><strong>B+Tree相对于B-Tree有几点不同:</strong></p><ul><li>非叶子节点只存储键值信息;(增大存储量,降低树的深度来实现 I/O次数)</li><li>所有叶子节点形成了一个双向有序链表; (可以不回溯根节点,实现范围查询)</li><li>数据记录都存放在叶子节点中;(全表扫描,只需要遍历叶子节点,无需要遍历整棵 B+Tree)</li></ul>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>Mysql</tag>
</tags>
</entry>
<entry>
<title>Java查找</title>
<link href="/2024/03/10/Java%E6%9F%A5%E6%89%BE/"/>
<url>/2024/03/10/Java%E6%9F%A5%E6%89%BE/</url>
<content type="html"><![CDATA[<p>Java查找算法</p><span id="more"></span><h2 id="二分查找"><a href="#二分查找" class="headerlink" title="二分查找"></a>二分查找</h2><p><strong>从一个有序数组中找出目标值</strong></p><p>二分法的思想很简单,因为整个数组是有序的,数组默认是递增的。</p><p>首先选择数组中间的数字和需要查找的目标值比较</p><ul><li>如果相等最好,就可以直接返回答案了</li><li>如果不相等</li><li>如果中间的数字大于目标值,则中间数字向右的所有数字都大于目标值,全部排除</li><li>如果中间的数字小于目标值,则中间数字向左的所有数字都小于目标值,全部排除</li></ul><h3 id="时间复杂度"><a href="#时间复杂度" class="headerlink" title="时间复杂度"></a>时间复杂度</h3><p><strong>O(logn)</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">BinarySearch</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">//有序数组</span><br> <span class="hljs-type">int</span>[] array={<span class="hljs-number">26</span>, <span class="hljs-number">31</span>, <span class="hljs-number">41</span>, <span class="hljs-number">41</span>, <span class="hljs-number">58</span>, <span class="hljs-number">59</span>};<br> <span class="hljs-type">int</span> sum=<span class="hljs-number">41</span>;<br> <span class="hljs-type">int</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span>Search(array, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>, sum);<br> System.out.println(target);<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> <span class="hljs-title function_">Search</span><span class="hljs-params">(<span class="hljs-type">int</span>[] nums,<span class="hljs-type">int</span> l,<span class="hljs-type">int</span> h,<span class="hljs-type">int</span> sum)</span>{<br> <span class="hljs-type">int</span> <span class="hljs-variable">mid</span> <span class="hljs-operator">=</span> l+(h-l)/<span class="hljs-number">2</span>;<br> <span class="hljs-comment">//目标对比</span><br> <span class="hljs-keyword">if</span>(nums[mid]==sum){<br> <span class="hljs-keyword">return</span> mid;<br> }<br> <span class="hljs-comment">//未查找到</span><br> <span class="hljs-keyword">if</span>(mid==l&&nums[h]!=sum){<br> <span class="hljs-keyword">return</span> -<span class="hljs-number">1</span>;<br> }<br> <span class="hljs-comment">//二分查询</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> nums[mid]>sum?Search(nums, l, mid, sum):Search(nums, mid+<span class="hljs-number">1</span>, h, sum);<br><br> <span class="hljs-keyword">return</span> target;<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>Java排序</title>
<link href="/2024/03/10/Java%E6%8E%92%E5%BA%8F/"/>
<url>/2024/03/10/Java%E6%8E%92%E5%BA%8F/</url>
<content type="html"><![CDATA[<p>Java排序算法</p><span id="more"></span><h2 id="插入排序(Insertion-Sort)"><a href="#插入排序(Insertion-Sort)" class="headerlink" title="插入排序(Insertion Sort)"></a>插入排序(Insertion Sort)</h2><p>输入一个长度为n的未排序数组array</p><p>定义初始有序数组, array[0] 为第一个初始数组</p><p>遍历n遍array,每一轮将有序数组后面一位和有序数组内所有成员比较排序,使得有序数组扩大</p><p>直到有序数组大小等于初始数组</p><h3 id="时间复杂度"><a href="#时间复杂度" class="headerlink" title="时间复杂度"></a>时间复杂度</h3><p><strong>O(n^2)</strong></p><h3 id="空间复杂度"><a href="#空间复杂度" class="headerlink" title="空间复杂度"></a>空间复杂度</h3><p><strong>O(1)</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">insert</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array={<span class="hljs-number">31</span>, <span class="hljs-number">41</span>, <span class="hljs-number">59</span>, <span class="hljs-number">26</span>, <span class="hljs-number">41</span>, <span class="hljs-number">58</span>};<br> <span class="hljs-type">int</span> n=<span class="hljs-number">6</span>;<br> <span class="hljs-type">int</span>[] end= sort(array, n);<br> System.out.println(<span class="hljs-string">"结果:"</span>+Arrays.toString(end));<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span>[] sort(<span class="hljs-type">int</span>[] array,<span class="hljs-type">int</span> n){<br> <span class="hljs-type">int</span> x,y;<br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;i<n-<span class="hljs-number">1</span>;i++){<br> x=i;<span class="hljs-comment">//x代表有序数组末尾的那个数</span><br> y=i+<span class="hljs-number">1</span>;<span class="hljs-comment">//y代表需要纳入有序数组的数</span><br> <span class="hljs-keyword">while</span> (array[x]>array[y]) {<br> <span class="hljs-comment">//通过不断比对y和前面数的大小,不断地往前排,直到大于前面的数</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span>array[y];<br> array[y]=array[x];<br> array[x]=temp;<br> x--;y--;<br> <span class="hljs-keyword">if</span>(x<<span class="hljs-number">0</span>){<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> System.out.println(<span class="hljs-string">"第"</span>+(i+<span class="hljs-number">1</span>)+<span class="hljs-string">"轮:"</span>+Arrays.toString(array));<br> }<br> <span class="hljs-keyword">return</span> array;<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="归并排序(Merge-sort)"><a href="#归并排序(Merge-sort)" class="headerlink" title="归并排序(Merge sort)"></a>归并排序(Merge sort)</h2><p>归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序</p><h3 id="算法性能"><a href="#算法性能" class="headerlink" title="算法性能"></a>算法性能</h3><p>速度仅次于快速排序。</p><h3 id="时间复杂度-1"><a href="#时间复杂度-1" class="headerlink" title="时间复杂度"></a>时间复杂度</h3><p><strong>O(nlogn)</strong></p><p><strong>最好情况下是O(n)</strong></p><h3 id="空间复杂度-1"><a href="#空间复杂度-1" class="headerlink" title="空间复杂度"></a>空间复杂度</h3><p>**O(N)**,归并排序需要一个与原数组相同长度的数组做辅助来排序。</p><h3 id="稳定性"><a href="#稳定性" class="headerlink" title="稳定性"></a>稳定性</h3><p><strong>稳定</strong>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">merge</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br><br> <span class="hljs-type">int</span>[] array =<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">3</span>, <span class="hljs-number">41</span>, <span class="hljs-number">52</span>, <span class="hljs-number">26</span>, <span class="hljs-number">38</span>, <span class="hljs-number">57</span>, <span class="hljs-number">9</span>, <span class="hljs-number">49</span>};<br> <span class="hljs-type">int</span>[] end =Sort(array, <span class="hljs-number">0</span>, <span class="hljs-number">7</span>);<br> System.out.println(Arrays.toString(end));<br> <br> }<br> <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span>[] Sort(<span class="hljs-type">int</span>[] nums, <span class="hljs-type">int</span> l, <span class="hljs-type">int</span> h) {<br> <span class="hljs-comment">//不能分离之后返回长度唯一的有序数组</span><br> <span class="hljs-keyword">if</span>(l==h){<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[] {nums[l]};<br> }<br> <span class="hljs-comment">//分离当前数组为左和右两个有序数组</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">mid</span> <span class="hljs-operator">=</span>l+(h-l)/<span class="hljs-number">2</span>;<br> <span class="hljs-type">int</span>[] leftNums = Sort(nums, l, mid);<br> <span class="hljs-type">int</span>[] rightNums = Sort(nums, mid+<span class="hljs-number">1</span>, h);<br> <span class="hljs-comment">//合并当前数组为一个有序结果数组</span><br> <span class="hljs-type">int</span>[] newNums =<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[leftNums.length+rightNums.length];<br> <span class="hljs-comment">//比较左右有序数组的首个数,取小的放进结果数组</span><br> <span class="hljs-type">int</span> i=<span class="hljs-number">0</span>;<span class="hljs-type">int</span> j=<span class="hljs-number">0</span>;<span class="hljs-type">int</span> m=<span class="hljs-number">0</span>;<br> <span class="hljs-keyword">while</span> (i<leftNums.length&&j<rightNums.length) {<br> newNums[m++]=leftNums[i]<rightNums[j]?leftNums[i++]:rightNums[j++];<br> }<br> <span class="hljs-comment">//获取剩余有序数组</span><br> <span class="hljs-keyword">while</span> (i<leftNums.length) {<br> newNums[m++]=leftNums[i++];<br> }<br> <span class="hljs-keyword">while</span> (j<rightNums.length) {<br> newNums[m++]=rightNums[j++];<br> }<br> <span class="hljs-comment">//打印过程</span><br> System.out.println(Arrays.toString(newNums));<br> <span class="hljs-keyword">return</span> newNums;<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="堆排序(Heap-Sort)"><a href="#堆排序(Heap-Sort)" class="headerlink" title="堆排序(Heap Sort)"></a>堆排序(Heap Sort)</h2><p><strong>堆是具有下列性质的完全二叉树:每个节点的值都大于或等于其左右孩子节点的值,称为:大顶堆(大堆);或者每个节点的值都小于或等于其左右孩子节点的值,称为小顶堆(小堆)</strong></p><p>父节点,左右孩子节点关系</p><blockquote><p>parent = (child - 1) / 2 ;</p><p>leftchild = parent * 2 + 1 ;</p><p>rightchild = parent * 2 + 2 ;</p><p>rightchild = leftchild + 1;</p></blockquote><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><p>不断构建大顶堆或者小顶堆(取决于升序还是降序),每次构建之后将堆顶元素(最大或最小)置于数组末尾,随后堆其它元素排序</p><h3 id="时间复杂度-2"><a href="#时间复杂度-2" class="headerlink" title="时间复杂度"></a>时间复杂度</h3><p><strong>O(nlogn)</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-comment">//堆排序</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Heap</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array=<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">31</span>, <span class="hljs-number">41</span>, <span class="hljs-number">59</span>, <span class="hljs-number">26</span>, <span class="hljs-number">41</span>, <span class="hljs-number">58</span>};<br> heapsort(array);<br> System.out.println(Arrays.toString(array));<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">heapsort</span><span class="hljs-params">(<span class="hljs-type">int</span>[] nums)</span>{<br> <span class="hljs-comment">//建堆,最后一个非叶子节点为nums.length/2-1,是最后的一个堆,从此开始向上建堆</span><br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> i=nums.length/<span class="hljs-number">2</span>-<span class="hljs-number">1</span>;i>=<span class="hljs-number">0</span>;i--){<br> createHeap(nums, i, nums.length);<br> }<br><br> <span class="hljs-comment">//将最大的元素(堆顶元素)与数组末尾元素交换,此时尾部即是最大数,是有序数组,数组待排序长度减一</span><br> <span class="hljs-comment">//此时只有顶部的堆是无序的,只需将顶部排好序,如此循环</span><br> <span class="hljs-comment">//j是当前最大元素的索引</span><br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j=nums.length-<span class="hljs-number">1</span>;j><span class="hljs-number">0</span>;j--){<br> <span class="hljs-comment">//交换元素</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span>nums[j];<br> nums[j]=nums[<span class="hljs-number">0</span>];<br> nums[<span class="hljs-number">0</span>]=temp;<br><br> <span class="hljs-comment">//重新排序顶堆</span><br> adjustHeap(nums, <span class="hljs-number">0</span>, j);<br> }<br><br> }<br><br> <span class="hljs-comment">//创建大堆或者小堆,升序大堆</span><br> <span class="hljs-comment">//如果父节点的索引是i,那么:</span><br> <span class="hljs-comment">//左子节点的索引是2i+1。</span><br> <span class="hljs-comment">//右子节点的索引是2i+2。</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">createHeap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] array,<span class="hljs-type">int</span> i,<span class="hljs-type">int</span> length)</span>{<br><br> <span class="hljs-type">int</span> l=<span class="hljs-number">2</span>*i+<span class="hljs-number">1</span>;<span class="hljs-comment">//左子节点</span><br> <span class="hljs-type">int</span> r=<span class="hljs-number">2</span>*i+<span class="hljs-number">2</span>;<span class="hljs-comment">//右边子节点</span><br> <span class="hljs-keyword">if</span>(length-<span class="hljs-number">1</span>>=r&&array[r]>array[i]){<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> array[r];<br> array[r] =array[i];<br> array[i]=temp;<br> }<br> <span class="hljs-keyword">if</span>(array[l]>array[i]){<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span> array[l];<br> array[l] =array[i];<br> array[i]=temp;<br> }<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">adjustHeap</span><span class="hljs-params">(<span class="hljs-type">int</span>[] array,<span class="hljs-type">int</span> i,<span class="hljs-type">int</span> length)</span>{<br><br> <span class="hljs-comment">//j不断指向需要排序的堆的左子节点</span><br> <span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> j=<span class="hljs-number">2</span>*i+<span class="hljs-number">1</span>;j<length;j=<span class="hljs-number">2</span>*j+<span class="hljs-number">1</span>){<br> <span class="hljs-comment">//当右节点大于左节点,j指向更大的节点</span><br> <span class="hljs-keyword">if</span>(j+<span class="hljs-number">1</span><length&&array[j]<array[j+<span class="hljs-number">1</span>]){<br> j++;<br> }<br> <span class="hljs-keyword">if</span>(array[j]>array[i]){<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span>array[i];<br> array[i]=array[j];<br> array[j]=temp;<br> i=j;<br> }<span class="hljs-keyword">else</span>{<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="快速排序(Quick-Sort)"><a href="#快速排序(Quick-Sort)" class="headerlink" title="快速排序(Quick Sort)"></a>快速排序(Quick Sort)</h2><p>快速排序是一种采用分治法解决问题的一个典型应用,也是冒泡排序的一种改进。它的基本思想是,<strong>通过一轮排序将待排记录分割成独立的两部分,其中一部分均比另一部分小</strong>,则可分别对这两部分继续进行排序,已达到整个序列有序。排序的时间复杂度为 O(nlogn),相比于简单排序算法,运算效率大大提高</p><h3 id="算法步骤"><a href="#算法步骤" class="headerlink" title="算法步骤"></a>算法步骤</h3><ol><li>从序列中取出一个数作为中轴数;</li><li>将比这个数大的数放到它的右边,小于或等于他的数放到它的左边;</li><li>再对左右区间重复第二步,直到各区间只有一个数。</li></ol><h3 id="复杂度"><a href="#复杂度" class="headerlink" title="复杂度"></a>复杂度</h3><p>最坏情况 <strong>O(n^2)</strong></p><p>最好情况 <strong>O(nlogn)</strong></p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">quick</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-type">int</span>[] array=<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">27</span>, <span class="hljs-number">17</span>, <span class="hljs-number">3</span>, <span class="hljs-number">16</span>, <span class="hljs-number">13</span>, <span class="hljs-number">10</span>, <span class="hljs-number">1</span>, <span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">12</span>, <span class="hljs-number">4</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">0</span>};<br> quickSort(array,<span class="hljs-number">0</span>,array.length-<span class="hljs-number">1</span>);<br> System.out.println(Arrays.toString(array));<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">quickSort</span><span class="hljs-params">(<span class="hljs-type">int</span>[] nums,<span class="hljs-type">int</span> i,<span class="hljs-type">int</span> j)</span>{<br><span class="hljs-comment">//当只有一个数时,返回</span><br> <span class="hljs-keyword">if</span>(j==i){<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-comment">//l代表左节点,代表右节点</span><br> <span class="hljs-type">int</span> <span class="hljs-variable">l</span> <span class="hljs-operator">=</span>i;<br> <span class="hljs-type">int</span> r=j;<br> <span class="hljs-keyword">while</span> (l<r) {<br> <span class="hljs-comment">//r先出发,i换l</span><br> <span class="hljs-keyword">while</span> (l<r&&nums[r]>=nums[i]) {<br> r--;<br> }<br> <span class="hljs-keyword">while</span> (l<r&&nums[l]<=nums[i]) {<br> l++;<br> }<br> <span class="hljs-keyword">if</span>(l<r){<br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span>nums[r];<br> nums[r]=nums[l];<br> nums[l]=temp;<br> } <br> }<br><br> <span class="hljs-type">int</span> <span class="hljs-variable">temp</span> <span class="hljs-operator">=</span>nums[i];<br> nums[i]=nums[l];<br> nums[l]=temp;<br><br> <span class="hljs-keyword">if</span>(l==i){<br> quickSort(nums, l+<span class="hljs-number">1</span>, j);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-keyword">if</span>(l==j){<br> quickSort(nums, i, j-<span class="hljs-number">1</span>);<br> <span class="hljs-keyword">return</span>;<br> }<br><span class="hljs-comment">//分别排序左右两边数组</span><br> quickSort(nums, i, l-<span class="hljs-number">1</span>);<br> quickSort(nums, l+<span class="hljs-number">1</span>, j);<br><br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>Java入门02</title>
<link href="/2024/03/09/Java%E5%85%A5%E9%97%A802/"/>
<url>/2024/03/09/Java%E5%85%A5%E9%97%A802/</url>
<content type="html"><![CDATA[<p>Java入门02</p><span id="more"></span><h1 id="Java入门02"><a href="#Java入门02" class="headerlink" title="Java入门02"></a>Java入门02</h1><h2 id="控制流程"><a href="#控制流程" class="headerlink" title="控制流程"></a>控制流程</h2><h3 id="块作用域(block)"><a href="#块作用域(block)" class="headerlink" title="块作用域(block)"></a>块作用域(block)</h3><p>块是指由Java语句组成的语句块,用一对大括号括起来 ‘ { } ’ 块确定了变量的作用域,,块之间可以镶嵌</p><blockquote><p>Java中不允许嵌套块存在同名的变量,C++可以但是内层的会覆盖外层的,Java优化了这一做法</p></blockquote><h3 id="条件语句"><a href="#条件语句" class="headerlink" title="条件语句"></a>条件语句</h3><h4 id="if-(condition)-statement"><a href="#if-(condition)-statement" class="headerlink" title="if (condition){ statement }"></a>if (condition){ statement }</h4><p>statement就是一个块</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">if</span>(condition){<br> statement1;<br>}<span class="hljs-keyword">else</span>{<br> <span class="hljs-comment">//不成立的时候执行</span><br>statement2;<br>}<br></code></pre></td></tr></table></figure><h3 id="循环"><a href="#循环" class="headerlink" title="循环"></a>循环</h3><h4 id="while-condition-statement"><a href="#while-condition-statement" class="headerlink" title="while(condition) { statement }"></a>while(condition) { statement }</h4><p>当conditon成立的时候,statement一直执行</p><h4 id="do-statement-while-condition"><a href="#do-statement-while-condition" class="headerlink" title="do { statement } while(condition)"></a>do { statement } while(condition)</h4><p>先执行statement,再比对条件</p><h3 id="确定循环"><a href="#确定循环" class="headerlink" title="确定循环"></a>确定循环</h3><h4 id="for-int-i-0-i"><a href="#for-int-i-0-i" class="headerlink" title="for(int i=0;i<n;i++){ statement }"></a>for(int i=0;i<n;i++){ statement }</h4><p>循环statement n 次,由计数器i计数</p><h3 id="多重选择"><a href="#多重选择" class="headerlink" title="多重选择"></a>多重选择</h3><h4 id="switch(choice)-case-1:-break-case-2:-break-case-3:-break-default-break"><a href="#switch(choice)-case-1:-break-case-2:-break-case-3:-break-default-break" class="headerlink" title="switch(choice){ case 1:..break; case 2:..break; case 3:..break; default : ..break ; }"></a>switch(choice){ case 1:..break; case 2:..break; case 3:..break; default : ..break ; }</h4><h3 id="中断控制流语句"><a href="#中断控制流语句" class="headerlink" title="中断控制流语句"></a>中断控制流语句</h3><h4 id="break;"><a href="#break;" class="headerlink" title="break;"></a>break;</h4><p>退出当前循环语句</p><h4 id="break-label"><a href="#break-label" class="headerlink" title="break label;"></a>break label;</h4><p>用于处理嵌套的循环,label是任何一个标签,指示break退出标签的到哪个循环语句语句</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java">read_data:<br><span class="hljs-keyword">while</span>(condition){<br> .....<br> <span class="hljs-keyword">break</span> read_date;<br>}<br></code></pre></td></tr></table></figure><h2 id="大数"><a href="#大数" class="headerlink" title="大数"></a>大数</h2><p>java.math的两个类BigInterger和BigDecimal</p><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>数组储存相同类型的值</p><p>以整数类型为例</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span>[] a;<span class="hljs-comment">//定义,在栈中分配基础内存</span><br><span class="hljs-type">int</span>[] a = <span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[<span class="hljs-number">100</span>];<span class="hljs-comment">//初始化,在堆中分配相应内存</span><br></code></pre></td></tr></table></figure><blockquote><p>在Java中,当你只是定义一个数组变量而没有使用new关键字来初始化它时,你只是声明了一个数组引用,这个引用本身会占用一些内存来存储它的元数据,但是它不会指向任何实际的数组对象。这意味着在这种情况下,不会为数组元素分配内存。</p></blockquote><h4 id="访问数组元素"><a href="#访问数组元素" class="headerlink" title="访问数组元素"></a>访问数组元素</h4><h5 id="使用下标索引"><a href="#使用下标索引" class="headerlink" title="使用下标索引"></a>使用下标索引</h5><p>一个长度为n的数组,访问索引是0~99</p><p>当访问100时会触发”array index out of bounds“异常</p><h5 id="for-each循环"><a href="#for-each循环" class="headerlink" title="for each循环"></a>for each循环</h5><p>for( variable : collection ){ statement }</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">for</span>(<span class="hljs-type">int</span> element:a){<br> <span class="hljs-comment">//遍历数组每个元素而不是下标值</span><br>...<br>}<br></code></pre></td></tr></table></figure><blockquote><p>打印数组元素</p><p>Arrays.toString(a);</p></blockquote><h4 id="数组拷贝"><a href="#数组拷贝" class="headerlink" title="数组拷贝"></a>数组拷贝</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span>[] nums = <span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>};<br><span class="hljs-comment">//直接拷贝,两个数组引用指向同一个数组内容</span><br><span class="hljs-type">int</span>[] newnums =nums;<br><span class="hljs-comment">//length时新数组的大小,可指定</span><br><span class="hljs-type">int</span>[] copy = Arrays.copy(nums,length);<br></code></pre></td></tr></table></figure><h4 id="数组排序"><a href="#数组排序" class="headerlink" title="数组排序"></a>数组排序</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span>[] a =<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>};<br>Arrays.sort(a);<span class="hljs-comment">//使用快速排序的优化方法</span><br></code></pre></td></tr></table></figure><h4 id="随机数"><a href="#随机数" class="headerlink" title="随机数"></a>随机数</h4><p>Math.random(),返回一个0~1之间的随机浮点数</p><p>将所有(假设为n个)抽奖结果存储进nums数组</p><p>然后用 *<em>Math.random()<em>n</em></em> 模拟抽到的数组元素的下标</p><h3 id="Arrays-API"><a href="#Arrays-API" class="headerlink" title="Arrays API"></a>Arrays API</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//以整数数组举例</span><br><span class="hljs-type">int</span>[] nums=<span class="hljs-keyword">new</span> <span class="hljs-title class_">int</span>[]{<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>};<br><span class="hljs-comment">//转换为数组字符串</span><br><span class="hljs-type">String</span> <span class="hljs-variable">strnum</span> <span class="hljs-operator">=</span> Arrays.toString(nums);<br><span class="hljs-comment">//拷贝出新数组</span><br><span class="hljs-type">int</span>[] copynum =Arrays.copyOf(nums, <span class="hljs-number">10</span>);<br><span class="hljs-comment">//指定从哪个位置开始拷贝,以及终止位置</span><br><span class="hljs-type">int</span>[] copynum2 =Arrays.copyOfRange(copynum, <span class="hljs-number">0</span>, <span class="hljs-number">2</span>);<br><span class="hljs-comment">//优化的快速排序</span><br>Arrays.sort(nums);<br><span class="hljs-type">int</span> <span class="hljs-variable">a</span> <span class="hljs-operator">=</span><span class="hljs-number">0</span>;<br><span class="hljs-comment">//二分查找a的值的下标,也可指定在哪个部分查找</span><br><span class="hljs-type">int</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> Arrays.binarySearch(nums, a);<br><span class="hljs-comment">//使得数组元素都变为a</span><br>Arrays.fill(nums, a);<br><span class="hljs-comment">//比较两个数组是否相等</span><br><span class="hljs-type">Boolean</span> <span class="hljs-variable">bool</span> <span class="hljs-operator">=</span> Arrays.equals(nums, copynum);<br></code></pre></td></tr></table></figure><h4 id="多维数组与不规则数组"><a href="#多维数组与不规则数组" class="headerlink" title="多维数组与不规则数组"></a>多维数组与不规则数组</h4>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>Java入门01</title>
<link href="/2024/03/08/Java%E5%85%A5%E9%97%A801/"/>
<url>/2024/03/08/Java%E5%85%A5%E9%97%A801/</url>
<content type="html"><![CDATA[<p>Java入门01</p><span id="more"></span><h1 id="Java入门01"><a href="#Java入门01" class="headerlink" title="Java入门01"></a>Java入门01</h1><h2 id="Java开发环境"><a href="#Java开发环境" class="headerlink" title="Java开发环境"></a>Java开发环境</h2><p>JDK:java开发工具包,<strong>实际上现在的JDK包含了JRE</strong></p><p>JRE:java程序运行软件环境,包含java虚拟机等</p><p>JavaSE(标准版):用于桌面或者简单服务器的Java平台</p><p>JavaEE(企业版):用于复杂服务器的Java平台</p><p>配置Java开发环境</p><ul><li><p>下载适合版本的JDK</p></li><li><p>设置环境变量</p></li><li><h3 id="代码生成:"><a href="#代码生成:" class="headerlink" title="代码生成:"></a>代码生成:</h3><ul><li><strong>C/C++程序:</strong> C/C++程序的源代码需要先经过编译器生成机器码(二进制代码),这是与特定硬件架构相关的。生成的可执行文件可以直接在目标计算机上运行。</li><li><strong>Java程序:</strong> Java程序先被编译成字节码(bytecode),而不是直接生成机器码。字节码是一种中间代码,它不针对特定硬件,而是针对Java虚拟机(JVM)的。这个字节码文件通常以<code>.class</code>为扩展名。</li></ul></li><li><h3 id="打包与运行:"><a href="#打包与运行:" class="headerlink" title="打包与运行:"></a>打包与运行:</h3><ul><li><strong>C/C++程序:</strong> 编译后的程序通常是一个可执行文件,可以在操作系统上直接运行。对于不同的操作系统,需要编译生成相应平台的可执行文件。</li><li><strong>Java程序:</strong> Java程序的字节码并不直接在计算机上运行,而是由Java虚拟机(JVM)解释执行。字节码文件可以被打包成Java归档文件(JAR),其中包含了所有需要的类文件和资源。JVM在运行时将字节码转换为机器码,以实现跨平台的执行。</li></ul></li><li><h3 id="运行环境:"><a href="#运行环境:" class="headerlink" title="运行环境:"></a>运行环境:</h3><ul><li><strong>C/C++程序:</strong> 编译后的程序是针对特定操作系统和硬件平台的。因此,同一个C/C++程序需要重新编译才能在不同的平台上运行。</li><li><strong>Java程序:</strong> Java程序通过JVM运行,而JVM是为不同平台设计的。一次编译生成的字节码可以在任何支持Java虚拟机的平台上运行,实现了跨平台性</li></ul></li></ul><h2 id="第一个程序"><a href="#第一个程序" class="headerlink" title="第一个程序"></a>第一个程序</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span><span class="hljs-comment">/*修饰符*/</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">hello</span><span class="hljs-comment">/*公共类名。必须与文件名相同*/</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">//当选择运行哪个文件之后,虚拟机会选择从这个类文件的main函数作为程序的出入口,所有类有且只能有一个main函数</span><br> System.out.println(<span class="hljs-string">"helloworld!"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><p>命令行运行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">javac hello.java<span class="hljs-comment">//产生hello.class</span><br>java hello<br><span class="hljs-comment">//java程序将hello.java编译成hello.class,然后启动虚拟机运行类文件中的字节码</span><br>helloworld!<br></code></pre></td></tr></table></figure><p>集成开发环境</p><p>选择适合的编译器进行编译,例如vscode</p><h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>8种<strong>基本数据类型</strong></p><p>4种整型</p><p>2种浮点类型</p><p>1种字符类型char(Unicode表示)</p><h3 id="整数"><a href="#整数" class="headerlink" title="整数"></a>整数</h3><table><thead><tr><th>类型</th><th>存储需求</th><th>取值范围</th></tr></thead><tbody><tr><td>int</td><td>4字节</td><td>-20亿~20亿</td></tr><tr><td>short</td><td>2字节</td><td>-32768~32767</td></tr><tr><td>long</td><td>8字节</td><td>-92….</td></tr><tr><td>byte</td><td>1字节</td><td>-128~127</td></tr></tbody></table><blockquote><p>C++/C语言的存储需求随不同的处理器平台不同而变化,例如int在32位占2字节,long4字节,64位则和java一样</p><p>以及java不同于C++,不存在无符号(unsigned)的整数</p></blockquote><h3 id="浮点类型"><a href="#浮点类型" class="headerlink" title="浮点类型"></a>浮点类型</h3><table><thead><tr><th>类型</th><th>存储需求</th><th>取值范围</th></tr></thead><tbody><tr><td>float</td><td>4字节</td><td>±3.40282347E+38F(6-7位有效位数)</td></tr><tr><td>double</td><td>8字节</td><td>±1.79769313486231570E+308(15位有效位数,双精度)</td></tr></tbody></table><blockquote><p>三个特殊的浮点值:</p><ul><li>正无穷,例如正整数除以0</li><li>负无穷</li><li>NaN(不是数字),0/0就是NaN</li></ul></blockquote><h3 id="char类型"><a href="#char类型" class="headerlink" title="char类型"></a>char类型</h3><p>用于表示一个Unicode字符,但是随着Unicode字符变的繁多,有些需要两个char值</p><p>用 ‘A’ 表示一个char字符A,“A”表示一个字符串A </p><p>char类型值可以表示为十六进制值,从 \u0000 ~ \uFFFF ,\u为转义序列</p><p>转义序列还包括</p><table><thead><tr><th>转义序列</th><th>名称</th><th>Unicode值</th></tr></thead><tbody><tr><td>\b</td><td>退格</td><td>\u0008</td></tr><tr><td>\t</td><td>制表</td><td>\u0009</td></tr><tr><td>\n</td><td>换行</td><td>\u000a</td></tr><tr><td>\r</td><td>回车</td><td>\u000d</td></tr><tr><td>\*</td><td>双引号</td><td>\u0022</td></tr><tr><td>\‘</td><td>单引号</td><td>\u0027</td></tr><tr><td>\</td><td>反斜杠</td><td>\u005c</td></tr></tbody></table><blockquote><p>这些转义序列可以放在字符常量和字符串之中 例如 ‘\u2122’ 和 “hello\n”</p><p>但只有\u能放在其之外,例如main(String\u005B\u005D args)= main(String[] args)</p></blockquote><h3 id="boolean类型"><a href="#boolean类型" class="headerlink" title="boolean类型"></a>boolean类型</h3><p>true和false</p><h2 id="变量和常量"><a href="#变量和常量" class="headerlink" title="变量和常量"></a>变量和常量</h2><p>Java中用变量来存储值</p><ul><li>声明变量</li></ul><p>声明变量时候必须指定变量的类型</p><blockquote><p>int a;</p></blockquote><p>变量名不能是java保留字以及一些特殊符号开头</p><ul><li>变量初始化</li></ul><p>声明变量后要用赋值语句对变量进行显示初始化</p><blockquote><p>Java10之后如果可以从变量值推断变量类型,则类型可以用var代替</p><p>var a = 10;</p></blockquote><ul><li>变量定义?</li></ul><p>Java中声明变量包含了定义</p><blockquote><p>C/C++中分离式链接编译要求编译器要求编译器能够在不查看完整实现的情况下理解代码的不同部分</p><p>声明:不分配内存,只告知存在,定义:分配内存</p><p>Java中不区分声明和定义的原因是因为Java采用的是一种不同的编译和运行模型。Java源代码被编译成字节码,而不是机器码</p><p>字节码是一种中间表示,它可以在任何安装了Java虚拟机(JVM)的平台上运行。Java的类加载机制和链接过程是在运行时进行的,而不是在编译时。因此,Java中的“声明”通常也包含了“定义”,因为类和方法的完整实现必须在类文件中,以便JVM能够加载和执行。</p></blockquote><ul><li>常量</li></ul><p>final指示常量,常量名大写</p><h2 id="枚举类型"><a href="#枚举类型" class="headerlink" title="枚举类型"></a>枚举类型</h2><p>枚举类型包含有限个命名的值</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">enum</span> <span class="hljs-title class_">Size</span>{SMALL,MEDIUM,LARGER,EXTRA,LARGE}<br><span class="hljs-comment">//声明枚举类型变量</span><br><span class="hljs-type">SIze</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span>Size.MEDIUM<br></code></pre></td></tr></table></figure><h2 id="运算符"><a href="#运算符" class="headerlink" title="运算符"></a>运算符</h2><p>用于连接值 +,- ,*,/</p><blockquote><p>Strictfp将所有值采用严格浮点计算</p></blockquote><h3 id="数学函数"><a href="#数学函数" class="headerlink" title="数学函数"></a>数学函数</h3><p>Math类中提供了很多数学函数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//平方根</span><br>Math.sqrt(x)<br><span class="hljs-comment">//幂运算,x的a次方</span><br>Math.pow(x,a) <br><span class="hljs-comment">//floodMod,取余数</span><br>floodMod(x,<span class="hljs-number">12</span>)<br><span class="hljs-comment">//三角函数,反函数以及Π和e常量等</span><br></code></pre></td></tr></table></figure><p>静态导入Math类可以不用写Math,直接用函数</p><h3 id="数值类型之间的转换"><a href="#数值类型之间的转换" class="headerlink" title="数值类型之间的转换"></a>数值类型之间的转换</h3><ul><li>运算时转换</li></ul><p>若两个运算的类型不同,优先自动转换为精度更高的那一方</p><ul><li>强制类型转换</li></ul><p>强制类型转换可以将精度高的类型强制转换为低的,但会损失信息</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">double</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span> <span class="hljs-number">9.997</span>;<br><span class="hljs-type">int</span> <span class="hljs-variable">nx</span> <span class="hljs-operator">=</span> (<span class="hljs-type">int</span>) x;<br></code></pre></td></tr></table></figure><blockquote><p>不要超出转换后类型的表示范围</p></blockquote><h3 id="结合赋值和运算符"><a href="#结合赋值和运算符" class="headerlink" title="结合赋值和运算符"></a>结合赋值和运算符</h3><p>也称二元运算符</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">x+=<span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><ul><li>自增与自减运算符</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java">n++;<span class="hljs-comment">//返回n</span><br>n--;<br>++n;<span class="hljs-comment">//返回n+1</span><br>--n;<br></code></pre></td></tr></table></figure><ul><li>boolean运算符</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//检测是否相等</span><br>x==<span class="hljs-number">7</span><br><span class="hljs-comment">//检测是否不相等</span><br>x!=<span class="hljs-number">7</span><br><span class="hljs-comment">//与运算符</span><br>&&<br><span class="hljs-comment">//或运算符</span><br>||<br><span class="hljs-comment">//三元运算符 </span><br> condition?expression1 : expression2<span class="hljs-comment">//true执行1.false执行2</span><br></code></pre></td></tr></table></figure><ul><li>位运算符</li></ul><p>对整数的各个位完成操作</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java">&(”and“),|(”or“),^(”xor“),~(”not“)<br><br><< ,>>左右移<br></code></pre></td></tr></table></figure><p>运算符级别</p><p>同级别基本都是从左至右,左结合</p><p>?:和=,+=,*=,等是右结合</p><h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><p>Unicode字符序列,用双引号括起来</p><h3 id="子串"><a href="#子串" class="headerlink" title="子串"></a>子串</h3><p>**substring(a,b)**方法,字符串从a到b位的子串</p><h3 id="拼接"><a href="#拼接" class="headerlink" title="拼接"></a>拼接</h3><p>使用” + “号连接两个字符串</p><p>join方法:将多个字符串连接在一起并用一个固定的分隔符连接</p><p><strong>repeat</strong>(n)方法:某个字符串重复n次</p><h3 id="不可变字符串"><a href="#不可变字符串" class="headerlink" title="不可变字符串"></a>不可变字符串</h3><p>String没有提供直接修改某个字符的说法,因此是不可变的</p><blockquote><p>虽然String底层使用了char[]数组实现但是封装更类似于char*指针</p></blockquote><h3 id="字符串是否相等"><a href="#字符串是否相等" class="headerlink" title="字符串是否相等"></a>字符串是否相等</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">s.equals(t)<br></code></pre></td></tr></table></figure><blockquote><p> C++的String类重载了==运算符可以检测字符的相等性,但是java没有</p></blockquote><h3 id="空串与NULL串"><a href="#空串与NULL串" class="headerlink" title="空串与NULL串"></a>空串与NULL串</h3><p>空串是一个Java对象,只是长度为0,内容为空</p><h3 id="码点与代码单元"><a href="#码点与代码单元" class="headerlink" title="码点与代码单元"></a>码点与代码单元</h3><p>char数据类型是一个采用UTF-16编码表示的Unicode码点的代码单元</p><p>调用s.charAt(n)返回位置n的代码单元,0<n<s.length-1</p><h3 id="StringAPI"><a href="#StringAPI" class="headerlink" title="StringAPI"></a>StringAPI</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//字符串是否为空,是否为空格,不接受null</span><br><span class="hljs-type">boolean</span> <span class="hljs-title function_">empty</span><span class="hljs-params">()</span><br><span class="hljs-type">boolean</span> <span class="hljs-title function_">blank</span><span class="hljs-params">()</span><br><span class="hljs-comment">//是否相等</span><br><span class="hljs-type">boolean</span> <span class="hljs-title function_">equals</span><span class="hljs-params">(Object other)</span><br><span class="hljs-comment">//是否以prefix开头/结尾</span><br><span class="hljs-type">boolean</span> <span class="hljs-title function_">startWith</span><span class="hljs-params">(String prefix)</span><br><span class="hljs-type">boolean</span> <span class="hljs-title function_">endWith</span><span class="hljs-params">(String prefix)</span><br><span class="hljs-comment">//返回与字符串str或者码点cp匹配的第一个子串开始的位置,从索引0或者fromIndex开始匹配,如果不存在,返回-1</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">indexOf</span><span class="hljs-params">(String str/<span class="hljs-type">int</span> cp[,<span class="hljs-type">int</span> fromIndex])</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">lastIndexOf</span><span class="hljs-params">(String str/<span class="hljs-type">int</span> cp[,<span class="hljs-type">int</span> fromIndex])</span><span class="hljs-comment">//最后一个子串位置</span><br><span class="hljs-comment">//返回字符串长度</span><br><span class="hljs-type">int</span> <span class="hljs-title function_">length</span><span class="hljs-params">()</span><br><span class="hljs-comment">//返回一个新字符串,这个字符串的所有oldString都被newString,CharSequence可以用String或者StringBuilder替代</span><br>String <span class="hljs-title function_">replace</span><span class="hljs-params">(CharSequence oldString,CharSequence newString)</span><br><span class="hljs-comment">//将左右小写字母改为大写或者大写改为小写</span><br>String <span class="hljs-title function_">toLowerCase</span><span class="hljs-params">()</span><br>String <span class="hljs-title function_">toUpperCase</span><span class="hljs-params">()</span> <br><span class="hljs-comment">//返回一个新字符串,这个字符串将删除头部尾部小于等于U+0020的字符(trim)或者空格(strip)</span><br>String <span class="hljs-title function_">trim</span><span class="hljs-params">()</span><br>String <span class="hljs-title function_">strip</span><span class="hljs-params">()</span><br><span class="hljs-comment">//连接不同字符串,以 delimiter 分隔</span><br>String <span class="hljs-title function_">join</span><span class="hljs-params">(CharSequence delimiter,CharSequence elements,....)</span><br><span class="hljs-comment">//使字符串重复并拼接</span><br>String <span class="hljs-title function_">repeat</span><span class="hljs-params">(<span class="hljs-type">int</span> count)</span><br><span class="hljs-comment">//匹配第一个a正则表达式所符合的内容,并替换为b</span><br>String <span class="hljs-title function_">replaceFirst</span><span class="hljs-params">(String a,String b)</span><br></code></pre></td></tr></table></figure><blockquote><p>Stringbuilder:字符串构建器</p></blockquote><h2 id="输入与输出"><a href="#输入与输出" class="headerlink" title="输入与输出"></a>输入与输出</h2><h3 id="读取输入"><a href="#读取输入" class="headerlink" title="读取输入"></a>读取输入</h3><p>构造一个“<strong>标准输入流</strong>”Sysytem.in关联的Scanner,读取控制台输入,同理其它输入流也可以</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Scanner</span> <span class="hljs-variable">in</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">Scanner</span>(System.in<span class="hljs-comment">/*InputStream in*/</span>);<br><span class="hljs-comment">//读取一行的输入</span><br><span class="hljs-type">String</span> <span class="hljs-variable">input</span> <span class="hljs-operator">=</span> in.nextLine();<br><span class="hljs-comment">//读取下一个字符的输入,用空格分开</span><br><span class="hljs-type">String</span> <span class="hljs-variable">input</span> <span class="hljs-operator">=</span> in.next();<br><span class="hljs-comment">//读取下一个整数</span><br><span class="hljs-type">String</span> <span class="hljs-variable">input</span> <span class="hljs-operator">=</span> in.nextInt();<br></code></pre></td></tr></table></figure><blockquote><p>console可以用于读密码,更安全</p></blockquote><h3 id="格式化输出"><a href="#格式化输出" class="headerlink" title="格式化输出"></a>格式化输出</h3><p>命令行输出:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs java">System.out.println(<span class="hljs-string">"hello"</span>);<br><span class="hljs-comment">//输出部分有小数,尽可能以最大非零数输出</span><br><span class="hljs-type">double</span> <span class="hljs-variable">x</span> <span class="hljs-operator">=</span><span class="hljs-number">10000</span>/<span class="hljs-number">3.0</span>;<br>System.out.println(x);<span class="hljs-comment">//输出3333.3333333333335</span><br><span class="hljs-comment">//Java5引入C语言的printf,规范小数输出格式</span><br>System.out.printf(<span class="hljs-string">"%8.2f"</span>,x);<span class="hljs-comment">//3333.33</span><br><span class="hljs-comment">//printf还能包含多个参数。一般由 %+转换符 构成</span><br>System.out.printf(<span class="hljs-string">"hello %s you are %d years old"</span>,<span class="hljs-string">"Jane"</span>,<span class="hljs-number">15</span>);<br><span class="hljs-comment">//也可以用这样的方法拼接一个字符串而不输出</span><br><span class="hljs-type">String</span> <span class="hljs-variable">message</span> <span class="hljs-operator">=</span> String.format(<span class="hljs-string">"hello %s you are %d years old"</span>,<span class="hljs-string">"Jane"</span>,<span class="hljs-number">15</span>);<br></code></pre></td></tr></table></figure><h3 id="文件输入与输出"><a href="#文件输入与输出" class="headerlink" title="文件输入与输出"></a>文件输入与输出</h3><p>创建一个文件的Scanner对象和PrintWriter对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//a.txt是相对路径,一般是java虚拟机启动目录相对路径,也就是相对工作目录的相对路径</span><br><span class="hljs-comment">//System.getProperty("user.dir")可以获取工作目录</span><br><span class="hljs-type">Scanner</span> <span class="hljs-variable">in</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Scanner</span>(Path.of(<span class="hljs-string">"a.txt"</span>),StandardCharsets.UTF_8);<br><span class="hljs-type">PrintWriter</span> <span class="hljs-variable">out</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">PrintWriter</span>(<span class="hljs-string">"a.txt"</span>,StandardCharsets.UTF_8);<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>Java入门02</title>
<link href="/2024/03/04/Redis%E4%BD%BF%E7%94%A8/"/>
<url>/2024/03/04/Redis%E4%BD%BF%E7%94%A8/</url>
<content type="html"><![CDATA[<p>Redis配置与使用</p><span id="more"></span><h1 id="Redis的使用"><a href="#Redis的使用" class="headerlink" title="Redis的使用"></a>Redis的使用</h1><h2 id="在Java中的简单使用与配置"><a href="#在Java中的简单使用与配置" class="headerlink" title="在Java中的简单使用与配置"></a>在Java中的简单使用与配置</h2><ul><li><p>引入依赖</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>redis.clients<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jedis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.7.0<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure></li><li><p>连接使用</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//建立连接</span><br><span class="hljs-keyword">private</span> Jedis jedis;<br>setUp() {<br><span class="hljs-comment">//建立连接</span><br>jedis = <span class="hljs-keyword">new</span> <span class="hljs-title class_">Jedis</span>(<span class="hljs-string">"192.168.150.101"</span>,<span class="hljs-number">6379</span>);<br> <span class="hljs-comment">//设置密码</span><br>jedis.auth(<span class="hljs-string">"123456"</span>);<br> <span class="hljs-comment">//选择库</span><br>jedis.select(<span class="hljs-number">0</span>);<br>}<br><span class="hljs-comment">//插入获取String</span><br>testString() {<br><span class="hljs-comment">//插入数据,方法名称就是redis命令名称,非常简单</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> jedis.set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"张三"</span>); <br> System.out.println(<span class="hljs-string">"result = "</span> + result);<br> <span class="hljs-comment">//获取数据</span><br><span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> jedis.get(<span class="hljs-string">"name"</span>) ;<br>System.out.println(<span class="hljs-string">"name = "</span> + name) ;<br>}<br><span class="hljs-comment">//释放资源</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">tearDown</span><span class="hljs-params">()</span> {<br><span class="hljs-comment">//释放资源</span><br><span class="hljs-keyword">if</span> (jedis != <span class="hljs-literal">null</span>) {<br>jedis.close();<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>连接池</p><p>Jedis本身是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此我们推荐大家使用Jedis连接池代替Jedis的直连方式</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JedisConnectionFactory</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> JedisPool jedisPool;<br> <br> <span class="hljs-keyword">static</span> {<br> <span class="hljs-comment">//配置连接池</span><br> <span class="hljs-type">JedisPoolConfig</span> <span class="hljs-variable">poolConfig</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPoolConfig</span>();<br> poolConfig.setMaxTotal(<span class="hljs-number">8</span>); <span class="hljs-comment">//最大连接数</span><br> poolConfig.setMaxIdle(<span class="hljs-number">8</span>); <span class="hljs-comment">//最大空闲连接</span><br> poolConfig.setMinIdle(<span class="hljs-number">0</span>); <span class="hljs-comment">//最小空闲连接</span><br> poolConfig.setMaxWaitMillis(<span class="hljs-number">1000</span>); <span class="hljs-comment">//没有连接可用的等待时长,默认-1,持续等待</span><br> <span class="hljs-comment">//创建连接池对象</span><br> jedisPool = <span class="hljs-keyword">new</span> <span class="hljs-title class_">JedisPool</span>(poolConfig, <span class="hljs-string">"192.168.72.128"</span>, <span class="hljs-number">6379</span>, <span class="hljs-number">1000</span>, <span class="hljs-string">"123456"</span>);<br> }<br> <br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Jedis <span class="hljs-title function_">getJedis</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> jedisPool.getResource();<br> }<br>}<br></code></pre></td></tr></table></figure></li></ul><h2 id="SpringDataRedis"><a href="#SpringDataRedis" class="headerlink" title="SpringDataRedis"></a>SpringDataRedis</h2><p>SpringData是Spring中数据操作的模块,包含对各种数据库的集成,其中对Redis的集成模块就叫做SpringDataRedis</p><p>提供了对不同Redis客户端的整合(Lettuce和Jedis)</p><p>提供了RedisTemplate统一API来操作Redis</p><p>支持Redis的发布订阅模型</p><p>支持Redis哨兵和Redis集群</p><p>支持基于Lettuce的响应式编程</p><p>支持基于JDK、JSON、字符串、Spring对象的数据序列化及反序列化</p><p>支持基于Redis的JDKCollection实现</p><h2 id="基于SpringBoot的使用"><a href="#基于SpringBoot的使用" class="headerlink" title="基于SpringBoot的使用"></a>基于SpringBoot的使用</h2><h2 id="引入依赖"><a href="#引入依赖" class="headerlink" title="引入依赖"></a>引入依赖</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!-- Redis依赖 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-data-redis<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-comment"><!-- 连接池依赖 --></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.commons<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>commons-pool2<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><p>由于序列化方式依靠的是fastjson序列化框架,因此也要有fastjson依赖,如果没有使用自定义序列化,则这个不是必须的,另外如果基于的是其它第三方序列化框架,则添加对应的依赖即可:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs XML"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>fastjson<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.2.33<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h2 id="配置文件"><a href="#配置文件" class="headerlink" title="配置文件"></a>配置文件</h2><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">redis:</span><br> <span class="hljs-attr">host:</span> <span class="hljs-number">192.168</span><span class="hljs-number">.72</span><span class="hljs-number">.128</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">6379</span><br> <span class="hljs-attr">password:</span> <span class="hljs-number">123456</span><br> <span class="hljs-attr">lettuce:</span><br> <span class="hljs-attr">pool:</span><br> <span class="hljs-attr">max-active:</span> <span class="hljs-number">8</span> <span class="hljs-comment"># 最大连接</span><br> <span class="hljs-attr">max-idle:</span> <span class="hljs-number">8</span> <span class="hljs-comment"># 最大空闲连接</span><br> <span class="hljs-attr">min-idle:</span> <span class="hljs-number">0</span> <span class="hljs-comment"># 最小空闲连接</span><br> <span class="hljs-attr">max-wait:</span> <span class="hljs-number">100</span> <span class="hljs-comment"># 连接等待时间</span><br></code></pre></td></tr></table></figure><h2 id="注入RedisTemplate"><a href="#注入RedisTemplate" class="headerlink" title="注入RedisTemplate"></a>注入RedisTemplate</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Autowired</span><br><span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br></code></pre></td></tr></table></figure><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><table><thead><tr><th>API</th><th>返回值类型</th><th>说明</th></tr></thead><tbody><tr><td>redisTemplate.opsForValue()</td><td>ValueOperations</td><td>操作String数据类型</td></tr><tr><td>redisTemplate.opsForHash()</td><td>HashOperations</td><td>操作Hash类型数据</td></tr><tr><td>redisTemplate.opsForList()</td><td>ListOperations</td><td>操作List类型数据</td></tr><tr><td>redisTemplate.opsForSet()</td><td>SetOperations</td><td>操作Set类型数据</td></tr><tr><td>redisTemplate.opsForZSet()</td><td>ZSetOperations</td><td>操作SortedSet类型数据</td></tr><tr><td>redisTemplate</td><td></td><td>通用的命令</td></tr></tbody></table><h2 id="测试样例"><a href="#测试样例" class="headerlink" title="测试样例"></a>测试样例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisTest</span> {<br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br> <br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testString</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//插入一条String类型数据</span><br> redisTemplate.opsForValue().set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"李四"</span>);<br> <span class="hljs-comment">//读取一条String数据</span><br> <span class="hljs-type">Object</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> redisTemplate.opsForValue().get(<span class="hljs-string">"name"</span>);<br> System.out.println(<span class="hljs-string">"name = "</span> + name);<br> }<br> <br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSaveUser</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//写入数据</span><br> redisTemplate.opsForValue().set(<span class="hljs-string">"user:100"</span>, <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-string">"Wang"</span>, <span class="hljs-number">21</span>));<br> <span class="hljs-comment">//获取数据</span><br> <span class="hljs-type">User</span> <span class="hljs-variable">o</span> <span class="hljs-operator">=</span> (User) redisTemplate.opsForValue().get(<span class="hljs-string">"user:100"</span>);<br> System.out.println(<span class="hljs-string">"o = "</span> + o);<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><ul><li><p>查看存入Redis的内容出现乱码,说明是字节码文件</p></li><li><p>原因:当 Java 对象被序列化并存储到 Redis 时,它们通常被转换为字节数组(byte[])。这是因为 Redis 是一个键值对存储系统,它不直接支持复杂的对象数据结构,而是将所有数据作为字节序列存储。</p></li><li><p><strong>序列化过程通常由 RedisSerializer 实现来完成,如 Spring Data Redis 提供的 JdkSerializationRedisSerializer、Jackson2JsonRedisSerializer 等。这些序列化器将 Java 对象转换为适合存储在 Redis 中的格式</strong></p></li><li><p>缺点:可读性差且占用内存大</p></li><li><p>解决办法:自定义序列化和反序列化</p></li></ul><h2 id="自定义序列化和反序列化"><a href="#自定义序列化和反序列化" class="headerlink" title="自定义序列化和反序列化"></a>自定义序列化和反序列化</h2><h3 id="使用GenericJackson2JsonRedisSerializer序列化工具"><a href="#使用GenericJackson2JsonRedisSerializer序列化工具" class="headerlink" title="使用GenericJackson2JsonRedisSerializer序列化工具"></a>使用GenericJackson2JsonRedisSerializer序列化工具</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisConfig</span> {<br><br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> RedisTemplate<String, Object> <span class="hljs-title function_">redisTemplate</span><span class="hljs-params">(RedisConnectionFactory connectionFactory)</span> {<br> <span class="hljs-comment">//创建RedisTemplate对象</span><br> RedisTemplate<String, Object> template = <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedisTemplate</span><>();<br> <span class="hljs-comment">//设置连接工厂</span><br> template.setConnectionFactory(connectionFactory);<br> <span class="hljs-comment">//创建JSON序列化工具</span><br> <span class="hljs-type">GenericJackson2JsonRedisSerializer</span> <span class="hljs-variable">jsonRedisSerializer</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">GenericJackson2JsonRedisSerializer</span>();<br> <span class="hljs-comment">//设置Key的序列化</span><br> template.setKeySerializer(RedisSerializer.string());<br> template.setHashKeySerializer(RedisSerializer.string());<br> <span class="hljs-comment">//设置Value的序列化</span><br> template.setValueSerializer(jsonRedisSerializer);<br> template.setHashValueSerializer(jsonRedisSerializer);<br> <span class="hljs-comment">//返回</span><br> <span class="hljs-keyword">return</span> template;<br> }<br>}<br></code></pre></td></tr></table></figure><p>缺点:为了在反序列化时知道对象的类型,JSON序列化器会将类的class类型写入json结果中,存入Redis,会带来额外的内存开销。</p><p>为了节省内存空间,我们并不会使用JSON序列化器来处理value,而是统一使用String序列化器,要求只能存储String类型的key和value。当需要存储Java对象时,手动完成对象的序列化和反序列化。</p><p>Spring默认提供了一个StringRedisTemplate类,它的key和value的序列化方式默认就是String方式。省去了我们自定义RedisTemplate的过程:</p><h3 id="使用StringRedisTemplate和手动序列化和反序列化"><a href="#使用StringRedisTemplate和手动序列化和反序列化" class="headerlink" title="使用StringRedisTemplate和手动序列化和反序列化"></a>使用StringRedisTemplate和手动序列化和反序列化</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@SpringBootTest</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisStringTests</span> {<br><br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br><br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testString</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//插入一条String类型数据</span><br> stringRedisTemplate.opsForValue().set(<span class="hljs-string">"name"</span>, <span class="hljs-string">"string测试"</span>);<br> <span class="hljs-comment">//读取一条String数据</span><br> <span class="hljs-type">Object</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> stringRedisTemplate.opsForValue().get(<span class="hljs-string">"name"</span>);<br> System.out.println(<span class="hljs-string">"name = "</span> + name);<span class="hljs-comment">//插入一条String类型数据</span><br> }<br><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-type">ObjectMapper</span> <span class="hljs-variable">mapper</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ObjectMapper</span>();<br><br> <span class="hljs-meta">@Test</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">testSaveUser</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> JsonProcessingException {<br> <span class="hljs-comment">//创建对象</span><br> <span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">User</span>(<span class="hljs-string">"Wang-nine"</span>, <span class="hljs-number">9</span>);<br> <span class="hljs-comment">//手动序列化</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">json</span> <span class="hljs-operator">=</span> mapper.writeValueAsString(user);<br> <span class="hljs-comment">//写入数据</span><br> stringRedisTemplate.opsForValue().set(<span class="hljs-string">"user:200"</span>, json);<br><br> <span class="hljs-comment">//获取数据</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">jsonUser</span> <span class="hljs-operator">=</span> stringRedisTemplate.opsForValue().get(<span class="hljs-string">"user:200"</span>);<br> <span class="hljs-comment">//手动反序列化</span><br> <span class="hljs-type">User</span> <span class="hljs-variable">user1</span> <span class="hljs-operator">=</span> mapper.readValue(jsonUser, User.class);<br> System.out.println(<span class="hljs-string">"user1 = "</span> + user1);<br> }<br><br>}<br></code></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>RedisTemplate的两种序列化实践方案:</p><ul><li>方案一:<br>1.自定义RedisTemplate<br>2.修改RedisTemplate的序列化器为GenericJackson2JsonRedisSerializer</li><li>方案二:<br>1.使用StringRedisTemplate<br>2.写入Redis时,手动把对象序列化为JSON<br>3.读取Redis时,手动把读取到的JSON反序列化为对象</li></ul><h2 id="注意"><a href="#注意" class="headerlink" title="注意"></a>注意</h2><h3 id="关于Hash的存取"><a href="#关于Hash的存取" class="headerlink" title="关于Hash的存取"></a>关于Hash的存取</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">TestHash</span><span class="hljs-params">()</span> {<br> stringRedisTemplate.opsForHash().put(<span class="hljs-string">"user:400"</span>, <span class="hljs-string">"测试300"</span>, <span class="hljs-string">"300"</span>);<br> stringRedisTemplate.opsForHash().put(<span class="hljs-string">"user:400"</span>, <span class="hljs-string">"测试400"</span>, <span class="hljs-string">"400"</span>);<br><br> Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(<span class="hljs-string">"user:400"</span>);<br> System.out.println(entries);<br>}<br></code></pre></td></tr></table></figure><h3 id="反序列化报错"><a href="#反序列化报错" class="headerlink" title="反序列化报错"></a>反序列化报错</h3><p>开发过程中经常会出现接口返回的字符串要转化为实体类对象,但是实体类中的字段有可能不完整 ,解析json串为实体对象的时候就会报错 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field</p><ul><li><strong>加在实体类上忽略解析字段注解 @JsonIgnoreProperties(ignoreUnknown = true)</strong></li></ul><h3 id="ObjectMapper类"><a href="#ObjectMapper类" class="headerlink" title="ObjectMapper类"></a>ObjectMapper类</h3><ul><li><p>ObjectMapper 是 Jackson 数据绑定库中的一个核心类,<strong>用于在 Java 对象和 JSON 数据格式之间进行转换</strong>。这个类提供了丰富的方法,用于将 Java 对象序列化为 JSON 字符串,以及将 JSON 字符串反序列化为 Java 对象。</p></li><li><p>以下是一些 ObjectMapper 常用的方法:</p><blockquote><p>writeValueAsString(Object value): 将 Java 对象转换为 JSON 字符串。<br>readValue(String value, Class<T> valueType): 将 JSON 字符串转换为 Java 对象。<br>writeValue(OutputStream outputStream, Object value): 将 Java 对象写入一个输出流,通常用于将数据写入文件。<br>readValue(InputStream inputStream, Class<T> valueType): 从一个输入流中读取数据并将其转换为 Java 对象,通常用于从文件读取数据。</p></blockquote></li><li><p>ObjectMapper 类还提供了一些高级功能,如自定义序列化器、日期和时间格式化、处理复杂的数据结构等。<br>在 Spring Boot 应用程序中,ObjectMapper 经常被用于将请求和响应对象转换为 JSON 格式,以支持 RESTful 服务的开发。</p></li></ul>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>Java入门02</title>
<link href="/2024/03/04/Redis%E5%85%A5%E9%97%A8/"/>
<url>/2024/03/04/Redis%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<p>Redis入门</p><span id="more"></span><h1 id="Redis入门"><a href="#Redis入门" class="headerlink" title="Redis入门"></a>Redis入门</h1><h2 id="什么是Redis?"><a href="#什么是Redis?" class="headerlink" title="什么是Redis?"></a>什么是Redis?</h2><ul><li><p><strong>Redis是一种基于键值对(key-value)的Nosql数据库</strong>。</p></li><li><p>Redis的键值对存储:</p><ul><li>比一般键值对数据库强大的地方,Redis中的value支持string(字符串)、hash(哈希)、 list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、 HyperLogLog、GEO(地理信息定位)等多种数据结构,因此 Redis可以满足很多的应用场景。</li></ul></li><li><p>性能</p><ul><li><p>Redis会将所有数据都<strong>存放在内存</strong>中,所以它的读写性能非常出色</p></li><li><p>Redis的(Redis的核心业务部分(命令处理))的<strong>单线程模型</strong>避免了多线程的上下文切换开销,同时也避免了锁竞争的问题,这使得Redis能够高效地利用CPU资源。</p><blockquote><p>1.抛开持久化不谈,Redis是纯 内存操作,执行速度非常快,它的性能瓶颈是网络延迟而不是执行速度,因此多线程并不会带来巨大的性能提升。</p><p>2.多线程会导致过多的上下文切换,带来不必要的开销</p><p>3.引入多线程会面临线程安全问题,必然要引入线程锁这样的安全手段,实现复杂度增高,而且性能也会大打折扣</p></blockquote></li><li><p>Redis使用非阻塞I/O,这样它可以同时处理多个请求,提高了吞吐量。</p><blockquote><p>在传统的阻塞I/O模型中,一个进程或线程在执行I/O操作(如读取文件或等待网络响应)时会阻塞,即它会被挂起,直到操作完成。这意味着在等待I/O操作完成期间,CPU无法执行其他任务,从而降低了CPU的利用率和系统的吞吐量。<br>相反,非阻塞I/O模型允许进程或线程在发起I/O操作后继续执行其他任务,而不需要等待I/O操作完成。操作系统会立即返回一个状态码,指示操作是否可以立即完成,或者是否需要稍后再次检查。如果操作不能立即完成,进程可以检查其他资源或执行其他任务,然后再回过头来检查I/O操作是否完成。</p><p>但本质上阻塞IO会使CPU阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用,非阻塞IO是对用户进程的优化</p></blockquote></li><li><p>多路复用</p><blockquote><p>Linux系统有三种方式实现IO多路复用:select、poll和epoll。</p><p>例如epoll方式是将用户socket对应的fd(文件是否就绪)注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。</p><p>这样,整个过程只在进行select、poll、epoll这些调用的时候才会阻塞,收发客户消息是不会阻塞的,整个进程或者线程就被充分利用起来,这就是事件驱动,所谓的reactor模式。</p></blockquote></li><li><p>C语言优化</p></li><li><p>Redis6.0的多线程是用多线程来处理数据的<strong>读写和协议解析</strong>(多线程处理网络IO,也就是解决网络原因的上限)</p></li></ul></li><li><p>Web应用场景</p><ul><li>Token存储:用户登录成功之后,使用Redis存储Token</li><li>登录失败次数计数:使用Redis计数,登录失败超过一定次数,锁定账号</li><li>地址缓存:对省市区数据的缓存</li><li>分布式锁:分布式环境下登录、注册等操作加分布式锁</li></ul></li></ul><h2 id="可存储数据的基本类型"><a href="#可存储数据的基本类型" class="headerlink" title="可存储数据的基本类型"></a>可存储数据的基本类型</h2><h3 id="1-String"><a href="#1-String" class="headerlink" title="1.String"></a>1.String</h3><ul><li><p>字符串最基础的数据结构。字符串类型的值实际可以是字符串(简单的字符串、复杂的字符串(例如JSON、XML))、数字 (整数、浮点数),甚至是二进制(图片、音频、视频),但是值最大不能超过512MB。</p></li><li><p>分类:</p><ul><li>string:普通字符串</li><li>int:整数类型,可以做自增、自减操作</li><li>float:浮点类型,可以做自增、自减操作</li></ul></li><li><p>一般应用场景</p><ul><li>缓存功能</li><li>计数</li><li>共享Session</li><li>限速</li></ul></li></ul><h3 id="2-Hash"><a href="#2-Hash" class="headerlink" title="2.Hash"></a>2.Hash</h3><ul><li>哈希类型是指键值本身又是一个键值对结构。</li><li><strong>Hash结构可以将对象中的每个字段独立存储,可以针对单个字段做CRUD</strong></li><li>哈希主要有以下典型应用场景:<ul><li>缓存用户信息</li><li>缓存对象</li></ul></li></ul><h3 id="3-List"><a href="#3-List" class="headerlink" title="3.List"></a>3.List</h3><ul><li>列表(list)类型是用来存储多个有序的字符串。列表是一种比较灵活的数据结构,它可以充当栈和队列的角色</li><li><strong>特点</strong><ul><li>有序</li><li>元素可以重复</li><li>插入和删除快</li><li>查询速度一般</li></ul></li><li>列表主要有以下几种使用场景:<ul><li>消息队列</li><li>文章列表</li></ul></li></ul><h3 id="4-set"><a href="#4-set" class="headerlink" title="4.set"></a>4.set</h3><ul><li>集合(set)类型也是用来保存多个的字符串元素,但和列表类型不一 样的是,集合中不允许有重复元素,并且集合中的元素是无序的。</li><li>特点:<strong>与Java中的HashSet类似,可以看做是一个value为null的HashMap(HashMap是一种实现了哈希表的数据结构)。因为也是一个hash表</strong></li><li>集合主要有如下使用场景:<ul><li>标签(tag)</li><li>共同关注</li></ul></li></ul><h3 id="5-sorted-set"><a href="#5-sorted-set" class="headerlink" title="5.sorted set"></a>5.sorted set</h3><ul><li>一个可排序的set集合,与Java中的TreeSet(红黑树)有些类似,但底层数据结构却差别很大。SortedSet中的每一个元素都带有一个score属性,可以基于score属性对元素排序,底层的实现是一个跳表(SkipList)加 hash表。</li><li>有序集合主要应用场景:<ul><li>用户点赞统计</li><li>用户排序</li></ul></li></ul><h2 id="持久化"><a href="#持久化" class="headerlink" title="持久化"></a>持久化</h2><ul><li>Redis持久化⽅案分为RDB和AOF两种。</li></ul>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>Redis</tag>
</tags>
</entry>
<entry>
<title>正则表达式</title>
<link href="/2024/02/23/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
<url>/2024/02/23/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/</url>
<content type="html"><![CDATA[<p>docker的解析使用</p><span id="more"></span><h1 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h1><h2 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h2><ul><li>用于匹配以及操作文本的工具</li><li>优点:功能强大,提高维护性</li><li>缺点:性能差,每次执行程序都要重新解析</li></ul><h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><h3 id="1-普通字符"><a href="#1-普通字符" class="headerlink" title="1.普通字符"></a>1.普通字符</h3><ul><li>明确的关键字,包括文字字符和符号</li><li>符号:@,%,__等</li><li>当符号和表达式特殊符号冲突时使用 ‘ \ ’ 转义</li></ul><h3 id="2-字符集合"><a href="#2-字符集合" class="headerlink" title="2.字符集合"></a>2.字符集合</h3><ul><li><strong>单个字符的值范围</strong>,<strong>包含在一对 [ ] 中间</strong>(实际上本质是个数组,采用数组匹配)</li></ul><blockquote><p>[ae]表示匹配a或者e</p></blockquote><ul><li><p><strong>‘ - ’ 表示连续值范围</strong>,匹配 - 本身则需要转义</p><blockquote><p>[a-d]表示[abcd]</p></blockquote></li><li><p><strong>‘ ^ ’</strong> <strong>表示排除在外的范围</strong></p><blockquote><p>[^o]匹配除以外字符</p></blockquote></li></ul><h3 id="3-限定符"><a href="#3-限定符" class="headerlink" title="3.限定符"></a>3.限定符</h3><ul><li><strong>‘ * ’ 前一个字符出现0次或多次</strong></li><li><strong>‘ + ’ 表示前一个字符出现一次或多次</strong></li><li><strong>‘ ? ’ 表示前一个字符出现0次或者多次</strong></li></ul><blockquote><p>go+d匹配god,good,goood等</p></blockquote><ul><li><p><strong>‘ {n,m} ’ 表示前面字符可以匹配m次或者n次</strong></p><blockquote><p> +={1,}</p><p>?={0.1}</p></blockquote></li><li><p>贪婪匹配问题</p><ul><li>默认情况下正则表达式会匹配尽可能多的字符</li></ul></li></ul><h3 id="4-定位符"><a href="#4-定位符" class="headerlink" title="4.定位符"></a>4.定位符</h3><ul><li><strong>^ 表示字符串的开端</strong></li><li><strong>$ 表示字符串结尾的位置</strong></li><li><strong>\b表示边界</strong></li><li><strong>\B表示非单词边界</strong></li></ul><h3 id="5-子表达式"><a href="#5-子表达式" class="headerlink" title="5.子表达式"></a>5.子表达式</h3><ul><li><strong>包含在一对 ( ) 之中,与</strong></li><li>将表达式内看为一个整体</li></ul><blockquote><p>(got)+ 匹配gotgotgot等</p></blockquote><ul><li><strong>‘ | ’ 用于分割多个表达式,表示多种情况</strong></li></ul><blockquote><p>g(e|oa)t 匹配get和goat</p></blockquote><ul><li><p><strong>\n 表示第n项子匹配项相同的内容</strong></p><blockquote><p>([a-z])\1([a-z])\2 匹配 AABB形式的字符串</p></blockquote></li><li><p>预查匹配项</p></li></ul><h3 id="6-省略符号"><a href="#6-省略符号" class="headerlink" title="6.省略符号"></a>6.省略符号</h3><ul><li>简化表达式</li></ul><h3 id="7-修饰符"><a href="#7-修饰符" class="headerlink" title="7.修饰符"></a>7.修饰符</h3><ul><li>指定匹配策略如多行匹配,大小写等</li></ul>]]></content>
<tags>
<tag>工具</tag>
</tags>
</entry>
<entry>
<title>文本数据格式使用</title>
<link href="/2024/02/23/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E4%BD%BF%E7%94%A8/"/>
<url>/2024/02/23/%E6%95%B0%E6%8D%AE%E6%A0%BC%E5%BC%8F%E4%BD%BF%E7%94%A8/</url>
<content type="html"><![CDATA[<p>文本数据格式的解析使用</p><span id="more"></span><h2 id="Json"><a href="#Json" class="headerlink" title="Json"></a>Json</h2><h3 id="Json介绍"><a href="#Json介绍" class="headerlink" title="Json介绍"></a>Json介绍</h3><ul><li><p><strong>一种文本数据格式</strong>,来源于JavaScirpt的对象语法</p></li><li><p>便于简单灵活的表示树形结构,本质就是一条字符串</p></li><li><p><strong>与xml比较</strong></p><ul><li>一般情况下可以和xml进行数据转换</li><li>文本比较小的时候更简洁更易处理</li><li>文本更复杂时候,xml在流式处理和易读性上更胜一筹例如HTML,XVG</li></ul></li><li><p>不同语言不一定<strong>原生支持Json</strong></p><ul><li>python以及JavaScript<strong>原生支持</strong></li><li>Java,C++不原生支持</li></ul></li><li><p>json5支持注释</p></li></ul><h3 id="Json规范"><a href="#Json规范" class="headerlink" title="Json规范"></a>Json规范</h3><h4 id="1-字符串"><a href="#1-字符串" class="headerlink" title="1.字符串"></a>1.字符串</h4><ul><li>包含在双引号内</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-string">"text string"</span><br><span class="hljs-comment">//当字符串内包含双引号时候,需要反斜杠进行转义</span><br><span class="hljs-string">"text \"string"</span><br></code></pre></td></tr></table></figure><h4 id="2-数字"><a href="#2-数字" class="headerlink" title="2.数字"></a>2.数字</h4><ul><li>整形,浮点型</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-number">0</span><span class="hljs-punctuation">,</span><span class="hljs-number">-1</span><span class="hljs-punctuation">,</span><span class="hljs-number">10</span><span class="hljs-punctuation">,</span><span class="hljs-number">1.1</span><br></code></pre></td></tr></table></figure><h4 id="3-布尔值"><a href="#3-布尔值" class="headerlink" title="3.布尔值"></a>3.布尔值</h4><ul><li>true,false</li></ul><h4 id="4-null"><a href="#4-null" class="headerlink" title="4.null"></a>4.null</h4><ul><li>空值</li></ul><blockquote><p> 常规类型相互之间不允许内嵌</p></blockquote><h4 id="5-对象,键值对,哈希表"><a href="#5-对象,键值对,哈希表" class="headerlink" title="5.对象,键值对,哈希表"></a>5.对象,键值对,哈希表</h4><ul><li>需要包含在一对{}之内,使用 , 隔开</li><li><strong>key必须为字符串,值可以为任意类型</strong></li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><span class="hljs-punctuation">{</span><span class="hljs-attr">"key"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"value"</span><span class="hljs-punctuation">}</span><br><span class="hljs-punctuation">{</span><span class="hljs-attr">"key"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"value"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"key"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"value"</span><span class="hljs-punctuation">,</span><span class="hljs-attr">"key"</span><span class="hljs-punctuation">:</span><span class="hljs-string">"value"</span><span class="hljs-punctuation">}</span><br></code></pre></td></tr></table></figure><h4 id="6-数组"><a href="#6-数组" class="headerlink" title="6.数组"></a>6.数组</h4><ul><li>包含在一对 [ ] 内,用 , 隔开</li><li>值可以为任何类型</li></ul><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">[</span><span class="hljs-string">"list"</span><span class="hljs-punctuation">,</span><span class="hljs-literal"><span class="hljs-keyword">true</span></span><span class="hljs-punctuation">,</span><span class="hljs-punctuation">{</span><span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span><span class="hljs-punctuation">[</span><span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><span class="hljs-number">123</span><span class="hljs-punctuation">]</span><br></code></pre></td></tr></table></figure><h3 id="json工具"><a href="#json工具" class="headerlink" title="json工具"></a>json工具</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.alibaba<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>fastjson<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.2.33<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>工具</tag>
</tags>
</entry>
<entry>
<title>Docker</title>
<link href="/2024/02/23/Docker%E5%8E%9F%E7%90%86/"/>
<url>/2024/02/23/Docker%E5%8E%9F%E7%90%86/</url>
<content type="html"><![CDATA[<p>docker的解析使用</p><span id="more"></span><h2 id="Docker原理"><a href="#Docker原理" class="headerlink" title="Docker原理"></a>Docker原理</h2><ul><li><strong>轻量级的虚拟机?</strong></li></ul><h3 id="四部分"><a href="#四部分" class="headerlink" title="四部分"></a>四部分</h3><h4 id="远程镜像仓库"><a href="#远程镜像仓库" class="headerlink" title="远程镜像仓库"></a>远程镜像仓库</h4><ul><li>接收从本地镜像的上传</li><li>提供给本地镜像下载镜像</li></ul><h4 id="本地镜像"><a href="#本地镜像" class="headerlink" title="本地镜像"></a>本地镜像</h4><ul><li>从远程镜像仓库下载镜像,以及上传</li><li>提供给本地容器镜像启动</li></ul><h4 id="本地容器"><a href="#本地容器" class="headerlink" title="本地容器"></a>本地容器</h4><ul><li>从本地镜像启动容器</li><li>打包镜像给本地仓库</li></ul><h4 id="docker软件"><a href="#docker软件" class="headerlink" title="docker软件"></a>docker软件</h4><ul><li><p>连接三个部分的基座</p></li><li><p>运行容器的引擎</p></li></ul><h2 id="docker镜像"><a href="#docker镜像" class="headerlink" title="docker镜像"></a>docker镜像</h2><ul><li><strong>镜像</strong>:容器的模板,可以理解电脑安装操作系统的光盘</li></ul><h3 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h3><ul><li>只读,不可作修改</li><li>linux镜像可以再Linux和Windows上使用</li><li>Windows只能在Windows上使用</li></ul><h3 id="组成"><a href="#组成" class="headerlink" title="组成"></a>组成</h3><ul><li>镜像名:tag版本</li><li>例如:mysql:5.7</li><li>版本之间相互独立</li></ul><h3 id="镜像相关命令"><a href="#镜像相关命令" class="headerlink" title="镜像相关命令,,,"></a>镜像相关命令,,,</h3><h2 id="docker容器"><a href="#docker容器" class="headerlink" title="docker容器"></a>docker容器</h2><ul><li><p><strong>容器</strong>:真正运行的实例,根据镜像创建,<strong>隔离网络,进程,文件的环境</strong></p></li><li><p>与虚拟机区别:</p><ul><li><p>容器之间共用宿主机资源,不需要分配独立的系统资源,不需要启动完整的操作系统</p></li><li><p>启动更快</p></li></ul></li></ul><h3 id="容器相关命令…"><a href="#容器相关命令…" class="headerlink" title="容器相关命令…."></a>容器相关命令….</h3><h3 id="容器创建命令"><a href="#容器创建命令" class="headerlink" title="容器创建命令"></a>容器创建命令</h3><ul><li>通过命令创建<ul><li>需要完整的镜像</li></ul></li><li>通过dockerfile创建<ul><li>镜像可不完整</li><li>通过dockerfile脚本进一步完善镜像</li><li>更灵活,但是创建时间长,稳定性差</li></ul></li><li><strong>组成部分</strong><ul><li>基础参数</li><li>挂载宿主机目录:修改容器目录会影响容器本身</li><li>网络设置:默认情况网络为隔离的,一般为端口映射</li><li>环境变量设置</li><li>基础镜像</li><li>每次启动执行命令</li></ul></li></ul>]]></content>
<tags>
<tag>工具</tag>
</tags>
</entry>
<entry>
<title>Qt学习</title>
<link href="/2024/02/04/Qt%E6%8A%A5%E9%94%99/"/>
<url>/2024/02/04/Qt%E6%8A%A5%E9%94%99/</url>
<content type="html"><![CDATA[<p>Qt学习</p><span id="more"></span> <ul><li>指针</li></ul><blockquote><ol><li><p><strong>声明指针</strong>:当你声明一个指针时,它包含一个未知的内存地址,这个地址可以是任何值。如果你不对指针进行初始化,它将包含一个未定义的值,这是危险的,因为使用一个未初始化的指针可能导致不可预测的行为。例如:</p></li><li><p><strong>nullptr指针</strong>:在C++11及更高版本中,建议使用<code>nullptr</code>来显式初始化指针,这是一种更安全的做法。<code>nullptr</code>是一个特殊的关键字,表示空指针。使用<code>nullptr</code>初始化指针可以确保它不包含任何有效的内存地址</p></li></ol></blockquote>]]></content>
<categories>
<category>C++</category>
</categories>
<tags>
<tag>C++</tag>
<tag>Qt</tag>
</tags>
</entry>
<entry>
<title>SSM05-原理进阶</title>
<link href="/2024/02/03/SSM05-%E5%8E%9F%E7%90%86/"/>
<url>/2024/02/03/SSM05-%E5%8E%9F%E7%90%86/</url>
<content type="html"><![CDATA[<p>Springboot的Web学习与应用</p><span id="more"></span><h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><h2 id="项目打包"><a href="#项目打包" class="headerlink" title="项目打包"></a>项目打包</h2><ul><li><h3 id="引入插件"><a href="#引入插件" class="headerlink" title="引入插件"></a>引入插件</h3></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">plugin</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-maven-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">plugin</span>></span><br><span class="hljs-comment"><!--基于官网构建的项目会自动下载--></span><br></code></pre></td></tr></table></figure><ul><li><h3 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h3><figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs clean">maven -> 生命周期 -> package<br></code></pre></td></tr></table></figure></li><li><h3 id="忽略测试文件"><a href="#忽略测试文件" class="headerlink" title="忽略测试文件"></a>忽略测试文件</h3></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">properties</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">java.version</span>></span>17<span class="hljs-tag"></<span class="hljs-name">java.version</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">skipTests</span>></span>true<span class="hljs-tag"></<span class="hljs-name">skipTests</span>></span><span class="hljs-comment"><!--跳过测试文件--></span><br><span class="hljs-tag"></<span class="hljs-name">properties</span>></span><br></code></pre></td></tr></table></figure><h2 id="Bean对象管理"><a href="#Bean对象管理" class="headerlink" title="Bean对象管理"></a>Bean对象管理</h2><ul><li><h3 id="Bean对象获取方式"><a href="#Bean对象获取方式" class="headerlink" title="Bean对象获取方式"></a>Bean对象获取方式</h3><ul><li>自动注入@Autowired</li><li>通过类名等获取</li></ul></li><li><h3 id="Bean对象作用域"><a href="#Bean对象作用域" class="headerlink" title="Bean对象作用域"></a>Bean对象作用域</h3><ul><li><p><strong>Singleton</strong>:容器内获取的Bean对象都是单例对象,默认情况</p></li><li><p><strong>prototype</strong>:每一次请求对应一个实例对象</p></li><li><p>…..</p></li><li><p><strong>作用域注解</strong></p><blockquote><p><strong>@Scope()</strong> : 设置作用域</p><p><strong>@Lazy</strong>: 懒汉模式,直到第一次被调用的才实例化,仅在单例模式使用</p></blockquote></li></ul></li><li><h3 id="获取第三方Bean对象"><a href="#获取第三方Bean对象" class="headerlink" title="获取第三方Bean对象"></a>获取第三方Bean对象</h3><ul><li><p>第三方jar包只读的情况下无法进行@Component注解</p></li><li><p><strong>@Bean</strong>注解</p><ul><li><p>一般在配置类里面配置第三方bean对象</p></li><li><p>@Bean可以配置对象的name和value</p><blockquote><bean name="myBean" class="com.example.MyBean"/></blockquote></li><li><p>如果需要依赖bean对象则只需要填入在参数中</p></li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">StudyinsConfig</span> {<br> <span class="hljs-meta">@Bean</span><br> <span class="hljs-keyword">public</span> JwtUtils <span class="hljs-title function_">returnJwtutils</span><span class="hljs-params">(DeptService deptService)</span>{<br> <span class="hljs-comment">//将此bean对象注入到DeptService类</span><br> <span class="hljs-comment">//bean对象名称默认就是方法名,也可自行修改</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">JwtUtils</span>();<span class="hljs-comment">//将这个对象交给IOC管理</span><br> }<br>}<br></code></pre></td></tr></table></figure></li></ul><h2 id="依赖与配置"><a href="#依赖与配置" class="headerlink" title="依赖与配置"></a>依赖与配置</h2><h3 id="起步依赖"><a href="#起步依赖" class="headerlink" title="起步依赖"></a>起步依赖</h3><ul><li><strong>引入spring相关模块依赖并进行版本适配</strong></li><li><strong>起步依赖内包含所需常见依赖,通过maven依赖传递获取某一模块所需常见依赖</strong></li></ul><h3 id="自动配置"><a href="#自动配置" class="headerlink" title="自动配置"></a>自动配置</h3><ul><li><p><strong>当spring容器启动后,一些配置类和Bean对象自动存入到IOC容器中管理,简化了开发</strong></p></li><li><p><strong>springboot只能扫描启动类所在包及其子包</strong></p><ul><li><strong>@ComponentScan指定扫描包</strong></li><li><strong>@Import直接导入类(推荐)</strong></li><li>此注解用于启动类之前</li></ul></li><li><p><strong>@SpringBootApplication启动类</strong></p><ul><li>@SpringBootConfiguration:提供配置类</li><li>@EnableAutoConfiguration:核心注解,自动配置的核心,内含@Import注解导入一个类返回所有IOC容器内的对象数组</li><li>@ComponentScan:提供包扫描功能</li></ul></li><li><p><strong>@Conditional</strong></p><ul><li>作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring IOC容器中。</li><li>位置:方法、类</li><li>@Conditional 本身是一个父注解,派生出大量的子注解<ul><li>@ConditionalOnClass:判断环境中是否有对应字节码文件,才注册bean到IOC容器</li><li>@ConditionalOnMissingBean:判断环境中没有对应的bean (类型或名称) , 才注册bean到IOC容器</li><li>@ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器</li></ul></li></ul></li></ul><h3 id="自定义Starter"><a href="#自定义Starter" class="headerlink" title="自定义Starter"></a>自定义Starter</h3><ul><li><p>自定义起步依赖</p></li><li><p><strong>包含所有该起步依赖功能所需的依赖</strong></p></li><li><p>一般命名格式:功能-spring-boot-starter,表示是第三方提供</p></li><li><p><strong>操作</strong></p><ul><li>创建对应功能模块,aliyun-oss-spring-boot-starter,对应依赖管理功能</li><li>创建自动配置模块,aliyun-oss-spring-boot-autoconfigure,对应自动配置功能</li><li>自己配置aliyun-oss-spring-boot-autoconfigure自动配置功能,并定义自动配置文件META-INF/spring/xxx.import</li></ul></li></ul>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>SSM04-AOP</title>
<link href="/2024/02/02/SSM04-AOP/"/>
<url>/2024/02/02/SSM04-AOP/</url>
<content type="html"><![CDATA[<p>Springboot的Web学习与应用</p><span id="more"></span><h1 id="AOP-面向切面编程"><a href="#AOP-面向切面编程" class="headerlink" title="AOP-面向切面编程"></a>AOP-面向切面编程</h1><ul><li><p>面向特定方法编程</p></li><li><p>应用:<strong>在不惊动原始设计的基础上为其进行功能增强</strong></p></li><li><p>实现原理</p><ul><li><p>在对bean对象的管理过程之中,主要通过<strong>动态代理技术</strong>,对特定方法进行编程</p></li><li><p>通过创建一个代理对象,在代理对象内填充原始代码,然后根据切面类插入相应的功能代码</p></li><li><p><strong>当自动注入的时候,注入的其实是代理对象</strong></p></li><li><p>Spring中,动态代理通常与AOP一起使用。AOP允许你以模块化的方式添加横切关注点(如日志、事务管理等),而不需要修改实际执行业务逻辑的代码。Spring AOP通过使用动态代理来实现这一目标</p></li></ul><blockquote><p>动态代理主要有两种类型:</p><p>1.JDK动态代理:基于Java反射API创建的代理,只能代理实现了接口的bean。当目标类实现了接口默认使用JDK代理<br>2.CGLIB代理:基于代码生成技术创建的代理,可以代理没有实现接口的bean。</p></blockquote></li></ul><h2 id="起步依赖"><a href="#起步依赖" class="headerlink" title="起步依赖"></a>起步依赖</h2><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-aop<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><h2 id="示例程序"><a href="#示例程序" class="headerlink" title="示例程序"></a>示例程序</h2><ul><li>计算方法耗时</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@Aspect</span><span class="hljs-comment">//当前类是一个AOP类</span><br><span class="hljs-meta">@Component</span><span class="hljs-comment">//交给IOC管理</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TimeAspect</span> {<br><br> <span class="hljs-comment">//通过切入点表达式指定此方法作用于哪一个(一群)方法</span><br> <span class="hljs-meta">@Around("execution(* com.studyins.service.*.*(..))")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">recordTime</span><span class="hljs-params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="hljs-keyword">throws</span> Throwable {<br> <span class="hljs-comment">//调用开始时间</span><br> <span class="hljs-type">long</span> <span class="hljs-variable">begin</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-comment">//调用原始方法运行</span><br> <span class="hljs-type">Object</span> <span class="hljs-variable">Result</span> <span class="hljs-operator">=</span> proceedingJoinPoint.proceed();<br> <span class="hljs-comment">//计算结束时间</span><br> <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span>System.currentTimeMillis();<br> log.info(proceedingJoinPoint.getSignature()+<span class="hljs-string">"方法执行耗时:{}ms"</span>,end-begin);<br><br> <span class="hljs-comment">//返回原始方法执行结果</span><br> <span class="hljs-keyword">return</span> Result;<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h2><h3 id="连接点"><a href="#连接点" class="headerlink" title="连接点"></a>连接点</h3><ul><li>JoinPoint:代表可以被AOP控制的方法</li><li>使用<ul><li>@Around只能使用ProceedingJoinPoint连接点</li><li>其它类型只能使用JoinPoint(ProceedingJoinPoint父类)</li><li>可以获取类名,方法名,参数等</li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Before("@annotation(com.studyins.aop.Annolog)")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">getName</span><span class="hljs-params">(JoinPoint joinPoint)</span>{<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> joinPoint.getTarget().getClass().getName();<span class="hljs-comment">//类名</span><br> <span class="hljs-type">Signature</span> <span class="hljs-variable">signature1</span> <span class="hljs-operator">=</span> joinPoint.getSignature();<span class="hljs-comment">//目标方法签名</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">name1</span> <span class="hljs-operator">=</span> joinPoint.getSignature().getName();<span class="hljs-comment">//目标方法名</span><br> Object[] args = joinPoint.getArgs();<span class="hljs-comment">//方法参数</span><br>}<br></code></pre></td></tr></table></figure><h3 id="通知"><a href="#通知" class="headerlink" title="通知"></a>通知</h3><ul><li>Adivce:也就是重新编程的逻辑,体现为一个方法</li></ul><h4 id="通知类型"><a href="#通知类型" class="headerlink" title="通知类型"></a>通知类型</h4><ul><li>@Around:环绕通知,通知方法在目标方法前后都可执行</li><li>@Before:前置通知,只能在调用方法前执行</li><li>@After:后置通知,也称最终通知,无论目标方法发生什么异常,后置通知都会执行</li><li>@AfterReturning:返回后通知,有异常不会执行</li><li>@AfterThrowing:异常后通知,发生异常后执行</li></ul><h4 id="通知顺序"><a href="#通知顺序" class="headerlink" title="通知顺序"></a>通知顺序</h4><ul><li>默认:同一种通知类型作用顺序和类名有关</li><li><strong>@Order</strong>(Integer i)注解控制通知顺序<ul><li>目标方法结束前越小越快执行</li><li>目标方法结束后越小越慢执行</li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Order(1)</span><br> <span class="hljs-meta">@Around("pt()")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">recordTime</span><span class="hljs-params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="hljs-keyword">throws</span> Throwable {}<br></code></pre></td></tr></table></figure><h3 id="切入点"><a href="#切入点" class="headerlink" title="切入点"></a>切入点</h3><ul><li>PointCut:匹配接入点的条件</li></ul><h4 id="切入点表达式"><a href="#切入点表达式" class="headerlink" title="切入点表达式"></a>切入点表达式</h4><ul><li>语法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//【】代表可省略,方法参数是指参数类型,(..)通配多个连续符号,如参数类型和包名类名,*通配单个名</span><br><span class="hljs-comment">//可多次匹配如 "execution()"+"execution()"</span><br>execution(【访问修饰符】 返回值 【包名.类名.】方法名(参数)【<span class="hljs-keyword">throws</span> 异常】);<br><br><span class="hljs-comment">//标识有特定注解的方法</span><br><span class="hljs-meta">@annotation()</span>;<br><span class="hljs-comment">//自定义注解</span><br><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span><span class="hljs-comment">//运行时执行</span><br><span class="hljs-meta">@Target(ElementType.METHOD)</span><span class="hljs-comment">//作用于方法上</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> Annolog {<br>}<br><span class="hljs-comment">//将注解注解在特定方法上</span><br><span class="hljs-meta">@Annolog</span><br><span class="hljs-meta">@Transactional(rollbackFor = Exception.class)</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">deleteById</span><span class="hljs-params">(Integer id)</span>{<br> deptMapper.deleteById(id);<br> empMapper.deleteByDeptId(id);<br>}<br><span class="hljs-comment">//将注解名称写入@annotation();</span><br><span class="hljs-meta">@Around("@annotation(com.studyins.aop.Annolog)")</span><br></code></pre></td></tr></table></figure><ul><li>复用</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">TimeAspect</span> {<br> <span class="hljs-meta">@Pointcut("execution(* com.studyins.service.*.*(..))")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">pt</span><span class="hljs-params">()</span>{}<br> <span class="hljs-comment">//private void pt(){} 表示只有当前切面类能用</span><br> <br> <span class="hljs-comment">//引用切入点表达式</span><br> <span class="hljs-meta">@Around("pt()")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">recordTime</span><span class="hljs-params">(ProceedingJoinPoint proceedingJoinPoint)</span> <span class="hljs-keyword">throws</span> Throwable {<br> <span class="hljs-keyword">return</span> Result;<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="切面"><a href="#切面" class="headerlink" title="切面"></a>切面</h3><ul><li>Aspect:描述通知与接入点的关系(通知+切入点),也叫切面类</li></ul><h3 id="目标对象"><a href="#目标对象" class="headerlink" title="目标对象"></a>目标对象</h3><ul><li>Target:通知所应用的对象</li></ul><h2 id="案例"><a href="#案例" class="headerlink" title="案例"></a>案例</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@Aspect</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LogAspect</span> {<br><br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> OperateLogMapper operateLogMapper;<br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> HttpServletRequest httpServletRequest;<br><br> <span class="hljs-meta">@Around("@annotation(com.studyins.aop.Annolog)")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">recordLog</span><span class="hljs-params">(ProceedingJoinPoint joinPoint)</span> <span class="hljs-keyword">throws</span> Throwable {<br><br> <span class="hljs-comment">//操作人id,通过令牌获取</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> httpServletRequest.getHeader(<span class="hljs-string">"token"</span>);<br> <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> JwtUtils.parseJWT(jwt);<br> <span class="hljs-type">Integer</span> <span class="hljs-variable">id</span> <span class="hljs-operator">=</span>(Integer) claims.get(<span class="hljs-string">"id"</span>);<br> <span class="hljs-comment">//操作时间</span><br> <span class="hljs-type">LocalDateTime</span> <span class="hljs-variable">localDateTime</span> <span class="hljs-operator">=</span> LocalDateTime.now();<br> <span class="hljs-comment">//操作类类名</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">className</span> <span class="hljs-operator">=</span> joinPoint.getClass().getName();<br> <span class="hljs-comment">//操作方法名</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">methodName</span> <span class="hljs-operator">=</span> joinPoint.getSignature().getName();<br> <span class="hljs-comment">//操作方法参数</span><br> Object[] args = joinPoint.getArgs();<br> <span class="hljs-type">String</span> <span class="hljs-variable">strArgs</span> <span class="hljs-operator">=</span> Arrays.toString(args);<br><br> <span class="hljs-type">long</span> <span class="hljs-variable">begin</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br><br> <span class="hljs-type">Object</span> <span class="hljs-variable">Result</span> <span class="hljs-operator">=</span> joinPoint.proceed();<br><br> <span class="hljs-type">long</span> <span class="hljs-variable">end</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br><br> <span class="hljs-comment">//方法返回值</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">strResult</span> <span class="hljs-operator">=</span> JSONObject.toJSONString(Result);<br> <span class="hljs-comment">//执行耗时</span><br> <span class="hljs-type">long</span> <span class="hljs-variable">consumeTime</span> <span class="hljs-operator">=</span> end-begin;<br><br> <span class="hljs-type">OperateLog</span> <span class="hljs-variable">operateLog</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">OperateLog</span>(<span class="hljs-literal">null</span>,id,localDateTime,className,methodName,strArgs,strResult,consumeTime);<br><br> operateLogMapper.insert(operateLog);<br><br> log.info(<span class="hljs-string">"操作日志录入:{}"</span>,operateLog);<br> <span class="hljs-keyword">return</span> Result;<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="JDK动态代理"><a href="#JDK动态代理" class="headerlink" title="JDK动态代理"></a>JDK动态代理</h2><h3 id="是什么"><a href="#是什么" class="headerlink" title="是什么"></a>是什么</h3><p>JVM层面:在运行时动态生成,即编译完成后没有世界的class文件,而是动态生成字节码</p>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>SSM03-登录</title>
<link href="/2024/01/30/SSM03-%E7%99%BB%E5%BD%95/"/>
<url>/2024/01/30/SSM03-%E7%99%BB%E5%BD%95/</url>
<content type="html"><![CDATA[<p>Springboot的Web学习与应用</p><span id="more"></span><h1 id="SpringWeb登录"><a href="#SpringWeb登录" class="headerlink" title="SpringWeb登录"></a>SpringWeb登录</h1><h2 id="登录功能"><a href="#登录功能" class="headerlink" title="登录功能"></a>登录功能</h2><ul><li>LoginController</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginController</span> {<br><br> <span class="hljs-meta">@Autowired</span><br> EmpService empService;<br><br> <span class="hljs-meta">@PostMapping("/login")</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">empLogin</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> Emp emp)</span>{<br> log.info(<span class="hljs-string">"员工登录:{}"</span>,emp);<br> <span class="hljs-type">Emp</span> <span class="hljs-variable">e</span> <span class="hljs-operator">=</span> empService.getByPW(emp);<br> <span class="hljs-keyword">return</span> e!=<span class="hljs-literal">null</span>?Result.success():Result.error(<span class="hljs-string">"登录失败"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="登录校验"><a href="#登录校验" class="headerlink" title="登录校验"></a>登录校验</h2><ul><li>http协议是无状态协议,请求之间相互独立,因此需要借助其它手段实现登录校验功能</li><li>方法:会话跟踪技术</li></ul><h3 id="会话跟踪技术"><a href="#会话跟踪技术" class="headerlink" title="会话跟踪技术"></a>会话跟踪技术</h3><ul><li>会话:用户使用浏览器可以对web端服务器建立会话,一次会话内可以进行多次请求和响应,直到断开连接</li><li>会话跟踪:服务器需要识别会话来自于哪一个浏览器,以便同一次会话共享数据</li><li>会话跟踪方案:<ul><li>客户端会话跟踪:Cookie</li><li>服务端会话跟踪:session</li><li>令牌</li></ul></li></ul><h4 id="Cookie"><a href="#Cookie" class="headerlink" title="Cookie"></a>Cookie</h4><ul><li><p>服务器端通过Cookie存储浏览器用户的用户信息(name+value),会话建立后服务器将Cookie返回给浏览器</p></li><li><p><strong>浏览器存储Cookie</strong>,此后每次请求都会携带Cookie</p></li><li><p>整个过程都是自动的:http协议支持,封装在请求头内</p></li><li><p>后端生成和解析Cookie:HttpServletResponse类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SessionController</span> {<br> <span class="hljs-comment">//返回Cookie</span><br> <span class="hljs-meta">@GetMapping("/cookie")</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">cookie</span><span class="hljs-params">(HttpServletResponse httpServletResponse)</span>{<br> httpServletResponse.addCookie(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Cookie</span>(<span class="hljs-string">"username"</span>,<span class="hljs-string">"uservalue"</span>));<br> <span class="hljs-keyword">return</span> Result.success();<br> }<br><br> <span class="hljs-comment">//分析Cookie</span><br> <span class="hljs-meta">@GetMapping("/cookie2")</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">cookie2</span><span class="hljs-params">(HttpServletRequest httpServletRequest)</span>{<br> Cookie[] cookies = httpServletRequest.getCookies();<br> <span class="hljs-keyword">for</span> (Cookie cookie : cookies){<br> <span class="hljs-keyword">if</span>(cookie.getName().equals(<span class="hljs-string">"username"</span>)){<br> log.info(cookie.getName()+<span class="hljs-string">" "</span>+cookie.getValue());<br> }<br> }<br> <span class="hljs-keyword">return</span> Result.success();<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>优点:自动进行</p></li><li><p>缺点:</p><ul><li>依赖浏览器,移动端app无法使用cookie</li><li>不安全,这种通信时裸露的、</li><li>用户不一定使用Cookie,可以禁用</li><li>不能跨域操作(受协议,ip和端口限制)</li></ul></li></ul><h4 id="Session"><a href="#Session" class="headerlink" title="Session"></a>Session</h4><ul><li><p>服务器第一次收到会话请求的时候生成一个Session和一个相应的id,存储Session并将id响应回去</p></li><li><p>随后浏览器将此id存储到本地,每次请求都会带上id,服务器根据id寻找session</p></li><li><p>后端生成和解析Session,<strong>HttpSession类</strong></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@GetMapping("/session")</span><br><span class="hljs-keyword">public</span> Result <span class="hljs-title function_">csession</span><span class="hljs-params">(HttpSession session)</span>{<br> session.setAttribute(<span class="hljs-string">"user"</span>,<span class="hljs-string">"value"</span>);<br> <span class="hljs-keyword">return</span> Result.success();<br>}<br><br><span class="hljs-meta">@GetMapping("/session2")</span><br><span class="hljs-keyword">public</span> Result <span class="hljs-title function_">session2</span><span class="hljs-params">(HttpServletRequest httpServletRequest)</span>{<br> <span class="hljs-type">HttpSession</span> <span class="hljs-variable">session</span> <span class="hljs-operator">=</span>httpServletRequest.getSession();<br> <span class="hljs-type">Object</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span> session.getAttribute(<span class="hljs-string">"user"</span>);<br> log.info((String) user);<br> <span class="hljs-keyword">return</span> Result.success();<br>}<br></code></pre></td></tr></table></figure><ul><li>优点:存储在服务器端,较为安全</li><li>缺点:<ul><li>只能实现在单个服务器上,但是单个服务器隐患极大</li></ul></li></ul><h4 id="令牌技术"><a href="#令牌技术" class="headerlink" title="令牌技术"></a>令牌技术</h4><ul><li><p>浏览器第一次请求时,服务器给对应的浏览器生成一个token,然后响应回去</p></li><li><p>浏览器将令牌储存在本地,然后每次请求将令牌携带,由服务器检验令牌是否合法</p></li><li><p>优点</p><ul><li>移动端,pc端都适用</li><li>解决多个服务器集群问题</li><li>减轻服务器储存压力</li></ul></li></ul><h3 id="JWT令牌"><a href="#JWT令牌" class="headerlink" title="JWT令牌"></a>JWT令牌</h3><ul><li><p>Json Web Token</p></li><li><p>用于在通信双方以安全的json格式然后封装传输信息</p></li><li><p>本质是一个字符串</p></li><li><p>组成</p><ul><li>Header头:记录令牌类型,签名算法等</li><li>payload有效载荷:记录自定义信息</li><li>Signature(签名):被签名算法加密后的结果,防止Token被篡改,确保安全性</li></ul></li><li><p>生成和校验测试代码</p></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>io.jsonwebtoken<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>jjwt<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">version</span>></span>0.9.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">JwtUtils</span> {<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">String</span> <span class="hljs-variable">signKey</span> <span class="hljs-operator">=</span> <span class="hljs-string">"zwms"</span>;<br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-type">Long</span> <span class="hljs-variable">expire</span> <span class="hljs-operator">=</span> <span class="hljs-number">43200000L</span>;<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 生成JWT令牌</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> claims JWT第二部分负载 payload 中存储的内容</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title function_">generateJwt</span><span class="hljs-params">(Map<String, Object> claims)</span>{<br> <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> Jwts.builder()<br> .addClaims(claims)<br> .signWith(SignatureAlgorithm.HS256, signKey)<br> <span class="hljs-comment">//java9以上高版本需要引入另外的依赖</span><br> <span class="hljs-comment">//<dependency></span><br><span class="hljs-comment">//<groupId>javax.xml.bind</groupId></span><br><span class="hljs-comment">//<artifactId>jaxb-api</artifactId></span><br><span class="hljs-comment">//<version>2.3.1</version></span><br> <span class="hljs-comment">//</dependency></span><br> .setExpiration(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>(System.currentTimeMillis() + expire))<br> .compact();<br> <span class="hljs-keyword">return</span> jwt;<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 解析JWT令牌</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> jwt JWT令牌</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> JWT第二部分负载 payload 中存储的内容</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Claims <span class="hljs-title function_">parseJWT</span><span class="hljs-params">(String jwt)</span>{<br> <span class="hljs-type">Claims</span> <span class="hljs-variable">claims</span> <span class="hljs-operator">=</span> Jwts.parser()<br> .setSigningKey(signKey)<br> .parseClaimsJws(jwt)<br> .getBody();<br> <span class="hljs-keyword">return</span> claims;<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>Controller层</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@PostMapping("/login")</span><br><span class="hljs-keyword">public</span> Result <span class="hljs-title function_">empLogin</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> Emp emp)</span>{<br><br> log.info(<span class="hljs-string">"员工登录:{}"</span>,emp);<br><br> <span class="hljs-type">Emp</span> <span class="hljs-variable">e</span> <span class="hljs-operator">=</span> empService.getByPW(emp);<br><br> <span class="hljs-keyword">if</span>(e!=<span class="hljs-literal">null</span>){<br> Map<String, Object> claims = <span class="hljs-keyword">new</span> <span class="hljs-title class_">HashMap</span><>();<br> claims.put(<span class="hljs-string">"id"</span>,e.getId());<br> claims.put(<span class="hljs-string">"username"</span>,e.getUsername());<br> claims.put(<span class="hljs-string">"name"</span>,e.getName());<br><br> <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> JwtUtils.generateJwt(claims);<br><br> <span class="hljs-keyword">return</span> Result.success(jwt);<br> }<br><br> <span class="hljs-keyword">return</span> Result.error(<span class="hljs-string">"NOT_LOGIN"</span>);<br>}<br></code></pre></td></tr></table></figure><h3 id="Filter-过滤器"><a href="#Filter-过滤器" class="headerlink" title="Filter: 过滤器"></a>Filter: 过滤器</h3><ul><li><p>Java三大组件之一(Servlet,Filter,Listener)</p></li><li><p>统一拦截资源的请求,放行</p></li><li><p>完成一些通用处理:登录校验,统一编码字符,敏感字符处理等等</p></li><li><p>操作:</p><ul><li>定义Filter,定义一个Filter,实现Fiter类的所有接口,重写所有方法</li><li>配置Filter,Fiter类加上**@WebFilter<strong>注解配置拦截资源路径。启动类加上</strong>@ServletComponentScan**开启Servlet组件支持</li></ul></li><li><p>示例</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@WebFilter(urlPatterns = "/*")</span><span class="hljs-comment">//拦截路径,例如 /emps/* 是emps里面的所有路径</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Filter</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilter</span><span class="hljs-params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> IOException, ServletException {<br> <br> log.info(<span class="hljs-string">"拦截到一次请求,拦截之前的操作"</span>);<br> <br> filterChain.doFilter(servletRequest,servletResponse);<span class="hljs-comment">//放行</span><br> <br> log.info(<span class="hljs-string">"拦截请求之后的操作"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li><p>过滤器链</p><ul><li><p>多个过滤器存在时候</p><p>filter1 ——-> filter2 ———> 资源 拦截放行的时候</p><p>filter1 <——- filter2 <——— 资源 放行之后</p></li><li><p>注解配置的过滤器按照名字首字母排优先级</p></li></ul></li><li><p>示例代码:</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@WebFilter(urlPatterns = "/*")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginFilter</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Filter</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">doFilter</span><span class="hljs-params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> IOException, ServletException {<br><br> <span class="hljs-comment">//将类型转换为HttpRequest和HttpResponse</span><br> <span class="hljs-type">HttpServletRequest</span> <span class="hljs-variable">httpRequest</span> <span class="hljs-operator">=</span> (HttpServletRequest) servletRequest;<br> <span class="hljs-type">HttpServletResponse</span> <span class="hljs-variable">httpResponse</span> <span class="hljs-operator">=</span>(HttpServletResponse) servletResponse;<br><br> <span class="hljs-comment">//获取请求的url</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> httpRequest.getRequestURI();<br><br> <span class="hljs-comment">//确认是否应该放行登录请求</span><br> <span class="hljs-keyword">if</span>(url.contains(<span class="hljs-string">"/login"</span>)){<br> filterChain.doFilter(servletRequest,servletResponse);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-comment">//获取请求头信息</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">jwt</span> <span class="hljs-operator">=</span> httpRequest.getHeader(<span class="hljs-string">"token"</span>);<br> <span class="hljs-keyword">if</span>(!StringUtils.hasLength(jwt)){<br> Result result= Result.error(<span class="hljs-string">"NOT_LOGIN"</span>);<br><br> <span class="hljs-comment">//因为不是在controller之内,需要手动将返回信息封装到json格式之中</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">strResult</span> <span class="hljs-operator">=</span> JSONObject.toJSONString(result);<br><br> <span class="hljs-comment">//将结果写入返回体之中</span><br> httpResponse.getWriter().write(strResult);<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-comment">//校验Jwt令牌</span><br> <span class="hljs-keyword">try</span> {<br> JwtUtils.parseJWT(jwt);<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> log.info(<span class="hljs-string">"令牌解析失败"</span>);<br> Result result= Result.error(<span class="hljs-string">"NOT_LOGIN"</span>);<br> <span class="hljs-comment">//因为不是在controller之内,需要手动将返回信息封装到json格式之中</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">strResult</span> <span class="hljs-operator">=</span> JSONObject.toJSONString(result);<br> <span class="hljs-comment">//将结果写入返回体之中</span><br> httpResponse.getWriter().write(strResult);<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br> }<br><br> filterChain.doFilter(servletRequest,servletResponse);<br> }<br>}<br></code></pre></td></tr></table></figure><h3 id="interceptor-拦截器"><a href="#interceptor-拦截器" class="headerlink" title="interceptor : 拦截器"></a>interceptor : 拦截器</h3><ul><li><p>动态拦截请求</p></li><li><p>使用</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LoginInterceptor</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">HandlerInterceptor</span> {<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-comment">//判断是否放行</span><br> <span class="hljs-keyword">public</span> <span class="hljs-type">boolean</span> <span class="hljs-title function_">preHandle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler)</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-keyword">return</span> HandlerInterceptor.<span class="hljs-built_in">super</span>.preHandle(request, response, handler);<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-comment">//调用完资源后调用此方法</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">postHandle</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)</span> <span class="hljs-keyword">throws</span> Exception {<br> HandlerInterceptor.<span class="hljs-built_in">super</span>.postHandle(request, response, handler, modelAndView);<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-comment">//最后调用此方法</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">afterCompletion</span><span class="hljs-params">(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)</span> <span class="hljs-keyword">throws</span> Exception {<br> HandlerInterceptor.<span class="hljs-built_in">super</span>.afterCompletion(request, response, handler, ex);<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>资源配置</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">WebConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">WebMvcConfigurer</span> {<br><br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> LoginInterceptor loginInterceptor;<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-comment">//注册拦截器</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">addInterceptors</span><span class="hljs-params">(InterceptorRegistry registry)</span> {<br> <span class="hljs-comment">//注册拦截器并指定拦截资源,并排除login资源路径请求(也就是不拦截)</span><br> registry.addInterceptor(loginInterceptor).addPathPatterns(<span class="hljs-string">"/**"</span>).excludePathPatterns(<span class="hljs-string">"/login"</span>);<br> WebMvcConfigurer.<span class="hljs-built_in">super</span>.addInterceptors(registry);<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="SpringSecurity"><a href="#SpringSecurity" class="headerlink" title="SpringSecurity"></a>SpringSecurity</h2><ul><li>SpringSecurity是一个基于spring开发的非常强大的权限验证框架</li><li>核心功能<ul><li>认证</li><li>授权</li><li>攻击防护</li></ul></li></ul><h3 id="环境配置"><a href="#环境配置" class="headerlink" title="环境配置"></a>环境配置</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-starter-security<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>初始化器(可选)</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityInitializer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">AbstractSecurityWebApplicationInitializer</span> {<br> <span class="hljs-comment">//无需重写方法</span><br> <span class="hljs-comment">//这里实际上注册了一个Filter</span><br> <span class="hljs-comment">//Spring Boot应用中通常不需要手动搭建初始化器。Spring Boot会自动配置Spring Security,并且通过WebSecurityConfigurerAdapter来设置安全策略</span><br>}<br></code></pre></td></tr></table></figure><ul><li>Security配置类</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@EnableWebSecurity</span><span class="hljs-comment">//开启Sercurity</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> {<br>}<br></code></pre></td></tr></table></figure><h3 id="认证"><a href="#认证" class="headerlink" title="认证"></a>认证</h3><ul><li>用户登录认证</li></ul><h4 id="1-直接配置认证"><a href="#1-直接配置认证" class="headerlink" title="1.直接配置认证"></a>1.直接配置认证</h4><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">#Security本身自带一个默认的用户名密码,在不改变过滤器本身逻辑链条的情况下会在控制台输出默认值,yml配置文件可以修改这个唯一的用户名和密码,缺点是及其固定,适用及其简单的项目</span><br><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">security:</span><br> <span class="hljs-attr">user:</span><br> <span class="hljs-attr">name:</span> <span class="hljs-number">123</span><br> <span class="hljs-attr">password:</span> <span class="hljs-number">1324</span><br><br></code></pre></td></tr></table></figure><h4 id="2-基于内存的验证"><a href="#2-基于内存的验证" class="headerlink" title="2.基于内存的验证"></a>2.基于内存的验证</h4><ul><li>直接在代码中设置用户用于验证,内存存储</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@EnableWebSecurity</span><span class="hljs-comment">//开启Sercurity</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">SecurityConfig</span> {<br> <span class="hljs-meta">@Bean</span><br> <br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>快捷键</title>
<link href="/2024/01/25/%E5%BF%AB%E6%8D%B7%E9%94%AE/"/>
<url>/2024/01/25/%E5%BF%AB%E6%8D%B7%E9%94%AE/</url>
<content type="html"><![CDATA[<p>一些快捷键记录</p><span id="more"></span><ul><li><p>shift+enter </p><p>无视接下来的输入直接跳到下一行</p></li><li><p>alt+enter</p><p>自动生成返回类型</p></li><li><p>xml注释快捷键:ctrl+shift+/</p></li><li><p>ctrl + alt +t </p><p>代码块生成包围语句如try catch</p></li><li><p>ctrl+o</p><p>快速重写方法</p></li><li><p>快捷键ctrl+alt+v</p><ul><li>生成返回值</li></ul></li><li><p>1:ctrl+alt+L 代码格式化,自动排版</p></li><li><p>2:alt+enter 对象set自动化</p></li><li><p>3: Ctrl+Shift+f 快速进入全局查找页面</p></li><li><p>4:ctrl + r: 当前文件内容替换,</p></li><li><p>redis-cli -h 127.0.0.1 -p 6379:进入redis</p></li><li><p><a href="https://mybatis.org/generator/">MyBatis Generator</a>,dao层代码生成</p></li><li><p>ctrl+/ yaml多行zhu’shi</p></li><li><p>hexo clean && hexo g && hexo d && hexo s 上传博客</p></li></ul>]]></content>
</entry>
<entry>
<title>Mybatis入门</title>
<link href="/2024/01/24/Mybatis%E5%85%A5%E9%97%A8/"/>
<url>/2024/01/24/Mybatis%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<p>Mybatis连接数据库的学习与应用</p><span id="more"></span> <h1 id="Mybatis入门"><a href="#Mybatis入门" class="headerlink" title="Mybatis入门"></a>Mybatis入门</h1><h2 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h2><ul><li>简单配置</li></ul><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-comment">#springboot依赖:Mybatis framwork,Mysql Driver</span><br><span class="hljs-comment"></span><br><span class="hljs-comment"> #驱动类名称</span><br> <span class="hljs-attr">spring.datasource.driver-class-name</span>=<span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br><span class="hljs-comment"> #url</span><br> <span class="hljs-attr">spring.datasource.url</span>=<span class="hljs-string">jdbc:mysql://localhost:3306/database</span><br><span class="hljs-comment"> #用户名</span><br> <span class="hljs-attr">spring.datasource.username</span>=<span class="hljs-string">root</span><br><span class="hljs-comment"> #密码</span><br> <span class="hljs-attr">spring.datasource.password</span>=<span class="hljs-string">1234</span><br><span class="hljs-comment"> </span><br><span class="hljs-comment"> #mybatis日志输出</span><br> <span class="hljs-attr">mybatis.configuration.log-impl</span>=<span class="hljs-string">org.apache.ibatis.logging.stdout.StdOutImpl</span><br><span class="hljs-comment"> </span><br><span class="hljs-comment"> #yml格式</span><br><span class="hljs-comment"> #驱动类名称</span><br><span class="hljs-attr">spring</span>:<span class="hljs-string"></span><br> <span class="hljs-attr">datasource</span>:<span class="hljs-string"></span><br> <span class="hljs-attr">driver-class-name</span>: <span class="hljs-string">com.mysql.cj.jdbc.Driver</span><br> <span class="hljs-attr">url</span>: <span class="hljs-string">jdbc:mysql://localhost:3306/database</span><br> <span class="hljs-attr">username</span>: <span class="hljs-string">root</span><br> <span class="hljs-attr">password</span>: <span class="hljs-string">1234</span><br><span class="hljs-comment"></span><br><span class="hljs-comment"> #mybatis日志输出</span><br><span class="hljs-attr">mybatis</span>:<span class="hljs-string"></span><br> <span class="hljs-attr">configuration</span>:<span class="hljs-string"></span><br> <span class="hljs-attr">log-impl</span>: <span class="hljs-string">org.apache.ibatis.logging.stdout.StdOutImpl</span><br></code></pre></td></tr></table></figure><h2 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h2><ul><li><p>相对JDBC</p><blockquote><p>代码简洁优化</p><p>不用硬编码</p><p>原理:采用一个连接池来管理数据库连接,而不是一个一个去创建连接和释放</p></blockquote></li><li><p><strong>mybatis持久层框架实现jdbc所规定的规范并简化开发</strong></p></li><li><p><strong>数据连接池</strong>:管理连接的容器(通过spring依赖引入,mybatis使用连接池来优化功能呢,而jdbc默认情况下不使用)</p><ul><li>资源复用(连接不用重复创建)</li><li>提高响应速度</li><li>避免数据遗漏</li></ul></li><li><p><strong>预编译sql语句</strong></p><ul><li><p>mysql对于每一条完全相同的语句都会进行缓存,当检测到相同语句的时候直接从缓存提取</p></li><li><p>但是大多数情况是语句只是有细小的区别例如参数不同,但又不能检测到缓存</p></li><li><p>预编译将参数部分用一个占位符(例如 ?)替代,当编译阶段的时候直接传参数</p></li><li><p><strong>预编译减少了相同结构但是参数不同的sql语句的编译次数</strong></p></li><li><p>同时<strong>防止了sql注入</strong></p><blockquote><p>sql注入:通过输入的参数直接改变sql语句本身的逻辑从而进行破坏</p></blockquote></li></ul></li></ul><h2 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h2><h3 id="创建Mapper接口为mybatis实现提供类"><a href="#创建Mapper接口为mybatis实现提供类" class="headerlink" title="创建Mapper接口为mybatis实现提供类"></a>创建Mapper接口为mybatis实现提供类</h3><ul><li><p>前提:相应的实体类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><span class="hljs-comment">//@Data注解为Lombok注解用于减少代码重复</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> {<br> <span class="hljs-keyword">private</span> Integer id;<br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Short age;<br> <span class="hljs-keyword">private</span> Short gender;<br> <span class="hljs-keyword">private</span> String phone;<br>}<br></code></pre></td></tr></table></figure></li><li><p>相应的mapper接口</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Mapper</span><br><span class="hljs-comment">//运行时,自动生成mapper实现类对象(动态代理技术),并将此对象交给IOC容器管理</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Usermapper</span> {<br>}<br></code></pre></td></tr></table></figure></li></ul><h3 id="定义相应的功能方法"><a href="#定义相应的功能方法" class="headerlink" title="定义相应的功能方法"></a>定义相应的功能方法</h3><ul><li><p>最简单的无参方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Mapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Usermapper</span> {<br> <span class="hljs-meta">@Select("select *from user")</span><br> <span class="hljs-keyword">public</span> List<User> <span class="hljs-title function_">listall</span><span class="hljs-params">()</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p>有参方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Mapper</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">Usermapper</span> {<br> <span class="hljs-comment">//#{} ---> 这样的形式可以在sql语句之中加入参数,原理是在中间填入一个占位符 ‘?’,然后使用参数替代</span><br> <span class="hljs-comment">//${} ---> 也可以传递参数,但是原理是直接拼接成sql语句,存在注入问题</span><br> <br> <span class="hljs-comment">//当只有一个参数的时候</span><br> <span class="hljs-meta">@Delete("delete from emp where id=#{id}")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">delet</span><span class="hljs-params">(Integer id)</span>;<br> <br> <span class="hljs-comment">//多个参数,#{}内部是实体类的属性,对应的username和name属性等会从emp里面找</span><br> <span class="hljs-comment">//#{}内部的名字要和参数名字一致,编译阶段会保留参数名称然后进行对比</span><br> <span class="hljs-meta">@Insert("insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)"+</span><br><span class="hljs-meta"> "values(#{usename},#{name},#{gender},#{image},#{job},#{entrydate},#{deptld},#{createTime},"+ "#{updateTime})")</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">insert</span><span class="hljs-params">(Emp emp)</span>;<span class="hljs-comment">//实体类封装了属性</span><br> <span class="hljs-comment">//主键返回:某些场景需要知道插入数据的主键以便于查找</span><br> <span class="hljs-meta">@Options(useGeneratedKeys = true,keyProperty = "id")</span><span class="hljs-comment">//此注解将主键返回到emp的属性id上</span><br> <span class="hljs-meta">@Insert("insert into emp(username,name,gender,image,job,entrydate,dept_id,create_time,update_time)"+</span><br><span class="hljs-meta"> "values(#{usename},#{name},#{gender},#{image},#{job},#{entrydate},#{deptld},#{createTime},"+ "#{updateTime})")</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">insert</span><span class="hljs-params">(Emp emp)</span>;<br> <br> <span class="hljs-comment">//更新操作</span><br> <span class="hljs-meta">@Update("update emp set username=#{username},name=#{name},gender=#{gender},image=#{image},"+</span><br><span class="hljs-meta"> "job=#{job},entrydate=#{entrydate},update_time=#{updateTime},dept_id=#{deptId} where id=#{id}")</span><br> <span class="hljs-keyword">void</span> <span class="hljs-title function_">update</span><span class="hljs-params">(Emp emp)</span>;<br> <br> <span class="hljs-comment">//查询操作</span><br> <span class="hljs-comment">//由于字段封装的存在,某些名字不一致的属性不会进行传递</span><br> <span class="hljs-meta">@Select("select *from emp where id=#{id}")</span><br> Emp <span class="hljs-title function_">getById</span><span class="hljs-params">(Integer id)</span>;<br> <span class="hljs-comment">//解决方案一,字段重命名</span><br> <span class="hljs-meta">@Select("select id,username,password,image,name,gender,job,entrydate,"+</span><br><span class="hljs-meta"> "dept_id deptId,create_time createTime,update_time updateTime from emp where id=#{id}")</span><br> Emp <span class="hljs-title function_">getById</span><span class="hljs-params">(Integer id)</span>;<br> <span class="hljs-comment">//解决方案二,@Result注解</span><br> <span class="hljs-meta">@Results({</span><br><span class="hljs-meta"> @Result(column ="dept_id",property = "deptId"),</span><br><span class="hljs-meta"> @Result(column = "create_time",property = "createTime"),</span><br><span class="hljs-meta"> @Result(column = "update_time",property = "updateTime"),</span><br><span class="hljs-meta"> })</span><br> <span class="hljs-meta">@Select("select *from emp where id =#{id}")</span><br> Emp <span class="hljs-title function_">getById</span><span class="hljs-params">(Integer id)</span>;<br> <span class="hljs-comment">//解决方案三,开启mybatis自动映射,驼峰命名和下划线的映射 a_column --> aColumn</span><br> 配置中:mybatis.configuration.map-underscore-to-camel-<span class="hljs-keyword">case</span>=<span class="hljs-literal">true</span><br> <br> <span class="hljs-comment">//模糊多表查询</span><br> <span class="hljs-meta">@Select("select *from emp where name like '%${name}%' and gender = #{gender} and"+</span><br><span class="hljs-meta"> " entrydate between #{begin} and #{end} order by update_time desc ")</span><br> List<Emp> <span class="hljs-title function_">list</span><span class="hljs-params">(String name, <span class="hljs-type">short</span> gender, LocalDate begin,LocalDate end)</span>;<br> <span class="hljs-comment">//使用concat</span><br> <span class="hljs-meta">@Select("select *from emp where name like concat('%',#{name},'%') and gender = #{gender} and"+</span><br><span class="hljs-meta"> " entrydate between #{begin} and #{end} order by update_time desc ")</span><br> List<Emp> <span class="hljs-title function_">list</span><span class="hljs-params">(String name, <span class="hljs-type">short</span> gender, LocalDate begin,LocalDate end)</span>;<br>}<br></code></pre></td></tr></table></figure></li><li><p><strong>动态sql语句</strong>(使用xml配置,具体看下文)</p><ul><li>通过检测输入参数的不同来改变sql结构,动态执行</li><li>if标签集</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.mybastudy.entity.Emp"</span>></span><br> <span class="hljs-comment"><!--select *from emp where name like concat('%',#{name},'%') and gender = #{gender} and</span><br><span class="hljs-comment"> entrydate between #{begin} and #{end} order by update_time desc--></span><span class="hljs-comment"><!--这个注释运行会报错--></span><br> select *from emp<br> <span class="hljs-tag"><<span class="hljs-name">where</span>></span><span class="hljs-comment"><!--where标签会检测内部的条件成立情况,会自动去除and,以及当所有条件不成立的时候不生成where--></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"name!= null"</span>></span><span class="hljs-comment"><!--if进行条件判断,test内是判断条件--></span><br> name like concat('%',#{name},'%')<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"gender!= null"</span>></span><br> and gender = #{gender}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"begin!= null and end!=null"</span>></span><br> and entrydate between #{begin} and #{end}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">where</span>></span> <br> order by update_time desc<br><span class="hljs-tag"></<span class="hljs-name">select</span>></span><br><br><span class="hljs-comment"><!--set例子--></span><br><span class="hljs-tag"><<span class="hljs-name">update</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"update"</span>></span><br> update emp<br> <span class="hljs-tag"><<span class="hljs-name">set</span>></span><span class="hljs-comment"><!--与where标签类似--></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"username!=null"</span>></span>username=#{username},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"name!=null"</span>></span>name=#{name},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"gender!=null"</span>></span>gender=#{gender},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"image!=null"</span>></span>image=#{image},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"job!=null"</span>></span>job=#{job},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"entrydate!=null"</span>></span>entrydate=#{entrydate},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"updateTime!=null"</span>></span>update_time=#{updateTime},<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"deptId!=null"</span>></span>dept_id=#{deptId}<span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">set</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">where</span>></span><br> id=#{id}<br> <span class="hljs-tag"></<span class="hljs-name">where</span>></span><br><span class="hljs-tag"></<span class="hljs-name">update</span>></span><br></code></pre></td></tr></table></figure><ul><li><strong>@foreach: 遍历某个集合的所有元素一起嵌入到sql语句里面</strong></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">delete</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"deleteByIds"</span>></span><br> delete from emp where id in<br> <span class="hljs-tag"><<span class="hljs-name">foreach</span> <span class="hljs-attr">collection</span>=<span class="hljs-string">"ids"</span> <span class="hljs-attr">item</span>=<span class="hljs-string">"id"</span> <span class="hljs-attr">separator</span>=<span class="hljs-string">","</span> <span class="hljs-attr">open</span>=<span class="hljs-string">"("</span> <span class="hljs-attr">close</span>=<span class="hljs-string">")"</span>></span><br> #{id}<br> <span class="hljs-tag"></<span class="hljs-name">foreach</span>></span><br> <span class="hljs-comment"><!--collection表述要遍历的集合,从参数中取,item表示集合每个的元素(可自定义),seperator表示以某种方式连接,open和close表示前后字符--></span><br><span class="hljs-tag"></<span class="hljs-name">delete</span>></span><br></code></pre></td></tr></table></figure><ul><li><strong>@sql和include: 标识某一段重复且未来可能需要修改的sql语句,并允许其它地方通过引用直接标上,减少代码重复</strong></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">sql</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"commonselect"</span>></span><br> select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time<br> from emp<br><span class="hljs-tag"></<span class="hljs-name">sql</span>></span><span class="hljs-comment"><!--片段抽取--></span><br><br><span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.mybastudy.entity.Emp"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">include</span> <span class="hljs-attr">refid</span>=<span class="hljs-string">"commonselect"</span>/></span><span class="hljs-comment"><!--片段引用--></span><br> <span class="hljs-tag"><<span class="hljs-name">where</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"name != null"</span>></span><br> name like concat('%',#{name},'%')<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"gender != null"</span>></span><br> and gender = #{gender}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">if</span> <span class="hljs-attr">test</span>=<span class="hljs-string">"begin != null and end !=null"</span>></span><br> and entrydate between #{begin} and #{end}<br> <span class="hljs-tag"></<span class="hljs-name">if</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">where</span>></span><br> order by update_time desc<br><span class="hljs-tag"></<span class="hljs-name">select</span>></span><br></code></pre></td></tr></table></figure><blockquote><p> <strong>#{}不能出现在 ‘ ’ 之内</strong>—— ?不能出现在 ‘ ’ 内</p><p>当必须要出现在 ‘ ’ 时,使用${}代替</p><p>或者使用concat字符串拼接来代替</p></blockquote><blockquote><p> <strong>字段封装</strong>(名字不一样不会封装)</p><p> 实体属性名和字段返回属性名一致,mybatis自动封装,不一致则不会传递</p></blockquote></li></ul><h4 id="XML配置文件"><a href="#XML配置文件" class="headerlink" title="XML配置文件"></a>XML配置文件</h4><ul><li><p><strong>通过XML来代替@Select等注解</strong></p></li><li><p>XML 映射文件的名称与Mapper接口名称一致, 并且将XML映射文件和Mapper接口放置在相同包下(同包同名)</p></li><li><p>XML映 射文件的namespace属性为Mapper接口全限定名一致。</p></li><li><p>XML映射文件中sql语句的id与Mapper接口中的方法名-致, 并保持返回类型一致</p></li><li><p>一般格式</p></li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta"><?xml version=<span class="hljs-string">"1.0"</span> encoding=<span class="hljs-string">"UTF-8"</span> ?></span><br><span class="hljs-meta"><!DOCTYPE <span class="hljs-keyword">mapper</span></span><br><span class="hljs-meta"> <span class="hljs-keyword">PUBLIC</span> <span class="hljs-string">"-//mybatis.org//DTD Mapper 3.0//EN"</span></span><br><span class="hljs-meta"> <span class="hljs-string">"http://mybatis.org/dtd/mybatis-3-mapper.dtd"</span>></span><br><span class="hljs-tag"><<span class="hljs-name">mapper</span> <span class="hljs-attr">namespace</span>=<span class="hljs-string">"com.mybastudy.mapper.Empmapper"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">select</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"list"</span> <span class="hljs-attr">resultType</span>=<span class="hljs-string">"com.mybastudy.entity.Emp"</span>></span><span class="hljs-comment"><!--id为方法名,resulttype为单条记录的封装类型--></span><br> select *from emp where name like concat('%',#{name},'%') and gender = #{gender} and<br> entrydate between #{begin} and #{end} order by update_time desc<br> <span class="hljs-tag"></<span class="hljs-name">select</span>></span><br><span class="hljs-tag"></<span class="hljs-name">mapper</span>></span><br></code></pre></td></tr></table></figure><ul><li>当sql语句过于复杂的时候使用xml语句映射</li></ul><h3 id="使用相应的方法"><a href="#使用相应的方法" class="headerlink" title="使用相应的方法"></a>使用相应的方法</h3><ul><li><p>在可自动注入的类或者方法之中注入mapper对象(例如测试类)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Autowired</span><br><span class="hljs-keyword">private</span> Usermapper usermapper;<br><span class="hljs-meta">@Autowired</span><br><span class="hljs-keyword">private</span> Empmapper empmapper;<br><span class="hljs-meta">@Test</span><br><span class="hljs-keyword">void</span> <span class="hljs-title function_">contextLoads</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//</span><br> List<User> userlist =usermapper.listall();<br> userlist.stream().forEach(user -> System.out.println(user));<span class="hljs-comment">//打印每一个User</span><br><br> <span class="hljs-comment">//</span><br> empmapper.delete(<span class="hljs-number">17</span>);<br>}<br></code></pre></td></tr></table></figure></li></ul><h2 id="其它插件和功能"><a href="#其它插件和功能" class="headerlink" title="其它插件和功能"></a>其它插件和功能</h2><h3 id="MybatisX"><a href="#MybatisX" class="headerlink" title="MybatisX"></a>MybatisX</h3><ul><li>指出xml配置文件和mapper方法的对应关系,方便查找和纠错</li></ul><h3 id="pageHelper"><a href="#pageHelper" class="headerlink" title="pageHelper"></a>pageHelper</h3><ul><li>便于实现分页查询</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.github.pagehelper<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>pagehelper-spring-boot-starter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.4.6<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ul><li>操作</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> PageBean <span class="hljs-title function_">page</span><span class="hljs-params">(Integer page, Integer pageSize)</span> {<br> <span class="hljs-comment">//传统分页查询</span><br> <span class="hljs-comment">/*Long count=empMapper.count();</span><br><span class="hljs-comment"> Integer start = (page-1)*pageSize;</span><br><span class="hljs-comment"> List<Emp> empList =empMapper.page(start,pageSize);*/</span><br><br> <span class="hljs-comment">//pageHelper插件的分页查询</span><br> PageHelper.startPage(page,pageSize);<br> List<Emp> empList= empMapper.list();<br> <span class="hljs-comment">//list() 获取所有列表,交由pagehelper进行分割</span><br> Page<Emp> p = (Page<Emp>) empList;<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PageBean</span>(p.getTotal(),p.getResult());<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>Mysql</tag>
</tags>
</entry>
<entry>
<title>计算机网络基础</title>
<link href="/2023/12/20/%E8%AE%A1%E7%BD%91%E5%AD%A6%E4%B9%A0/"/>
<url>/2023/12/20/%E8%AE%A1%E7%BD%91%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<p>计算机网络基础知识点</p><span id="more"></span><h1 id="计网学习"><a href="#计网学习" class="headerlink" title="计网学习"></a>计网学习</h1><h1 id="因特网"><a href="#因特网" class="headerlink" title="因特网"></a>因特网</h1><h2 id="因特网概述"><a href="#因特网概述" class="headerlink" title="因特网概述"></a>因特网概述</h2><h3 id="网络,互联网和因特网"><a href="#网络,互联网和因特网" class="headerlink" title="网络,互联网和因特网"></a>网络,互联网和因特网</h3><ul><li>网络由若干个结点(Node)和链路(link)组成</li><li>不同网络连接起来叫互联网</li><li>因特网(Internet)是最大的互联网</li></ul><h3 id="因特网的三个阶段"><a href="#因特网的三个阶段" class="headerlink" title="因特网的三个阶段"></a>因特网的三个阶段</h3><ul><li>从单个ARPANET(TCP/IP)向互联网发展</li><li>逐步构建成三级结构因特网</li><li>逐渐形成了多层次ISP(服务提供者如联通电信)结构的因特网</li></ul><blockquote><p>因特网三层结构:主干网(国际性区域)->ISP运营商层->本地层(公司,学校,家等)</p></blockquote><h3 id="因特网的标准化工作"><a href="#因特网的标准化工作" class="headerlink" title="因特网的标准化工作"></a>因特网的标准化工作</h3><ul><li>ISOC国际组织对因特网全面管理</li></ul><h2 id="三种交换"><a href="#三种交换" class="headerlink" title="三种交换"></a>三种交换</h2><h3 id="电路交换"><a href="#电路交换" class="headerlink" title="电路交换"></a>电路交换</h3><ul><li><p>起因:所有电话两两相连极不现实、</p></li><li><p>电话交换机:将任意两个电话按需要连接</p></li><li><p>电路交换:建立连接占用资源,结束释放</p></li><li><p>对于计算机数据不通用,因为计算机数据是突发的,建立释放连接需要的资源消耗过大,优点是时延小</p></li></ul><h3 id="报文交换"><a href="#报文交换" class="headerlink" title="报文交换"></a>报文交换</h3><ul><li>直接发送报文,由路由器进行转发,不需要建立连接</li><li>缺点:报文过大时容易占用电路,不利于差错控制,存储转发时延过大</li></ul><h3 id="分组交换(一般使用方式)"><a href="#分组交换(一般使用方式)" class="headerlink" title="分组交换(一般使用方式)"></a>分组交换(一般使用方式)</h3><ul><li>将需要转发的数据(报文)分组然后分别添加头部识别</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E5%9B%A0%E7%89%B9%E7%BD%91-%E4%B8%89%E7%A7%8D%E4%BA%A4%E6%8D%A2.png" alt="image-20231220143135866"></p><h2 id="计算机网络定义以及分类"><a href="#计算机网络定义以及分类" class="headerlink" title="计算机网络定义以及分类"></a>计算机网络定义以及分类</h2><ul><li><p>互连,自治的计算机集合</p></li><li><p>按照覆盖范围分类</p><blockquote><p>广域网WAN:不同国家之间</p><p>城域网MAN:城市之间</p><p>局域网LAN:家,公司,学校之间</p><p>个域网PAN:。。</p></blockquote></li></ul><h2 id="计算机网络性能指标(重点)"><a href="#计算机网络性能指标(重点)" class="headerlink" title="计算机网络性能指标(重点)"></a>计算机网络性能指标(重点)</h2><h3 id="速率"><a href="#速率" class="headerlink" title="速率"></a>速率</h3><ul><li><p>比特(bit):计算机之中的数据量单位,通常意义为一个1或者0</p></li><li><p>常用数据量单位:</p><blockquote><p>8位 bit = 1 byte</p><p><strong>KB = 2^10 B</strong></p><p>MB = K*KB = 2^20 B</p><p>GB = K*MB = 2^30 B</p><p>TB = K*GB = 2^40 B</p></blockquote><p>对比速率常用单位</p><blockquote><p>bit/s</p><p><strong>kb/s = 10^3 b/s(bps)</strong></p><p>Mb/s = k*kb/s = 10^6 bps</p><p>Gb/s = k*Mb/s = 10^9 bps</p><p>Tb/s = k*Gb/s = 10^12 bps</p></blockquote><p>例子:MB/Mbps = 2^20b / 10^6 bps = 8.38 s 约等于 B/bps = 8b/bps =8 s (可以估算使用)</p><blockquote><h2 id="字与字节的关系"><a href="#字与字节的关系" class="headerlink" title="字与字节的关系"></a>字与字节的关系</h2><p><strong>字和字节都是计算机的存储单元</strong>。字由若干个字节组成,一个字节是8个比特bit。字的位数叫做字长,即cpu一次处理二进制代码的位数。字的长度与计算架构有关,比如32位机,一个字就是32位,换算成字节就是4字节;同样的64位机,一个字就是64位,也就是8字节。字也是计算机一次处理数据的最大单位。</p></blockquote></li></ul><h3 id="带宽"><a href="#带宽" class="headerlink" title="带宽"></a>带宽</h3><ul><li><p>模拟信号系统之中的意义</p><ul><li><p>信号所包含的各种不同频率的成分所占据频率范围</p></li><li><p>单位:Hz (kHz ,MHz,GHz)</p></li></ul></li><li><p>计算机网络之中的意义</p><ul><li>表示通信线路所能传送数据的能力,表示<strong>“最高数据率”</strong></li><li>单位: b/s (kb/s,Mb/s ,Gb/s ,Tb/s)<strong>也称兆</strong></li></ul></li></ul><h3 id="吞吐量"><a href="#吞吐量" class="headerlink" title="吞吐量"></a>吞吐量</h3><ul><li>单位时间内通过某个网络的数据量</li><li>吞吐量受带宽或额定速率限制</li></ul><h3 id="时延"><a href="#时延" class="headerlink" title="时延"></a>时延</h3><ul><li>由不同部分组成</li><li>发送时延,传播时延(多个传播线路),处理时延(多个路由器)</li><li><strong>发送时延</strong><ul><li><strong>分组长度(b) / 发送速率 (b/s)</strong></li></ul></li><li><strong>传播时延</strong><ul><li><strong>信道长度(m)/ 电磁传播率(m/s)</strong></li><li>不同介质<strong>信号传播率</strong>:自由空间:3<em>10^8 m/s ,铜线:2.3</em>10^8 m/s,光纤:2*10^8 m/s</li></ul></li><li>处理时延<ul><li>难以计算</li></ul></li></ul><h3 id="时延带宽积"><a href="#时延带宽积" class="headerlink" title="时延带宽积"></a>时延带宽积</h3><ul><li><strong>传播时延与带宽的乘积</strong></li></ul><h3 id="往返时间"><a href="#往返时间" class="headerlink" title="往返时间"></a>往返时间</h3><ul><li>RTT:一次双向交互所需要的时间</li></ul><h3 id="利用率"><a href="#利用率" class="headerlink" title="利用率"></a>利用率</h3><ul><li>信道利用率:某信道被利用的占比</li><li>网络利用率:全网络信道利用率占权平均</li><li><strong>利用率越高,时延越高</strong>,50%占比时会使时延翻倍,50%往后时延急剧增大</li></ul><h3 id="丢包率"><a href="#丢包率" class="headerlink" title="丢包率"></a>丢包率</h3><ul><li>分组丢失率,丢失分组占总分组比率</li></ul><h2 id="计算机分层体系结构"><a href="#计算机分层体系结构" class="headerlink" title="计算机分层体系结构"></a>计算机分层体系结构</h2><ul><li><p><strong>OSI</strong>模型:应用层,表示层,会话层,运输层,网络层,数据链路层,物理层</p></li><li><p><strong>TCP/IP结构体系</strong>:应用层,运输层,网际层,网络接口层</p></li><li><p><strong>五层体系架构</strong>:</p><blockquote><p>应用层: 实现特定网络应用问题 </p><p>运输层: 解决进程之间基于网络通信问题</p><p>网络层: 解决分组在多个网络上传输的问题</p><p>数据链路层: 解决一个网络上或者链路上传输的问题</p><p>物理层:解决使用何种信号传输比特的问题</p></blockquote><ul><li><p>应用举例:</p><p><strong>HTTP报文(应用层)+ TCP头部(运输层)+ IP首部(网络层)+ 添加首部尾部成帧(数据链路层)+前导码(物理层)</strong></p><p>之后一层层向上传播剥离</p></li><li><p>一般描述:</p><p>在协议的控制下,同层对等实体之间能够进行逻辑通信(使用下一层服务)从而向上一层提供服务</p></li></ul></li></ul><h1 id="物理层"><a href="#物理层" class="headerlink" title="物理层"></a>物理层</h1><ul><li><strong>为不同传输媒体设计适当的传输模式以传输比特数据流</strong></li></ul><h2 id="不同的传输媒体"><a href="#不同的传输媒体" class="headerlink" title="不同的传输媒体"></a>不同的传输媒体</h2><ul><li><p>导引型传输媒体</p><ul><li>同轴电缆(局域网领域后被集线器代替),双绞线,光纤(容量大,损耗小,广域网使用),电力线</li></ul></li><li><p>非导引型传输媒体</p><ul><li>无线电波,微波,红外线,可见光</li></ul></li></ul><h2 id="不同的传输方式"><a href="#不同的传输方式" class="headerlink" title="不同的传输方式"></a>不同的传输方式</h2><h3 id="串行和并行"><a href="#串行和并行" class="headerlink" title="串行和并行"></a>串行和并行</h3><ul><li>串行传输:一个接一个,只有一个传输口,常用于计算机之间传输</li><li>并行传输:一次性传多根,成本高,主要用于计算机内部的传输</li></ul><h3 id="同步与异步"><a href="#同步与异步" class="headerlink" title="同步与异步"></a>同步与异步</h3><ul><li><strong>同步传输</strong>:在比特信号中间进行检测信号,这之中存在时间误差,字节长度会累计误差,<ul><li>时钟同步:<ul><li>外同步:外加一根时钟信号线单独传输时钟信号</li><li>内同步:将同步信号编码发送到数据之中一起传输(例如曼彻斯特编码)</li></ul></li></ul></li><li><strong>异步传输</strong>:每个字节由起始表明,即每个字节内每个比特同步(间隔一样),字节之间不同步(间隔不一样)</li></ul><h3 id="单工,半双工,全双工"><a href="#单工,半双工,全双工" class="headerlink" title="单工,半双工,全双工"></a>单工,半双工,全双工</h3><ul><li>单工 :只能往一个方向发送数据,即一个结点只能固定收或者固定发(收音机)</li><li>半双工:一个节点可以发也可以收,但是同时只能执行一样(BB机)</li><li>全双工:可以同时发和收(电话)</li></ul><h2 id="编码与编制"><a href="#编码与编制" class="headerlink" title="编码与编制"></a>编码与编制</h2><ul><li>信号的演变</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E7%89%A9%E7%90%86%E5%B1%82-%E7%BC%96%E7%A0%81%E4%B8%8E%E7%BC%96%E5%88%B6.png" alt="image-20231220201855337"></p><h3 id="常用编码"><a href="#常用编码" class="headerlink" title="常用编码"></a>常用编码</h3><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E7%89%A9%E7%90%86%E5%B1%82-%E7%BC%96%E7%A0%812.png" alt="image-20231220203120009"></p><ul><li><strong>不归零编码</strong>:高低电平区分比特0和1,存在同步问题,无法确认连续的0和1</li><li><strong>归零编码</strong>:后半电平重回0电平,以此区分每个码元,但是大部分数据带宽都用来传输归零而浪费了,编码效率低</li><li><strong>曼彻斯特编码</strong>:码元中间的条边既表示数据又表示时钟,被传统以太网应用(10 Mb/s)</li><li><strong>差分曼彻斯特编码</strong>:跳变仅仅表示时钟,码元开始处电平是否发生变化表示数据,相较于原版变化更少</li></ul><h3 id="常用调制"><a href="#常用调制" class="headerlink" title="常用调制"></a>常用调制</h3><ul><li><p>将数字基带信号转化为模拟信号</p><ul><li><p>调幅:有振幅为1,无为0</p></li><li><p>调频:频率识别01</p></li><li><p>调相:初相位不同代表不同比特</p></li></ul></li><li><p>缺点:一个比特一个信号,浪费</p></li><li><p>改进:因为频率和相位是相关的,所以可以同时调整相位和振幅以代表不同信号</p></li><li><p><strong>正交振幅调制</strong>(QAM)</p><p>例子:QAM-16</p><ul><li>在星图之中与原点之间距离代表振幅,x轴夹角为相位</li><li><strong>可以调制出16种码元,所以一个码元可以传输4个比特</strong></li><li>码元种类越多,出现偏差时出现识别错误概论越大<ul><li>改进方法:4个比特关系采用<strong>格雷码</strong>,相邻码元仅仅有一个比特不同</li></ul></li></ul></li></ul><h3 id="信道极限容量"><a href="#信道极限容量" class="headerlink" title="信道极限容量"></a>信道极限容量</h3><ul><li><p>信号传输会产生失真,影响因素</p><ul><li><strong>码元传输速率</strong></li><li>信号传输距离</li><li>噪声干扰等</li></ul></li><li><p><strong>奈氏准则</strong>:理想状态下码元传输速率(调制速度)上限</p><ul><li><p>理想低通信道的最高码元传输速率 = 2W Baud = 2W 码元/秒(<strong>一般情况下,除非题目说明</strong>)</p></li><li><p>理想带通信道的最高码元传输速率 = W Baud = W 码元/秒</p><blockquote><ul><li>理想低通信道:信号的所有低频分量,只要其频率不超过某个上限值,都能够不失真地通过此信道。而频率超过该上限值的所有高频分量都不能通过该信道</li><li>理想带通信道:只允许上下限之间的信号频率成分不失真地通过,其他频率成分不通过</li></ul></blockquote></li><li><p>W :信道带宽(Hz)</p></li><li><p>Baud:波特,即码元/秒,又称<strong>波特率</strong></p><ul><li>在一个码元只携带一比特时,<strong>波特率和比特率</strong>数值上相等</li><li><strong>当一个码元携带n比特信息时(多元制),则波特率转换成比特率,数值要乘以n</strong>,例如时QAM-16的时候要乘以4</li></ul></li></ul></li><li><p><strong>香农公式</strong></p><blockquote><p><strong>C = W * log 2 ( 1 + S/N )</strong></p></blockquote><ul><li>C:信道极限传输速率(单位b/s)</li><li>W:信道带宽(单位:Hz)</li><li>S:信道内传输信号平均功率</li><li>N:信道内高斯噪声功率</li><li><strong>信噪比(dB)= 10 * log10(S/N)</strong></li></ul><blockquote><p> 举例:相同信道在无噪声条件下的速率要大于信噪比为30dB的极限传输速率,信号状态至少为:</p><p>10 * log10(S/N)= 30</p><p>2W * log2(X) = W * log 2 ( 1 + 1000 )</p><p>x>=32</p></blockquote></li><li><p><strong>奈奎斯特采样率(Nyquist sampling rate)</strong></p><p>指在进行模拟信号数字化(采样)时,采样频率应该至少是信号最高频率的两倍。这是为了避免采样引起的混叠(Aliasing)现象,即高频信号的频谱与采样频率的一部分重叠,导致无法正确还原原始信号。</p><p>奈奎斯特采样率的计算公式是:</p><blockquote><p><strong>Nyquist Rate=2×Highest Frequency Component in the SignalNyquist Rate=2×Highest Frequency Component in the Signal</strong></p></blockquote></li></ul><h1 id="数据链路层"><a href="#数据链路层" class="headerlink" title="数据链路层"></a>数据链路层</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><ul><li><p>链路(Link):就是从一个结点到相邻结点的一段物理电路,从而中间没有其它任何交换结点</p></li><li><p>数据链路(Data Link) :就是将是西安通信协议的硬件和软件加到链路,构成了数据链路</p></li><li><p>数据链路层作用(点对点)</p><ul><li>封装成帧</li><li>差错检测</li><li>可靠传输</li></ul></li><li><p>广播信道(共享式局域网)</p><ul><li>发送帧传输线路问题</li><li>不同信号碰撞问题:<strong>CSMA/CD</strong> 控制协议</li></ul></li><li><p>交换式局域网</p><ul><li>通过交换机交换信息在有线领域取代了共享式局域网,无线局域网仍然时共享式局域网</li></ul></li></ul><h3 id="封装成帧"><a href="#封装成帧" class="headerlink" title="封装成帧"></a>封装成帧</h3><ul><li><p><strong>以太网V2</strong>(版本二)MAC帧格式</p><blockquote><p>帧头 :目的地址(6 Byte)+ 源地址(6 Byte)+ 数据类型(2 Byte) </p><p>数据载荷:46~1500 Byte</p><p>帧尾:FCS</p></blockquote></li><li><p><strong>PPP</strong>帧格式</p><blockquote><p>帧头 :标志(1 Byte)+ 地址(1 Byte)+ 控制(1 Byte)+ 协议(2 Byte) </p><p>数据载荷:<1500 Byte</p><p>帧尾:FCS + 标志(1 Byte)</p></blockquote></li><li><p>帧在传输给物理层后会加上8字节的前导码,分别是1字节的帧开始定界符和7字节的前同步码,帧的后面时96比特的时间,称为<strong>帧间间隔</strong></p></li><li><p><strong>帧定界符</strong>:帧前后界定帧</p><ul><li>问题:数据内包含有与帧界定符一样的数据如何识别?</li><li>解决办法:<ul><li>在数据内相同字节前面加上转义字符,如果数据内还含有与转义字符一样的数据,则一样在前面加上转义字符</li><li>比特填充例子:<ul><li>在和帧定界符一样的数据内插入一个比特0或者1,接收时剔除</li></ul></li></ul></li><li>考虑到差错控制等因素,帧数据部分有上限称为<strong>MTU最大传送单元</strong></li></ul></li></ul><h3 id="差错检测"><a href="#差错检测" class="headerlink" title="差错检测"></a>差错检测</h3><ul><li>误码率:传输错误比特占总比特数的比率为误码率BER</li></ul><h4 id="奇偶校验"><a href="#奇偶校验" class="headerlink" title="奇偶校验"></a>奇偶校验</h4><ul><li>在传输数据后面加上一位奇偶校验码</li><li>缺点:无法检测多个或者偶数个误码</li></ul><h4 id="循环冗余校验码CRC"><a href="#循环冗余校验码CRC" class="headerlink" title="循环冗余校验码CRC"></a>循环冗余校验码CRC</h4><ul><li>收发双方约定好一个生成多项式G(X)</li><li>发送防基于待发送的数据和生成多项式计算出差错检测码(冗余码),将其添加到待传输数据的后面一起传输</li><li>接收方通过生成多项式来计算收到的庶几乎是否产生了误码</li></ul><blockquote><p> 举例:G(x) = x^3 + x^2 + 1 ,发送信息为 101001</p><p>1.提取系数(x^3 到 x^0) 1101</p><p>2.在待发送信息后面加上生成式G(x)最高次数(3)个的0,也就是101001000</p><p>3.做“除法”,101001000除以1101,不是减去而是做异或操作</p><p>得到余数为001,添加到发送信息后面,接收方将所有数据除以产生式系数余数为0则表示没有误码</p></blockquote><ul><li>检测码无法进行纠错,一般都是使用CRC码</li></ul><h3 id="可靠传输的基本概念"><a href="#可靠传输的基本概念" class="headerlink" title="可靠传输的基本概念"></a>可靠传输的基本概念</h3><ul><li><p>当检测到传输错误的时候该如何处理</p></li><li><p>可靠传输服务:<strong>尽可能实现发送端发送什么,接收端就收到什么</strong></p></li><li><p>不可靠传输服务:仅仅丢弃误码的帧</p></li><li><p>一般情况下,有限链路的误码率较低,为了减少开销,并不要求数据链路层提供可靠服务,交由上层处理</p></li><li><p>无线电路易受干扰,因此要求数据链路层必须向上册提供可靠传输服务</p></li><li><p>传输差错除了比特差错还有分组丢失,分组失序,分组重复等。这些交由上层处理</p></li></ul><h2 id="几种可靠传输的协议"><a href="#几种可靠传输的协议" class="headerlink" title="几种可靠传输的协议"></a>几种可靠传输的协议</h2><ul><li>这几种可靠传输协议不局限于数据链路层</li></ul><h3 id="停止等待协议SW(-或-自动请求重传协议ARQ)"><a href="#停止等待协议SW(-或-自动请求重传协议ARQ)" class="headerlink" title="停止等待协议SW( 或 自动请求重传协议ARQ)"></a>停止等待协议SW( 或 自动请求重传协议ARQ)</h3><ul><li><p><strong>接收方确认收到数据分组后才能发送下一分组</strong></p></li><li><p>发送方发送一个分组(DATA)后就停止发送,等待收到接收方确认分组(<strong>ACK</strong>)才继续发送</p></li><li><p>问题1:刚开始发送就丢失,接收方无法得知是否发送了分组</p><ul><li>解决办法:设置一个<strong>超时计时器</strong>,超过时间将重传,设置时间一般为略大于接收双方平均往返时间</li></ul></li><li><p>问题2:发送方发送后,接收方确认分组丢失,发送方超时重传,接收方无法确定是否是重复分组,造成分组重复</p><ul><li>解决办法:发送方给<strong>数据分组带上分组序号</strong>,使得接收方可以识别,<strong>且只需要一个比特编号因为最多同时两组</strong></li></ul></li><li><p>问题3:发送方发送0号后,接收方0号回答延迟了,发送方超时重传0号,并在之后接收到确认分组,发送下一个分组1号,接收方收到超时重传的0号又发送一个确认分组因为无法确定是不是第一个回答丢失了,发送方因此收到两个确认分组,造成重复确认</p><ul><li>解决办法:给<strong>确认分组带上序号</strong></li></ul></li><li><p>注意:当数据分组有误码时候,可以发送否认分组(<strong>NAK</strong>)示意发送方重新发送</p></li><li><p><strong>信道利用率U</strong></p><ul><li><p>忽略处理时延,十分低</p><blockquote><p><strong>U = Td / (Td + RTT + Ta)</strong></p><p>Td:数据发送(传输时延)耗费时间</p><p>RTT:数据<strong>往返</strong>(传播时延)时间</p><p>Ta:发送确认分组的时间 (因远小于Td,一般可忽略)</p></blockquote></li></ul></li></ul><h3 id="回退N帧协议GBN-(滑动窗口协议)"><a href="#回退N帧协议GBN-(滑动窗口协议)" class="headerlink" title="回退N帧协议GBN (滑动窗口协议)"></a>回退N帧协议GBN (滑动窗口协议)</h3><ul><li><p>采用3个比特给分组编序号,即0~7</p></li><li><p>发送窗口尺寸Wt取值:<strong>1< Wt <= 2^3 - 1</strong> </p></li><li><p>接收窗口尺寸Wr取值只能为1</p></li><li><p>无差错情况:</p><ul><li>发送方不断发送发送窗口序号的数据分组</li><li>接收方每确认(<strong>和接收窗口序号比对</strong>)一个数据分组就将接收窗口向后移动,同时发送确认分组</li><li>发送方收到确认分组后同样向后移动,同时新的序号加入窗口,继续发送</li></ul></li><li><p>累积确认:一次性确认多个分组减少占用资源,但是容易出错</p></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3%E5%8D%8F%E8%AE%AE.png" alt="image-20231221232743895"></p><ul><li><p>发送数据有误码</p><ul><li>接收方只发送接收窗口前确认序列</li><li><strong>发送方收到后将整组序列重传</strong>(因此在通信质量差时,这个协议效率并不比停止等待高)</li></ul></li><li><p>发送窗口尺寸等于上限</p><ul><li>发送方发送0~7组数据,接收方回答确认分组7,当确认分组丢失时候,发送方超时重传8组数据,接收方无法分辨这一组是重传还是下一组的8组数据,会视为确认成功继续接收</li></ul></li></ul><h3 id="选择重传协议SR"><a href="#选择重传协议SR" class="headerlink" title="选择重传协议SR"></a>选择重传协议SR</h3><ul><li>Wr不再限制为1,Wr取值一般可与Wt相同</li><li>不再使用累积确认</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-%E9%80%89%E6%8B%A9%E9%87%8D%E4%BC%A0%E5%8D%8F%E8%AE%AE.png" alt="image-20231221232544313"></p><ul><li><p>接收方收到数据的时候将对接收窗口所有序列进行比对,然后对确认成功的序列进行滑动</p></li><li><p>数据中间丢失只能靠发送窗口超时重发,但<strong>只需要重传错误分组</strong></p></li><li><p>Wt取值问题:<strong>1 < Wt <= 2^(n-1)</strong> ,超过这个范围会出现相似的无法区分新旧分组问题</p></li><li><p>Wr取值问题:1 < Wr <= Wt</p></li></ul><h2 id="点对点协议PPP"><a href="#点对点协议PPP" class="headerlink" title="点对点协议PPP"></a>点对点协议PPP</h2><ul><li>三部分构成<ul><li>对各种协议封装方法(封装成帧)</li><li>链路控制协议LCP (用于建立,配置以及测试数据链路连接)</li><li>一套网络控制协议NCPs (用以支持不同的网络层协议)</li></ul></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-PPP%E5%8D%8F%E8%AE%AE%E5%B8%A7.png" alt="image-20231221234107598"></p><ul><li><p>透明传输问题:帧数据部分和定界符号一样</p><ul><li>面向字节的异步链路:字节填充法:插入转义字符</li><li>面向比特的同步电路:比特填充法:插入比特流,例如连续5个1,则填充一个0</li></ul></li><li><p>差错检测:</p><ul><li>CRC计算:x^16 + x^12 + x^5 + 1,结果检验通过查表比对减少cpu占用</li><li>出现差错时候直接放弃帧,因此PPP提供的是不可靠传输服务</li></ul></li><li><p>工作状态</p></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-PPP%E5%B7%A5%E4%BD%9C%E7%8A%B6%E6%80%81.png" alt="image-20231221235011792"></p><h2 id="媒体接入控制(MAC)"><a href="#媒体接入控制(MAC)" class="headerlink" title="媒体接入控制(MAC)"></a>媒体接入控制(MAC)</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><ul><li><p>共享信道中信号容易发生碰撞,要考虑如何处理和协调占用</p></li><li><p>组成</p><ul><li>静态划分:按照频分,时分,码分进行信道划分为每一个划分固定信道<ul><li>缺点:不灵活,信道利用率低,通常再无线网络的物理层使用</li></ul></li><li>动态划分<ul><li>受控接入<ul><li>集中控制:建立一个主站使用轮询制度询问是否需要发送数据,缺点是单点故障问题大</li><li>分散控制(传花球):使用一个令牌在各个结点之间传播,拿到的可以传送数据</li></ul></li><li>随机接入<ul><li>所有站点通过竞争来发送数据,具体方式各有差异,<strong>共享式以太网便是采用这种方式</strong></li></ul></li></ul></li></ul></li><li><p>注意:随着技术的发展,更高性能的点对点链路和链路交换机式的局域网在有线领域已经完全取代了共享式局域网</p><p> 但是无线网络因为其特性仍然使用这种技术</p></li></ul><h3 id="静态划分"><a href="#静态划分" class="headerlink" title="静态划分"></a>静态划分</h3><ul><li><p>信道复用,多个用户共用一个信道</p></li><li><p>频分复用FDM</p><ul><li>复用器将不同子信道(子频带)调节到各个频带上,中间设置隔离频带</li><li>分用器将信道里面的各个子频带分开发送</li></ul></li><li><p>时分复用TDM</p><ul><li>将时间划分为各个不同的时隙,轮流将时隙分配给不同用户使用</li><li>每一轮(所有用户都使用一次)称为一个TDM帧</li></ul></li><li><p>波分复用WDM(光的频分复用)</p><ul><li><p>光调制器将多路光载波划分为不同波长的光,通过光复用器合在一起在光纤上传播,因为光纤的衰减性还</p><p>必须设置<strong>掺铒光纤放大器EDFA</strong>放大,最后进行光分用器</p></li><li><p>每一路的光载波波长必须相同,且传播速度一致,<strong>当n路传播速率为v光复用后,总光路的传播速率为n*v</strong></p></li></ul></li><li><p>码分复用CDM</p><ul><li>将频带资源划分为很多子信道,每个只占一部分,用户根据特殊的码分序列区分</li><li>一个站要发送比特1:则发送他自己的 m bit码分序列</li><li>一个站要发送比特0:则发送他自己的 m bit码分序列的二进制反码</li><li>不同站之间的码分序列必须不相同且相互正交(内积为0)【正交时将0看作-1】</li><li>接收方将收到的数据加起来(每一位都相加)和各个站点的码分序列进行相乘,结果为1则视为此站发送了比特1,结果为-1则是比特0,为0则是没有发送(根据各个码分序列相交)</li></ul></li></ul><h3 id="动态划分"><a href="#动态划分" class="headerlink" title="动态划分"></a>动态划分</h3><p>随机接入</p><ul><li>多址接入MA <ul><li>多个站连接在一条总线上。竞争使用总线</li></ul></li><li>载波监听CS<ul><li>每个站在发送帧之前先要检测一下总线是否有其他站点在发送帧(先听后说)</li><li>若检测到忙,则继续检测并等待总线空闲96bit时间,然后发送这个帧</li></ul></li><li>碰撞检测CD<ul><li>每一个正在发送的帧的站点边发送帧边检测碰撞(边说边听)</li><li>检测到碰撞之后立刻重新发送,退避一段时间后发送</li></ul></li></ul><h4 id="CSMA-CD-(广播信道的有线局域网)"><a href="#CSMA-CD-(广播信道的有线局域网)" class="headerlink" title="CSMA/CD (广播信道的有线局域网)"></a>CSMA/CD (广播信道的有线局域网)</h4><ul><li><p>状态转换</p><ul><li>持续监听 –> 发送数据 –> 碰撞检测 ->退避 ->重新监听</li></ul></li><li><p>碰撞检测的争用期(碰撞窗口)</p><ul><li>当信息碰撞到其它站点的信息时会<strong>原路返回并被站点检测到</strong></li><li>因此,发生碰撞的时候,发送出去的信息到回来的时间<strong>会小于整个以太网端到端往返的传播时间</strong></li><li>这个传播时间被称为争用期或碰撞窗口</li><li>碰撞窗口越长,碰撞概率越大,因此总长度不能太大</li></ul></li><li><p><strong>最小帧长</strong></p><ul><li>如果主机发送的帧太短,则<strong>在帧发送完毕后就不会再对其负责</strong>,然而帧仍然在信道传播,当发生碰撞的时候,主机将不会检测到碰撞并重发帧</li><li><strong>以太网规定最小帧长为64字节,即512比特</strong>(即为争用期)</li><li>最小帧长确保了主机在发送完成之前就检测到是否发生了碰撞</li><li>当检测到发生的时候立马停止发送,已经发送的<strong>小于64字节的帧会被视为无效帧</strong></li></ul><blockquote><p><strong>最小帧长=2 * 数据传输速率 * 传输时延</strong></p></blockquote></li><li><p><strong>最大帧长</strong></p><ul><li>主机缓存区有上限,且帧过长会导致信道堵塞使其它主机服务无法满足</li><li>帧64 ~ 1518字节,数据46 ~ 1500</li></ul></li><li><p>退避时间</p><ul><li>退避时间 = 基本退避时间(争用期2t)* 随机数r </li><li>r 从离散的整数集合{0,1,……..,2^k -1} 随机选出一个数</li><li>k = min {重传次数,10} </li><li>当k > 16 时候,丢弃该帧,并向上层报告</li></ul></li><li><p>信道利用率(不考虑碰撞等情况)</p><ul><li>S = t0 / ( t0 + t )</li><li>t0:发送时间</li><li>t:传播时间</li></ul></li></ul><h4 id="CSMA-CA-(广播信道的无线局域网)"><a href="#CSMA-CA-(广播信道的无线局域网)" class="headerlink" title="CSMA/CA (广播信道的无线局域网)"></a>CSMA/CA (广播信道的无线局域网)</h4><ul><li><p>由于无线信道条件特殊,接收信号强度远小于发送信号强度并且存在隐蔽站(无法检测其它站点信号)问题,无法实现碰撞检测CD</p></li><li><ol start="802"><li>11无线局域网采用CSMA/CA协议,使用碰撞避免CA功能</li></ol></li><li><p>由于不可能完全避免且无线信道误码率较高,所以碰撞避免实际上是停止等待协议的翻版</p></li><li><p>802.11实际上在MAC层定义了两种不同的媒体接入方式</p><ul><li>分布式协调功能DCF:没有中心站点,各自竞争,是默认的主要使用方式</li><li>点协调功能PCF:集中控制算法在接入点AP实现集中控制,使用较少</li></ul></li><li><p>帧间间隔IFS</p><ul><li>所有站点必须在持续检测到信道空闲在一段指定时间后才能发送帧,这个时间被称为IFS</li><li>帧间间隔长度主要受帧优先级影响,高优先级间隔更短<ul><li>短帧间间隔SIFS:最短帧间间隔,用于分隔开属于同一次对话的各个帧,多用来发送ACK帧,CTS帧等</li><li>DCF帧间间隔DIFS:多用来发送数据帧和管理帧</li></ul></li></ul></li><li><p>退避算法</p><ul><li>减少碰撞</li><li>使用时机:<ul><li>在发送数据帧之前检测到信道处于忙状态时,在经过DIFS后,还要退避一段时间</li><li>重传帧时</li><li>成功发送一个帧后连续发送下一个帧时</li></ul></li><li>执行退避算法:<ul><li>开始时,站点为退避计时器设置一个随机的退避时间</li><li>退避时间为0时,开始发送数据</li><li>当退避计时器计时的时候信道变忙,则冻结计时器,等待信道空闲后再经过DIFS后,继续启动计时器</li></ul></li><li>执行第 <strong>i</strong> 次退避的时候<ul><li>退避时间将在时隙编号{0,1,….,2^(k)-1}之中随机选择并乘以基本退避时间(一个时隙的长度)就可以得到随机的退避时间,从而减少不同站点拥有一样的退避时间,当时隙编号到达255时(也就是第六次退避),就不再增加</li></ul></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-CSMA%2FCA%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.png" alt="image-20231222172938230"></p></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E6%95%B0%E6%8D%AE%E9%93%BE%E8%B7%AF%E5%B1%82-CSMA%E9%80%80%E9%81%BF%E7%AE%97%E6%B3%95.png" alt="image-20231222173850892"></p><ul><li><p>CSMA/CA的信道预约和虚拟载波监听</p><ul><li><p>为尽可能减少碰撞影响,要求发送数据节点对信进行<strong>预约</strong></p><ul><li><p>源站在发送数据帧之前发送一个短的控制帧,称为<strong>请求发送RTS</strong>,包括源地址,目的地址以及这次通信所持续时间</p></li><li><p>目的站收到RTS后,若媒体空闲(等待一个SIFS)则发送一个响应控制帧,称为<strong>允许发送CTS</strong>,包括持续时间</p></li><li><p>源站收到CTS,等待SIFS后,便发送数据帧</p></li><li><p>其它站点接收到CTS和RTS后就推迟接入到无线局域网之中</p></li></ul></li><li><p>虚拟载波监听</p><ul><li>只要收到RTS或者CTS就知道信道要被占用多久</li></ul></li></ul></li></ul><h3 id="MAC地址,IP地址,ARP协议"><a href="#MAC地址,IP地址,ARP协议" class="headerlink" title="MAC地址,IP地址,ARP协议"></a>MAC地址,IP地址,ARP协议</h3><h4 id="MAC地址"><a href="#MAC地址" class="headerlink" title="MAC地址"></a>MAC地址</h4><ul><li>多个主机连接在同一个广播信道时,要想实现两个主机之间的通信,每个主机必须有一个唯一的标识,即<strong>一个数据链路层的地址</strong></li><li>这个地址也被称为MAC(Media Access Control 媒体控制接收)地址</li><li>MAC地址被固化在网卡的只读存储器之中,因此也被称为硬件地址或者物理地址</li></ul><p>IEEE 802局域网Mac地址格式</p><ul><li><p>48个比特,六个字节,十二个十六进制字符表示</p></li><li><p>MAC地址全为1时,也就是FF-FF-FF-FF-FF-FF,表示<strong>广播地址</strong></p></li><li><p>第一个字节的b0和b1位比特0分别表示单播(广播)地址,全球(相反就是本地)</p><ul><li><p>比如:07-E0-12-F6-2A-D8,其中第一字节07表示00000111,b0是1,第一位发送表示广播地址,b1是1表示是本地</p><p>本地多播就是属于某个多播组内,组内会选择接收</p></li><li><p>一般设备都是全球单播地址</p></li></ul></li><li><p>随机MAC地址</p><ul><li>防止被监听</li></ul></li></ul><h4 id="IP地址"><a href="#IP地址" class="headerlink" title="IP地址"></a>IP地址</h4><ul><li>IP地址是因特网上的主机和路由器所使用的地址</li><li>组成<ul><li>网络编号:用于标识因特网上数以百万计的接口,例如:192.168.1.X</li><li>主机编号:标识同一网络的不同主机,例如:XXX.XXX.X.1</li></ul></li><li>作用:<strong>MAC地址不具备区分不同网络的功能。IP地址因此用于在用户接入因特网的时候来区分不同网络</strong></li><li>在因特网的数据传输之中,一次不同网络主机之间的通信随着路由器的转发,<strong>帧中MAC地址目的地址和源地址会随着设备的不同而变化</strong>,但是IP地址则是一直不变,MAC地址只在同一个网络有效</li></ul><h4 id="ARP地址解析协议"><a href="#ARP地址解析协议" class="headerlink" title="ARP地址解析协议"></a>ARP地址解析协议</h4><ul><li><p>通过IP地址获取MAC地址,用于进行MAC帧封装</p></li><li><p>在数据通信之中,发送信息方无法获取对方的MAC地址,需要通过<strong>ARP高速缓存表</strong>(储存在主机之中)来获取IP地址所对应的MAC地址,当ARP表中没有记录IP所对应的MAC地址时将发送<strong>ARP请求报文</strong>(MAC文的广播帧)来获取MAC地址</p></li><li><p>ARP报文</p><ul><li>包含自身的IP和MAC地址</li><li>收到报文的主机识别到正确请求报文会先记录这个报文内容在ARP表内,然后发送ARP响应(MAC帧中的单播地址),包含自身信息</li></ul></li><li><p>ARP高速缓存表</p><ul><li><p>记录IP地址所对应的MAC地址,类型为静态或者动态</p><ul><li><p>动态:自动获取,生命周期默认为两分钟</p></li><li><p>静态:手工配置,不同操作系统的生命周期不同,例如系统重启后不存在或者系统重启后依然有效</p></li></ul></li></ul></li><li><p>作用范围</p><ul><li>同一网络之中</li><li><strong>不同网络的通信通过获取路由器MAC来发送信息,事实上,发送方不知道接收方MAC地址</strong>,但是知道IP地址</li></ul></li></ul><h2 id="集线器和交换机"><a href="#集线器和交换机" class="headerlink" title="集线器和交换机"></a>集线器和交换机</h2><ul><li><p>早期的总线型以太网:同轴电缆(被淘汰)</p></li><li><p>集线器HUB的星型以太网:双绞线电缆连接</p><ul><li><p>逻辑上仍是一个总线网,使用CSMA/CD协议</p></li><li><p>集线器只用于进行转发比特,作用于物理层,也具备一定的网络管理功能</p></li><li><p>集线器扩展网络方便,但同时会扩展广播域和碰撞域</p></li></ul></li></ul><h3 id="以太网交换机"><a href="#以太网交换机" class="headerlink" title="以太网交换机"></a>以太网交换机</h3><ul><li>特性<ul><li><strong>交换机可以将数据比特流转发给特定主机而不是所有主机</strong>,对比集线器拥有显著优势</li><li>以太网交换机拥有多个接口,每个接口都连接在主机上或者交换机上,采用全双工方式</li><li>以太网交换机具有<strong>并行性</strong>,<strong>同时连通多个接口,使多对主机同时通信而无碰撞</strong></li><li>以太网交换机支持多种速率</li><li>工作在数据链路层,收到帧后,<strong>在帧交换表之中查找帧的目的MAC地址对应的接口号</strong>,然后转发该帧</li><li>扩展以太网时,只会扩大广播域而不会扩展碰撞域</li></ul></li></ul><blockquote><p>由于交换机的出现基本杜绝的帧碰撞的发生,许多高性能的网络例如千兆万兆以太网之中不再使用CSMA/CD协议,因为退避算法可能会反而使得性能下降</p></blockquote><ul><li><p>以太网自学习和转发帧的流程</p><ul><li>以太网交换机拥有一个<strong>帧交换表</strong>用于查找MAC地址对应的接口</li><li>交换机时即插即用的设备,刚上电的帧交换表是空的,随着网络只占总各主机通信的进行,通过子<strong>学习算法自动建立</strong>起帧交换表</li><li>遇到没有记录的MAC地址时,将会<strong>盲目的对所有接口转发该帧</strong>(与集线器一样,也称泛洪),在泛洪前先记录来源地址的MAC地址以及对应接口</li><li>收到正确主机发送的接收信息后会先记录相应的MAC地址和接口然后转发给来源地址接口</li><li><strong>可以理解为过路的帧都要记录</strong></li><li>每条地址也具有生命周期,到期自动删除</li></ul></li><li><p>以太网交换机的<strong>生成树协议STP</strong></p><ul><li>添加冗余链路可以提高以太网的可靠性,但是冗余链路会带来负面效应–<strong>形成网络环路</strong><ul><li>网络环路上会重复发送给广播帧引发网络风暴</li><li>同一MAC地址来源可能来自不同接口造成反复帧交换表震荡</li></ul></li><li><strong>生成树协议STP可以在增加冗余链路来提高网络的可靠性的同时又避免网络环路的各种问题</strong></li><li>不论交换机之间采用怎样的物理连接,交换机都能自动的计算并构建一个逻辑上没有闭环的网络并连接到每个网络</li></ul></li></ul><h2 id="虚拟局域网VLAN"><a href="#虚拟局域网VLAN" class="headerlink" title="虚拟局域网VLAN"></a>虚拟局域网VLAN</h2><h3 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h3><ul><li>不断拓展以太网后,巨大的广播域即使不存在网络闭环也会消耗许多资源并变得难以维护带来许多安全问题</li><li>分割广播域方法<ul><li>使用路由器隔离广播域,路由器默认情况下不会转发广播,但是成本较高</li><li>虚拟局域网来隔离,VLAN将局域网之中的设备划分为与物理位置无光的逻辑组的技术,这些逻辑具有某些共同的需求</li></ul></li><li>同一个VLAN之间可以广播通信,不同的VLAN之间则不可以</li></ul><h3 id="实现机制"><a href="#实现机制" class="headerlink" title="实现机制"></a>实现机制</h3><ul><li><p>在以太网的MAC帧上进行扩展,扩展4字节的VLAN标记</p></li><li><p>交换机的端口类型</p><ul><li>Access<ul><li>只连接用户计算机,只能属于一个VLAN</li><li>交换机会对此端口发送到广播帧插入VLANID,然后转发到那些相同的ID的端口上</li></ul></li><li>Trunk<ul><li>用于交换机之间或者交换机和路由器之间连接,可以属于多个VLAN</li><li>对于VID不等于Trunk端口的默认VID的帧,Trunk端口是<strong>不去掉VID</strong>直接转发的,接收到不去掉VID的帧时候则直接放到交换机里匹配转发</li><li>对于VID等于Trunk端口的默认ID的帧,Trunk端口是去掉VID直接转发的,接收到去掉VID的帧时候则<strong>先插入Trunk端口的默认VID</strong>,然后直接放到交换机里匹配转发</li></ul></li><li>Hybrid<ul><li>华为交换机私有类型</li></ul></li></ul></li><li><p>缺省VLAN ID</p><ul><li><p>在思科交换机称为本征VLAN,所有端口交换机的都是VLAN1</p></li><li><p>华为交换机上称为Port VLAN ID,即端口VLAN ID ,简称PVID</p></li></ul></li></ul><h1 id="网络层"><a href="#网络层" class="headerlink" title="网络层"></a>网络层</h1><h2 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h2><ul><li>作用:实现网络互联,实现数据包之间在各个网络之间的传输,又称网际层</li><li>主要问题:<ul><li>是否提供可靠或者不可靠传输服务(IP不可靠)</li><li>网络层寻址问题(路由表)</li><li>路由选择问题</li></ul></li></ul><h2 id="网络层的两种服务"><a href="#网络层的两种服务" class="headerlink" title="网络层的两种服务"></a>网络层的两种服务</h2><h3 id="面向连接的虚电路服务"><a href="#面向连接的虚电路服务" class="headerlink" title="面向连接的虚电路服务"></a>面向连接的虚电路服务</h3><ul><li><p>可靠通信需要通过网络来保证</p></li><li><p>必须建立网络层的连接-虚电路VC(逻辑连接)</p></li><li><p>通信双方沿着已建立的虚电路发送分组</p></li><li><p>目的主机的地址仅在建立连接的阶段使用,之后再每个分组的首部携带一条虚电路编号即可</p></li><li><p>通信结束,再释放之前建立的虚电路</p></li></ul><h3 id="无连接的数据报服务(实际采用)"><a href="#无连接的数据报服务(实际采用)" class="headerlink" title="无连接的数据报服务(实际采用)"></a>无连接的数据报服务(实际采用)</h3><ul><li>可靠通信应该由用户主机来保证</li><li>不需要建立网络连接</li><li>每个分组可走不同捷径</li><li>每个分组的首部必须携带目的主机的完整地址</li><li>不提供端到端的可靠传输服务,路由器造假更为简单,且价格低廉</li><li>但同时通信质量有所下降</li></ul><h2 id="IPv4地址"><a href="#IPv4地址" class="headerlink" title="IPv4地址"></a>IPv4地址</h2><ul><li>IPv4地址是给因特网上每一台主机(或者路由器)的每一个接口分配一个在全世界范围内是唯一的32比特的标识符</li><li>表示:<ul><li>每八个bit分为一组,共4组</li><li>将八个比特表示为10进制数,然后用“ . ”分割</li><li><strong>IPv4地址由网络号(前)和主机号(后)构成</strong></li></ul></li><li>三种分址方式</li></ul><h3 id="分类编址"><a href="#分类编址" class="headerlink" title="分类编址"></a>分类编址</h3><ul><li><p>分为ABCDE类地址</p><ul><li><p><strong>A类</strong>:网络号占8位,首位(也就是网络号第一位)固定为0</p><ul><li><p><strong>主机号全0地址是网络地址,主机号全1是广播地址,不能分配给主机或者路由器接口</strong></p></li><li><p>最小网络号0保留不指派,0.0.0.0只能作为源地址使用,表示“在本网络上的本主机”</p></li><li><p>第一个可指派网络号1,<strong>网络地址</strong>(本身不能指派)为1.0.0.0,最后一个可指派为126,即<strong>网络地址</strong>为126.0.0.0</p><blockquote><p>共有2^7 - 2 = 126个网路号可以指派</p><p>共有2^24-2个主机号供一个网络号指派</p></blockquote></li><li><p>最大网络号为127,即0111111,作为本地回环测试地址,也不能指派</p><blockquote><p>本地环回测试指派地址由<strong>127.0.01~127.255.255.254</strong></p></blockquote></li></ul></li><li><p><strong>B类</strong>:网络号占16位,最高两位固定为10</p><ul><li><p>最小网络号也就是第一个可指派的网络号128.0,网络地址为128.0.0.0</p></li><li><p>最大网络号也就是最后一个可指派的网络号191.255,网络地址为191.255.0.0</p><blockquote><p>共有2^(16-2)=16384个网络号可分配</p><p>每个网络号可指派 2^16 - 2 = 65534</p></blockquote></li></ul></li><li><p><strong>C类</strong>:网络号占24位,最高三位固定为110</p><ul><li><p>最小网络号也就是第一个可指派的网络号192.0,网络地址为192.0.0.0</p></li><li><p>最大网络号也就是最后一个可指派的网络号223.255,网络地址为191.255.255.0</p><blockquote><p>共有2^(24-3)=16384个网络号可分配</p><p>每个网络共有2^8-2=254个可分配IP地址</p></blockquote></li></ul></li><li><p>D类:分为多播地址(<strong>multicast address</strong>),最高四位固定为1110,不区分网络号与主机号</p></li><li><p>E类:保留为今后使用,最高位1111</p></li></ul></li></ul><h4 id="划分子网的IPv4地址"><a href="#划分子网的IPv4地址" class="headerlink" title="划分子网的IPv4地址"></a>划分子网的IPv4地址</h4><ul><li><p>缘由:ABC类每个网络号可分配IP地址差异巨大,不能满足需求</p><blockquote><p>例如某个公司网络需要300个IP地址分配,但是每个C类网络号只能分配254个,于是分配B类,但是B类足足有65534个地址,</p><p>这造成资源严重浪费</p><p>注意:每个网络只能分配给一个网络号</p><p>其次,进行网络拓展的时候,新增网络号需要等待较长的时间并花费更高的费用,会增加路由表的记录负担</p></blockquote></li><li><p>解决办法:<strong>从主机号部分借用一部分作为子网号</strong></p><ul><li>问题:如何让其它计算机区分主机号哪部分比特被当作子网号了呢?</li><li>划分子网工具:子网掩码</li></ul><blockquote><p>将IP地址划分为网络、子网和主机有多种重要作用,包括:</p><ol><li><strong>资源管理:</strong> 子网划分允许网络管理员更有效地管理IP地址,确保它们被合理地分配给不同的网络和子网。这有助于防止IP地址的浪费,并使网络更容易维护。</li><li><strong>网络设计和规划:</strong> 子网划分使网络设计更加灵活。不同的部门或功能组可以被分配到不同的子网,使网络结构更加有条理。这有助于规划和适应不同网络需求的变化。</li><li><strong>安全性:</strong> 子网划分有助于提高网络的安全性。通过对不同子网之间的流量进行控制,可以实施安全策略,减少横向移动和未经授权的访问。</li><li><strong>路由效率:</strong> 子网划分有助于提高路由效率。路由器可以更准确地识别目标子网,减少广播和无效的路由请求,从而提高整个网络的性能。</li><li><strong>故障隔离:</strong> 子网划分可以帮助隔离网络故障。如果一个子网出现问题,它不会直接影响其他子网,从而减小故障对整个网络的影响范围。</li><li><strong>IP地址分配:</strong> 子网划分有助于更灵活地分配IP地址,确保每个子网都有足够的地址空间,同时避免浪费。</li></ol></blockquote></li><li><p>子网掩码</p><ul><li>32比特的子网掩码可以表明分类IP地址的主机号部分被借用了几个比特作为子网号</li><li>子网烟漫使用连续的比特1来表示网络号和子网号</li><li>比特0来对应主机号</li><li>使用划分子网的IPv4地址与其对应的子网掩码进行逻辑运算就可以得到IPv4地址躲在子网的网络地址</li></ul></li></ul><blockquote><p>例:某个网络地址为218.75.230.0,子网掩码为255.255.128</p><p>可知为C类地址,子网掩码除去网络号部分为128为10000000,子网部分为1</p><p>可以得出划分了 2^1 = 2 个子网,每个子网可分配IP数量为2^(8-1)-2 = 126 个</p><p>第一个子网可分配地址为 218.75.230.1 ~ 218.75.230.126,子网也有网络地址和广播地址</p></blockquote><h3 id="无分类编制的IPv4地址"><a href="#无分类编制的IPv4地址" class="headerlink" title="无分类编制的IPv4地址"></a>无分类编制的IPv4地址</h3><ul><li><p>划分子网在一定程度上缓解了因特网在发展中遇到的困难,但是因特网的急速扩张,IPv4难以满足要求</p></li><li><p>出现了IETF发布的无分类路由选择CIDR</p><ul><li>CIDR消除了传统的A类,B类和C类地址</li><li>CIDR更加有效的分配IPv4的地址空间</li></ul></li><li><p>CIDR表现方式</p><ul><li><p>采用斜线记法,在IPv4地址后面加上斜线 “ / ” 来协商网络前缀所占的比特数量</p><blockquote><p>举例: 128.14.35.7/20,表示前20号为网络前缀所占用的比特数量</p></blockquote></li></ul></li><li><p>CIDR实际上是将网络前缀相同的连续的IP地址组成了一个 “ CIDR地址块 ”</p><ul><li>地址块中的所有地址都包含了<ul><li>该地址块的最小地址,最大地址</li><li>地址数量</li><li>地址块聚合某类网络(ABC类)的数量</li><li>地址掩码(或称子网掩码)</li></ul></li></ul></li><li><p>路由聚合(超网)</p><ul><li><p>将具有相同网络前缀的路由器接口一端的所有地址块聚合成同一地址块,<strong>相同前缀保留,其余取比特0</strong></p><blockquote><p>举例:路由器上某一接口具有网络地址35.230.32.0/21,35.230.40.0/21,35.230.48.0/21,35.230.56.0/21</p><p>聚合找到共同前缀35.230.001,即前缀35.230.32,共19位</p><p>最后聚合为35.230.32.0/19</p></blockquote></li><li><p>这种经过路由聚合的网络称为超网</p></li><li><p>前缀越长,地址块越小,路由越具体</p></li></ul></li></ul><h3 id="IPv4地址的应用规划"><a href="#IPv4地址的应用规划" class="headerlink" title="IPv4地址的应用规划"></a>IPv4地址的应用规划</h3><p>不同的应用需求</p><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/%E8%AE%A1%E7%BD%91-%E7%BD%91%E7%BB%9C%E5%B1%82-IP%E5%9C%B0%E5%9D%80%E5%BA%94%E7%94%A8%E8%A7%84%E5%88%92.png" alt="image-20231224164400881"></p><ul><li><p>定长子网掩码FLSM</p><ul><li><p>使用同一个子网掩码来划分子网</p></li><li><p>每个子网所分配的IP地址数量相同,造成IP地址的浪费</p><blockquote><p>对于上述图片的应用需求,若申请到的C类网络号位218.75.230.0,剩余8位</p><p>共有5个子网数量,选择2^3=8个子网数量的比特位,即3个比特位的子网位</p><p>每个子网地址数量即位2^(8-3) = 32位 </p><p>但即使这样也仍然造成了IP地址浪费</p></blockquote></li></ul></li><li><p>变长子网掩码VLSM</p><ul><li>使用不同的子网掩码来划分子网</li><li>每个子网所分配的IP地址数量可以不相同,减少浪费</li></ul></li></ul><blockquote><p>变长子网掩码便是尽可能分配更少的子网地址拥有数量</p></blockquote><h2 id="IP数据报的发送与转发"><a href="#IP数据报的发送与转发" class="headerlink" title="IP数据报的发送与转发"></a>IP数据报的发送与转发</h2><ul><li><p>直接交付:同一网络发送数据</p></li><li><p>间接交付:不同网络发送数据</p></li><li><p><strong>发送主机将目的IP地址与自身子网掩码进行相 “ 与 ” ,得到的网络地址与自己的网络地址不相符合便是不同网络</strong></p></li><li><p><strong>默认网关</strong></p><ul><li><strong>路由器将接口的IP地址设为接口下主机的默认网关</strong></li><li>主机要发送间接交付的时候直接将数据发送给默认网关,也就是交给路由器处理</li><li>对于需要转发的数据包,路由器会在路由表中查找下一跳所需要进入的路由器或子网接口</li></ul></li><li><p>对于子网广播,路由器不会进行转发</p></li></ul><h2 id="静态路由配置与环路问题"><a href="#静态路由配置与环路问题" class="headerlink" title="静态路由配置与环路问题"></a>静态路由配置与环路问题</h2><ul><li><p>采用人工配置路由表,开销小但是不能及时适应网络状态变化</p></li><li><p>默认路由条目</p><ul><li>路由器中配置因特网所有目的IP网络的下一跳显然不太现实</li><li>默认条目由人工配置,属于静态类型,目的网络为0.0.0.0/0,下一跳为默认接口</li><li>当找不到路由表时默认转发为默认条目,网络前缀最短,路由最模糊</li></ul></li><li><p>特定主机路由条目</p><ul><li>与默认路由相反,目的网络时某一特定的主机</li><li>网络前缀最长,路由最具体</li><li>多条路由(例如默认路由)可选时,最长前缀匹配</li></ul></li><li><p>路由环路</p><ul><li>配置错误<ul><li>反复互相转发</li><li>防止路由环路的出现,出现了<strong>生存时间TTL字段</strong>,规定了数据包转发次数,TTL为0时,被丢弃</li></ul></li><li>聚合了不存在的网络<ul><li>也就是聚合的地址块中包含了不存在的主机</li><li>解决办法:将不存在的主机设为黑洞路由,不可转发</li></ul></li></ul></li></ul><blockquote><p>在路由表中,当网络地址中主机号部分不为0时,通常表示这个路由表项是在指定的网络范围内的一个子网(Subnet)。这是由于子网划分的实施,其中网络地址被分为更小的子网,而主机号部分被用于区分这些子网内的主机。</p><p>IPv4地址由网络地址和主机地址组成,例如 “192.168.1.0” 是一个IPv4网络地址,而 “.1” 是主机号部分。在常规的IP地址中,主机号部分为0表示整个网络,而当主机号部分不为0时,通常意味着该地址被用于子网内的主机。</p><p>例如,假设有一个网络 “192.168.1.0”,而路由表中有一项 “192.168.1.16”,这可能表示一个子网,其中 “.16” 是子网内的主机地址。这种子网划分有助于更有效地管理IP地址,提高网络的安全性和可伸缩性。</p></blockquote><h2 id="路由选择协议概述"><a href="#路由选择协议概述" class="headerlink" title="路由选择协议概述"></a>路由选择协议概述</h2><h3 id="静态路由选择与动态路由选择"><a href="#静态路由选择与动态路由选择" class="headerlink" title="静态路由选择与动态路由选择"></a>静态路由选择与动态路由选择</h3><ul><li><p>由人工配置的网络路由,默认路由,特定主机路由,黑洞路由等都属于静态路由</p></li><li><p>人工配置方式方式简单,开销小,但是不能即使适应网络状态变化</p></li><li><p>适用于小规模网络</p></li><li><p>由路由器通过路由选择协议自动获取路由信息</p></li><li><p>比较复杂,开销较大</p></li><li><p>适用于大规模网络</p></li></ul><h4 id="因特网路由选择"><a href="#因特网路由选择" class="headerlink" title="因特网路由选择"></a>因特网路由选择</h4><ul><li>自适应:采用动态路由选择</li><li>分布式:路由之间交换信息</li><li>分层次:将许多因特网划分为许多较小的<strong>自治系统AS</strong>(Autonomous System)<ul><li>自治系统由内部网关(路由)协议<strong>IGP</strong>和外部网关(路由)协议<strong>EGP</strong></li></ul></li></ul><h3 id="内部网关协议IGP"><a href="#内部网关协议IGP" class="headerlink" title="内部网关协议IGP"></a>内部网关协议IGP</h3><ul><li>路由信息协议RIP<ul><li>基于距离向量</li><li>RIP在因特网上最早使用,是思科最早的私有的协议,现已经被EIGRP取代</li></ul></li><li>内部网关路由协议IGRP</li><li>增强型内部网关路由协议EIFRP</li><li>开放式最短路径优先OSPF<ul><li>基于链路状态</li><li>OSPF在各种网络之中广泛使用</li></ul></li><li>中间系统到中间系统IS-IS<ul><li>基于链路状态</li><li>主要应用与骨干网上</li></ul></li></ul><h3 id="外部网关协议EGP"><a href="#外部网关协议EGP" class="headerlink" title="外部网关协议EGP"></a>外部网关协议EGP</h3><ul><li>边界网关协议BGP</li></ul><h3 id="路由表结构"><a href="#路由表结构" class="headerlink" title="路由表结构"></a>路由表结构</h3><ul><li><p>路由选择部分</p><ul><li>路由表<ul><li>仅仅包含从目的地址到下一跳的映射</li><li>需要对网络拓扑结构变化的计算最优化</li></ul></li><li>路由选择处理机</li></ul></li><li><p>分组转发部分</p><ul><li>输入端口:物理层 -> 链路层->网络层</li><li>输出端口:网络层 -> 链路层->物理层</li><li>分组处理:转发表(由路由表得来,使得查找过程最优化)</li></ul></li></ul><h3 id="路由信息协议RIP"><a href="#路由信息协议RIP" class="headerlink" title="路由信息协议RIP"></a>路由信息协议RIP</h3><ul><li><p>RIP定义一组每一个路由器到其它网络的距离称为<strong>距离向量D-V</strong></p></li><li><p>RIP使用条数来衡量到达目的网络的距离</p><ul><li>到直连网络距离为1</li><li>路由器到非直连网络的定义为所经过路由器数加一</li><li>允许经过一条路径最多只能包含15个路由器,等于16时候相当于不可达,只适用于小型互联网</li></ul></li><li><p><strong>RIP认为好的路由就是距离短的路由,也就是通过路由器数量最少的路由</strong></p><ul><li>当有多条距离相等的时候,进行<strong>等价负载均衡</strong>,也就是将通信量均衡分发至两条道路上</li></ul></li><li><p>RIP获取路由信息三要素</p><ul><li>仅和相邻路由器交换信息,也就是直连的路由器</li><li>交换路由表信息</li><li>周期性交换信息(例如30秒)</li></ul></li><li><p>基本工作过程</p><ul><li><p>开始时候只知道直连网络距离为1</p></li><li><p>然后开始和相邻路由器周期性交换路由信息</p></li><li><p>多次交换后,每个路由器都知道到达本AS各网络的最短距离和下一跳地址,称为<strong>收敛</strong></p></li><li><p>更新规则</p><ul><li>新路由表(也就是其它路由发过来的路由表)有更高的优先级,这标示着网络拓扑结构的最新变化</li><li><strong>所有新路由表条目距离都要加一</strong></li><li>相同目的网络,相同下一跳:选择新路由表的距离</li><li>相同目的网络,不同下一跳,新路由器距离更短:将原来记录覆盖为新路由表条目</li><li>相同目的网络,不同下一跳,新路由器距离和原来一样:添加一条新条目,等价负载均衡</li><li>相同目的网络,不同下一跳,新路由器距离比原来长:不更新</li></ul></li><li><p>RIP坏消息传播缓慢</p><ul><li><p>路由判断出某一网络出现故障并将距离改写为16,发送给其它路由</p></li><li><p>但同时其它路由发送了包含R1距离“ 正常 ”的路由表,路由将会误判(新路由表优先级更高)然后继续更新</p></li><li><p>然后一直循环形成路由环路,直到到达故障网络的条目距离不断累加到16</p></li><li><p>解决办法:(减缓,不能解决)</p><ul><li>限制最大路径距离为15</li><li>路由表发生变化的时候立刻发送更新报文,即“<strong>触发更新</strong>”</li><li>收到异常信息后不再发送,“水平分割”</li></ul></li></ul></li></ul></li></ul><h3 id="开放性最短路径优先OSPF"><a href="#开放性最短路径优先OSPF" class="headerlink" title="开放性最短路径优先OSPF"></a>开放性最短路径优先OSPF</h3><ul><li><p>使用了Dijkstra的最短路径算法SPF</p></li><li><p>基于链路状态的</p></li><li><p>不会产生路由环路</p></li><li><p>不限制网络规模,更新效率块,归敛速度快</p></li><li><p>链路状态是指本路由器和哪些路由相邻,以及相应的“代价”</p></li><li><p>代价表示cost,表示费用,距离等,这些都由网络管理人员决定,思科路由器采用100Mbps / 链路带宽</p></li><li><p>OSPF相邻路由器通过交互<strong>问候(Hello)</strong>分组来进行维护<strong>邻居关系</strong></p><ul><li>Hello分组封装在IP数据报之中,发往组播地址224.0.0.5</li><li>发送周期为10秒</li><li>用于发现和维护邻居路由器的可达性</li></ul></li><li><p>OSPF链路路由器,都会产生<strong>链路状态警告LSA</strong></p><ul><li>直连网络的链路数据状态</li><li>邻居路由器的链路状态信息</li></ul></li><li><p>LSA被封装在<strong>链路状态更新分组LSU</strong>中,采用洪泛法发送</p></li><li><p>每个路由器都有一个链路状态数据库<strong>LSDB</strong>,用于<strong>存储LSA</strong></p></li><li><p>基于LSDB进行最短路径优先SPF计算,即构建出各自到达其他各路由器的最短路径</p></li><li><p>基本工作过程</p><ul><li>先进行问候分组,建立和维护邻居关系</li><li>然后进行链路状态数据库同步(通过交换数据库描述分组,和发送链路状态请求分组和链路状态更新分组,链路状态确认分组)</li><li>每30分钟或者链路状态变化的时候路由器都会发送链路状态更新分组(洪泛)</li><li>当多个路由器相互连接的时候,会产生多个广播分组(例如hello分组)<ul><li>采用<strong>指定路由器DR</strong>和<strong>备用的指定路由器BDR</strong></li><li>非BDR和DR之间不能交换信息,只能通过DR,BDR进行交换</li><li>这种方法类似于交换机生成树,叶子节点之间不能发送信息</li></ul></li></ul></li><li><p>OSPF能用于非常大的网络,OSPF把一个自治系统AS再划分为若干个更小的<strong>区域Area</strong></p><ul><li>限制洪泛法交换信息的范围,减少整个网络通信量</li></ul></li></ul><h3 id="边界网关协议BGP"><a href="#边界网关协议BGP" class="headerlink" title="边界网关协议BGP"></a>边界网关协议BGP</h3><ul><li><p>在不同的自治系统内,通过度量路由的“代价”(距离,带宽,费用等)可能不同</p><p>因此,对于自治系统之间的路由选择,使用“代价”作为度量来寻找最佳路由是不行的</p></li><li><p>必须考虑相关策略(政治,经济,安全等)</p></li><li><p>配置BGP的时候,每个自治系统要至少配置至少<strong>一个路由器</strong>作为该自治系统的<strong>“BGP”发言人</strong></p></li><li><p>不同自治系统的发言人要交换路由信息,首先必须确立TCP连接,端口号为179</p><ul><li>在<strong>TCP连接</strong>上交换BGP报文以<strong>建立BGP会话</strong></li><li><strong>利用BGP会话交换路由信息</strong></li><li>这两个路由器称为邻站或者对等站</li></ul></li></ul><h2 id="IPv4数据报首部格式"><a href="#IPv4数据报首部格式" class="headerlink" title="IPv4数据报首部格式"></a>IPv4数据报首部格式</h2><ul><li><p>20字节固定部分,每行4字节,32比特</p><ul><li><p>第一行</p><ul><li><p>【0~4】4比特,版本字段,表示<strong>IP协议版本</strong>,例如IPv4</p></li><li><p>【4~8】4比特,首部长度字段,该字段取值<strong>单位是4字节</strong>,最小十进制取值为5,表示IP数据报首部只有20字节固定部分</p><p>最大取值15,表示IP数据报首部包含20字节固定部分和最大40字节的可变部分</p></li><li><p>【8~16】8比特,区分服务字段,用来获得更好服务,实际上一直未使用</p></li><li><p>【16~32】16比特,总长度,表示<strong>IP数据报的总长度</strong>,即 首部+数据载荷 ,最大取值未十进制65535,<strong>字节为单位</strong></p></li></ul></li><li><p>第二行</p><ul><li>【0~16】16比特,标识,当IP数据报超过封装成帧的最大长度(例如以太网的1500字节)时候,需要进行<strong>分片操作</strong>,标识则用来区分这些分片属于哪一个IP数据报,<strong>属于同一IP数据报,标识应该一样</strong>,标识产生于一个IP软件的计数器,每产生一个IP数据报,计数器加一并赋值给此字段</li><li>【16~19】3比特,标志位,分为DF,MF和一个保留位<ul><li><strong>DF</strong>:1表示不允许分片,0表示允许分片</li><li><strong>MF</strong>:1表示后面还有分片,0表示这是最后一个分片</li></ul></li><li>【19~32】13比特,片偏移,指出<strong>分片数据报的数据载荷部分偏移在源(最开始,因为可能会多次分片)数据报位置多少个单位</strong>,以8字节为单位,例如0/8,意思就是第一个分片的片偏移,/8是因为以8字节为单位,除以8后必须为整数</li></ul></li><li><p>第三行</p><ul><li>【0~8】8比特,生存时间TTL,以跳数为单位</li><li>【8~16】8比特,协议,指明IP数据报的数据部分是何种协议单元,例如1表示ICMP,2为IGMP,6为TCP,17是UDP</li><li>【16~32】16比特,首部检验和,用于检测首部传输过程中是否出现差错,比CRC简单名称为因特网检验和</li></ul></li><li><p>第四行</p><ul><li>【0~32】,源IP地址</li></ul></li><li><p>第五行</p><ul><li>【0~32】,目的IP地址</li></ul></li></ul></li><li><p>1~40字节可变部分</p><ul><li>可选字段,用来支持排错,测量和安全等措施</li><li>填充字段,保证可变部分是4字节的整数倍,采用全0填充,服务于首部长度字段</li></ul></li></ul><h2 id="网际控制报文协议ICMP"><a href="#网际控制报文协议ICMP" class="headerlink" title="网际控制报文协议ICMP"></a>网际控制报文协议ICMP</h2><ul><li><p>主机或者路由器使用路由器来发送差错报告报文和询问报文,封装在IP数据报中</p></li><li><p>共有以下5种:</p><ul><li>终点不可达:当路由器或者主机不能交付数据的时候,就向源地址发送不可达报文,里面包含各种错误</li><li>源点抑制:当路由器或者主机因为拥塞丢弃数据的时候,回发此报文</li><li>时间超过:当路由器或者主机因为生存时间TTL归零丢弃数据的时候,回发此报文</li><li>参数问题:当路由器或者主机因为误码等丢弃数据的时候,回发此报文</li><li>改变路由(重定向):用于提醒主机下次应该将数据报发给另外的路由器</li></ul></li><li><p>对于以下几种情况不再发送ICMP错误报文</p><ul><li>由于ICMP差错报文引起的差错</li><li>第一个分片的数据报片出错了,所有后续数据报片不发送</li><li>具有多播地址的数据报不发送</li><li>对于具有特殊地址的(如127.0.0.0或者0.0.0.0)不发送</li></ul></li><li><p>应用举例</p><ul><li>分组间探测PING<ul><li>用于测试主机或者路由器之间的连通性</li><li>直接使用ICMP协议而不是TCP和UDP</li></ul></li><li>跟踪路由<ul><li>测试IP数据报从源主机到达目的主机要经过哪些路由器</li></ul></li></ul></li></ul><h2 id="虚拟专用网VPN和网络地址转换NAT"><a href="#虚拟专用网VPN和网络地址转换NAT" class="headerlink" title="虚拟专用网VPN和网络地址转换NAT"></a>虚拟专用网VPN和网络地址转换NAT</h2><ul><li><p>在所有IP地址中有一些专业私有地址给与单独的网络使用而不能在公网使用,例如192.168.0.0~192.168.255.255</p></li><li><p>用于两个专用网之间进行通信</p></li><li><p>类似于在专用网IP之中套了一层公网的壳,这个壳只需要一个公网的IP地址(路由器中)即可</p></li><li><p><strong>NAT能使大量使用内部专用地址的专用网络用户共享少量外部全球地址来访问因特网上的主机和资源</strong></p><blockquote><p>例如学校就是一个巨大的专用内网使用192.168.0.0~192.168.255.255然后申请一个公有地址172.38.1.5放在NNAT路由器上</p><p>在因特网中,来自学校的数据报的IP地址都是公有地址,而在学校网内部都是用私有地址</p></blockquote></li><li><p>减少了IPv4地址的消耗</p></li><li><p>问题</p><ul><li>如果NAT路由器上只有n个全球IP地址,则最多只有n个内网主机能够同时和因特网上的主机通信</li><li>解决办法:<ul><li>理由运输层的端口号和IP地址一起转换,这样就可以一个IP多个主机进行通信,这种技术称为<strong>NAPT</strong></li></ul></li></ul></li><li><p>外网主机不能首先发起访问内网的主机,也就是内网主机不能当服务器</p></li></ul><h1 id="运输层"><a href="#运输层" class="headerlink" title="运输层"></a>运输层</h1><h2 id="概述-3"><a href="#概述-3" class="headerlink" title="概述"></a>概述</h2><ul><li>网络层之前的服务解决了主机之间通过网络互联起来所面临的问题,实现了主机到主机之间的通信</li><li>但实际上计算机网络中进行通信的真正时提示是位于通信两端中的进程</li><li>运输层的任务是为不同主机上应用进程提供直接的通信服务,运输层协议又称端到端协议</li></ul><h2 id="端口号,复用与分用的概念"><a href="#端口号,复用与分用的概念" class="headerlink" title="端口号,复用与分用的概念"></a>端口号,复用与分用的概念</h2><ul><li>运行在计算机的进程使用进程标识符PID来标志,但是不同操作系统进程标识不统一</li><li>TCP/IP体系的运输层使用<strong>端口号</strong>来区分应用层的不同应用进程</li><li>端口号使用16比特表示,取值范围0~65535<ul><li>0~1023,熟知端口号被分配给了一些重要的应用协议</li><li>1024~49151,不熟知应用使用</li><li>其余:留给客户进程短暂使用</li></ul></li><li>复用:不同的协议都使用TCP或者UDP协议,被TCP或者UDP进行封装</li></ul><h2 id="TCP与UDP对比"><a href="#TCP与UDP对比" class="headerlink" title="TCP与UDP对比"></a>TCP与UDP对比</h2><h3 id="UDP"><a href="#UDP" class="headerlink" title="UDP"></a>UDP</h3><ul><li>可以随时发送数据</li><li>支持单播,多播和广播</li><li>UDP对上层的应用报文仅仅包裹一层UDP首部发送,是面向应用报文的</li><li>UDP基于同样无连接不可靠的IP协议,因此UDP是无连接不可靠的,对误码,丢失问题不关心,这种情况适用于IP电话,视频会议等实时应用</li><li>UDP封装的首部组成<ul><li>【0~2】2字节,源端口</li><li>【2~4】2字节,目的端口</li><li>【4~6】2字节,长度</li><li>【6~8】2字节,检验和</li><li>数据部分</li></ul></li></ul><h3 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h3><ul><li>传输数据前必须建立连接</li><li>仅支持单播</li><li>TCP仅将应用报文视作字节流,按照自身的算法提取字节逐个包装发送,并不在乎应用报文实际的大小和用户接收大小是否一致,识别字节流的工作由上层处理,TCP是面向字节流的</li><li>TCP提供可靠传输服务,提供流量控制和拥塞服务,适用于要求可靠传输的应用,例如文件传输</li><li>TCP报文段<ul><li>更加复杂</li></ul></li></ul><h2 id="TCP流量控制"><a href="#TCP流量控制" class="headerlink" title="TCP流量控制"></a>TCP流量控制</h2><ul><li>当发送方发送数据过快,接收方可能来不及接收</li><li><strong>流量控制来控制发送方的发送速率</strong></li><li>工作原理<ul><li>类似于回退N帧协议,拥有一个<strong>发送窗口rwnd</strong>标志着哪些字节要发送</li><li>在收到累积确认前,不能超前发送数据</li><li>内置有超时重传计时器,超时了就重新发送未确认的可发送数据</li><li>当收到接受方的累积确认后移动发送窗口,但不同的是,<strong>累积确认报文含有调整发送窗口大小的命令,这样实现流量控制</strong></li><li>当发送方收到一个窗口大小调控为0的时候,就启动一个持续计时器,当计时器超时的时候,发送一个一字节数据的零窗口探测报文段,如果接收方回复仍然是0,则再次启动计时器</li></ul></li></ul><h2 id="TCP拥塞控制"><a href="#TCP拥塞控制" class="headerlink" title="TCP拥塞控制"></a>TCP拥塞控制</h2><ul><li>某一时间,网络中的某一资源需求超过了该资源的所能提供的可用部分,网络性能被破坏,这便是拥塞</li><li>四种拥塞控制办法:慢开始,拥塞避免,快重传,快恢复</li><li>几个前提<ul><li>数据单方向传输</li><li>接收方缓存空间足够</li></ul></li></ul><h3 id="慢开始和拥塞避免"><a href="#慢开始和拥塞避免" class="headerlink" title="慢开始和拥塞避免"></a>慢开始和拥塞避免</h3><ul><li><p><strong>发送方维护一个拥塞窗口cwnd,其值取决于网络的拥塞程度,并且动态变化</strong></p><ul><li>维护原则:只要网络没有拥塞,拥塞窗口就变大一些,一旦出现拥塞,拥塞窗口就缩小</li><li>判断网络拥塞出现机制:没有按时收到应当到达的确认报文</li></ul></li><li><p>发送方将拥塞窗口作为<strong>发送窗口swnd,即swnd=cwnd</strong></p></li><li><p>维护一个<strong>慢开始门限ssthresh变量</strong></p><ul><li>当cwnd < ssthresh时,使用慢开始算法</li><li>当cwnd > ssthresh,停止慢开始算法而是使用拥塞避免算法</li><li>当cwnd = ssthresh,两种算法都可以使用</li></ul><blockquote><p>假设cwnd初始为1,ssthresh门限为16,因为cwnd为1,只能发送一个报文段</p><p>于是发送报文段0,接收到确认报文段0</p><p>cwnd慢启动,值加1,能发送两个报文段</p><p>于是发送报文段1,2,接收到确认报文段1,2</p><p>cwnd慢启动,值加2,能发送4个报文段 (<strong>每收到一个报文段,值加一</strong>)</p><p>于是发送报文段3,4,5,6,接收到确认报文段</p><p>cwnd慢启动,值加4,能发送8个报文段</p><p>于是发送报文段7~14,接收到确认报文段</p><p>cwnd慢启动,值加8,能发送16个报文段,此时等于ssthresh</p><p>改用拥塞调控算法(<strong>每传输一次,只能线性加一</strong>)</p><p>发送报文段15~30,接收到确认报文段</p><p>cwnd拥塞调度,值加1,能发送17个报文段</p><p>…………….</p><p>直到重传计时器超时,出现拥塞,执行以下步骤</p><ul><li><strong>将ssthresh值更新为拥塞时候cwnd的一半</strong></li><li><strong>将cwnd值减少为1,并重新开始执行慢开始算法</strong></li></ul></blockquote></li></ul><h3 id="快重传和快恢复"><a href="#快重传和快恢复" class="headerlink" title="快重传和快恢复"></a>快重传和快恢复</h3><ul><li><p>快重传:使发送发尽快重传而不是等待重传计时器超时再重传</p><ul><li>要求接受方不是自己要发送数据的时候捎带确认,而是<strong>立刻确认</strong></li><li>即使收到了<strong>失序的报文段</strong>也要立即发出对已收到的报文段的重复确认</li><li>发送发一旦受到了3个连续的重复确认就要立刻将相应报文段重传,而不是等计时器超时</li></ul><blockquote><p>重复确认,当收到失序的报文段的时候(例如中间报文段丢失),发送应该受到的报文段的重复确认报文段</p></blockquote><ul><li>提高了网络吞吐量</li></ul></li><li><p>快恢复</p><ul><li>发送方一旦受到<strong>3个重复的确认</strong>,于是知道有数据报文段丢失,于是不启动慢开始算法,而是执行<strong>快恢复算法</strong></li><li><strong>发送方将慢开始们限ssthresh值和拥塞窗口cwnd调整为当前窗口一半;开始执行拥塞避免算法</strong></li><li>不同的设备有不同的实现方式</li></ul></li></ul><h2 id="TCP超时重传时间选择"><a href="#TCP超时重传时间选择" class="headerlink" title="TCP超时重传时间选择"></a>TCP超时重传时间选择</h2><ul><li><p>如果重传时间小于一次传播的时间,会造成不必要的重传</p></li><li><p>如果重传时间远大于<strong>一次往返时间RTT</strong>的值,则会使网络的空闲时间增大,降低了传输效率</p></li><li><p>因此<strong>超时重传时间RTO</strong>应该略大于一次往返的时间</p></li><li><p>不能使用某一次测量的带的RTT样本来计算<strong>超时重传时间RTO</strong></p></li><li><p>利用每次测量的RTT样本,计算加权平均往返时间RTTs</p><blockquote><p>newRTT = (1-a)旧RTT+a*新RTT样本</p></blockquote></li></ul><h2 id="TCP可靠传输的实现"><a href="#TCP可靠传输的实现" class="headerlink" title="TCP可靠传输的实现"></a>TCP可靠传输的实现</h2><ul><li><strong>以基于字节为单位的滑动窗口来实现可靠传输</strong></li><li>发送窗口和接收窗口不总是一样大<ul><li>发送窗口受拥塞控制影响</li></ul></li><li>TCP不处理不按序到达的报文段,而是缓存起来等都集齐后同一交给上层处理</li><li>接收方回答报文必须有<strong>累积确认和捎带确认机制</strong><ul><li>接收方不应该过分推迟发送确认</li></ul></li><li>TCP是全双工通信</li></ul><h2 id="TCP连接的确立"><a href="#TCP连接的确立" class="headerlink" title="TCP连接的确立"></a>TCP连接的确立</h2><ul><li><strong>三次报文握手建立TCP连接</strong></li><li><strong>四次挥手释放TCP连接</strong></li></ul><h3 id="三次握手"><a href="#三次握手" class="headerlink" title="三次握手"></a>三次握手</h3><ul><li><p>TCP用户向服务器发起建立连接过程</p></li><li><p>SYN:同步位,表明是否是一个连接请求报文</p></li><li><p>seq:表明TCP客户进程所选择的初始序号</p></li><li><p>ack:表示发送发期望从接收方接收到起始报文序号</p></li><li><p>具体过程</p><ul><li><p>第一次握手:客户机向服务器申请建立连接:报文内容STN=1,seq=x</p></li><li><p>第二次握手:当服务器同意此报文时,返回一个确认报文SYN=1,ACK=1,seq=y,ack=x+1</p></li><li><p>第三次握手:受到服务器连接确认报文后,再发送<strong>一个普通的确认报文段</strong>,ACK=1,seq=x+1,ack=y+1</p><blockquote><p><strong>SYN报文段即使不携带数据也要消耗一个序号,普通的TCP数据包如果不携带数据则不消耗序号</strong></p></blockquote></li><li><p>两次握手的不可行性:</p><ul><li>当客户端发送的请求报文超时重传了,客户端将此报文视为无效,但服务器不知道,会回应此请求报文,客户端对服务器的报文不予理会,但是服务器会以为进入了连接</li></ul></li></ul></li></ul><h3 id="四次挥手"><a href="#四次挥手" class="headerlink" title="四次挥手"></a>四次挥手</h3><ul><li><p>FIN:是否是连接释放报文</p></li><li><p>具体过程</p><ul><li><p>第一次挥手: 客户主动发起断开连接(同时对之前的报文进行确认),连接释放报文为:FIN=1,ACK=1,seq=u,ack=v</p></li><li><p>第二次挥手:服务器受到后会发送<strong>一个普通的TCP确认报文段</strong>:ACK=1,seq=v,ack=u+1</p><blockquote><p>此时从客户进程到服务器进程的TCP连接已经被释放了</p><p>客户主动发起的连接释放称为主动释放连接,相应服务器进程就叫被动关闭连接</p></blockquote></li><li><p>第三次挥手:服务器发送完数据后被动关闭连接,发送TCP连接释放报文:FIN=1,ACK=1,seq=w,ack=u+1</p></li><li><p>第四次挥手:客户进程收到服务器连接释放请求后发送一个普通的TCP确认报文:ACK=1,seq=u+1,ack=w+1</p></li></ul></li></ul><blockquote><p><strong>FIN报文段不携带数据也要消耗报文段</strong></p></blockquote><ul><li>MSL:最长报文段寿命,<strong>第四次挥手后还要等待两个MSL才能彻底关闭进程</strong></li></ul><h2 id="TCP报文段首部格式"><a href="#TCP报文段首部格式" class="headerlink" title="TCP报文段首部格式"></a>TCP报文段首部格式</h2><ul><li><p>报文段分为首部和数据载荷</p></li><li><p>首部分为固定首部(20字节)和扩展首部</p></li><li><p>固定首部组成,5行,每行4个字节32个比特</p><ul><li><p>第一行</p><ul><li>【0~15】,源端口,用于标识发送该报文的应用进程</li><li>【16~31】,目的端口,用于标识接收该报文的应用进程</li></ul></li><li><p>第二行</p><ul><li>【0~31】,序号,用于指出数据载荷部分开始序号,序号最大值为2^32-1,扩展到最大值后,下一个序号为0</li></ul></li><li><p>第三行</p><ul><li>【0~31】,确认号,期望收到对方下一个TCP报文段的数据载荷部分第一个字节的序号,标志位ACK取值为1时候才有效</li></ul></li><li><p>第四行</p><ul><li><p>【0~3】,数据偏移,以4字节为单位,用于指出TCP报文段数据载荷部分距离报文段起始处有多远,<strong>也就是首部长度</strong></p></li><li><p>【4~9】,保留部分,置0</p></li><li><p>【10~15】,6个标志位</p><ul><li>URG:紧急标志位URG,标志紧急指针是否有效</li><li>ACK</li><li>PSH</li><li>RST:用于复位TCP连接,比特为1时候表明TCP连接出现了异常,必须释放连接再建立连接,也可用于拒绝连接</li><li>SYN:在TCP连接建立时用来同步序号</li><li>FIN:用于释放TCP连接</li></ul></li><li><p>【16~31】,窗口,指出本报文段的一方的接收窗口,也就是以接受方的能力来控制发送方的能力,也就是流量控制</p><blockquote><p>发送窗口还受拥塞窗口影响,从接收窗口和拥塞窗口之间取小者</p></blockquote></li></ul></li><li><p>第五行</p><ul><li><p>【0~15】,校验和,检查包括TCP报文首部和数据载荷部分</p></li><li><p>【16~31】,紧急指针,指明紧急数据长度</p><blockquote><p>紧急数据:发送方有紧急数据的时候,可以将紧急数据插队发送到缓存最前面</p></blockquote></li></ul></li><li><p>扩展首部:一些其它功能,并确保首部为4字节整数倍</p></li></ul></li></ul><h1 id="应用层"><a href="#应用层" class="headerlink" title="应用层"></a>应用层</h1><ul><li>解决通过应用进程交互来实现网络应用的问题</li></ul><h2 id="客户服务器方式和对等方式(P2P)"><a href="#客户服务器方式和对等方式(P2P)" class="headerlink" title="客户服务器方式和对等方式(P2P)"></a>客户服务器方式和对等方式(P2P)</h2><ul><li>网络应用程序运行在处于网络边缘的不同端系统上,通过彼此通信来协作任务</li><li>网络应用在各种端系统上的组织方式和它们之间的关系</li></ul><h3 id="客户服务器方式"><a href="#客户服务器方式" class="headerlink" title="客户服务器方式"></a>客户服务器方式</h3><ul><li><p>进程之间是服务和被服务的关系</p></li><li><p>服务器总是处于运行状态并等待客户请求</p></li><li><p>服务器具有固定的端口号</p></li><li><p>运行服务器主机具有固定的IP地址</p></li><li><p>最成熟的方式</p><ul><li>例如万维网www,电子邮件,文件传输FTP都是这个</li></ul></li></ul><h3 id="P2P对等方式"><a href="#P2P对等方式" class="headerlink" title="P2P对等方式"></a>P2P对等方式</h3><ul><li><p>没有固定的服务请求和服务提供者</p></li><li><p>对等方之间直接通信</p></li><li><p>应用举例:P2P文件共享,即时通信,分布式存储等</p></li><li><p>系统性能不会因为对等方的增加而降低性能</p></li></ul><h2 id="动态主机配置协议DHCP"><a href="#动态主机配置协议DHCP" class="headerlink" title="动态主机配置协议DHCP"></a>动态主机配置协议DHCP</h2><ul><li>给网络中的各个主机分配相应的IP</li><li>主机启动的时候向DHCP服务器请求网络配置</li><li>工作过程<ul><li>客户广播DHCP发现报文(<strong>DHCP DISCOVER</strong>),源IP地址为0.0.0.0,封装有事务ID和DHCP客户端MAC地址</li><li>DHCP收到后广播回应报文(<strong>DHCP OFFER</strong>),配置有IP地址,子网掩码,地址租期,默认网关,DNS服务器等</li><li>客户选择其中一个DHCP服务器,然后广播请求请求报文,源地址仍为0.0.0.0</li><li>被选中DHCP给主机发送确认广播报文(同时检查此IP有没有被占用),客户开始使用IP地址</li><li>然后是租用期</li></ul></li></ul><h2 id="域名系统DNS"><a href="#域名系统DNS" class="headerlink" title="域名系统DNS"></a>域名系统DNS</h2><ul><li><p>层次树状结构</p></li><li><p>…次级域名.二级域名。顶级域名</p></li><li><p>每一级域名都由英文和字母组成,不超过63个字符,不区分大小写字母</p></li><li><p>完整域名不超过255个字符</p></li><li><p>域名解析潮汛</p><ul><li>递归查询:一层一层往上反应最后返回给主机,开销较大</li><li>迭代查询:主机向所有级别服务器都请求一次然后获得地址</li><li>一般采用本地服务递归,其它迭代</li></ul></li></ul><h2 id="文件传送协议FTP"><a href="#文件传送协议FTP" class="headerlink" title="文件传送协议FTP"></a>文件传送协议FTP</h2><ul><li>具有一台FTP服务器</li><li>应用:<ul><li>用户登录后可以访问FTP上所有文件并下载</li></ul></li></ul><h2 id="电子邮件"><a href="#电子邮件" class="headerlink" title="电子邮件"></a>电子邮件</h2><ul><li>组成:邮件客户端+邮件服务器+邮件传输协议</li><li>SMTP协议<ul><li>使用TCP,端口25</li><li>传呼过程三个阶段,建立连接,报文传输,关闭</li><li>命令响应交互模式LASCII文本,状态代码和语句</li></ul></li><li>MIME多媒体扩展协议</li><li>邮件读取协议POP3,IMAP</li></ul>]]></content>
<tags>
<tag>计算机基础</tag>
<tag>计算机网络</tag>
</tags>
</entry>
<entry>
<title>Mysql操作02</title>
<link href="/2023/12/17/Mysql%E6%93%8D%E4%BD%9C02/"/>
<url>/2023/12/17/Mysql%E6%93%8D%E4%BD%9C02/</url>
<content type="html"><![CDATA[<p>Mysql数据库的学习与应用</p><span id="more"></span> <h1 id="Mysql操作02"><a href="#Mysql操作02" class="headerlink" title="Mysql操作02"></a>Mysql操作02</h1><h2 id="Mysql单表查询"><a href="#Mysql单表查询" class="headerlink" title="Mysql单表查询"></a>Mysql单表查询</h2><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">select</span><br>字段列表<br><span class="hljs-keyword">from</span><br>表名列表<span class="hljs-comment">-- 基本查询</span><br><span class="hljs-keyword">where</span><br>条件列表<span class="hljs-comment">-- 条件查询</span><br><span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span><br>分组字段列表 <br><span class="hljs-keyword">having</span><br>分组后条件列表<span class="hljs-comment">-- 分组查询</span><br><span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span><br>排序列表字段<span class="hljs-comment">-- 拍寻查询</span><br>limit<br>分页参数<span class="hljs-comment">-- 分页查询</span><br></code></pre></td></tr></table></figure><h3 id="基本查询"><a href="#基本查询" class="headerlink" title="基本查询"></a>基本查询</h3> <figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 查询多个字段</span><br><span class="hljs-keyword">select</span> 字段<span class="hljs-number">1</span>,字段<span class="hljs-number">2</span>,字段<span class="hljs-number">3</span> <span class="hljs-keyword">from</span> 表名;<br><br><span class="hljs-comment">-- 查询所有字段</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> 表名;<br><br><span class="hljs-comment">-- 设置别名(表现出来的名字),as可省略</span><br><span class="hljs-keyword">select</span> 字段<span class="hljs-number">1</span> [<span class="hljs-keyword">as</span> <span class="hljs-string">'别名1'</span> ] ,字段<span class="hljs-number">2</span> [<span class="hljs-keyword">as</span> <span class="hljs-string">'别名2'</span> ] <span class="hljs-keyword">from</span> 表名;<br><br><span class="hljs-comment">-- 去除重复记录</span><br><span class="hljs-keyword">select</span> <span class="hljs-keyword">distinct</span> 字段<span class="hljs-number">1</span>,字段<span class="hljs-number">2</span>,字段<span class="hljs-number">3</span> <span class="hljs-keyword">from</span> 表名;<br></code></pre></td></tr></table></figure><h3 id="条件查询"><a href="#条件查询" class="headerlink" title="条件查询"></a>条件查询</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">select</span> 字段列表 <span class="hljs-keyword">from</span> 表名 <span class="hljs-keyword">where</span> 条件列表;<br></code></pre></td></tr></table></figure><ul><li>一些特殊运算符</li></ul><table><thead><tr><th>比较运算符</th><th>功能</th></tr></thead><tbody><tr><td><> 或 |=</td><td>不等于</td></tr><tr><td>between … and …</td><td>在范围内</td></tr><tr><td>in(…)</td><td>在in后面列表之中的值,多选一</td></tr><tr><td>like 占位符</td><td>模糊匹配(_匹配单个字符,%匹配任意个字符)</td></tr><tr><td>is null</td><td>是null</td></tr><tr><td><strong>逻辑运算符</strong></td><td></td></tr><tr><td>and 或 &&</td><td>并且,同时成立</td></tr><tr><td>or 或 ||</td><td>或者,二者其中之一</td></tr><tr><td>not 或 |</td><td>非,不是</td></tr></tbody></table><h3 id="分组查询"><a href="#分组查询" class="headerlink" title="分组查询"></a>分组查询</h3><ul><li>聚合函数</li><li>将表中的一列数据作为一个整体进行纵向计算</li></ul><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>count</td><td>统计数量</td></tr><tr><td>max</td><td>最大值</td></tr><tr><td>min</td><td>最小值</td></tr><tr><td>avg</td><td>平均值</td></tr><tr><td>sum</td><td>求和</td></tr></tbody></table><ul><li>语法</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 聚合函数使用</span><br><span class="hljs-keyword">select</span> 聚合函数(字段列表) <span class="hljs-keyword">from</span> 表名;<br><br><span class="hljs-comment">-- 分组查询</span><br><span class="hljs-keyword">select</span> 字段列表 <span class="hljs-keyword">from</span> 表名 [<span class="hljs-keyword">where</span> 条件] <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> 分组字段名 [<span class="hljs-keyword">having</span> 分组后的过滤条件];<br></code></pre></td></tr></table></figure><ul><li>举例</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 错误示范:查询对应性别的数量</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender;<br><span class="hljs-comment">-- 注意:分组查询使用group by ,select返回的是分组字段+聚合函数</span><br><span class="hljs-comment">-- 正确用法</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender;<br><br><span class="hljs-comment">-- 示例二:找到年龄小于等于20的用户并根据性别进行分组,获取人数大于20的组的人数</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">where</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">20</span> <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender <span class="hljs-keyword">having</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-operator">></span> <span class="hljs-number">20</span>;<br></code></pre></td></tr></table></figure><ul><li>执行顺序:where > 聚合函数 > having</li></ul><h3 id="排序查询"><a href="#排序查询" class="headerlink" title="排序查询"></a>排序查询</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">select</span> 字段列表 <span class="hljs-keyword">from</span> 表名 [<span class="hljs-keyword">where</span> 条件列表][<span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> 分组字段] <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> 字段<span class="hljs-number">1</span> 排序方式<span class="hljs-number">1</span>,字段<span class="hljs-number">2</span> 排序方式<span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><blockquote><p>升序:asc (默认值)</p><p>降序: desc</p></blockquote><ul><li><p>注:多字段排序时,当第一个字段相同的时候,才会根据第二个字段进行排序,<strong>也就是先排字段一,在不影响字段一排序的基础上再 进行后续排序</strong>,是一种逻辑处理</p></li><li><p>示例</p></li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 找到年龄小于等于20的用户并根据性别进行分组,获取数量并进行降序排序</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">where</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">20</span> <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">DESC</span>;<br></code></pre></td></tr></table></figure><h3 id="分页排序"><a href="#分页排序" class="headerlink" title="分页排序"></a>分页排序</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">select</span> 字段列表 <span class="hljs-keyword">from</span> 表名 limit 起始索引,查询记录数; <br></code></pre></td></tr></table></figure><ul><li>示例</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 起始索引0,5条记录</span><br><span class="hljs-keyword">select</span> <span class="hljs-operator">*</span> <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> limit <span class="hljs-number">0</span>,<span class="hljs-number">5</span>;<br><br><span class="hljs-comment">-- 找到年龄小于等于20的用户并根据性别进行分组,获取数量最多的那一组</span><br><span class="hljs-keyword">select</span> gender,<span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">from</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">where</span> age <span class="hljs-operator"><=</span> <span class="hljs-number">20</span> <span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span> gender <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> <span class="hljs-built_in">count</span>(<span class="hljs-operator">*</span>) <span class="hljs-keyword">DESC</span> limit <span class="hljs-number">1</span>;<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>Mysql</tag>
</tags>
</entry>
<entry>
<title>Mysql操作01</title>
<link href="/2023/12/17/Mysql%E6%93%8D%E4%BD%9C01/"/>
<url>/2023/12/17/Mysql%E6%93%8D%E4%BD%9C01/</url>
<content type="html"><![CDATA[<p>Mysql数据库的学习与应用</p><span id="more"></span> <h1 id="Mysql学习(一)"><a href="#Mysql学习(一)" class="headerlink" title="Mysql学习(一)"></a>Mysql学习(一)</h1><h2 id="打开数据库"><a href="#打开数据库" class="headerlink" title="打开数据库"></a>打开数据库</h2><blockquote><p>mysql -u root -p</p></blockquote><h2 id="表(table)结构操作"><a href="#表(table)结构操作" class="headerlink" title="表(table)结构操作"></a>表(table)结构操作</h2><h3 id="创建表"><a href="#创建表" class="headerlink" title="创建表"></a>创建表</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> 表名(<br>字段<span class="hljs-number">1</span> 字段类型 [约束] [comment <span class="hljs-string">'字段1注释'</span>],<span class="hljs-comment">-- []代表可写可不写</span><br> .......<br> 字段n 字段类型 [约束] [comment <span class="hljs-string">'字段n注释'</span>]<br>)[comment <span class="hljs-string">'字段1注释'</span>];<br></code></pre></td></tr></table></figure><ul><li>示例</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">user</span>(<br>id <span class="hljs-type">int</span> comment <span class="hljs-string">'id 唯一标识'</span>,<br> username <span class="hljs-type">varchar</span>(<span class="hljs-number">20</span>) comment <span class="hljs-string">'用户名'</span>,<br> age <span class="hljs-type">int</span> comment <span class="hljs-string">'年龄'</span>,<br> gender <span class="hljs-type">char</span>(<span class="hljs-number">1</span>) comment <span class="hljs-string">'性别'</span><br>)comment <span class="hljs-string">'用户表'</span>;<br></code></pre></td></tr></table></figure><ul><li>约束:保证数据库之中数据的正确性,有效性,完整性</li></ul><table><thead><tr><th>约束</th><th>描述</th><th>关键字</th></tr></thead><tbody><tr><td>非空约束</td><td>限制该字段不能为空</td><td>not null</td></tr><tr><td>唯一约束</td><td>保证字段的所有数据都是唯一,不重复的</td><td>unique</td></tr><tr><td>主键约束</td><td>主键是一行数据的唯一标识,要求非空且唯一</td><td>primary key(auto_increment自增)</td></tr><tr><td>默认约束</td><td>未指定则为默认值</td><td>default</td></tr><tr><td>外键约束</td><td>让两张表建立连接,保证数据的一致性和完整性</td><td>foreign key</td></tr></tbody></table><ul><li>约束使用</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 可以连续添加多个约束</span><br>use sqlstudy;<br><span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> <span class="hljs-keyword">user</span>(<br>id <span class="hljs-type">int</span> <span class="hljs-keyword">primary</span> key auto_increment comment <span class="hljs-string">'id 唯一标识'</span>,<br> username <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> <span class="hljs-type">varchar</span>(<span class="hljs-number">20</span>) comment <span class="hljs-string">'用户名'</span>,<br> age <span class="hljs-type">int</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> comment <span class="hljs-string">'年龄'</span>,<br> gender <span class="hljs-type">char</span>(<span class="hljs-number">1</span>) <span class="hljs-keyword">not</span> <span class="hljs-keyword">null</span> comment <span class="hljs-string">'性别'</span><br>)comment <span class="hljs-string">'用户表'</span>;<br></code></pre></td></tr></table></figure><h3 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h3><h4 id="数值类型"><a href="#数值类型" class="headerlink" title="数值类型"></a>数值类型</h4><table><thead><tr><th>数值类型</th><th>大小(byte)</th><th>无符号范围(unsigned)</th><th>描述</th></tr></thead><tbody><tr><td>tinyint</td><td>1</td><td>(0,255)</td><td>小整数值</td></tr><tr><td>smallint</td><td>2</td><td>(0,65535)</td><td>大整数值</td></tr><tr><td>mediumint</td><td>3</td><td>(0,16777215)</td><td>大整数值</td></tr><tr><td>int</td><td>4</td><td>(0,4294967295)</td><td>大整数值</td></tr><tr><td>bigint</td><td>8</td><td>(0,2^64-1)</td><td>极大整数值</td></tr><tr><td>float</td><td>4</td><td>略</td><td>单精度浮点数</td></tr><tr><td>double</td><td>8</td><td>略</td><td>双精度浮点数</td></tr><tr><td>decimal</td><td></td><td></td><td>以字符串形式存储,精度更高</td></tr></tbody></table><ul><li>注:float(5,2)和double(5,2)表示5个数字长度,2为小数为个数</li><li><strong>尽可能选择占用磁盘小的满足需求的数值类型</strong></li><li>在数值声明后面加<strong>unsigned</strong>表示无符号数</li></ul><blockquote><p>注:linestring会造成错误修改</p></blockquote><h4 id="字符串类型"><a href="#字符串类型" class="headerlink" title="字符串类型"></a>字符串类型</h4><table><thead><tr><th>字符串类型</th><th>大小(bytes)</th><th>描述</th></tr></thead><tbody><tr><td>char</td><td>0-255</td><td>定长字符串,char(10)表示最多只能存10个字符,且必定占用十个字符空间,但性能高</td></tr><tr><td>varchar</td><td>0-65535</td><td>变长字符串,varchar(10),超出十个字符报错,不足10个字符灰暗实际长度存储</td></tr><tr><td>tinyblob</td><td>0-255</td><td>不能超过255个字符的二进制数据</td></tr><tr><td>tinytext</td><td>0-255</td><td>短文本字符串</td></tr><tr><td>blob</td><td>0-65535</td><td>二进制形式长文本数据</td></tr><tr><td>text</td><td>0-65535</td><td>长文本数据</td></tr><tr><td>mediumblob</td><td>0-16777215</td><td>二进制形式的中等长度文本数据</td></tr><tr><td>mediumtext</td><td>0-16777215</td><td>中等文本数据</td></tr><tr><td>longblob</td><td>0-4294967295</td><td>二进制形式的极大长度文本数据</td></tr><tr><td>longtext</td><td>0-4294967295</td><td>极大文本数据</td></tr></tbody></table><h4 id="日期类型"><a href="#日期类型" class="headerlink" title="日期类型"></a>日期类型</h4><table><thead><tr><th>类型</th><th>大小(byte)</th><th>范围</th><th>格式</th><th>描述</th></tr></thead><tbody><tr><td>date</td><td>3</td><td>1000-01-01 至 9999-12-31</td><td>YY-MM-DD</td><td>日期值</td></tr><tr><td>time</td><td>3</td><td>-838:59:59 至 838:59:59</td><td>HH:MM:SS</td><td>时间值或者持续时间</td></tr><tr><td>year</td><td>1</td><td>1901 至 2155</td><td>YYYY</td><td>年份值</td></tr><tr><td>datetime</td><td>8</td><td>1000-01-01 00:00:00 至 9999-12-31 23:59:59</td><td>YYYY-MM-DD HH:MM:SS</td><td>混合日期和时间值</td></tr><tr><td>timestamp</td><td>4</td><td>1970-01-01 00:00:00 至 2038-01-19 03:14:07</td><td>YYYY-MM-DD HH:MM:SS</td><td>混合日期和时间值,时间戳</td></tr></tbody></table><h3 id="表查询"><a href="#表查询" class="headerlink" title="表查询"></a>表查询</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 查询当前数据库下所有表</span><br><span class="hljs-keyword">show</span> tables;<br><span class="hljs-comment">-- 查看指定表结构</span><br><span class="hljs-keyword">desc</span> 表名;<br><span class="hljs-comment">-- 查询建表语句,图形化操作则是go to DDL</span><br><span class="hljs-keyword">show</span> <span class="hljs-keyword">create</span> <span class="hljs-keyword">table</span> 表名;<br></code></pre></td></tr></table></figure><h3 id="表修改"><a href="#表修改" class="headerlink" title="表修改"></a>表修改</h3><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 在表中添加字段</span><br><span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> 表名 <span class="hljs-keyword">add</span> 字段名 类型(长度) [comment 注释] [约束];<br><span class="hljs-comment">-- 修改字段类型</span><br><span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> 表名 modify 字段名 新数据类型(长度);<br><span class="hljs-comment">-- 修改字段名和字段类型</span><br><span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> 表名 change 旧字段名 新字段名 新数据类型(长度) [comment 注释] [约束];<br><span class="hljs-comment">-- 删除字段</span><br><span class="hljs-keyword">alter</span> <span class="hljs-keyword">table</span> 表名 <span class="hljs-keyword">drop</span> <span class="hljs-keyword">column</span> 字段名;<br><span class="hljs-comment">-- 修改表名</span><br>rename <span class="hljs-keyword">table</span> 表名 <span class="hljs-keyword">to</span> 新表名;<br><span class="hljs-comment">-- 删除表</span><br><span class="hljs-keyword">drop</span> <span class="hljs-keyword">table</span> [if <span class="hljs-keyword">exists</span>] 表名;<br></code></pre></td></tr></table></figure><h2 id="添加数据(insert)"><a href="#添加数据(insert)" class="headerlink" title="添加数据(insert)"></a>添加数据(insert)</h2><ul><li>insert 语法</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 指定字段添加数据</span><br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> 表名(字段名<span class="hljs-number">1</span>,字段名<span class="hljs-number">2</span>) <span class="hljs-keyword">values</span>(值<span class="hljs-number">1</span>,值<span class="hljs-number">2</span>);<br><span class="hljs-comment">-- 全部字段添加数据</span><br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> 表名 <span class="hljs-keyword">values</span>(值<span class="hljs-number">1</span>,值<span class="hljs-number">2.</span>...);<br><span class="hljs-comment">-- 批量添加数据(指定字段)</span><br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> 表名(字段名<span class="hljs-number">1</span>,字段名<span class="hljs-number">2</span>) <span class="hljs-keyword">values</span> (值<span class="hljs-number">1</span>,值<span class="hljs-number">2</span>),(值<span class="hljs-number">1</span>,值<span class="hljs-number">2</span>);<br><span class="hljs-comment">-- 批量添加数据(所有字段)</span><br><span class="hljs-keyword">insert</span> <span class="hljs-keyword">into</span> 表名 <span class="hljs-keyword">values</span> (值<span class="hljs-number">1</span>,值<span class="hljs-number">2.</span>..),(值<span class="hljs-number">1</span>,值<span class="hljs-number">2.</span>..);<br></code></pre></td></tr></table></figure><h2 id="修改数据(update)"><a href="#修改数据(update)" class="headerlink" title="修改数据(update)"></a>修改数据(update)</h2><ul><li>update语法</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-comment">-- 修改数据</span><br><span class="hljs-keyword">update</span> 表名 <span class="hljs-keyword">set</span> 字段<span class="hljs-number">1</span><span class="hljs-operator">=</span>值<span class="hljs-number">1</span>,字段名<span class="hljs-number">2</span><span class="hljs-operator">=</span>值<span class="hljs-number">2</span>,...[<span class="hljs-keyword">where</span> 条件];<br></code></pre></td></tr></table></figure><ul><li><strong>没有修改条件会修改整张表数据</strong></li></ul><h2 id="删除数据(delete)"><a href="#删除数据(delete)" class="headerlink" title="删除数据(delete)"></a>删除数据(delete)</h2><ul><li>delete语法</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs sql"><span class="hljs-keyword">delete</span> <span class="hljs-keyword">from</span> 表名 [<span class="hljs-keyword">where</span> 条件];<br></code></pre></td></tr></table></figure><ul><li><strong>没有修改条件会删除整张表数据</strong></li></ul>]]></content>
<categories>
<category>SQL</category>
</categories>
<tags>
<tag>Mysql</tag>
</tags>
</entry>
<entry>
<title>SSM02-进阶</title>
<link href="/2023/12/12/SSM02-%E8%BF%9B%E9%98%B6/"/>
<url>/2023/12/12/SSM02-%E8%BF%9B%E9%98%B6/</url>
<content type="html"><![CDATA[<p>Springboot的Web学习与应用</p><span id="more"></span><h1 id="SpringBoot的web学习"><a href="#SpringBoot的web学习" class="headerlink" title="SpringBoot的web学习"></a>SpringBoot的web学习</h1><h2 id="请求响应原理"><a href="#请求响应原理" class="headerlink" title="请求响应原理"></a>请求响应原理</h2><ul><li>图例</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-1%E8%AF%B7%E6%B1%82%E5%93%8D%E5%BA%94%E5%8E%9F%E7%90%86.png" alt="image-20231213141803762"></p><ul><li>HttpServletRequest:DispathcherServlet生成的请求体</li><li>HttpServletReponse:DispathcherServlet生成的响应体</li><li>CS架构:客户端/服务器架构(开发维护麻烦,体验良好)</li><li>BS架构:浏览器/服务器的架构。客户只需要浏览器,应用程序的逻辑和数据存储抖子啊服务端</li></ul><h2 id="测试工具:Postman(Apipost-Apifox)"><a href="#测试工具:Postman(Apipost-Apifox)" class="headerlink" title="测试工具:Postman(Apipost,Apifox)"></a>测试工具:Postman(Apipost,Apifox)</h2><h2 id="请求参数"><a href="#请求参数" class="headerlink" title="请求参数"></a>请求参数</h2><h3 id="简单参数"><a href="#简单参数" class="headerlink" title="简单参数"></a>简单参数</h3><ul><li><strong>Parameter:(GET)附带在URL后面</strong></li><li>获取方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//原始方法,较为繁琐以及类型转换</span><br><span class="hljs-meta">@RequestMapping("/simpleParam")</span><span class="hljs-comment">//此注解用于容器前则是此容器所有方法的前缀</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">simpleParam</span><span class="hljs-params">(HttpServletRequest request)</span>{<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> request.getParameter(<span class="hljs-string">"name"</span>);<br> <span class="hljs-type">String</span> <span class="hljs-variable">password</span> <span class="hljs-operator">=</span> request.getParameter(<span class="hljs-string">"password"</span>);<br> <span class="hljs-comment">//验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br><br><span class="hljs-comment">//建议方法,会自动进行类型转换,但是要保持名字于请求参数一样</span><br><span class="hljs-meta">@RequestMapping("/simpleParam")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">simpleParam</span><span class="hljs-params">(String name,String password)</span>{<br> <span class="hljs-comment">//使用@RequestParam(name="name")+参数 可以进行映射而不必参数名一致,但此时参数必须传递,也可以设置为不必须传递</span><br> <span class="hljs-comment">//@RequestParam同时还可以进行设置默认参数值</span><br> <span class="hljs-comment">//验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br></code></pre></td></tr></table></figure><ul><li><strong>Post</strong>请求方式</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-2%E7%AE%80%E5%8D%95%E5%8F%82%E6%95%B0Post.png" alt="image-20231213150925747"></p><h3 id="实体参数"><a href="#实体参数" class="headerlink" title="实体参数"></a>实体参数</h3><ul><li>简单实体对象:请求参数名于实体类属性名一致,定义POJO接收即可</li><li>接收方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Address</span>{<br> <span class="hljs-keyword">private</span> String city;<br>}<br><br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span>{<br> <span class="hljs-keyword">private</span> String name;<br> <span class="hljs-keyword">private</span> Integer age;<br> <span class="hljs-keyword">private</span> Address address;<span class="hljs-comment">//另外一个实体类作为属性</span><br> <span class="hljs-comment">//.....实现方法</span><br>}<br><br><span class="hljs-meta">@RequestMapping("/simplePojo")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">simplePojo</span><span class="hljs-params">(User user)</span>{<br> <span class="hljs-comment">//......验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br></code></pre></td></tr></table></figure><ul><li>传递方式:与简单参数基本一致</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-5%E5%AE%9E%E4%BD%93%E8%AF%B7%E6%B1%82.png" alt="image-20231213172515628"></p><ul><li>address.city表示adress的属性</li></ul><h3 id="数组参数"><a href="#数组参数" class="headerlink" title="数组参数"></a>数组参数</h3><ul><li>请求参数名和数组名称相同且有多个</li><li>请求方式</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-%E6%95%B0%E7%BB%84%E8%AF%B7%E6%B1%82.png" alt="image-20231214134154738"></p><ul><li>接收方式</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping("/arrayParam")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">arrayParam</span><span class="hljs-params">(String[] hobby)</span>{<br> <span class="hljs-comment">//......验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br><span class="hljs-comment">//或者</span><br><span class="hljs-meta">@RequestMapping("/arrayParam")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">arrayParam</span><span class="hljs-params">(<span class="hljs-meta">@RequestParam</span> List<String> hobby)</span>{<br> <span class="hljs-comment">//......验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br></code></pre></td></tr></table></figure><h3 id="日期参数"><a href="#日期参数" class="headerlink" title="日期参数"></a>日期参数</h3><ul><li>使用@DateTimeFormat注解完成日期参数格式转换</li><li>请求方式</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-%E6%97%B6%E9%97%B4%E5%8F%82%E6%95%B0%E8%AF%B7%E6%B1%82.png" alt="image-20231214134957056"></p><ul><li>请求方式</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping("/dateParam")</span><br><span class="hljs-keyword">public</span> String <span class="hljs-title function_">dateParam</span><span class="hljs-params">(<span class="hljs-meta">@DateTimeFormat(pattern = "yyyy-MM-dd HH:MM:ss")</span> LocalDateTime updateTime)</span>{<br> <span class="hljs-comment">//......验证方法</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br>}<br></code></pre></td></tr></table></figure><h3 id="Json格式"><a href="#Json格式" class="headerlink" title="Json格式"></a>Json格式</h3><ul><li>附在请求体之中,所以是Post请求</li><li>Json参数键名需要与形参对象属性名相同,可以自定义接受数据来接收**@RequestBody**</li><li>请求方法</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-Json%E8%AF%B7%E6%B1%82.png" alt="image-20231214144132444"></p><ul><li>接收方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping("/jsonParam")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">jsonParam</span><span class="hljs-params">(<span class="hljs-meta">@RequestBody</span> User user)</span>{<br> System.out.println(user);<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br> }<br></code></pre></td></tr></table></figure><h3 id="路径参数"><a href="#路径参数" class="headerlink" title="路径参数"></a>路径参数</h3><ul><li><strong>@PathVarible获取路径参数</strong></li><li>请求方法</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-%E8%B7%AF%E5%BE%84%E5%8F%82%E6%95%B0%E8%8E%B7%E5%8F%96.png" alt="image-20231214145347401"></p><ul><li>接收方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RequestMapping("/path/{id}/{name}")</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">path</span><span class="hljs-params">(<span class="hljs-meta">@PathVariable</span> Integer id,<span class="hljs-meta">@PathVariable</span> String name)</span>{<br> System.out.println(id+<span class="hljs-string">" "</span>+name);<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"ok"</span>;<br> }<br></code></pre></td></tr></table></figure><h2 id="文件上传"><a href="#文件上传" class="headerlink" title="文件上传"></a>文件上传</h2><ul><li><strong>MultipartFile file</strong>作为参数</li></ul><h3 id="本地存储:MultipartFile类"><a href="#本地存储:MultipartFile类" class="headerlink" title="本地存储:MultipartFile类"></a>本地存储:MultipartFile类</h3><ul><li><strong>本地存储</strong>示例</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UploadController</span> {<br><br> <span class="hljs-meta">@RequestMapping("/upload")</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">upload</span><span class="hljs-params">(String username, String age, MultipartFile image)</span>{<span class="hljs-comment">//image就是一个从前端提交的文件</span><br> log.info(<span class="hljs-string">"上传员工信息:{}{}{}"</span>,username,age,image);<br> <br> <span class="hljs-comment">//获取源文件名字</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">oroginFilename</span> <span class="hljs-operator">=</span> image.getOriginalFilename();<br> <span class="hljs-comment">//从源文件名字获得文件后缀,后自定义文件新名字</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">newFileName</span> <span class="hljs-operator">=</span><span class="hljs-string">"1"</span>+ oroginFilename.substring(oroginFilename.lastIndexOf(<span class="hljs-string">"."</span>));<br> log.info(<span class="hljs-string">"新文件名{}"</span>,newFileName);<br> <span class="hljs-comment">//通过MultipartFile的transferTo方法保存文件</span><br> image.transferTo(<span class="hljs-keyword">new</span> <span class="hljs-title class_">File</span>(<span class="hljs-string">"D:\\projecttest\\store\\"</span>+newFileName));<br> <span class="hljs-keyword">return</span> Result.success();<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>文件上传大小限制默认为1MB,但可以通过配置文件修改</li></ul><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-comment">#单个文件最大</span><br><span class="hljs-attr">spring.servlet.multipart.max-file-size</span>=<span class="hljs-string">10MB</span><br><span class="hljs-comment">#单次请求最大</span><br><span class="hljs-attr">spring.servlet.multipart.max-request-size</span>=<span class="hljs-string">100MB</span><br></code></pre></td></tr></table></figure><ul><li>修改资源路径</li></ul><h4 id="1、为什么要改变存储的路径?"><a href="#1、为什么要改变存储的路径?" class="headerlink" title="1、为什么要改变存储的路径?"></a>1、为什么要改变存储的路径?</h4><p>如果都放在classpath目录下打包的文件就会很大<br>代码与文件数据不能分开存储,就意味着文件数据的备份将变得复杂</p><h4 id="2、解决存储问题的方法"><a href="#2、解决存储问题的方法" class="headerlink" title="2、解决存储问题的方法"></a>2、解决存储问题的方法</h4><p>springboot提供了 spring.resources.static-locations 配置自定义静态文件的位置:</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">spring:</span><br> <span class="hljs-attr">web:</span><br> <span class="hljs-attr">resources:</span><br> <span class="hljs-attr">static-locations:</span> <span class="hljs-string">classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${demo.web.upload-path}</span><br><span class="hljs-comment">#设置Http能访问的本地资源路径</span><br><span class="hljs-attr">demo:</span><br> <span class="hljs-attr">web:</span><br> <span class="hljs-attr">upload-path:</span> <span class="hljs-string">D:/MineFile/zuoye/xm/equipment-management-system/qhjdata/</span><br></code></pre></td></tr></table></figure><ul><li>配置 demo.web.upload-path 为与项目代码分离的静态资源路径,即:文件上传保存根路径</li><li>配置 spring.web.resources.static-locations 除了带上SpringBoot默认的静态资源路径之外,加上file:${demo.web.upload-path}指向外部的文件资源上传路径,即:该路径下的静态资源可以直接对外提供HTTP访问服务</li></ul><blockquote><p>SimpleDateFormat是一个用于格式化和解析日期的具体类</p></blockquote><h3 id="云端存储:阿里云为例"><a href="#云端存储:阿里云为例" class="headerlink" title="云端存储:阿里云为例"></a>云端存储:阿里云为例</h3><ul><li><p>前置:阿里云注册,bucket创建等</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment"><!--前置--></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>com.aliyun.oss<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>aliyun-sdk-oss<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.15.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure></li><li><p>示例代码</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.studyins;<br><br><span class="hljs-keyword">import</span> com.aliyun.oss.ClientException;<br><span class="hljs-keyword">import</span> com.aliyun.oss.OSS;<br><span class="hljs-keyword">import</span> com.aliyun.oss.common.auth.*;<br><span class="hljs-keyword">import</span> com.aliyun.oss.OSSClientBuilder;<br><span class="hljs-keyword">import</span> com.aliyun.oss.OSSException;<br><span class="hljs-keyword">import</span> com.aliyun.oss.model.PutObjectRequest;<br><span class="hljs-keyword">import</span> com.aliyun.oss.model.PutObjectResult;<br><span class="hljs-keyword">import</span> java.io.FileInputStream;<br><span class="hljs-keyword">import</span> java.io.InputStream;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Demo</span> {<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">endpoint</span> <span class="hljs-operator">=</span> <span class="hljs-string">"https://oss-cn-beijing.aliyuncs.com"</span>;<br> <span class="hljs-comment">// 从环境变量中获取访问凭证。运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。</span><br> <span class="hljs-type">EnvironmentVariableCredentialsProvider</span> <span class="hljs-variable">credentialsProvider</span> <span class="hljs-operator">=</span> CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();<br> <span class="hljs-comment">// 填写Bucket名称,例如examplebucket。</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">bucketName</span> <span class="hljs-operator">=</span> <span class="hljs-string">"zwms-public"</span>;<br> <span class="hljs-comment">// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">objectName</span> <span class="hljs-operator">=</span> <span class="hljs-string">"1.jpg"</span>;<br> <span class="hljs-comment">// 填写本地文件的完整路径,例如D:\\localpath\\examplefile.txt。</span><br> <span class="hljs-comment">// 如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件流。</span><br> String filePath= <span class="hljs-string">"D:\\projecttest\\store\\安达与岛村.jpg"</span>;<br><br> <span class="hljs-comment">// 创建OSSClient实例。</span><br> <span class="hljs-type">OSS</span> <span class="hljs-variable">ossClient</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">OSSClientBuilder</span>().build(endpoint, credentialsProvider);<br><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">FileInputStream</span>(filePath);<br> <span class="hljs-comment">// 创建PutObjectRequest对象。</span><br> <span class="hljs-type">PutObjectRequest</span> <span class="hljs-variable">putObjectRequest</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">PutObjectRequest</span>(bucketName, objectName, inputStream);<br> <span class="hljs-comment">// 创建PutObject请求。</span><br> <span class="hljs-type">PutObjectResult</span> <span class="hljs-variable">result</span> <span class="hljs-operator">=</span> ossClient.putObject(putObjectRequest);<br> } <span class="hljs-keyword">catch</span> (OSSException oe) {<br> System.out.println(<span class="hljs-string">"Caught an OSSException, which means your request made it to OSS, "</span><br> + <span class="hljs-string">"but was rejected with an error response for some reason."</span>);<br> System.out.println(<span class="hljs-string">"Error Message:"</span> + oe.getErrorMessage());<br> System.out.println(<span class="hljs-string">"Error Code:"</span> + oe.getErrorCode());<br> System.out.println(<span class="hljs-string">"Request ID:"</span> + oe.getRequestId());<br> System.out.println(<span class="hljs-string">"Host ID:"</span> + oe.getHostId());<br> } <span class="hljs-keyword">catch</span> (ClientException ce) {<br> System.out.println(<span class="hljs-string">"Caught an ClientException, which means the client encountered "</span><br> + <span class="hljs-string">"a serious internal problem while trying to communicate with OSS, "</span><br> + <span class="hljs-string">"such as not being able to access the network."</span>);<br> System.out.println(<span class="hljs-string">"Error Message:"</span> + ce.getMessage());<br> } <span class="hljs-keyword">finally</span> {<br> <span class="hljs-keyword">if</span> (ossClient != <span class="hljs-literal">null</span>) {<br> ossClient.shutdown();<br> }<br> }<br> }<br>} <br></code></pre></td></tr></table></figure><ul><li>详情见阿里云操作文档</li><li>优化后的代码</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><span class="hljs-comment">//既不属于controller也不属于service也不是数据访问,使用此注解加入IOC管理</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AliOSSUtils</span> {<br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">String</span> <span class="hljs-variable">endpoint</span> <span class="hljs-operator">=</span> <span class="hljs-string">"https://oss-cn-beijing.aliyuncs.com"</span>;<br> <span class="hljs-type">EnvironmentVariableCredentialsProvider</span> <span class="hljs-variable">credentialsProvider</span> <span class="hljs-operator">=</span> CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();<br> <span class="hljs-keyword">private</span> <span class="hljs-type">String</span> <span class="hljs-variable">bucketName</span> <span class="hljs-operator">=</span> <span class="hljs-string">"zwms-public"</span>;<br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">AliOSSUtils</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> ClientException {<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 实现上传图片到OSS</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">upload</span><span class="hljs-params">(MultipartFile file)</span> <span class="hljs-keyword">throws</span> IOException {<br> <span class="hljs-comment">// 获取上传的文件的输入流</span><br> <span class="hljs-type">InputStream</span> <span class="hljs-variable">inputStream</span> <span class="hljs-operator">=</span> file.getInputStream();<br><br> <span class="hljs-comment">// 避免文件覆盖</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">originalFilename</span> <span class="hljs-operator">=</span> file.getOriginalFilename();<br> <span class="hljs-type">String</span> <span class="hljs-variable">fileName</span> <span class="hljs-operator">=</span> UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf(<span class="hljs-string">"."</span>));<br><br> <span class="hljs-comment">//上传文件到 OSS</span><br> <span class="hljs-type">OSS</span> <span class="hljs-variable">ossClient</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">OSSClientBuilder</span>().build(endpoint,credentialsProvider);<br> ossClient.putObject(bucketName, fileName, inputStream);<br><br> <span class="hljs-comment">//文件访问路径</span><br> <span class="hljs-type">String</span> <span class="hljs-variable">url</span> <span class="hljs-operator">=</span> endpoint.split(<span class="hljs-string">"//"</span>)[<span class="hljs-number">0</span>] + <span class="hljs-string">"//"</span> + bucketName + <span class="hljs-string">"."</span> + endpoint.split(<span class="hljs-string">"//"</span>)[<span class="hljs-number">1</span>] + <span class="hljs-string">"/"</span> + fileName;<br> <span class="hljs-comment">// 关闭ossClient</span><br> ossClient.shutdown();<br> <span class="hljs-keyword">return</span> url;<span class="hljs-comment">// 把上传到oss的路径返回</span><br> }<br><br>}<br></code></pre></td></tr></table></figure><ul><li>前端代码</li></ul><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-meta"><!DOCTYPE <span class="hljs-keyword">html</span>></span><br><span class="hljs-tag"><<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>></span><br><span class="hljs-tag"><<span class="hljs-name">head</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">title</span>></span>上传文件<span class="hljs-tag"></<span class="hljs-name">title</span>></span><br><span class="hljs-tag"></<span class="hljs-name">head</span>></span><br><span class="hljs-tag"><<span class="hljs-name">body</span>></span><br><br> <span class="hljs-tag"><<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">"/upload"</span> <span class="hljs-attr">method</span>=<span class="hljs-string">"post"</span> <span class="hljs-attr">enctype</span>=<span class="hljs-string">"multipart/form-data"</span>></span>//enctype一定要修改为这种值来进行文件提交<br> 姓名: <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>></span><span class="hljs-tag"><<span class="hljs-name">br</span>></span><br> 年龄: <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"age"</span>></span><span class="hljs-tag"><<span class="hljs-name">br</span>></span><br> 头像: <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"file"</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"image"</span>></span><span class="hljs-tag"><<span class="hljs-name">br</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"提交"</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">form</span>></span><br><br><span class="hljs-tag"></<span class="hljs-name">body</span>></span><br><span class="hljs-tag"></<span class="hljs-name">html</span>></span><br></code></pre></td></tr></table></figure><h2 id="开发规范"><a href="#开发规范" class="headerlink" title="开发规范"></a>开发规范</h2><ul><li><p>Restful风格</p><ul><li>根据需求封装返回类型</li><li>一般单独定义返回封装类Result</li></ul></li><li><p>Rest转换</p><ul><li>更具请求类型不同返回不同方法结果</li><li>结合请求类型来简化url的格式(@PathVarible获取路径参数)</li><li>Result类(依据需求文档构成)</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@NoArgsConstructor</span><br><span class="hljs-meta">@AllArgsConstructor</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Result</span> {<br> <span class="hljs-keyword">private</span> Integer code;<span class="hljs-comment">//响应码,1 代表成功; 0 代表失败</span><br> <span class="hljs-keyword">private</span> String msg; <span class="hljs-comment">//响应信息 描述字符串</span><br> <span class="hljs-keyword">private</span> Object data; <span class="hljs-comment">//返回的数据</span><br><br> <span class="hljs-comment">//增删改 成功响应</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title function_">success</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Result</span>(<span class="hljs-number">1</span>,<span class="hljs-string">"success"</span>,<span class="hljs-literal">null</span>);<br> }<br> <span class="hljs-comment">//查询 成功响应</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title function_">success</span><span class="hljs-params">(Object data)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Result</span>(<span class="hljs-number">1</span>,<span class="hljs-string">"success"</span>,data);<br> }<br> <span class="hljs-comment">//失败响应</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Result <span class="hljs-title function_">error</span><span class="hljs-params">(String msg)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Result</span>(<span class="hljs-number">0</span>,msg,<span class="hljs-literal">null</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>方法测试</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@GetMapping("/depts")</span><span class="hljs-comment">//此注解限定请求方法为get,等同于RequestMapping注解加上method属性</span><br><span class="hljs-keyword">public</span> Result <span class="hljs-title function_">list</span><span class="hljs-params">()</span>{<br> log.info(<span class="hljs-string">"查询所有部门日志记录"</span>);<br> <span class="hljs-keyword">return</span> Result.success();<br>}<br></code></pre></td></tr></table></figure></li></ul><h2 id="配置文件配置参数"><a href="#配置文件配置参数" class="headerlink" title="配置文件配置参数"></a>配置文件配置参数</h2><ul><li><strong>xml -> properties ->yml/yaml 结构越来越清晰,代码简化</strong></li><li>优先级:properties > yml > yaml</li></ul><h3 id="properties配置"><a href="#properties配置" class="headerlink" title="properties配置"></a>properties配置</h3><ul><li>配置文件设置参数</li></ul><figure class="highlight properties"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs properties"><span class="hljs-attr">aliyun.oss.endpoint</span>=<span class="hljs-string">https://oss-cn-beijing.aliyuncs.com</span><br><span class="hljs-attr">aliyun.oss.bucketName</span>=<span class="hljs-string">zwms-public</span><br></code></pre></td></tr></table></figure><ul><li>使用配置文件参数 : @Value</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Value("${aliyun.oss.endpoint}")</span><br><span class="hljs-keyword">private</span> String endpoint;<br><br><span class="hljs-type">EnvironmentVariableCredentialsProvider</span> <span class="hljs-variable">credentialsProvider</span> <span class="hljs-operator">=</span> CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();<br><br><span class="hljs-meta">@Value("${aliyun.oss.bucketName}")</span><br><span class="hljs-keyword">private</span> String bucketName;<br></code></pre></td></tr></table></figure><h3 id="yml配置文件"><a href="#yml配置文件" class="headerlink" title="yml配置文件"></a>yml配置文件</h3><blockquote><p>1.大小写严格</p><p>2.数值前面必须有空格</p><p>3.缩进表示层级关系,但是不能使用Tab键</p><p>4.相同层级左对齐,空格数目无所谓</p><p>5.#表示行注释</p></blockquote><ul><li>举例</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">#配置端口</span><br><span class="hljs-attr">server:</span><br> <span class="hljs-attr">port:</span> <span class="hljs-number">9000</span><br><span class="hljs-comment">#定义对象</span><br><span class="hljs-attr">user:</span><br> <span class="hljs-attr">username:</span> <span class="hljs-string">zhang</span><br> <span class="hljs-attr">age:</span> <span class="hljs-number">17</span><br><span class="hljs-comment">#定义数组</span><br><span class="hljs-attr">hobby:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">c</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">java</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">C++</span><br><span class="hljs-comment">#阿里云配置</span><br><span class="hljs-attr">aliyun:</span><br> <span class="hljs-attr">oss:</span><br> <span class="hljs-attr">endpoint:</span> <span class="hljs-string">https://oss-cn-beijing.aliyuncs.com</span><br> <span class="hljs-attr">bucketName:</span> <span class="hljs-string">zwms-public</span><br></code></pre></td></tr></table></figure><ul><li><p>调用自定义参数</p><ul><li><p>注解@ConfigurationProperties指定前缀,<strong>批量注入</strong></p></li><li><p>@Data给属性提供get等方法</p></li><li><p>示例</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@ConfigurationProperties(prefix = "aliyun.oss")</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AliyunOssProperties</span> {<br> <span class="hljs-keyword">private</span> String bucketName;<br> <span class="hljs-keyword">private</span> String endpoint;<br>}<br><br><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AliOSSUtils</span> {<br> <span class="hljs-meta">@Autowired</span><br> AliyunOssProperties aliyunOssProperties;<br><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">upload</span><span class="hljs-params">(MultipartFile file)</span> <span class="hljs-keyword">throws</span> IOException {<br> String endpoint= aliyunOssProperties.getEndpoint();<br> String bucketName= aliyunOssProperties.getBucketName();<br> }<br></code></pre></td></tr></table></figure></li><li><p>插件依赖:帮助识别自定义参数配置</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework.boot<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br><span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-boot-configuration-processor<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure></li><li><p>java系统属性和命令行参数也可以配置,系统属性 > 命令行参数</p></li></ul><h2 id="日志"><a href="#日志" class="headerlink" title="日志"></a>日志</h2><ul><li>记录对应程序代码段记录情况</li></ul><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><ul><li>@Slf4j注解</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><span class="hljs-comment">//等同于在里面加入了log</span><br><span class="hljs-meta">@RestController</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DeptController</span> {<br><br> <span class="hljs-comment">//生成一个log记录器</span><br> <span class="hljs-comment">//private static Logger log =LoggerFactory.getLogger(DeptController.class);</span><br><br> <span class="hljs-meta">@RequestMapping("/depts")</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">list</span><span class="hljs-params">()</span>{<br> log.info(<span class="hljs-string">"查询所有部门日志记录"</span>);<span class="hljs-comment">//打印记录日志</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Result</span>();<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="异常处理"><a href="#异常处理" class="headerlink" title="异常处理"></a>异常处理</h2><ul><li><p>全局异常处理器</p><ul><li>捕获项目中所有的异常</li></ul></li><li><p>示例</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestControllerAdvice</span><span class="hljs-comment">//表明此是异常处理器并且包装返回值到Json格式</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">exceptionhandler</span> {<br><br> <span class="hljs-meta">@ExceptionHandler(Exception.class)</span><span class="hljs-comment">//捕获所有异常</span><br> <span class="hljs-keyword">public</span> Result <span class="hljs-title function_">ex</span><span class="hljs-params">(Exception e)</span>{<br> e.printStackTrace();<br> <span class="hljs-keyword">return</span> Result.error(<span class="hljs-string">"出现错误"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="事务管理"><a href="#事务管理" class="headerlink" title="事务管理"></a>事务管理</h2><ul><li>@Transactional注解</li><li>业务层(@Service)类,接口上</li><li>作用:将当前方法交给spring进行管理,方法执行前开启事务,执行后提交事务,出现异常回滚事务</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//当业务层出现多个事务访问的情况时候</span><br><span class="hljs-meta">@Transactional</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">deleteById</span><span class="hljs-params">(Integer id)</span> {<br> deptMapper.deleteById(id);<br> empMapper.deleteByDeptId(id);<br>}<br></code></pre></td></tr></table></figure><ul><li>事务管理日志开启</li></ul><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-comment">#spring事务管理日志</span><br><span class="hljs-attr">logging:</span><br> <span class="hljs-attr">level:</span><br> <span class="hljs-attr">org.springframework.jdbc.support.JdbcTransactionManager:</span> <span class="hljs-string">debug</span><br></code></pre></td></tr></table></figure><ul><li><strong>注解属性:rollbackfor</strong>,回滚<ul><li>默认情况下,只有出现RunTimeException才会回滚事务</li><li>此属性用于指定可以处理什么异常</li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Transactional(rollbackFor = Exception.class)</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">deleteById</span><span class="hljs-params">(Integer id)</span> <span class="hljs-keyword">throws</span> Exception {<br> deptMapper.deleteById(id);<br> <span class="hljs-keyword">if</span>(<span class="hljs-literal">true</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Exception</span>(<span class="hljs-string">"error"</span>);<br> }<br> empMapper.deleteByDeptId(id);<br>}<br></code></pre></td></tr></table></figure><ul><li><p><strong>注解属性:propagation</strong>,事务的传播行为</p><ul><li><p>当一个方法包含另外一个方法的时候,事务该如何处理</p></li><li><p>选择合并为一个事务(<strong>非独立</strong>)或者新建一个事务(<strong>独立</strong>)</p></li><li><p>值</p><ul><li><p>REQUIRED(默认值):此方法被包含的时候,如果包含的方法有事务则加入到里面去,没有则创建一个</p></li><li><p>REQUIRED_NEW :无论无任何,当此方法被包含时候,都新创建一个事务(<strong>意为此事务是独立的事务,不受外部影响</strong>,一般作为日志等事务)</p></li><li><p>……..</p></li></ul></li><li><p>注:事务之间单独进行。当一个事务包含一个新事务的时候,被包含的事务提交后,外事务的回滚不会影响到里事务</p></li></ul></li></ul><h2 id="错误日志"><a href="#错误日志" class="headerlink" title="错误日志"></a>错误日志</h2><ul><li>自动注入的对象不能在方法外,类中使用?</li></ul>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>SSM01-原理入门</title>
<link href="/2023/12/12/SSM01-%E5%8E%9F%E7%90%86%E5%85%A5%E9%97%A8/"/>
<url>/2023/12/12/SSM01-%E5%8E%9F%E7%90%86%E5%85%A5%E9%97%A8/</url>
<content type="html"><![CDATA[<p>Springboot的Web学习与应用</p><span id="more"></span><h1 id="SpringBoot的web学习"><a href="#SpringBoot的web学习" class="headerlink" title="SpringBoot的web学习"></a>SpringBoot的web学习</h1><h2 id="Http协议"><a href="#Http协议" class="headerlink" title="Http协议"></a>Http协议</h2><ul><li><p><strong>超文本传输协议</strong>,规定了浏览器和服务器之间数据传输的规则</p></li><li><p>特点:</p><ul><li>基于TCP协议,稳定安全</li><li>基于请求-响应模型:一次请求对应一次响应</li><li>HTTP协议是无状态协议,对于事务处理没有记忆能力,每次请求都是独立的<ul><li>优点:速度快</li><li>缺点:多次请求不能共享数据</li></ul></li></ul></li></ul><h3 id="HTTP请求数据格式"><a href="#HTTP请求数据格式" class="headerlink" title="HTTP请求数据格式"></a>HTTP请求数据格式</h3><h4 id="POST请求和GET请求形式"><a href="#POST请求和GET请求形式" class="headerlink" title="POST请求和GET请求形式"></a>POST请求和GET请求形式</h4><ul><li>图例</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/Javaweb-1http%E8%AF%B7%E6%B1%82.png" alt="image-20231212150159442"></p><ul><li>请求头</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-2http%E8%AF%B7%E6%B1%82%E5%A4%B4.png" alt="image-20231212150419411"></p><ul><li><p>请求体:POST请求,存放请求参数</p></li><li><p>GET请求与POST请求区别</p><ul><li><p>GET请求无请求体,大小有限制,<strong>通过URL参数传递的,这些参数属于查询字符串(Query String)</strong></p><blockquote><p>查询字符串是位于URL末尾的一组键值对,键值对之间用”&”符号连接,键和值之间使用”=”符号连接</p><p>GET请求的查询字符串通常用于向服务器传递简单的数据</p></blockquote></li><li><p>POST请求有大小限制</p></li></ul></li></ul><h3 id="HTTP响应数据格式"><a href="#HTTP响应数据格式" class="headerlink" title="HTTP响应数据格式"></a>HTTP响应数据格式</h3><ul><li>图例</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-3http%E5%93%8D%E5%BA%94%E6%A0%BC%E5%BC%8F.png" alt="image-20231212160809457"></p><ul><li>响应码</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaWeb-4http%E5%93%8D%E5%BA%94%E7%A0%81.png" alt="image-20231212161134707"></p><ul><li>常见状态码</li></ul><table><thead><tr><th>状态码</th><th>英文描述</th><th>解释</th></tr></thead><tbody><tr><td>200</td><td>OK</td><td>请求处理<strong>成功</strong></td></tr><tr><td>302</td><td>Found</td><td>只是所请求的<strong>资源已移动</strong>到由Location响应头给定的URL,浏览器会自动重新访问</td></tr><tr><td>304</td><td>Not Modified</td><td>隐式重定向。请求<strong>资源上次获取后并未更改</strong>,可以自己在本地缓存访问</td></tr><tr><td>400</td><td>Bad Request</td><td>请求有<strong>语法错误</strong>,服务器并不理解</td></tr><tr><td>403</td><td>Forbidden</td><td>收到请求,但是<strong>拒绝提供服务</strong>(无权限等)</td></tr><tr><td>404</td><td>Not Found</td><td>请求<strong>资源不存在</strong></td></tr><tr><td>405</td><td>Method Not Allowed</td><td>请求<strong>方法有误</strong>,例如POST请求用了GET</td></tr><tr><td>428</td><td>Precondition Required</td><td>服务器要求此请求<strong>有条件</strong>,比如特定的请求头</td></tr><tr><td>429</td><td>Too Many Requests</td><td>太多请求被<strong>限速</strong>了,配合Retry-After(多长时间后可以请求)请求头使用</td></tr><tr><td>431</td><td>Request Header Fields Too Large</td><td><strong>请求头太大</strong>,服务器不愿意处理这个请求</td></tr><tr><td>500</td><td>Internal Serval Error</td><td>服务器<strong>发生不可预期的错误</strong></td></tr><tr><td>503</td><td>Service Unavailable</td><td><strong>服务器未能准备</strong>好处理请求</td></tr></tbody></table><h3 id="协议解析"><a href="#协议解析" class="headerlink" title="协议解析"></a>协议解析</h3><ul><li>web服务器用于处理和返回http格式的请求和数据,对HTTP协议进行封装</li><li>常见的web服务器: <strong>Tomcat</strong></li></ul><h2 id="Tomcat"><a href="#Tomcat" class="headerlink" title="Tomcat"></a>Tomcat</h2><ul><li>一个Apache旗下的开源免费的Web服务器,支持Servlet/JSP少量javaEE规范</li><li>JavaEE:Java企业版,指Java企业级开发的计数规范的总和,包括JDBC,JNDL等等</li><li>Tomcat也被称为Web容器,Servlet容器,Servclet依赖于Tomcat运行</li></ul><h3 id="基本使用"><a href="#基本使用" class="headerlink" title="基本使用"></a>基本使用</h3><ul><li>SpringBoot内嵌了Tomcat,也可自行下载</li></ul><h2 id="分层解耦"><a href="#分层解耦" class="headerlink" title="分层解耦"></a>分层解耦</h2><h3 id="三层架构"><a href="#三层架构" class="headerlink" title="三层架构"></a>三层架构</h3><ul><li>Controller:接收请求,响应数据</li><li>Servicce:逻辑处理</li><li>Dao:数据访问,包括数据的增删改查</li></ul><h3 id="分层解耦-1"><a href="#分层解耦-1" class="headerlink" title="分层解耦"></a>分层解耦</h3><ul><li><p>内聚:软件之中各个功能呢模块的数据关系</p></li><li><p>耦合:衡量软件中各个层/模块之间的依赖,关联的程度</p></li><li><p>高内聚,低耦合</p></li></ul><h3 id="IOC与DI"><a href="#IOC与DI" class="headerlink" title="IOC与DI"></a>IOC与DI</h3><ul><li><p>控制反转(IOC):对象的控制权由程序自身转移到外部,这种思想称为控制反转</p><blockquote><p>控制反转的概念是通过Spring的IOC容器来实现的,容器负责实例化对象,并确保它们的依赖项被正确地注入。这样,对象的创建和生命周期管理就不再由对象本身控制,而是由外部容器来控制,这有助于减少组件之间的耦合度,提高代码的模块化和可测试性。</p></blockquote></li><li><p>依赖注入(DI):容器为应用程序提供运行时所依赖的资源,称之为依赖注入</p><blockquote><p>IoC是一种设计原则,它将对象的创建和管理的控制权从程序代码转移到外部容器或框架。<strong>依赖注入是实现IoC的一种方法</strong></p><p>有多种注入方法,主要使用注解注入@Autowired</p></blockquote></li><li><p>Bean对象:IOC容器中创建,管理的对象,称之为Bean</p></li></ul><h3 id="IOC详解"><a href="#IOC详解" class="headerlink" title="IOC详解"></a>IOC详解</h3><ul><li>Bean对象的声明:将某个对象交给IOC容器管理</li></ul><table><thead><tr><th>注解</th><th>说明</th><th>位置</th></tr></thead><tbody><tr><td>@Component</td><td>声明bean的基础注解</td><td>不属于以下三类的时候使用这个注解</td></tr><tr><td>@Controller</td><td>@Component衍生注解</td><td>标注在控制器上</td></tr><tr><td>@Service</td><td>@Component衍生注解</td><td>标注在业务类上</td></tr><tr><td>@Respository</td><td>@Component衍生注解</td><td>标注在数据访问类上</td></tr></tbody></table><ul><li>Bean组件扫描<ul><li>Bean声明注解生效需要被组件扫描注解@ComponetScan扫面</li><li>@ComponentScan虽然无显示配置但实际上包含在启动类注解@SpringBootApplication之中,默认扫描范围在启动类所在包以及子包</li></ul></li></ul><h3 id="DI详解"><a href="#DI详解" class="headerlink" title="DI详解"></a>DI详解</h3><ul><li><p><strong>Bean注入</strong></p><ul><li><p>@Autowired注解,默认时按照类型进行,如果存在多个类型相同的Bean,将会报出错误</p></li><li><p>可以通过以下几个注解解决</p><blockquote><p>@Primary:设置bean的优先级</p><p>@Qualifier:配合@Autowired注解指定注入特定Bean</p><p>@Resource:相当于@Autowired与@Qualifier合并,直接指定并注入</p></blockquote></li></ul></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Primary</span><br><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">EmpServiceimpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">EmpService</span>{<br> <span class="hljs-comment">//注入时优先使用此bean</span><br>}<br><br><span class="hljs-comment">//注入</span><br><span class="hljs-meta">@Autowired</span><br><span class="hljs-meta">@Qualifier("EmpServiceimpl")</span><br><span class="hljs-comment">//或者</span><br><span class="hljs-meta">@Resource(name="EmpServiceimpl")</span>;<br></code></pre></td></tr></table></figure><ul><li><p>注入方法分类</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyService</span> {<br> <br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> MyDependency myDependency;<br> <br> <span class="hljs-comment">// 构造函数注入</span><br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">MyService</span><span class="hljs-params">(MyDependency myDependency)</span> {<br> <span class="hljs-built_in">this</span>.myDependency = myDependency;<br> }<br> <span class="hljs-comment">// 字段注入(最简单最推荐的注入) </span><br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">private</span> AnotherDependency anotherDependency;<br> <span class="hljs-comment">//方法注入</span><br> <span class="hljs-keyword">private</span> YetAnotherDependency yetAnotherDependency;<br> <span class="hljs-meta">@Autowired</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setYetAnotherDependency</span><span class="hljs-params">(YetAnotherDependency yetAnotherDependency)</span> {<br> <span class="hljs-built_in">this</span>.yetAnotherDependency = yetAnotherDependency;<br> }<br> <span class="hljs-comment">// 方法参数注入</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">someMethod</span><span class="hljs-params">(<span class="hljs-meta">@Autowired</span> RequiredService requiredService)</span> {<br> <span class="hljs-comment">// 使用 requiredService</span><br> }<br>}<br></code></pre></td></tr></table></figure></li></ul>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>Vue学习</title>
<link href="/2023/12/11/Vue%E5%AD%A6%E4%B9%A0/"/>
<url>/2023/12/11/Vue%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<p>Vuet的学习与应用</p><span id="more"></span><h1 id="Vue学习"><a href="#Vue学习" class="headerlink" title="Vue学习"></a>Vue学习</h1><h2 id="Vue是什么"><a href="#Vue是什么" class="headerlink" title="Vue是什么"></a>Vue是什么</h2><ul><li>是一套前端开那关键,免除原生JavaScript中的DOM操作</li><li>基于MVVM的思想,实现数据的双向绑定</li></ul><h2 id="Vue的快速使用"><a href="#Vue的快速使用" class="headerlink" title="Vue的快速使用"></a>Vue的快速使用</h2><ul><li>定义数据模型</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><script><br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Vue</span>({<br> <span class="hljs-attr">e1</span>: <span class="hljs-string">"#app"</span>,<br> <span class="hljs-attr">data</span>: {<br> <span class="hljs-attr">message</span>:<span class="hljs-string">"Hello Vue!"</span><br> }<br>})<br></script><br></code></pre></td></tr></table></figure><ul><li>编写试图</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><div id=<span class="hljs-string">"app"</span>><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"message"</span>></span>//输入框</span><br><span class="language-xml"> {{message}}//一个插值表达式,文本会随着输入框内容变化而变化</span><br><span class="language-xml"> /*表达式内容</span><br><span class="language-xml"> 变量</span><br><span class="language-xml"> 三元运算符</span><br><span class="language-xml"> 函数调用</span><br><span class="language-xml"> 算术运算</span><br><span class="language-xml"> */</span><br><span class="language-xml"><span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br></code></pre></td></tr></table></figure><p>效果图:</p><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaVue-1%E6%95%88%E6%9E%9C%E5%9B%BE.png" alt="image-20231211213929358"></p><ul><li><p>常用指令</p><ul><li><p>带有**v-**前缀的特殊指令</p></li><li><p><strong>v-bind与v-model</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><script><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Vue</span>({<br> <span class="hljs-attr">e1</span>: <span class="hljs-string">"#app"</span>,<br> <span class="hljs-attr">data</span>: {<br> <span class="hljs-attr">url</span>:<span class="hljs-string">"https://www.XXX.com"</span><br> }<br>})<br></script><br><span class="hljs-comment">//v-bind为标签绑定属性值</span><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">a</span> <span class="hljs-attr">v-bind:href</span>=<span class="hljs-string">"url"</span>></span>XXX网<span class="hljs-tag"></<span class="hljs-name">a</span>></span></span><br>或者<br><a :href=<span class="hljs-string">"url"</span>><span class="hljs-variable constant_">XXX</span>网</a><br><span class="hljs-comment">//v-model在表单元素上创建双向数据绑定</span><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"url"</span>></span></span><br></code></pre></td></tr></table></figure></li><li><p><strong>v-if和v-show</strong></p></li></ul><table><thead><tr><th>v-if</th><th>条件性渲染某元素,判断为true渲染,否则不渲染</th></tr></thead><tbody><tr><td><strong>v-else-if</strong></td><td><strong>同上</strong></td></tr><tr><td><strong>v-else</strong></td><td><strong>同上</strong></td></tr><tr><td><strong>v-show</strong></td><td><strong>根据条件展示某元素,区别子啊与切换的是display属性的值</strong></td></tr></tbody></table><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">//年龄{{age}},经判定为</span><br><span v-<span class="hljs-keyword">if</span>=<span class="hljs-string">"age<=35"</span>>年轻人</span><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">v-else-if</span>=<span class="hljs-string">"age>35&&age<60"</span>></span>中年人<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">v-else</span>></span>老年人<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="language-xml">//v-show</span><br><span class="language-xml"><span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">v-show</span>=<span class="hljs-string">"age<=35"</span>></span>年轻人<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br></code></pre></td></tr></table></figure></li></ul><h2 id="Vue生命周期"><a href="#Vue生命周期" class="headerlink" title="Vue生命周期"></a>Vue生命周期</h2><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaVue-2%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.png" alt="image-20231211220648485"></p><h2 id="Vue实例"><a href="#Vue实例" class="headerlink" title="Vue实例"></a>Vue实例</h2><ul><li>组件名要以驼峰格式命名</li></ul>]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>Vue</tag>
</tags>
</entry>
<entry>
<title>JavaScript学习</title>
<link href="/2023/12/11/JavaScript/"/>
<url>/2023/12/11/JavaScript/</url>
<content type="html"><![CDATA[<p>JavaScript的学习与应用</p><span id="more"></span><h1 id="JavaScript学习"><a href="#JavaScript学习" class="headerlink" title="JavaScript学习"></a>JavaScript学习</h1><h2 id="JacaScript是什么"><a href="#JacaScript是什么" class="headerlink" title="JacaScript是什么"></a>JacaScript是什么</h2><ul><li><strong>是一门跨平台的,面向对象的,不需要编译而仅仅需要解析的脚本语言</strong>,用于控制网页行为来进行交互</li></ul><h2 id="JavaScript基本使用"><a href="#JavaScript基本使用" class="headerlink" title="JavaScript基本使用"></a>JavaScript基本使用</h2><ul><li><script></script>标签用于Html文档之中<blockquote><p>注:此标签不能自闭合</p></blockquote></li><li><p>内部注入:一般置于<body>元素底部,以改善显示速度</p></li><li><p>外部脚本:<Script src="js/demo.js"></script></p></li><li><p>注释://,/**/</p></li><li><p><strong>输出语句</strong>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><script><br><span class="hljs-variable language_">window</span>/<span class="hljs-title function_">alert</span>(<span class="hljs-string">"Hello JavaScript"</span>);<span class="hljs-comment">//弹出警告框</span><br><span class="hljs-variable language_">document</span>.<span class="hljs-title function_">write</span>(<span class="hljs-string">"Hello JavaScript"</span>);<span class="hljs-comment">//写入Html在浏览器显示</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">"Hello JavaScript"</span>);<span class="hljs-comment">//写入控制台</span><br></script><br></code></pre></td></tr></table></figure></li><li><p><strong>声明变量</strong>:var (variable)</p><ul><li><p>弱类型语言,变量可以重复输入类型或者值进行覆盖,且作用域一律为全局</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> a=<span class="hljs-number">20</span>;<br>a=<span class="hljs-string">"张三"</span><br><span class="hljs-comment">//ES6新增了let声明局部变量,只在对应代码块生效,const声明变量不能修改</span><br></code></pre></td></tr></table></figure></li></ul></li><li><p><strong>数据类型</strong></p><ul><li>原始类型<ul><li>number:整数,小数类型,<strong>条件判断时</strong>只有0与NaN视为为false</li><li>string: 字符串,单双引号均可,只有空字符串视为为false</li><li>Boolean:布尔类型</li><li>null:对象为空</li><li>undefined:变量未进行初始化,视为false</li></ul></li><li><strong>typeof+变量名</strong> 可以获取变量类型</li></ul></li><li><p><strong>函数</strong>:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">function</span> <span class="hljs-title function_">functioname</span>(<span class="hljs-params">参数列表</span>){<br><span class="hljs-comment">//有返回值则返回,没有则不需要操作</span><br>}<br>另外的形式:<br><span class="hljs-keyword">function</span> <span class="hljs-title function_">add</span>(<span class="hljs-params">a,b</span>){<br><span class="hljs-keyword">return</span> a+b;<br>}<br>或者<br><span class="hljs-keyword">var</span> add=<span class="hljs-keyword">function</span>(<span class="hljs-params">a,b</span>){<br><span class="hljs-keyword">return</span> a+b;<br>}<br></code></pre></td></tr></table></figure><p>函数调用</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> result=<span class="hljs-title function_">add</span>(<span class="hljs-number">10</span>,<span class="hljs-number">20</span>);<br><span class="hljs-comment">//超出函数列表的参数会被视为抛弃</span><br></code></pre></td></tr></table></figure></li><li><p><strong>Array数组</strong></p><ul><li>定义</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> 变量名=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Array</span>(元素列表);<br><span class="hljs-keyword">var</span> 变量名=[元素列表];<br>例子<br><span class="hljs-keyword">var</span> arr=<span class="hljs-keyword">new</span> <span class="hljs-title class_">Array</span>(<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>);<br></code></pre></td></tr></table></figure><ul><li>调用</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript">arr[索引]=值;<br><span class="hljs-comment">//例子:</span><br>arr[<span class="hljs-number">10</span>]=<span class="hljs-string">"hello"</span>;<br></code></pre></td></tr></table></figure></li><li><p><strong>String对象</strong></p><ul><li>定义</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> 变量名 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">String</span>(<span class="hljs-string">"..."</span>);<br><span class="hljs-keyword">var</span> 变量名=<span class="hljs-string">".."</span>;<br></code></pre></td></tr></table></figure><ul><li><p>调用</p><ul><li>属性:length</li><li>方法:</li></ul><table><thead><tr><th>charAt();</th><th>返回指定位置的字符</th></tr></thead><tbody><tr><td><strong>indexOf();</strong></td><td><strong>检索字符串</strong></td></tr><tr><td><strong>trim();</strong></td><td><strong>去除字符串两边空格</strong></td></tr><tr><td><strong>substring();</strong></td><td><strong>提取字符串两个指定的索引号值之间的字符</strong></td></tr></tbody></table></li></ul></li><li><p><strong>自定义对象</strong></p><ul><li><p>定义格式:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> 对象名 ={<br><br> 属性名<span class="hljs-number">1</span>:属性值,<br><br> 属性名<span class="hljs-number">2</span>:属性值,<br><br> 函数名称: <span class="hljs-keyword">function</span>(<span class="hljs-params">形参列表</span>){}<br>}<br><span class="hljs-comment">//例子</span><br><span class="hljs-keyword">var</span> user={<br> <span class="hljs-attr">name</span>:<span class="hljs-string">"Tom"</span>,<br> <span class="hljs-attr">age</span>:<span class="hljs-number">20</span>,<br> <span class="hljs-attr">gender</span>:<span class="hljs-string">"male"</span>,<br> eat=<span class="hljs-string">"function"</span>(){<br> <span class="hljs-title function_">alert</span>(<span class="hljs-string">"用膳"</span>);<br> }<br>}<br></code></pre></td></tr></table></figure></li><li><p>调用</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript">user.<span class="hljs-title function_">eat</span>();<span class="hljs-comment">//对象.函数名();</span><br><span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(user.<span class="hljs-property">name</span>);<span class="hljs-comment">//对象.属性名</span><br></code></pre></td></tr></table></figure></li></ul></li><li><p><strong>Json对象</strong></p><ul><li><p>通过JavaScript对象标记法书写的文本,<strong>本质是一个带键值对的字符串</strong></p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs javascript">{<br> <span class="hljs-string">"name"</span>:<span class="hljs-string">"Tom"</span>,<br> <span class="hljs-string">"age"</span>:<span class="hljs-number">20</span>,<br> <span class="hljs-string">"gender"</span>:<span class="hljs-string">"male"</span><br>}<br><span class="hljs-comment">//key必须用""标记</span><br></code></pre></td></tr></table></figure></li><li><p>定义</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> 变量名=<span class="hljs-string">'{"key1":value1,"key2":value2}'</span>;<br><span class="hljs-comment">//示例</span><br><span class="hljs-keyword">var</span> userstr=<span class="hljs-string">'{"name":"Herry","age":18,"addr":["北京","上海"]}'</span>;<br></code></pre></td></tr></table></figure></li><li><p>Json字符串转换为JS,JSON字符串对象</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> jsObject =<span class="hljs-title class_">Json</span>.<span class="hljs-title function_">parse</span>(userstr);<br><span class="hljs-keyword">var</span> jsonStr=<span class="hljs-title class_">Json</span>.<span class="hljs-title function_">stringify</span>(jsObject);<br></code></pre></td></tr></table></figure></li></ul></li><li><p><strong>BOM对象</strong></p><ul><li><p>JavaScript将浏览器的各个组成部分封装成对象</p></li><li><p>组成</p><ul><li>Window:浏览器窗口对象</li><li>Navigator::浏览器对象</li><li>Screen:屏幕对象</li><li>History:历史记录对象</li><li>Location: 地址栏对象</li></ul></li><li><p>Window</p><ul><li>属性(window.属性名直接调用,window.可以省略)<ul><li>history:对History的只读引用</li><li>Location:用于窗口或者框架的Location对象</li><li>navigator:对Nagvitor对象的只读引用</li></ul></li><li>方法<ul><li>alert():显示带有一段消息和一个确认按钮的警告框</li><li>confirm():显示带有一段消息以及确认按钮和取消按钮的对话框</li><li>setInterval:按照指定的周期(ms)来调用安徽念书或者计算表达式</li><li>setTimeout:在指定的毫秒后调用函数或计算表达式</li></ul></li></ul></li><li><p>Location</p><ul><li><p>window对象调用</p></li><li><p>一般调用</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript">location.属性;<br></code></pre></td></tr></table></figure></li><li><p>属性:</p><ul><li>href:设置返回完整的URL</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs javascript">location.<span class="hljs-property">href</span>=<span class="hljs-string">"https://www.XXX.cn"</span>;<br></code></pre></td></tr></table></figure></li></ul></li></ul></li><li><p><strong>DOM</strong></p><ul><li><p><strong>文档对象模型</strong></p><ul><li>将标记语言的各个组成部分封装为对应的对象<ul><li>Docurment:整个文档对象</li><li>Element:元素对象</li><li>Attribute:属性对象</li><li>Text:文本对象</li><li>Comment:注释对象</li></ul></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaScript-1DOM.png" alt="image-20231211211737750"></p><ul><li>XMLDOM-XML 文档标准模型</li><li>HTMLDOM-HTML 文档的标准模型<ul><li>Image:<img></li><li>Button:<input type='button'></li></ul></li></ul></li><li><p>Dcument对象中提供了以下获取Element元素</p><table><thead><tr><th>getElementById(‘h1’);</th><th>根据id属性获取,返回单个Element对象</th></tr></thead><tbody><tr><td><strong>getElementByTagname(‘div’);</strong></td><td><strong>根据标签名称获取,返回单个Element对象数组</strong></td></tr><tr><td><strong>getElementByName(‘hobby’);</strong></td><td><strong>根据name属性获取,返回单个Element对象数组</strong></td></tr><tr><td><strong>getElementByClassName(‘hobby’);</strong></td><td><strong>根据class属性获取,返回单个Element对象数组</strong></td></tr></tbody></table></li></ul></li></ul>]]></content>
<categories>
<category>前端</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>Javascript</tag>
</tags>
</entry>
<entry>
<title>Java反射</title>
<link href="/2023/12/09/java%E5%8F%8D%E5%B0%84/"/>
<url>/2023/12/09/java%E5%8F%8D%E5%B0%84/</url>
<content type="html"><![CDATA[<p>网络编程基础知识和反射讲解</p><span id="more"></span><h1 id="反射"><a href="#反射" class="headerlink" title="反射"></a>反射</h1><h2 id="类加载机制"><a href="#类加载机制" class="headerlink" title="类加载机制"></a>类加载机制</h2><h3 id="加载过程"><a href="#加载过程" class="headerlink" title="加载过程"></a>加载过程</h3><p>当程序运行后,第一次使用某个类的时候,会将此类的class文件读取到内存之中,并将此类的所有信息存储到一个Class对象之中</p><blockquote><p>执行代码遇到未加载的类–>读取类的class文件–>将类的信息存储到方法区–>在堆中创建对象空间</p></blockquote><p><strong>Class对象是指Java.lang.Class类对象,由Java专门提供存储类型</strong></p><h3 id="加载时机"><a href="#加载时机" class="headerlink" title="加载时机"></a>加载时机</h3><ul><li>创建类对象</li><li>调用静态方法</li><li>使用子类</li><li>反射加载类</li><li>运行某个类(例如主启动类)</li></ul><h3 id="加载器"><a href="#加载器" class="headerlink" title="加载器"></a>加载器</h3><p>类加载器可分为三种</p><ul><li><p>引导类(启动类)加载器:加载系统类库</p></li><li><p>扩展类加载器:加载jdk扩展库</p></li><li><p>应用程序加载器:程序员自己写的类</p></li><li><p><strong>反射允许对封装类的字段,方法和构造函数的信息进行编程访问</strong></p></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/Java%E5%8F%8D%E5%B0%84-1%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9B" alt="image-20231210163819945"></p><ul><li>能够获取函数的所有信息</li></ul><h2 id="获取字节码对象的三种方式(获取Class类对象)"><a href="#获取字节码对象的三种方式(获取Class类对象)" class="headerlink" title="获取字节码对象的三种方式(获取Class类对象)"></a>获取字节码对象的三种方式(获取Class类对象)</h2><table><thead><tr><th align="left">1.Class.forName(“全类名”);</th><th>源代码阶段使用(最为常用)</th></tr></thead><tbody><tr><td align="left"><strong>2.类名.class</strong></td><td><strong>加载阶段</strong></td></tr><tr><td align="left"><strong>3.对象.getClass</strong></td><td><strong>运行阶段</strong></td></tr></tbody></table><h2 id="利用反射获取构造方法"><a href="#利用反射获取构造方法" class="headerlink" title="利用反射获取构造方法"></a>利用反射获取构造方法</h2><ul><li><p>Class类之中用于获取构造函数方法的方法</p><table><thead><tr><th>Constructor<?>[] getConstructors();</th><th>返回所有公共构造方法</th></tr></thead><tbody><tr><td><strong>Constructor<?>[] getDeclaredConstructors();</strong></td><td><strong>返回所有构造方法对象的数组</strong></td></tr><tr><td><strong>Constructor<T> getConstructor(Class<?>…parameterTypes)</strong></td><td><strong>返回单个公共构造方法对象</strong></td></tr><tr><td><strong>Constructor<T> getDeclaredConstructor(Class<?>..paramterTypes)</strong></td><td><strong>返回单个构造函数方法对象</strong></td></tr></tbody></table></li><li><p>Constructor类中用于创建对象的方法</p><table><thead><tr><th>T newInstance(Object… initargs);</th><th>根据指定的构造方法创建对象</th></tr></thead><tbody><tr><td><strong>setAccessible(boolean flag)</strong></td><td><strong>设置为true,表示取消访查</strong></td></tr></tbody></table></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Class</span> <span class="hljs-variable">clazz</span> <span class="hljs-operator">=</span>Class.forName(<span class="hljs-string">"com.itheima.myreflect.Student"</span>);<br>Constructor[] cons1=clazz.getContructors();<br><span class="hljs-comment">//获取所有构造函数</span><br><span class="hljs-keyword">for</span>(Constructor con: cons2){<br>System.out.println(cons2);<br>}<br><span class="hljs-comment">//获取空参构造</span><br>Constructor con2=clazz.getDeclaredConstructor();<br><span class="hljs-comment">//一个String参数构造,依次类推,有多个的话会抛异常</span><br>Constructor con3=clazz.getDeclaredConstructor(String.class);<br><span class="hljs-comment">//获取所有参数</span><br>Parameter[]=parameter=con3.getParameters();<br></code></pre></td></tr></table></figure><ul><li>示例代码</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Class</span> <span class="hljs-variable">clazz</span> <span class="hljs-operator">=</span>Class.forName(<span class="hljs-string">"org.example.student"</span>);<br>Constructor con3=clazz.getDeclaredConstructor(<span class="hljs-type">int</span>.class);<br>System.out.println(con3);<br><span class="hljs-comment">//通过构造函数创建对象</span><br>student stu=(student) con3.newInstance(<span class="hljs-number">23</span>);<br>System.out.println(stu);<br></code></pre></td></tr></table></figure><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/Java%E5%8F%8D%E5%B0%84-2%E7%BB%93%E6%9E%9C%2Cpng" alt="image-20231210233613971"></p><ul><li>后面的标识符号为16进制的指针</li><li>getClass().getName()+‘@’+Integer.toHexString(hashCode())</li><li>类所在的包名.类名 + @ + 哈希码值</li></ul><h2 id="获取成员变量"><a href="#获取成员变量" class="headerlink" title="获取成员变量"></a>获取成员变量</h2><ul><li><p>Class内获取成员变量</p><table><thead><tr><th><strong>Field[] getFields();</strong></th><th><strong>返回所有公共成员变量数组</strong></th></tr></thead><tbody><tr><td><strong>Field[] getDeclaredFields();</strong></td><td><strong>返回所有成员变量数组</strong></td></tr><tr><td><strong>Field getField(String name);</strong></td><td><strong>返回单个公共成员变量数组</strong></td></tr><tr><td><strong>Field getDeclaredField(String name);</strong></td><td><strong>返回单个成员变量数组</strong></td></tr></tbody></table></li><li><p>Field用于创建对象方法</p><blockquote><p>void set(Object obj,Object value);</p><p>Object get(Object obj);</p><p>int getmodifier();//获取修饰符号</p><p>String getName();</p></blockquote></li></ul><h2 id="获取成员方法"><a href="#获取成员方法" class="headerlink" title="获取成员方法"></a>获取成员方法</h2><ul><li><p>Class之中获取成员方法</p><table><thead><tr><th>Method[] getmethods();</th><th>获取所有公共成员方法</th></tr></thead><tbody><tr><td><strong>Method[] getDeclaredmethods();</strong></td><td><strong>获取所有成员方法</strong></td></tr><tr><td><strong>Method getmethod(Class<?>…parameterTypes);</strong></td><td><strong>返回单个公共成员方法</strong></td></tr><tr><td><strong>Method getDeclaredmethod(Class<?>…parameterTypes);</strong></td><td><strong>返回单个成员方法</strong></td></tr></tbody></table></li><li><p>Method类用于创建对象</p><blockquote><p>Object invoke(Object obj,Object…args);</p><p>参数一:用obj对象调用</p><p>参数二:调用方法传递的参数</p></blockquote></li></ul>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>反射</tag>
<tag>网络</tag>
</tags>
</entry>
<entry>
<title>Java多线程</title>
<link href="/2023/12/08/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<url>/2023/12/08/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/</url>
<content type="html"><![CDATA[<p>Java多线程的一些解释与应用</p><span id="more"></span><h1 id="Java多线程"><a href="#Java多线程" class="headerlink" title="Java多线程"></a>Java多线程</h1><h2 id="多线程的一些概念"><a href="#多线程的一些概念" class="headerlink" title="多线程的一些概念"></a>多线程的一些概念</h2><ul><li><strong>并发和并行</strong>,二者并不排斥<ul><li>并发: <strong>单个CPU</strong>交替执行指令</li><li>并行: 多个指令在<strong>多个CPU</strong>上执行</li></ul></li><li><strong>死锁</strong><ul><li>一种错误情形,两个锁互相钳制无法释放锁导致崩溃</li></ul></li></ul><h2 id="多线程的实现"><a href="#多线程的实现" class="headerlink" title="多线程的实现"></a>多线程的实现</h2><h3 id="继承Thread类"><a href="#继承Thread类" class="headerlink" title="继承Thread类"></a>继承Thread类</h3><blockquote><p>重写run方法</p><p>启动start</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThreadDemo2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 2.创建MyRunnable对象</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">Runnable</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>();<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 3.把MyRunnable对象交给Thread处理</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(target);<br> t.start();<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"主线程执行输出"</span> + i);<br> }<br> }<br>}<br><br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 1.定义一个线程任务类MyRunnable实现Runnable接口,重写run方法</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"子线程执行输出"</span> + i);<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li><p>注意:</p><ul><li>不使用run方法而是start方法是因为调用run方法只是调用执行一次,<strong>start才是开启线程</strong></li></ul></li><li><p>优缺点:</p><p><strong>优点:编程简单</strong></p><p><strong>缺点:单继承的局限性,不能继承其他类,不便于扩展。</strong></p></li></ul><h3 id="实现Runnable接口"><a href="#实现Runnable接口" class="headerlink" title="实现Runnable接口"></a>实现Runnable接口</h3><blockquote><p>1.定义一个线程任务类MyRunnable实现Runnable接口,重写run方法</p><p>2.创建MyRunnable对象</p><p>3.把MyRunnable对象交给Thread处理 </p></blockquote><p>代码如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//一,常规写法</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThreadDemo2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 2.创建MyRunnable对象</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">Runnable</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>();<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 3.把MyRunnable对象交给Thread处理</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(target);<br> t.start();<br><br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"主线程执行输出"</span> + i);<br> }<br> }<br>}<br><br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 1.定义一个线程任务类MyRunnable实现Runnable接口,重写run方法</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span> {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"子线程执行输出"</span> + i);<br> }<br> }<br>}<br><br><br><span class="hljs-comment">//二,建议写法</span><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 通过匿名内部类来实现</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>() {<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"匿名内部类子线程的输出"</span> + i);<br><br> }<br> }<br>}).start();<br></code></pre></td></tr></table></figure><h3 id="实现Callable接口,通过FutureTask接口接收返回值(JDK5新增)"><a href="#实现Callable接口,通过FutureTask接口接收返回值(JDK5新增)" class="headerlink" title="实现Callable接口,通过FutureTask接口接收返回值(JDK5新增)"></a>实现Callable接口,通过FutureTask接口接收返回值(JDK5新增)</h3><blockquote><p>1.得到任务对象</p><p>第一步:定义一个线程任务类MyCallable实现Callable接口,重写call方法,该方法可以返回结果</p><p>第二步:用Future吧Callable对象封装成线程任务对象</p><p>2.把线程任务对象交给Thread处理</p><p>3.调用Thread的start方法启动任务</p><p>4.线程执行完毕后,通过FutureTask的get方法获得结果</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.concurrent.Callable;<br><span class="hljs-keyword">import</span> java.util.concurrent.FutureTask;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThreadDemo3</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 3.创建任务对象</span><br><span class="hljs-comment"> */</span><br> Callable<String> call1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">100</span>);<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 4.把Callable任务对象交给FutureTask对象</span><br><span class="hljs-comment"> * FutureTask的作用1:FutureTask实现了Runnable接口,此时就可以交给Thread了</span><br><span class="hljs-comment"> * FutureTask的作用2:可以在线程执行完毕后调用get方法得到线程执行的结果</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-comment">//Thread t = new Thread(call); 报错,Thread不能接收call对象</span><br> FutureTask<String> f1 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span><>(call1);<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 5.交给线程处理</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(f1);<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 6.启动线程</span><br><span class="hljs-comment"> */</span><br> t1.start();<br><br><br><br> Callable<String> call2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">200</span>);<br> FutureTask<String> f2 = <span class="hljs-keyword">new</span> <span class="hljs-title class_">FutureTask</span><>(call2);<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Thread</span>(f2);<br> t2.start();<br><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">rs1</span> <span class="hljs-operator">=</span> f1.get(); <span class="hljs-comment">//直接调用call方法,可能还没有执行完,使用get时若发现线程未执行完会先等线程执行完毕</span><br> System.out.println(<span class="hljs-string">"第一个结果为:"</span> + rs1);<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">rs2</span> <span class="hljs-operator">=</span> f2.get(); <span class="hljs-comment">//直接调用call方法,可能还没有执行完,使用get时若发现线程未执行完会先等线程执行完毕</span><br> System.out.println(<span class="hljs-string">"第二个结果为:"</span> + rs2);<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br>}<br><br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 1.定义一个实现类,实现Callable接口,记得声明结果的数据类型</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">class</span> <span class="hljs-title class_">MyCallable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Callable</span><String> {<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 2.重写call方法</span><br><span class="hljs-comment"> */</span><br><br> <span class="hljs-keyword">private</span> <span class="hljs-type">int</span> n;<br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">MyCallable</span><span class="hljs-params">(<span class="hljs-type">int</span> n)</span> {<br> <span class="hljs-built_in">this</span>.n = n;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-type">int</span> <span class="hljs-variable">sum</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <= n; i++) {<br> sum += i;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-string">"子线程执行的结果是"</span> + sum;<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>后两种方式优缺点:</p><p>1.优点: 可扩展性强,实现接口同时还可继承其它类</p><p>2.缺点: 编程相对复杂,不能直接使用Thread的方法</p></blockquote><h2 id="常见成员方法"><a href="#常见成员方法" class="headerlink" title="常见成员方法"></a>常见成员方法</h2><ul><li>具体例子</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThreadDemo1</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyThread</span>(<span class="hljs-string">"一号线程"</span>);<br> t1.start();<br><span class="hljs-comment">// t1.setName("一号线程");</span><br> System.out.println(t1.getName());<br><br> <span class="hljs-type">Thread</span> <span class="hljs-variable">t2</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyThread</span>(<span class="hljs-string">"二号线程"</span>);<br> t2.start();<br><span class="hljs-comment">// t2.setName("二号线程");</span><br> System.out.println(t2.getName());<br><br> <span class="hljs-comment">//获取当前线程对象</span><br> <span class="hljs-type">Thread</span> <span class="hljs-variable">m</span> <span class="hljs-operator">=</span> Thread.currentThread();<br> System.out.println(m.getName());<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <= <span class="hljs-number">5</span>; i++) {<br> System.out.println(<span class="hljs-string">"Main线程输出"</span> + i);<br> <span class="hljs-keyword">if</span>(i == <span class="hljs-number">3</span>)<br> Thread.sleep(<span class="hljs-number">3</span> * <span class="hljs-number">1000</span>); <span class="hljs-comment">//主线程休眠3000ms</span><br> }<br> }<br>}<br><br><br><br>自定义线程<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span>{<br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">MyThread</span><span class="hljs-params">(String name)</span> {<br> <span class="hljs-comment">//让父类的构造器进行构造</span><br> <span class="hljs-built_in">super</span>(name);<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">1</span>; i <= <span class="hljs-number">5</span>; i++) {<br> System.out.println(Thread.currentThread().getName() + <span class="hljs-string">"输出"</span> + i);<br> }<br> }<br> }<br></code></pre></td></tr></table></figure><ul><li>一些特殊线程:<ul><li>守护线程:会随着非守护线程的消亡而消亡</li><li>出让线程:将自身CPU执行权出让,重新和其它线程抢夺</li></ul></li></ul><h2 id="线程的生命周期"><a href="#线程的生命周期" class="headerlink" title="线程的生命周期"></a>线程的生命周期</h2><ul><li><strong>基本周期</strong>:</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaSethread-1.png" alt="image-20231208151125777"></p><ul><li><strong>6种状态</strong>:</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaSethread-3%E7%8A%B6%E6%80%81%E5%9B%BE.png" alt="JavaSE01-08线程状态之间的转换"></p><blockquote><p><strong>注意以下几点:</strong><br><strong>sleep()是睡眠,过程中不会释放锁对象,醒来后直接进入可运行状态</strong><br><strong>wait()是等待,过程中会释放锁对象,但是时间到了或者被notity时会重新获得锁对象,进入可运行状态</strong></p></blockquote><h2 id="线程的安全问题"><a href="#线程的安全问题" class="headerlink" title="线程的安全问题"></a>线程的安全问题</h2><h3 id="问题描述:"><a href="#问题描述:" class="headerlink" title="问题描述:"></a>问题描述:</h3><p><strong>多个线程同时操作一个共享资源可能会出现业务安全问题,称为线程安全问题</strong></p><ul><li><strong>源码解析</strong>:</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//两个售票员卖同一批票</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span>{<br> <span class="hljs-type">MyThread</span> <span class="hljs-variable">t1</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">MyThread</span>();<br> <span class="hljs-type">MyThread</span> <span class="hljs-variable">t2</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">MyThread</span>();<br> <br> t1.setName(<span class="hljs-string">"窗口一"</span>);<br> t2.setName(<span class="hljs-string">"窗口二"</span>);<br> <br> t1.start();<br> t2.start();<br>}<br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span>{<br> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> ticket=<span class="hljs-number">0</span>;<br> <br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>){<br> <span class="hljs-keyword">if</span>(ticket<<span class="hljs-number">100</span>){<br> ticket++;<br> System.out.println(getName()+<span class="hljs-string">"正在卖第"</span>+ticket+<span class="hljs-string">"张票"</span>);<br> }<span class="hljs-keyword">else</span>{<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> }<br>}<br><br></code></pre></td></tr></table></figure><ul><li><strong>运行上述代码后会出现一些错误现象</strong>:</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/Javasethread-2%E9%94%99%E8%AF%AF%E7%A4%BA%E4%BE%8B.png" alt="image-20231208155324026"></p><ul><li><p><strong>解决办法</strong>: </p><ul><li><strong>同步</strong>代码块——<strong>对象锁</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-number">1.</span><br>synchronize(锁对象){<span class="hljs-comment">//锁对象要唯一</span><br><span class="hljs-comment">//临界数据操作代码</span><br>}<br><span class="hljs-number">2.</span><br>synchronize关键字修饰方法<br><span class="hljs-comment">//锁对象:1静态:当前字节码文件对象,2静态:this</span><br><span class="hljs-number">3.</span><br>Stringbuffer类方法自动上锁<br></code></pre></td></tr></table></figure><ul><li><p><strong>Lock</strong>锁(JDK5之后)</p><p><strong>Lock是接口,采用实现类ReentrantLock来构建Lock锁对象</strong></p><blockquote><table><thead><tr><th>public ReentrantLock()</th><th>获得Lock锁的实现类对象</th></tr></thead><tbody><tr><td>void lock()</td><td>获得锁</td></tr><tr><td>void unlock()</td><td>释放锁</td></tr></tbody></table></blockquote></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span>{<br> <span class="hljs-keyword">static</span> <span class="hljs-type">int</span> ticket=<span class="hljs-number">0</span>;<br><br> <span class="hljs-keyword">static</span> <span class="hljs-type">Lock</span> <span class="hljs-variable">lock</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">ReentrantLock</span>();<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span>{<br> <span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>){<br> lock.lock();<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">if</span>(ticket<<span class="hljs-number">100</span>){<br> ticket++;<br> System.out.println(getName()+<span class="hljs-string">"正在卖第"</span>+ticket+<span class="hljs-string">"张票"</span>);<br> }<span class="hljs-keyword">else</span>{<br> <span class="hljs-keyword">break</span>;<br> }<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">RuntimeException</span>(e);<br> }<span class="hljs-keyword">finally</span> {<br> lock.unlock();<span class="hljs-comment">//放进finally以保证锁的释放</span><br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>这两种同步方式有很多相似之处,它们都是加锁方式同步,而且都是阻塞式的同步,也就是说当如果一个线程获得了对象锁,进入了同步块,其他访问该同步块的线程都必须阻塞在同步块外面等待,而进行线程阻塞和唤醒的代价是比较高的(操作系统需要在用户态与内核态之间来回切换,代价很高,不过可以通过对锁优化进行改善)</p></blockquote><h3 id="锁的释放"><a href="#锁的释放" class="headerlink" title="锁的释放"></a>锁的释放</h3><blockquote><p>在以下情况下,<strong>持有锁的线程会释放锁</strong>:</p><ol><li><p>执行完同步代码块。</p></li><li><p>在执行同步代码块的过程中,遇到异常而导致线程终止。</p></li><li><p>在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进行对象的等待池。</p></li></ol><p>除了以上情况外,只要持有锁的此案吃还没有执行完同步代码块,就不会释放锁。<strong>因此在以下情况下,线程不会释放锁</strong>:</p><ol><li><p>在执行同步代码块的过程中,执行了Thread.sleep()方法,当前线程放弃CPU,开始睡眠,在睡眠中不会释放锁。</p></li><li><p>在执行同步代码块的过程中,执行了Thread.yield()方法,当前线程放弃CPU,但不会释放锁。</p></li><li><p>在执行同步代码块的过程中,其他线程执行了当前对象的suspend()方法,当前线程被暂停,但不会释放锁。但Thread类的suspend()方法已经被废弃。</p></li></ol></blockquote><h2 id="线程之间的通信"><a href="#线程之间的通信" class="headerlink" title="线程之间的通信"></a>线程之间的通信</h2><ul><li><strong>常用的模型:生产者与消费者模型</strong></li></ul><p><strong>object中提供的等待唤醒方法如下</strong></p><table><thead><tr><th>方法名称</th><th>说明</th></tr></thead><tbody><tr><td>void wait()</td><td>当前线程等待并且释放所占锁,直到另外一个线程调用notify()或者notifyaAll</td></tr><tr><td>void notify()</td><td>唤醒正在等待的单个线程</td></tr><tr><td>void notufyAll()</td><td>唤醒正在等待的所有线程</td></tr></tbody></table><blockquote><p><strong>上述方法应该使用当前同步锁对象调用</strong><br><strong>注意:一定是先唤醒别人在等待,相当于自己叫醒别人再晕过去,否则相当于自己先晕过去没法叫人</strong></p></blockquote><p>代码实现:</p><ul><li>账户类</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Account</span> {<br> <span class="hljs-keyword">private</span> String cardId;<br> <span class="hljs-keyword">private</span> <span class="hljs-type">double</span> money; <span class="hljs-comment">//余额</span><br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">Account</span><span class="hljs-params">(String cardId, <span class="hljs-type">double</span> money)</span> {<br> <span class="hljs-built_in">this</span>.cardId = cardId;<br> <span class="hljs-built_in">this</span>.money = money;<br> }<br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 小红 小明:取钱</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> money</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">DrawMoney</span><span class="hljs-params">(<span class="hljs-type">double</span> money)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> Thread.currentThread().getName(); <span class="hljs-comment">//取钱人</span><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.money >= money) {<br> <span class="hljs-comment">// 钱够,可以取钱</span><br> <span class="hljs-built_in">this</span>.money -= money;<br> System.out.println(name + <span class="hljs-string">"取钱成功,取出"</span> + money + <span class="hljs-string">"元!余额是:"</span> + <span class="hljs-built_in">this</span>.money);<br> <span class="hljs-comment">// 没钱了</span><br> <span class="hljs-comment">// 钱不够</span><br> <span class="hljs-built_in">this</span>.notifyAll(); <span class="hljs-comment">//唤醒其他所有的线程</span><br> <span class="hljs-built_in">this</span>.wait(); <span class="hljs-comment">//锁对象,让当前线程进入等待</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 钱不够,不可取</span><br> <span class="hljs-comment">// 唤醒别人存钱,等待自己取钱</span><br> <span class="hljs-built_in">this</span>.notifyAll(); <span class="hljs-comment">//唤醒其他所有的线程</span><br> <span class="hljs-built_in">this</span>.wait(); <span class="hljs-comment">//锁对象,让当前线程进入等待,顺便放弃锁</span><br> }<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br><br><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 亲爹 干爹 岳父:取钱</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> money</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">depositMoney</span><span class="hljs-params">(<span class="hljs-type">double</span> money)</span> {<br> <span class="hljs-type">String</span> <span class="hljs-variable">name</span> <span class="hljs-operator">=</span> Thread.currentThread().getName(); <span class="hljs-comment">//存钱人</span><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.money == <span class="hljs-number">0</span>) {<br> <span class="hljs-comment">//没钱了,存钱</span><br> <span class="hljs-built_in">this</span>.money += money;<br> System.out.println(name + <span class="hljs-string">"存钱"</span> + money + <span class="hljs-string">"元成功,余额为"</span> + <span class="hljs-built_in">this</span>.money + <span class="hljs-string">"元!"</span>);<br> <span class="hljs-comment">// 存完钱了</span><br> <span class="hljs-built_in">this</span>.notifyAll(); <span class="hljs-comment">//唤醒其他所有的线程</span><br> <span class="hljs-built_in">this</span>.wait(); <span class="hljs-comment">//锁对象,让当前线程进入等待</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 有钱了,不用存钱</span><br> <span class="hljs-built_in">this</span>.notifyAll(); <span class="hljs-comment">//唤醒其他所有的线程</span><br> <span class="hljs-built_in">this</span>.wait(); <span class="hljs-comment">//锁对象,让当前线程进入等待</span><br> }<br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br><br> <span class="hljs-keyword">public</span> String <span class="hljs-title function_">getCardId</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> cardId;<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setCardId</span><span class="hljs-params">(String cardId)</span> {<br> <span class="hljs-built_in">this</span>.cardId = cardId;<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-type">double</span> <span class="hljs-title function_">getMoney</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">return</span> money;<br> }<br><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">setMoney</span><span class="hljs-params">(<span class="hljs-type">double</span> money)</span> {<br> <span class="hljs-built_in">this</span>.money = money;<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li><strong>取钱线程类:</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 取钱的线程类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DrawThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span> {<br> <span class="hljs-keyword">private</span> Account acc; <span class="hljs-comment">//定义一个账户对象,便于对账户对象进行操作</span><br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">DrawThread</span><span class="hljs-params">(Account acc, String name)</span> {<br> <span class="hljs-built_in">super</span>(name);<br> <span class="hljs-built_in">this</span>.acc = acc;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//不断的存取前</span><br> <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {<br> acc.DrawMoney(<span class="hljs-number">100000</span>);<br> <span class="hljs-keyword">try</span> {<br> Thread.sleep(<span class="hljs-number">3</span> * <span class="hljs-number">1000</span>); <span class="hljs-comment">//每隔三秒取一次钱</span><br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li><strong>存钱线程类:</strong></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 取钱的线程类</span><br><span class="hljs-comment"> */</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DepositThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">Thread</span> {<br> <span class="hljs-keyword">private</span> Account acc; <span class="hljs-comment">//定义一个账户对象,便于对账户对象进行操作</span><br><br> <span class="hljs-keyword">public</span> <span class="hljs-title function_">DepositThread</span><span class="hljs-params">(Account acc, String name)</span> {<br> <span class="hljs-built_in">super</span>(name);<br> <span class="hljs-built_in">this</span>.acc = acc;<br> }<br><br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-comment">//不断的存取前</span><br> <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {<br> acc.depositMoney(<span class="hljs-number">100000</span>);<br> <span class="hljs-keyword">try</span> {<br> Thread.sleep(<span class="hljs-number">2</span> * <span class="hljs-number">1000</span>); <span class="hljs-comment">//每隔两秒存一次钱</span><br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><ul><li>主程序</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">ThreadDemo</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">// 三个父亲线程(生产者) 两个孩子线程(消费者) 模拟存取钱通信思想(一存 一取)</span><br> <span class="hljs-comment">// 1.创建账户对象,代表五个人共同操作的账户</span><br> <span class="hljs-type">Account</span> <span class="hljs-variable">acc</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Account</span>(<span class="hljs-string">"ICBC-999"</span>, <span class="hljs-number">0</span>); <span class="hljs-comment">//初始化零元</span><br><br> <span class="hljs-comment">// 2.创建两个取钱线程 小明 小红</span><br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DrawThread</span>(acc, <span class="hljs-string">"小明"</span>).start();<br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DrawThread</span>(acc, <span class="hljs-string">"小红"</span>).start();<br><br> <span class="hljs-comment">// 3.创建三个存钱线程 亲爹 干爹 岳父</span><br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DepositThread</span>(acc, <span class="hljs-string">"亲爹"</span>).start();<br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DepositThread</span>(acc, <span class="hljs-string">"干爹"</span>).start();<br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">DepositThread</span>(acc, <span class="hljs-string">"岳父"</span>).start();<br> }<br>}<br></code></pre></td></tr></table></figure></li></ul><h2 id="线程池的创建与使用"><a href="#线程池的创建与使用" class="headerlink" title="线程池的创建与使用"></a>线程池的创建与使用</h2><ul><li><p><strong>线程池是一个可以复用线程的技术。解决了每次都为新请求创建线程,导致开销较大的问题</strong></p><p>JDK5.0起提供了代表线程池的接口:ExecutorService</p><blockquote><p>得到线程池对象的两种办法:<br>一、使用ExecutorService的实现类ThreadPoolExecutor自己创建一个线程池对象。<br>二、使用Executors(线程池的工具类)调用方法放回不同特点的线程池对象。</p></blockquote></li><li><p><strong>ThreadPoolExecutor构造器</strong>:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> ThreadPoolExecutor(<span class="hljs-type">int</span> corePoolSize<span class="hljs-comment">//指定线程池的线程数量(核心线程),不小于0</span><br><span class="hljs-type">int</span> maximumPoolSize,<span class="hljs-comment">//指定线程池可支持的最大线程数,最大数量>=核心线程数量</span><br><span class="hljs-type">long</span> keepAliveTIme,<span class="hljs-comment">//指定临时线程的最大存活时间,大于0</span><br>TimeUnit unit,<span class="hljs-comment">//存活时间的单位(秒,分,时,天):unit,时间单位</span><br>BlockingQueue workQueue,<span class="hljs-comment">//指定任务队列:workQueue,不能为null</span><br>ThreadFactory threadFactory,<span class="hljs-comment">//指定用那个线程工厂创建线程:threadFactory ,不能为null</span><br>RejectedExecutionHandler handler)<span class="hljs-comment">//指定线程忙,任务慢的时候,新任务来了怎么办,即拒绝策略:handler,不能为null</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>(重要!!!)注意两点:</strong><br><strong>临时线程什么时候创建:新任务提交时发现核心线程都在忙,任务队列满,还可以创建临时线程时才会创建。</strong><br><strong>什么时候开始拒绝任务:核心线程和临时线程都在忙,任务队列都满,新任务过来会拒绝。</strong></p></blockquote><p>ExecutorService的常用方法:</p><table><thead><tr><th>方法名称</th><th>说明</th></tr></thead><tbody><tr><td>void execute(Runable command)</td><td>执行任务,无返回值,一般用于执行Runnable任务</td></tr><tr><td>Future submit(Callable Task</td><td>执行任务,返回未来任务对象获取线程结果,一般用于执行Callable任务</td></tr><tr><td>void shutdown()</td><td>等待任务执行完毕后关闭线程池</td></tr><tr><td>List shutdownNow()</td><td>立即关闭,停止正在执行的任务,返回队列中未执行的任务</td></tr></tbody></table><p>拒绝策略:</p><table><thead><tr><th>策略</th><th>详解</th></tr></thead><tbody><tr><td>ThreadPoolExecutor.AbortPolicy</td><td>丢弃任务抛出RejectedExecutionException异常</td></tr><tr><td>ThreadPoolExecutor.DiscardPolicy</td><td>丢弃任务不抛出异常(不推荐)</td></tr><tr><td>ThreadPoolExecutor.DiscardOldestPolicy</td><td>抛弃等待最久的任务并把当前任务加入队列</td></tr><tr><td>ThreadPoolExecutor.CallerRunsPolicy</td><td>由主线程负责调用的run()方法绕过线程池直接执行,即来新任务主线程亲自服务</td></tr></tbody></table></li><li><p><strong>Runnabler任务处理</strong></p><p>Runnable实现代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Runnable</span>{<br> <span class="hljs-meta">@Override</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">run</span><span class="hljs-params">()</span> {<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span>; i < <span class="hljs-number">5</span>; i++) {<br> System.out.println(Thread.currentThread().getName() + <span class="hljs-string">"输出了:HelloWorld"</span> + i);<br> }<br> <span class="hljs-comment">// 睡眠线程,观察线程的执行情况</span><br> <span class="hljs-keyword">try</span> {<br> System.out.println(Thread.currentThread().getName() + <span class="hljs-string">"本任务与线程绑定了,进入休眠状态了~~~"</span>);<br> Thread.sleep(<span class="hljs-number">1000000</span>); <span class="hljs-comment">//让其睡眠时间久一点,便于观察线程池的复用情况</span><br> } <span class="hljs-keyword">catch</span> (Exception e) {<br> e.printStackTrace();<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>线程池创建代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.concurrent.*;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">threadPoolDemo1</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> {<br> <span class="hljs-comment">// 1.创建线程池对象</span><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * public ThreadPoolExecutor(int corePoolSize,</span><br><span class="hljs-comment"> * int maximumPoolSize,</span><br><span class="hljs-comment"> * long keepAliveTime,</span><br><span class="hljs-comment"> * TimeUnit unit,</span><br><span class="hljs-comment"> * BlockingQueue<Runnable> workQueue,</span><br><span class="hljs-comment"> * ThreadFactory threadFactory,</span><br><span class="hljs-comment"> * RejectedExecutionHandler handler) {</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">pool</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>,<br> TimeUnit.SECONDS, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBlockingQueue</span><>(<span class="hljs-number">5</span>), Executors.defaultThreadFactory(),<br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>.AbortPolicy());<br><br> <span class="hljs-comment">// 2.把任务给线程池处理</span><br> <span class="hljs-type">Runnable</span> <span class="hljs-variable">target</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">MyRunnable</span>();<br> <span class="hljs-comment">//核心线程运行</span><br> pool.execute(target);<br> pool.execute(target);<br> pool.execute(target);<br><br> <span class="hljs-comment">//不创建临时线程</span><br> pool.execute(target);<br> pool.execute(target);<br> pool.execute(target);<br> pool.execute(target);<br> pool.execute(target); <span class="hljs-comment">//任务队列满了!!核心线程都忙!!</span><br><br> <span class="hljs-comment">//创建临时线程</span><br> pool.execute(target);<br> pool.execute(target);<br><br> <span class="hljs-comment">//拒绝策略触发</span><br> pool.execute(target);<br><br> <span class="hljs-comment">//关闭线程池(开发一般不使用,线程池作用就是长久存活)</span><br> <span class="hljs-comment">//pool.shutdownNow(); //立即关闭,即使任务未完成也关闭</span><br> <span class="hljs-comment">//pool.shutdown(); //等待任务执行完毕再关闭</span><br> }<br>}<br></code></pre></td></tr></table></figure><p>运行结果:</p><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/Javasethread-4%E8%BF%90%E8%A1%8C%E7%BB%93%E6%9E%9C.png" alt="image-20231208193517302"></p></li><li><p><strong>callable任务处理</strong></p></li></ul><p>代码:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.concurrent.*;<br><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">threadPoolDemo2</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> Exception {<br> <span class="hljs-comment">// 1.创建线程池对象</span><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * public ThreadPoolExecutor(int corePoolSize,</span><br><span class="hljs-comment"> * int maximumPoolSize,</span><br><span class="hljs-comment"> * long keepAliveTime,</span><br><span class="hljs-comment"> * TimeUnit unit,</span><br><span class="hljs-comment"> * BlockingQueue<Runnable> workQueue,</span><br><span class="hljs-comment"> * ThreadFactory threadFactory,</span><br><span class="hljs-comment"> * RejectedExecutionHandler handler) {</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-type">ExecutorService</span> <span class="hljs-variable">pool</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>(<span class="hljs-number">3</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>,<br> TimeUnit.SECONDS, <span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayBlockingQueue</span><>(<span class="hljs-number">5</span>), Executors.defaultThreadFactory(),<br> <span class="hljs-keyword">new</span> <span class="hljs-title class_">ThreadPoolExecutor</span>.AbortPolicy());<br><br> <span class="hljs-comment">// 2.把任务给线程池处理</span><br> Future<String> f1 = pool.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">100</span>));<br> Future<String> f2 = pool.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">200</span>));<br> Future<String> f3 = pool.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">300</span>));<br> Future<String> f4 = pool.submit(<span class="hljs-keyword">new</span> <span class="hljs-title class_">MyCallable</span>(<span class="hljs-number">400</span>));<br><br> System.out.println(f1.get());<br> System.out.println(f2.get());<br> System.out.println(f3.get());<br> System.out.println(f4.get());<br> }<br>}<br></code></pre></td></tr></table></figure><h2 id="重要!)-Executors工具类实现线程池"><a href="#重要!)-Executors工具类实现线程池" class="headerlink" title="重要!) Executors工具类实现线程池"></a><strong>重要!) Executors工具类实现线程池</strong></h2><p>Executors获得线程池的常用方法:</p><table><thead><tr><th align="left">方法名称</th><th>说明</th></tr></thead><tbody><tr><td align="left">public static ExecutorService newCachedThreadPool()</td><td>线程数随着任务增加而增加,若线程任务执行完毕且空闲一段时间会被回收</td></tr><tr><td align="left">public static ExecutorService newFixedThreadPool()</td><td>创建固定数量的线程池。若某个线程因为异常而结束,那么线程池会补充一个新线程代替它</td></tr><tr><td align="left">public static ExecutorService newSingleThreadExecutor()</td><td>创建只有一个线程对象的线程池对象,如果该线程因为异常而结束,那么线程池会补充一个新线程</td></tr><tr><td align="left">public static ScheduledExecutorService newScheduledThreadPool(int PoolSize)</td><td>创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务</td></tr></tbody></table><blockquote><p><strong>注意:Executors的底层其实也是基于线程池的实现类ThreadPoolExecutor创建线程池对象</strong><br><strong>注意:大型并发环境使用Executors如果不注意可能会出现系统风险,例如阿里开发手册不建议使用Executors创建线程池对象,而是建 议使用ThreadPoolExecutor自己创建</strong></p></blockquote><table><thead><tr><th align="left">方法名称</th><th>存在问题</th></tr></thead><tbody><tr><td align="left">public static ExecutorService newCachedThreadPool()</td><td>允许请求的任务队列长度为Integer.MAX_VALUE,可能出现OOM错误(OutOfMemoryError)</td></tr><tr><td align="left">public static ExecutorService newFixedThreadPool()</td><td>同上</td></tr><tr><td align="left">public static ExecutorService newSingleThreadExecutor()</td><td>创建的线程数量上限为Integer.MAX_VALUE,线程数随着任务1:1增长,可能出现OOM错误(OutOfMemoryError)</td></tr><tr><td align="left">public static ScheduledExecutorService newScheduledThreadPool(int PoolSize)</td><td>同上</td></tr></tbody></table>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>多线程</tag>
</tags>
</entry>
<entry>
<title>Java面向对象(二)</title>
<link href="/2023/12/05/Java%E5%85%A5%E9%97%A804/"/>
<url>/2023/12/05/Java%E5%85%A5%E9%97%A804/</url>
<content type="html"><![CDATA[<p>Java面向对象主要知识点</p><span id="more"></span><h1 id="Java面向对象(二)"><a href="#Java面向对象(二)" class="headerlink" title="Java面向对象(二)"></a>Java面向对象(二)</h1><h2 id="static"><a href="#static" class="headerlink" title="static"></a>static</h2><ul><li>static修饰的静态变量和方法是所有实例或者类名共用。<strong>堆之中的内存也是如此,随着类的加载而加载</strong></li><li>调用:类名和实例都可以调用</li><li>作用: 多用于工具类和测试类</li><li><strong>注意事项:静态方法只能访问静态变量和方法,但非静态可以访问静态</strong></li></ul><h2 id="this"><a href="#this" class="headerlink" title="this"></a>this</h2><ul><li>本质:一个指向对象地址的变量,在函数参数列表内默认加载</li></ul><h2 id="面向对象三大特征"><a href="#面向对象三大特征" class="headerlink" title="面向对象三大特征"></a>面向对象三大特征</h2><h3 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h3><ul><li>封装对应数据并提供数据对应的行为</li></ul><h3 id="继承(extends)"><a href="#继承(extends)" class="headerlink" title="继承(extends)"></a>继承(extends)</h3><ul><li><p>类于类之间存在共性的时候,考虑使用继承,减少代码复用以及代码维护</p></li><li><p>区别于C++:Java只能单继承,不能多继承但是能多次继承</p></li><li><p>特点:</p><ul><li><strong>子类继承父类的所有成员变量和非私有的成员方法</strong>(构造函数不继承),存储在堆之中的形式和方法也有所不同</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaSe-2.png" alt="image-20231205232908915"></p><ul><li><p>变量,方法的赋值与调用:</p><ul><li>当子类的相关(未区分)变量被调用或者赋值的时候会优先寻找子类的变量,<strong>就近原则</strong>(局部->本类->父类)</li><li><strong>Super</strong>修饰方法被调用的时候直接调用父类中的同名方法,本质是父类存储空间</li><li><strong>@Override</strong>重写父类方法<ul><li>重写的本质:<strong>虚方法表</strong>(一个类之中非静态,非私有,非固定(final)的方法表)<ul><li>当子类继承的时候会将父类的虚方法表中的方法加到虚方法表内,在此基础上加上自己的方法</li><li>当重写发生的时候会将父类的继承方法进行<strong>覆盖</strong></li><li>重写的方法会影响继承子类的子类</li></ul></li><li>重写方法名称和形参列表应与父类一致</li></ul></li></ul></li><li><p>构造方法:</p><ul><li><p>无法继承</p></li><li><p>子类之中的所有构造方法<strong>默认</strong>先访问父类之中的无参构造,再执行自己<strong>以访问父类数据</strong></p></li><li><p>可以使用super(参数列表)调用父类有参构造</p></li></ul></li></ul></li></ul><h3 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h3><ul><li><p>代码形式:(<strong>继承/实现是多态的前提条件</strong>)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//当Person是Student父类时候</span><br><span class="hljs-type">Student</span> <span class="hljs-variable">s</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Student</span>();<br><span class="hljs-type">Person</span> <span class="hljs-variable">p</span> <span class="hljs-operator">=</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">Student</span>();<br></code></pre></td></tr></table></figure></li><li><p>本质:将子类对象赋值给父类类型引用</p></li><li><p>特点:</p><ul><li><p>调用变量时优先调用<strong>父类变量</strong></p></li><li><p>调用方法时调用<strong>子类方法</strong>,但是父类未含有此方法编译会报错</p><ul><li>原因:子类方法将虚方法表内父类方法覆盖了,但变量无法覆盖</li></ul></li></ul></li><li><p>优势:</p><ul><li>可以实现解耦合,便于维护</li><li>函数参数传入方便,子类共用父类传参函数</li></ul></li></ul><h2 id="final"><a href="#final" class="headerlink" title="final"></a>final</h2><ul><li><p>修饰变量</p><ul><li>修饰基本类型: 值不变</li><li>修饰引用类型: <strong>地址不变,但是内部值可以改变</strong></li></ul></li><li><p>修饰方法: 最终方法,不能重写</p></li><li><p>修饰类:最终类,无法继承</p></li></ul><h2 id="抽象类(abstract)"><a href="#抽象类(abstract)" class="headerlink" title="抽象类(abstract)"></a>抽象类(abstract)</h2><ul><li><p><strong>无法实例化</strong>,但可以有构造方法</p></li><li><p>有抽象方法一定是抽象类,反之不一定</p></li><li><p>抽象子类:</p><ul><li>要实例化则要重写所有抽象方法,否则仍就是抽象类</li></ul></li><li><p>目的:规范子类格式</p></li></ul><h2 id="接口(interface)"><a href="#接口(interface)" class="headerlink" title="接口(interface)"></a>接口(interface)</h2><ul><li><p>无法实例化</p></li><li><p>通过implements实现</p></li><li><p>接口子类:</p><ul><li>要实例化则要重写所有接口方法,否则仍就是抽象类</li></ul></li><li><p><strong>一个类可以实现多个接口</strong>,还可以同时继承</p></li><li><p>特点:</p><ul><li>成员变量: 只能是常量,默认public static final修饰</li><li>不含有构造方法</li><li>成员方法: 只能是抽象方法,默认public abstract修饰</li></ul></li></ul><h2 id="内部类和外部类"><a href="#内部类和外部类" class="headerlink" title="内部类和外部类"></a>内部类和外部类</h2><p>…….</p>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>面向对象</tag>
</tags>
</entry>
<entry>
<title>Java入门03</title>
<link href="/2023/12/05/Java%E5%85%A5%E9%97%A803/"/>
<url>/2023/12/05/Java%E5%85%A5%E9%97%A803/</url>
<content type="html"><![CDATA[<p>Java入门03 面向对象基础</p><span id="more"></span><h1 id="Java基础:面向对象"><a href="#Java基础:面向对象" class="headerlink" title="Java基础:面向对象"></a>Java基础:面向对象</h1><p>面向对象对应的时面向过程的编程思想,面向过程更适合小规模工程,面向对象适合较大的项目</p><h2 id="类与类的关系"><a href="#类与类的关系" class="headerlink" title="类与类的关系"></a>类与类的关系</h2><h3 id="依赖"><a href="#依赖" class="headerlink" title="依赖"></a>依赖</h3><p>如果一个类的方法使用或者操纵另一个类的对象,我们便说一个类依赖于另外一个类</p><h3 id="聚合"><a href="#聚合" class="headerlink" title="聚合"></a>聚合</h3><p>一个类的对象包含另外一个类的对象(成员变量),就可以说时聚合</p><h3 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h3><p>表示一个更特殊的类和一个更一般类的关系</p><h3 id="预定义类与用户自定义类"><a href="#预定义类与用户自定义类" class="headerlink" title="预定义类与用户自定义类"></a>预定义类与用户自定义类</h3><p>预定义类一般是JavaSE提供的一些基础的类,例如Date类</p><p>用户自定义的类在不继承任何其它类的情况下都继承自Object类,所有类都来源于Object这个超类</p><p> 一个自定义类包含类字段,类方法以及构造器</p><h2 id="对象构造"><a href="#对象构造" class="headerlink" title="对象构造"></a>对象构造</h2><h3 id="构造器"><a href="#构造器" class="headerlink" title="构造器"></a>构造器</h3><ul><li>与类同名</li><li>无返回值</li><li>构造器总是伴随new操作符一起使用</li><li>每个类都至少有一个构造器,默认会有一个无参构造,除非将其覆盖</li></ul><blockquote><p>所有Java对象都是在堆中创造的,总是需要new操作符,类的定义只是在栈上分配了一个引用变量 myObject 的内存空间,用于存储一个对象的引用,它本身并不占用任何内存。只有当您使用 new 关键字创建 MyClass 的实例时,比如 new MyClass(),Java 虚拟机才会为这个对象分配内存。</p><p>C++可以 Item item(num,name);但是java不行</p></blockquote><ul><li>默认初值:当构造器中没有显式的设置字段初值,那么字段就会被自动的设为默认值:数值为0,布尔值为false,对象引用为null</li></ul><h3 id="对象引用与null"><a href="#对象引用与null" class="headerlink" title="对象引用与null"></a>对象引用与null</h3><p>只是声明一个对象变量的时候,产生的对象引用默认为null</p><h3 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h3><p>多个构造器/方法,选择不同的参数,这个叫做重载</p><h3 id="初始化块"><a href="#初始化块" class="headerlink" title="初始化块"></a>初始化块</h3><p>一个类中可以包含任意多个代码块,只要构造这个类的对象,这些块就会被执行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">block</span> {<br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">block</span><span class="hljs-params">()</span>{}<br> {<br> <span class="hljs-comment">//代码块,一般其实直接放构造器里</span><br> }<br>}<br><br></code></pre></td></tr></table></figure><blockquote><p>对象析构:因为Java支持自动垃圾回收,所以不支持析构器的构造</p><p>但是当某些对象使用的内存之外的资源比如文件,还是要进行回收,应当提供一个close方法来完成必要的清理工作</p><p>,可以在对象使用完时调用这个close方法</p></blockquote><h2 id="权限修饰符"><a href="#权限修饰符" class="headerlink" title="权限修饰符"></a>权限修饰符</h2><ul><li><strong>private</strong> :可以修饰成员变量,成员方法,构造方法,不能修饰类(此刻指的是外部类,内部类不加以考虑)。被private修饰的成员<strong>只能在其修饰的本类中访问</strong>,在其他类中不能调用(这里也可以看出为什么不能修饰class,因为private本来就是作用于类内部的东西)。</li><li><strong>default</strong> :可以修饰类,成员变量,成员方法,构造方法。被默认权限修饰后,其只能被<strong>本类以及同包下</strong>的其他类访问。</li><li><strong>protected</strong>:可以修饰成员变量,成员方法,构造方法,但不能修饰类(此处指的是外部类,内部类不加以考虑)。被protected修饰后,只能被<strong>同包下的其他类</strong>访问。如果不同包下的类要访问被protected修饰的成员,这个类必须是<strong>其子类</strong>。</li><li><strong>public</strong> :权限最大的修饰符,他可以修饰类,成员变量,成员方法,构造方法。被public修饰后,可以再任何一个类中,不管同不同包,任意使用。</li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaSe-3.png" alt="image-20231205233323143"></p><h2 id="工作内存管理"><a href="#工作内存管理" class="headerlink" title="工作内存管理"></a>工作内存管理</h2><ul><li><p>方法区:字节码文件记载时进入的内存(<strong>类似于声明,不同文件class记录的变量和方法</strong>)</p></li><li><p>栈: 方法运行时所进入的内存,变量也是一样。运行完毕的时候移出,相应的指向堆中的变量存储空间也会消失</p></li><li><p>堆:new出来的变量(<strong>非静态实例</strong>)都会储存在这里(<strong>但是new的本体是一个指向堆空间的变量存储在栈之中</strong>)</p></li></ul><p><img src="https://fluid-1322937929.cos.ap-beijing.myqcloud.com/image/JavaSe-1.png" alt="image-20231205232708431"></p><h3 id="类方法的载入过程大致如下:"><a href="#类方法的载入过程大致如下:" class="headerlink" title="类方法的载入过程大致如下:"></a>类方法的载入过程大致如下:</h3><h4 id="1-加载(Loading):"><a href="#1-加载(Loading):" class="headerlink" title="1.加载(Loading):"></a>1.加载(Loading):</h4><ul><li>当JVM遇到一个new指令,或者通过反射、动态代理等机制显式地要求加载一个类时,JVM会首先查找这个类是否已经被加载。<br>如果类没有被加载,JVM会找到相应的.class文件,并将其数据读取到内存中。这一过程包括验证.class文件的格式、解析字节码、生成内部数据结构等。</li><li>一旦类被加载,它的类型信息将被存储在方法区中。</li></ul><h4 id="2-链接(Linking):"><a href="#2-链接(Linking):" class="headerlink" title="2.链接(Linking):"></a>2.链接(Linking):</h4><ul><li>验证(Verification):确保加载的类符合JVM规范,没有安全问题。</li><li>准备(Preparation):为类变量分配内存,并设置默认初始值(对于非静态变量,这一步骤发生在对象实例化时)。</li><li>解析(Resolution):将符号引用替换为直接引用。这一步会确保所有的方法引用、字段引用等都能正确地指向实际的方法和字段。</li></ul><h4 id="3-初始化(Initialization):"><a href="#3-初始化(Initialization):" class="headerlink" title="3.初始化(Initialization):"></a>3.初始化(Initialization):</h4><ul><li>初始化阶段是执行类构造器<clinit>()方法的过程,这个方法是由编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的。在这个阶段,JVM会根据程序代码初始化类变量和静态代码块。</li></ul><p> </p><p>一旦类被加载并初始化,它的方法、字段和其他类型信息就存储在方法区中,供JVM在运行时使用。当调用一个类的方法时,JVM会在方法区中查找该方法的具体实现,并执行相应的字节码。</p><h2 id="静态方法和静态字段"><a href="#静态方法和静态字段" class="headerlink" title="静态方法和静态字段"></a>静态方法和静态字段</h2><p><strong>static</strong></p><p>被static修饰的成员和方法属于类本身,被所有实例共享</p><p>静态方法不能调用动态字段和方法</p><p>一般静态变量搭配final变为静态常量</p><h2 id="方法参数"><a href="#方法参数" class="headerlink" title="方法参数"></a>方法参数</h2><p>Java总是采用按值调用,也就是参数都是值传递,方法得到的所有参数值都是一个副本,方法不能改变传递的任何参数变量的内容</p><p>但是存在两种类型的方法参数:</p><ul><li>基本数据类型</li><li>对象引用</li></ul><p>一个方法不能改变基本数据类型的参数,但是可以改变对应引用的参数</p><p>原因时参数值的副本也是一个对象引用,并且指向和参数变量相同的一个对象</p><blockquote><p>C++区分了两种参数传递的方式分为值传递和引用传递(&),Java则是都是值传递,但是引用同一份变量</p></blockquote><h2 id="包"><a href="#包" class="headerlink" title="包"></a>包</h2><p>Java用包(package)来将类组织在一个集合中</p><h3 id="包名"><a href="#包名" class="headerlink" title="包名"></a>包名</h3><p>保证类名的唯一性,相同类名,不同包下还是不一样的类</p><p>为了保证包名的唯一性,用一个因特网域名命名就可以</p><blockquote><p>对于域名 horstmann.com 得到包名com.horstmann,后面加上工程名比如com.horstmann.corejava,再加上类名Item</p><p>这个类完全限定名就是 com.horstmann.corejava.Item</p></blockquote><h3 id="类的导入"><a href="#类的导入" class="headerlink" title="类的导入"></a>类的导入</h3><p>一个类可以使用所属包下的所有类,其他包中的公共类</p><p>使用方式</p><ul><li>完全限定名</li><li>import语句</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">import</span> java.util.Arrays;<br></code></pre></td></tr></table></figure><blockquote><p>#include 主要用于 C 和 C++ 语言,在编译阶段将文件内容包含到当前文件中,而 import 是许多高级语言中的一个关键字,用于在运行时加载模块或库。</p></blockquote><h4 id="静态导入"><a href="#静态导入" class="headerlink" title="静态导入"></a>静态导入</h4><p>在import后加入static字段,可以在使用该类的静态方法时候不用加类名</p><h4 id="在包中增加类名"><a href="#在包中增加类名" class="headerlink" title="在包中增加类名"></a>在包中增加类名</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> caculate;<span class="hljs-comment">//加入包</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">mycalss</span>(){<br> <br>}<br></code></pre></td></tr></table></figure><h2 id="工具类ArrayList集合使用"><a href="#工具类ArrayList集合使用" class="headerlink" title="工具类ArrayList集合使用"></a>工具类ArrayList集合使用</h2><ul><li><p>对比于数组: 长度不固定,有自然的相应接口函数</p></li><li><p>特点:只能存引用数据类型而非基本数据类型</p></li><li><p>使用:<strong>ArrayList本身含有索引</strong></p><ul><li><p>定义</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">ArrayList<String> list =<span class="hljs-keyword">new</span> <span class="hljs-title class_">ArrayList</span><>;<br></code></pre></td></tr></table></figure></li><li><p>操作函数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">boolean</span> <span class="hljs-title function_">add</span><span class="hljs-params">(E e)</span>;<br><span class="hljs-type">boolean</span> <span class="hljs-title function_">remove</span><span class="hljs-params">(E e)</span>;<span class="hljs-comment">//删除</span><br>E <span class="hljs-title function_">remove</span><span class="hljs-params">(<span class="hljs-type">int</span> index)</span>;<span class="hljs-comment">//索引删除</span><br>set(); size();<span class="hljs-comment">//等等</span><br></code></pre></td></tr></table></figure></li></ul></li><li><p>集合拓展:</p><ul><li><p>单列集合<Conllection></p><ul><li><p>List系列集合:有序,可重复,有索引</p></li><li><p>Set系列:无序,不重复,无索引</p></li><li><p>迭代器扫描集合:</p></li><li><pre><code class="java">Iteretor<String> it = list.iterator();while(it.hasNext()){ String str=it.next();}<figure class="highlight arduino"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs arduino"><br>+ 增强<span class="hljs-keyword">for</span>遍历:<br><br> ```<span class="hljs-function">java</span><br><span class="hljs-function"> <span class="hljs-title">for</span><span class="hljs-params">(<span class="hljs-type">String</span> s:List)</span></span>{<br> <span class="hljs-comment">//s的操作不会影响list的数据</span><br> }<br></code></pre></td></tr></table></figure></code></pre></li><li><p>lamada表达式</p><p>…</p></li></ul></li><li><p>双列集合<map></p><ul><li>键值集合对象</li><li>键不可以重复,值可以重复,无序无索引</li><li>三种遍历..</li><li>hashmap(<strong>重复的键put进去会覆盖原有键值,与hashset相反</strong>)<ul><li>HashMap底层是哈希表结构的</li><li>依赖hashCode方法和equals方法保证键的唯一</li><li><strong>如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储自定义对象,不需要重写hashCode和equals方法</strong></li></ul></li><li>treemap等….</li></ul></li></ul></li></ul><h2 id="泛型"><a href="#泛型" class="headerlink" title="泛型"></a>泛型</h2><ul><li>泛型介绍:泛型是Java编程语言的一种特性,它允许在定义类、接口和方法时使用类型参数。这使得代码变得更加通用、类型安全,并提高了代码的可读性和重用性。</li><li>泛型类型只能是引用类型:在Java中,泛型类型<strong>只能是引用类型,不能指代基本数据类型</strong>。</li><li>泛型<strong>实例化:在使用泛型时,我们需要为泛型类型指定具体的实例化类型</strong>。例如,定义一个泛型ArrayList对象,添加不同类型的数据,如字符串、整数和学生对象。</li><li>自定义泛型:我们可以定义自己的泛型类、接口,如MyArrayList,MyArrayList2等。自定义泛型时,需要注意的是<strong>普通成员可以使用泛型,但泛型数组不能初始化,静态方法中也不能使用类的泛型等</strong>。</li><li>泛型方法:泛型方法的基本语法和注意事项。泛型方法可以定义在普通类中,也可以定义在泛型类中。<strong>当泛型方法被调用时,类型被确定。</strong></li><li>泛型集合:在Java中,我们可以使用List,Set等泛型集合类来存储和管理不同类型的数据。使用泛型集合可以提高代码重用性、类型安全和性能。</li><li>泛型上限和下限通配符:<strong>泛型上限通配符(?)和下限通配符(<?>)用于表示泛型类型的范围</strong>。上限通配符表示未知具体类型,下限通配符表示已知具体类型但允许子类型。</li></ul><blockquote><p>在Java中,你不能直接在方法参数中使用通配符T。通配符T是用来表示类型参数的占位符,它不能直接用作方法参数的类型。当你在方法中使用泛型参数时,你需要指定一个具体的类型参数。</p></blockquote>]]></content>
<categories>
<category>Java-SE</category>
</categories>
<tags>
<tag>JAVA</tag>
</tags>
</entry>
<entry>
<title>JDBC学习</title>
<link href="/2023/12/01/JDBC%E5%AD%A6%E4%B9%A0/"/>
<url>/2023/12/01/JDBC%E5%AD%A6%E4%B9%A0/</url>
<content type="html"><![CDATA[<p>JDBC连接数据库的学习与应用</p><span id="more"></span><h1 id="JDBC是什么"><a href="#JDBC是什么" class="headerlink" title="JDBC是什么"></a>JDBC是什么</h1><ul><li>Java DataBase Connectivity</li><li>JDBC就是一套使用java语言操作关系数据库的一套API,不同类型数据库都有相应实现类</li></ul><h1 id="JDBC基本流程"><a href="#JDBC基本流程" class="headerlink" title="JDBC基本流程"></a>JDBC基本流程</h1><ul><li><p>导入jar包</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs java"><dependency><br> <groupId>mysql</groupId><br> <artifactId>mysql-connector-java</artifactId><br> <version><span class="hljs-number">8.0</span><span class="hljs-number">.28</span></version><br></dependency><br></code></pre></td></tr></table></figure></li><li><p>注册驱动</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">Class.forName(<span class="hljs-string">"com.mysql.jdbc.Driver"</span>);<br></code></pre></td></tr></table></figure></li><li><p>获取连接</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Connection</span> <span class="hljs-variable">conn</span> <span class="hljs-operator">=</span> DriverMannager.getConnection(url,username,password);<br></code></pre></td></tr></table></figure></li><li><p>自定义sql语句</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">String sql=<span class="hljs-string">"Update..."</span>;<br></code></pre></td></tr></table></figure></li><li><p>获取执行对象并执行</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">Statement</span> <span class="hljs-variable">stmt</span> <span class="hljs-operator">=</span> conn.creatStatement();<br><span class="hljs-comment">//执行,返回影响行数</span><br><span class="hljs-type">int</span> <span class="hljs-variable">count</span> <span class="hljs-operator">=</span> stmt.executeUpdate(sql);<br></code></pre></td></tr></table></figure></li><li><p>处理返回结果</p></li><li><p>释放资源</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">stmt.close();<br>conn.close();<span class="hljs-comment">//Resultset资源也要释放</span><br></code></pre></td></tr></table></figure></li></ul><h1 id="JDBC具体API实现"><a href="#JDBC具体API实现" class="headerlink" title="JDBC具体API实现"></a>JDBC具体API实现</h1><h2 id="Drivermanager驱动管理类"><a href="#Drivermanager驱动管理类" class="headerlink" title="Drivermanager驱动管理类"></a>Drivermanager驱动管理类</h2><ul><li><p>本质:<strong>一个可以直接调用其静态方法的工具类</strong></p></li><li><p>作用:注册驱动并且获取数据库连接</p></li><li><p>注册驱动</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//forName方法实际调用</span><br>DriverManager.registerDriver(<span class="hljs-keyword">new</span> <span class="hljs-title class_">Driver</span>());<br></code></pre></td></tr></table></figure><p>在jar包里面的有自动的加载驱动方法,可以不写</p></li><li><p>获取数据库连接</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">Static Connection <span class="hljs-title function_">getconnection</span><span class="hljs-params">(string url,string user,string passowrd)</span>;<br></code></pre></td></tr></table></figure><ul><li><p>url: 连接路径</p><ul><li><p>语法: ==jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2==</p></li><li><p>示例:jdbc:mysql://127.0.0.1:3306/db1</p></li><li><p>如果连接的是本地的mysql则可以省略ip地址</p></li><li><p>注:配置userSSL=false,禁用安全连接方式,解决ssl警告提示:jdbc:mysql://127.0.0.1:3306/db1?useSSL=false</p></li></ul></li><li><p>user: 连接端口用户名</p></li><li><p>password: 密码</p></li></ul></li></ul><h2 id="Connection数据库连接对象"><a href="#Connection数据库连接对象" class="headerlink" title="Connection数据库连接对象"></a>Connection数据库连接对象</h2><ul><li><p>作用: 获取sql执行对象,管理事务</p></li><li><p>获取执行sql对象</p><ul><li><p>普通执行对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">Statement <span class="hljs-title function_">creatstatement</span><span class="hljs-params">()</span>;<br></code></pre></td></tr></table></figure></li><li><p>预编译sql执行对象:防止sql注入</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">PreparedStatement <span class="hljs-title function_">preparedStatement</span><span class="hljs-params">(sql)</span>;<br></code></pre></td></tr></table></figure></li><li><p>执行存储过程对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">CallableStatement <span class="hljs-title function_">prepareCall</span><span class="hljs-params">(sql)</span>;<br></code></pre></td></tr></table></figure></li></ul></li><li><p>事务管理</p><ul><li><p>Mysql事务管理</p><ul><li>开启事务:BEGIN;/START TRANSACTION;</li><li>提交事务:COMMIT;</li><li>回滚事务:ROLLBACK;</li></ul></li><li><p>JDBC事务管理</p><ul><li><p>==一般用于异常处理==</p></li><li><p>开启: </p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">setAutoCommit(<span class="hljs-type">boolean</span> autoCommit);<span class="hljs-comment">//true为自动提交,false为手动提交即为开启事务</span><br></code></pre></td></tr></table></figure></li><li><p>提交</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">commit();<br></code></pre></td></tr></table></figure></li><li><p>回滚</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">rollback();<br></code></pre></td></tr></table></figure></li></ul></li></ul></li></ul><h2 id="Statement执行对象"><a href="#Statement执行对象" class="headerlink" title="Statement执行对象"></a>Statement执行对象</h2><ul><li><p>作用:执行sql语句</p></li><li><p>==执行sql语句细节==</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">int</span> exeuteUpdate;<span class="hljs-comment">//执行DML(增删改),DDL(库的增删改)语句</span><br>->返回值(<span class="hljs-number">1</span>)DML为影响行数,<span class="hljs-number">0</span>为失败 (<span class="hljs-number">2</span>)DDL执行成功为<span class="hljs-number">0</span><br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">ResultSet <span class="hljs-title function_">exeuteQuery</span><span class="hljs-params">(sql)</span>; <span class="hljs-comment">//执行DQL(查询)语句</span><br>->返回值: ResultSet结果集对象<br></code></pre></td></tr></table></figure></li></ul><h2 id="ResultSet结果集对象"><a href="#ResultSet结果集对象" class="headerlink" title="ResultSet结果集对象"></a>ResultSet结果集对象</h2><ul><li><p>作用:<strong>对查询结果进行封装</strong></p></li><li><p>获取查询结果</p><ul><li><p>结果行移动以及判断是否有效</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">boolean</span> <span class="hljs-title function_">next</span><span class="hljs-params">()</span>;<span class="hljs-comment">//(1)将光标向下移动一行 (2)判断当前行是否有效,true为有效</span><br></code></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">(xxx数据类型) getxxx(参数); <span class="hljs-comment">//获取一行里面的数据,由resultset对象调用</span><br>->参数:<span class="hljs-number">1.</span> <span class="hljs-type">int</span> 类型参数: 列编号,从<span class="hljs-number">1</span>开始 <span class="hljs-number">2.</span>String 类型参数:列名称<br></code></pre></td></tr></table></figure><ul><li>注:<strong>每调用一次next()方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用next()应把光标置于第一行上,使它成为当前行。</strong></li></ul></li></ul></li></ul><h2 id="PreparedStatement"><a href="#PreparedStatement" class="headerlink" title="PreparedStatement"></a>PreparedStatement</h2><ul><li><p>作用:继承自statement,用于执行预编译sql对象,防止sql注入</p><blockquote><p>sql注入:通过操作输入修改事先定义的sql语句,以达到执行代码对服务器攻击的方法(核心是字符拼接)</p></blockquote></li><li><p>==一般用于用户登录==</p><ul><li><p>获取Preparedstatement对象</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">//?使用占位符替代</span><br><span class="hljs-type">String</span> <span class="hljs-variable">sql</span> <span class="hljs-operator">=</span> <span class="hljs-string">"select *from user where username=? and password=?"</span>;<br><span class="hljs-comment">//通过Connection对象的函数preparestatement()方法获取,传入相应sql语句</span><br><span class="hljs-type">PreparedStatement</span> <span class="hljs-variable">pstmt</span> <span class="hljs-operator">=</span>conn.preparestatement(sql);<br></code></pre></td></tr></table></figure></li><li><p>设置参数值</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">setxxx(参数<span class="hljs-number">1</span>,参数<span class="hljs-number">2</span>);<span class="hljs-comment">//给?赋值</span><br>->参数:参数<span class="hljs-number">1</span>:?的位置编号,从<span class="hljs-number">1</span>开始;参数<span class="hljs-number">2</span>:?的值<br></code></pre></td></tr></table></figure></li><li><p>执行sql</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">exeuteUpdate()/exeuteQuery();<span class="hljs-comment">//不需要再传sql</span><br></code></pre></td></tr></table></figure></li><li><p>预编译功能开启</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">jdbc:mysql:<span class="hljs-comment">//127.0.0.1:3306/db1?useSSL=false&useServerPreStmts=true</span><br></code></pre></td></tr></table></figure></li></ul></li></ul><h1 id="数据库连接池"><a href="#数据库连接池" class="headerlink" title="数据库连接池"></a>数据库连接池</h1><ul><li><p>介绍: 数据库连接池本质是个容器,负责分配管理数据库连接;</p></li><li><p>功能:允许应用重复使用一个现有的数据库连接,而不是重新建立一个;<strong>意思就是从一个用户一个连接到所有用户共享一个到数据库的连接</strong>,==本质是通过管理多个connection都通过一个连接到数据库==</p></li><li><p>数据库连接池是一种用于管理和复用数据库连接的缓存机制,它可以提高应用程序对数据库的访问效率并减少资源消耗。在数据库连接池的配置中,通常会涉及到几个关键的参数,包括最大空闲连接数和最小空闲连接数</p></li></ul><h2 id="最大空闲连接数(Maximum-Idle-Connections)"><a href="#最大空闲连接数(Maximum-Idle-Connections)" class="headerlink" title="最大空闲连接数(Maximum Idle Connections)"></a>最大空闲连接数(Maximum Idle Connections)</h2><p> 最大空闲连接数定义了连接池中可以保持的空闲连接的最大数量。当连接被创建并返回到连接池时,如果连接池中的连接数已经达到了最大空闲连接数,那么这个连接可能会被关闭,或者如果该连接是新创建的,则可能不会被添加到池中。设置一个合理的最大空闲连接数可以避免资源的浪费,同时确保有足够的连接来处理潜在的高并发请求。</p><h2 id="最小空闲连接数(Minimum-Idle-Connections)"><a href="#最小空闲连接数(Minimum-Idle-Connections)" class="headerlink" title="最小空闲连接数(Minimum Idle Connections)"></a>最小空闲连接数(Minimum Idle Connections)</h2><p> 最小空闲连接数定义了连接池中应该保持的空闲连接的最小数量。连接池会根据这个参数来维护一定数量的空闲连接,以减少获取连接时的延迟。当连接池中的连接数低于这个阈值时,连接池会创建新的连接,直到达到最小空闲连接数。这样可以确保总是有可用的连接来处理请求,特别是在系统负载较高时。</p><ul><li><p>参数配置的影响</p><blockquote><p>如果最大空闲连接数设置得太小,可能会导致在高并发情况下,连接池需要频繁地创建和关闭连接,从而增加了系统的开销。<br> 如果最大空闲连接数设置得太大,可能会浪费系统资源,因为维持过多的空闲连接会占用不必要的数据库资源。<br> 最小空闲连接数设置得太小,可能会导致在请求高峰时,连接池需要临时创建大量新连接,从而增加了响应时间。<br> 最小空闲连接数设置得太大,可能会导致在低负载时,维持过多的空闲连接,同样浪费资源。</p></blockquote></li><li><p>因此,合理配置最大空闲连接数和最小空闲连接数对于平衡系统的性能和资源利用率是非常重要的。通常,这些参数需要根据应用程序的预期负载、数据库的性能和可用的系统资源来进行调整。</p></li></ul><h2 id="数据库池实现"><a href="#数据库池实现" class="headerlink" title="数据库池实现"></a>数据库池实现</h2><ul><li><p>标准接口: ==Datasource==</p><ul><li><p>官方(SUN)提供的数据库连接池标准接口,由第三方实现此接口</p></li><li><p>功能:获取连接</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">Connection <span class="hljs-title function_">getConnection</span><span class="hljs-params">()</span>;<br></code></pre></td></tr></table></figure></li></ul></li><li><p>常见数据库连接池</p><ul><li>DBCP</li><li>C3P0</li><li>Druid</li></ul></li><li><p>==Druid(德鲁伊)==</p><ul><li>Druid是阿里巴巴开源的数据库连接池项目</li><li>功能强大</li></ul></li></ul>]]></content>
<categories>
<category>Java</category>
</categories>
<tags>
<tag>JAVA</tag>
<tag>JDBC</tag>
</tags>
</entry>
<entry>
<title>Spring Security介绍与认证功能</title>
<link href="/2023/11/25/Spring%20Security%E4%BB%8B%E7%BB%8D%E4%B8%8E%E8%AE%A4%E8%AF%81%E5%8A%9F%E8%83%BD/"/>
<url>/2023/11/25/Spring%20Security%E4%BB%8B%E7%BB%8D%E4%B8%8E%E8%AE%A4%E8%AF%81%E5%8A%9F%E8%83%BD/</url>
<content type="html"><![CDATA[<p>Spring sercurity框架介绍</p><span id="more"></span><h1 id="Security-是什么"><a href="#Security-是什么" class="headerlink" title="Security 是什么"></a>Security 是什么</h1><p><strong>Spring Security 是一个功能强大且可高度定制的身份验证和访问控制框架,它专注于为 Java 应用程序提供身份验证和授权。</strong></p><h1 id="Sercurity-的主要功能构成"><a href="#Sercurity-的主要功能构成" class="headerlink" title="Sercurity 的主要功能构成"></a>Sercurity 的主要功能构成</h1><p>==Spring sercurity 所有的功能都来源于来自构成它的各种 Filters (过滤器)==</p><ul><li>Sercurity 的功能我们可以划分为传统的两大类:</li></ul><p><strong>1. 认证:确认用户身份的合法性即识别用户</strong><br><strong>2. 授权:检验用户所拥有权限并返回给用户</strong></p><p>这里主要介绍认证的应用接口</p><h1 id="Sercurity-认证流程"><a href="#Sercurity-认证流程" class="headerlink" title="Sercurity 认证流程"></a>Sercurity 认证流程</h1><h2 id="识别"><a href="#识别" class="headerlink" title="识别"></a>识别</h2><p>Sercurity 识别框架主要提供了四个接口:</p><ul><li>Userdetails 接口:用于封装用户的详细信息例如用户名和密码之类,默认实现类是 User,自定义实现需求的用户主体只需要实现这个接口即可,下面是示例代码:</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserDetails</span> { <br><span class="hljs-comment">//必要方法请自行补充,table注释为数据库链接注释,下文会讲</span><br><span class="hljs-meta">@TableId(value="id",type= IdType.AUTO)</span><br><span class="hljs-keyword">private</span> <span class="hljs-type">long</span> id; <br><span class="hljs-meta">@TableField("username")</span><br><span class="hljs-keyword">private</span> String username; <br><span class="hljs-meta">@TableField("password")</span><br><span class="hljs-keyword">private</span> String password; <br><span class="hljs-meta">@TableField(exist = false)</span><span class="hljs-comment">//不在表内</span><br><span class="hljs-keyword">private</span> List<GrantedAuthority> authorities;<br>}<br></code></pre></td></tr></table></figure><ul><li>UserDetailsService 接口:用于进行用户验证的地方,其中提供了一个加载用户的方法 loadUserByUsername,重写这段方法即可自定义用户验证方法, 常见的比如通过数据库验证,这里以 mysql 数据库为例(这里实际上实现通过数据库授权):</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/*</span><br><span class="hljs-comment">使用user类型均为自定义用户类型</span><br><span class="hljs-comment">此外,除了需要配置数据库端口,还需要引入入mybatis-plus进行连接和sql语句操作</span><br><span class="hljs-comment">具体需求如下:1,多余的对应数据库中需要操作的表的类,我这里多加了一个权限类perm对应权限表,并进行相应注释</span><br><span class="hljs-comment"> 2,实现相应的数据库操作marpper类</span><br><span class="hljs-comment"> 3,写出对应表的service接口 </span><br><span class="hljs-comment">*/</span><br><span class="hljs-comment">//实现service,以user为例子</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserService</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">IService</span><User> { <br>}<br><br><span class="hljs-comment">//实现marpper类,以user类为例子</span><br><span class="hljs-meta">@Mapper</span> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">interface</span> <span class="hljs-title class_">UserMapper</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">BaseMapper</span><User> { <br>}<br><br><span class="hljs-comment">//具体重写方法</span><br><span class="hljs-comment">//拦截返回的用户信息(username)进行user加载 </span><br><span class="hljs-meta">@Service</span> <br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserServiceImpl</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">ServiceImpl</span><UserMapper, User> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">UserService</span>, UserDetailsService { <br><br><span class="hljs-meta">@Autowired</span> <br>UserMapper userMapper; <br><br><span class="hljs-meta">@Autowired</span> <br>PermMapper permMapper;<br><br><span class="hljs-keyword">public</span> UserDetails <span class="hljs-title function_">loadUserByUsername</span><span class="hljs-params">(String username)</span> <span class="hljs-keyword">throws</span> UsernameNotFoundException { <br><span class="hljs-comment">//在数据库内查找username对应的用户对象 </span><br>QueryWrapper queryWrapper=<span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span>(); <br>queryWrapper.eq(<span class="hljs-string">"username"</span>,username); <br><span class="hljs-type">User</span> <span class="hljs-variable">user</span> <span class="hljs-operator">=</span>userMapper.selectOne(queryWrapper); <br><br><span class="hljs-comment">//异常处理 </span><br><span class="hljs-keyword">if</span> (user==<span class="hljs-literal">null</span>) { <br><span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">UsernameNotFoundException</span>(<span class="hljs-string">"用户名未找到"</span>); <br>} <br><br><span class="hljs-comment">//根据id查找权限 </span><br><span class="hljs-type">QueryWrapper</span> <span class="hljs-variable">permQueryWrapper</span> <span class="hljs-operator">=</span><span class="hljs-keyword">new</span> <span class="hljs-title class_">QueryWrapper</span>(); <br>permQueryWrapper.eq(<span class="hljs-string">"id"</span>,user.getId()); <br><br>List<Perm> perms= permMapper.selectList(permQueryWrapper); <br><br>List<String> permTags=perms.stream().map(Perm::getAuthority).collect(Collectors.toList()); <br><br>user.setAuthorities(AuthorityUtils.createAuthorityList(permTags)); <br><br><span class="hljs-keyword">return</span> user; <br>}<br>}<br></code></pre></td></tr></table></figure><ul><li><p>Authentication:主要包含认证过的用户信息,UsernamePasswordAuthenticationToken 便是它的一个实现类</p></li><li><p>AuthenticationManager:接口用于执行认证 Authentication 操作,这里直接在 userdetailservice执行认证即可</p></li></ul><h2 id="校验"><a href="#校验" class="headerlink" title="校验"></a>校验</h2><ul><li>初步认证过后可以进行 token 令牌校验来减少反复认证,默认实现是 UsernamePasswordAuthenticationToken</li></ul><p>这里我们将进行自定义的 Jwt 令牌校验,以及</p>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>Spring</tag>
<tag>Sercurity</tag>
</tags>
</entry>
<entry>
<title>SSM05-Spring中的AOP概念</title>
<link href="/2023/05/30/SSM05-Spring%E4%B8%AD%E7%9A%84AOP%E6%A6%82%E5%BF%B5/"/>
<url>/2023/05/30/SSM05-Spring%E4%B8%AD%E7%9A%84AOP%E6%A6%82%E5%BF%B5/</url>
<content type="html"><![CDATA[<p>详细的描述了Spring中的AOP概念。</p><span id="more"></span><h1 id="AOP简介"><a href="#AOP简介" class="headerlink" title="AOP简介"></a>AOP简介</h1><h2 id="基础概念"><a href="#基础概念" class="headerlink" title="基础概念"></a>基础概念</h2><p><strong>AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构</strong></p><p><strong>OOP(object Oriented Programming)面向对象编程</strong></p><p><strong>作用:在不惊动原始设计的基础上为其进行功能增强</strong></p><p><strong>Spring理念:无入侵式/无侵入式</strong></p><h2 id="核心概念"><a href="#核心概念" class="headerlink" title="核心概念"></a>核心概念</h2><ol><li><p>连接点 ( JoinPoint ):程序执行过程中的任意位置,粒度为执行方法、抛出异常、设置变量等</p><ul><li><p>在SpringAOP中,理解为方法的执行</p></li><li><p>切入点 ( Pointcut ):匹配连接点的式子</p><ul><li>在springAoP中,一个切入点可以只描述一个具体方法,也可以匹配多个方法<ul><li>一个具体方法: com.itheima.dao包下的BookDao接口中的无形参无返回值的save方法</li><li>匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao结尾的接口中的任意方法,所有带有一个参数的方法</li></ul></li></ul></li></ul></li><li><p>通知(Advice) :在切入点处执行的操作,也就是共性功能</p><ul><li>在SpringAOP中,功能最终以方法的形式呈现</li></ul></li><li><p>通知类∶定义通知的类</p></li><li><p>切面( Aspect )︰描述通知与切入点的对应关系</p></li></ol><p>样例(源自黑马程序员,<strong>侵删</strong>)</p><p><img src="https://wang-nine.cn/images/SSM04-01AOP%E6%A0%B7%E4%BE%8B.png" alt="SSM04-01AOP样例"></p><h1 id="AOP入门案例"><a href="#AOP入门案例" class="headerlink" title="AOP入门案例"></a>AOP入门案例</h1><p>案例设定:测定接口执行效率<br>简化设定:在接口执行前输出当前系统时间<br>开发模式:XML or <strong>注解</strong></p><p>思路分析∶</p><ol><li>导入坐标( pom.xml )</li><li>制作连接点方法(原始操作,Dao接口与实现类)</li><li>制作共性功能(通知类与通知)</li><li>定义切入点</li><li>绑定切入点与通知关系(切面)</li></ol><p>基础代码样例(源自黑马程序员):</p><ol><li>导入坐标</li></ol><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.springframework<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>spring-context<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>5.2.10.RELEASE<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br><span class="hljs-tag"><<span class="hljs-name">dependency</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.aspectj<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>aspectjweaver<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.9.4<span class="hljs-tag"></<span class="hljs-name">version</span>></span><br><span class="hljs-tag"></<span class="hljs-name">dependency</span>></span><br></code></pre></td></tr></table></figure><ol start="2"><li>切面类</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@Aspect</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyAdvice</span> {<br><br> <span class="hljs-meta">@Pointcut("execution(切入点表达式)")</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">pt</span><span class="hljs-params">()</span>{}<br><br> <span class="hljs-meta">@Around("pt()")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">around</span><span class="hljs-params">(ProceedingJoinPoint pjp)</span> <span class="hljs-keyword">throws</span> Throwable {<br> System.out.println(<span class="hljs-string">"------------------------------"</span>);<br> <span class="hljs-type">Long</span> <span class="hljs-variable">startTime</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-keyword">for</span> (<span class="hljs-type">int</span> <span class="hljs-variable">i</span> <span class="hljs-operator">=</span> <span class="hljs-number">0</span> ; i<<span class="hljs-number">10000</span> ; i++) {<br> <span class="hljs-comment">//调用原始操作</span><br> pjp.proceed();<br> }<br> <span class="hljs-type">Long</span> <span class="hljs-variable">endTime</span> <span class="hljs-operator">=</span> System.currentTimeMillis();<br> <span class="hljs-type">Long</span> <span class="hljs-variable">totalTime</span> <span class="hljs-operator">=</span> endTime-startTime;<br> System.out.println(<span class="hljs-string">"执行万次消耗时间:"</span> + totalTime + <span class="hljs-string">"ms"</span>);<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br> }<br><br>}<br></code></pre></td></tr></table></figure><ol start="3"><li>主配置类</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs java">加上注解 <span class="hljs-meta">@EnableAspectJAutoProxy</span><br></code></pre></td></tr></table></figure><p>剩余的代码不变,例如Dao接口和实现类,主函数</p><p>以上步骤总结:</p><ol><li>导入AOP坐标</li><li>定义dao接口与实现类</li><li>定义通知类,制作通知</li><li>定义切入点 <strong>说明︰切入点定义依托一个不具有实际意义的方法进行,即无参数,无返回值,方法体无实际逻辑</strong></li><li>绑定切入点与通知关系,并指定通知添加到原始连接点的具体执行<strong>位置</strong></li><li>定义通知类受Spring容器管理,并定义当前类为切面类,即添加 <strong>@Component</strong> 和 <strong>@Aspect</strong></li><li>开启Spring对AOP注解驱动支持,即在主配置类添加 <strong>@EnableAspectJAutoProxy</strong></li></ol><h1 id="AOP工作流程"><a href="#AOP工作流程" class="headerlink" title="AOP工作流程"></a>AOP工作流程</h1><ol><li>Spring容器启动</li><li>读取所有切面配置中的切入点</li><li>初始化bean,判定bean对应的类中的方法是否匹配到任意切入点<ul><li>匹配失败:创建对象</li><li>匹配成功:创建原始对象(<strong>目标对象</strong>)的<strong>代理对象</strong></li></ul></li><li>获取bean的执行方法<ul><li>获取bean,调用方法并执行,完成操作</li><li>获取的bean是代理对象时,根据代理对象的运行模式运行原始方法与增强的内容,完成操作</li></ul></li></ol><ul><li>目标对象(Target ):原始功能去掉共性功能对应的类产生的对象,这种对象是无法直接完成最终工作的</li><li>代理(Proxy):目标对象无法直接完成工作,需要对其进行功能回填,通过原始对象的代理对象实现</li></ul><h1 id="AOP深入"><a href="#AOP深入" class="headerlink" title="AOP深入"></a>AOP深入</h1><h2 id="AOP的切入点表达式"><a href="#AOP的切入点表达式" class="headerlink" title="AOP的切入点表达式"></a>AOP的切入点表达式</h2><p>切入点∶要进行增强的方法<br>切入点表达式∶要进行增强的方法的描述方式</p><h3 id="语法格式"><a href="#语法格式" class="headerlink" title="语法格式"></a>语法格式</h3><p><strong>即可以对接口进行描述,也可以对实现类进行描述</strong></p><h4 id="切入点表达式标准格式"><a href="#切入点表达式标准格式" class="headerlink" title="切入点表达式标准格式"></a>切入点表达式标准格式</h4><p>动作关键字(访问修饰符 返回值.包名.类/接口名.方法名(参数)异常名)</p><blockquote><p>execution (public User com.xxx.service.UserService.findById (int))</p></blockquote><ul><li>动作关键字∶描述切入点的行为动作,例如execution表示执行到指定切入点</li><li>访问修饰符:public , private等,可以省略</li><li>返回值</li><li>包名</li><li>类/接口名</li><li>方法名</li><li>参数</li><li>异常名:方法定义中抛出指定异常,可以省略</li></ul><h4 id="可以使用通配符描述切入点,快速描述"><a href="#可以使用通配符描述切入点,快速描述" class="headerlink" title="可以使用通配符描述切入点,快速描述"></a>可以使用通配符描述切入点,快速描述</h4><ul><li><ul><li>︰单个独立的任意符号,可以独立出现,也可以作为前缀或者后缀的匹配符出现</li></ul><blockquote><p>execution (public * com.xxx.<em>.UserService.find</em>(*))<br>匹配com.xxx包下的任意包中的UserService类或接口中所有find开头的带有一个参数的方法</p></blockquote></li><li><p>.. :多个连续的任意符号,可以独立出现,常用于简化包名与参数的书写</p><blockquote><p>execution (public User com..UserService.findById (..) )<br>匹配com包下的任意包中的UserService类或接口中所有名称为findByld的方法</p></blockquote></li><li><p>+︰专用于匹配子类类型<br>execution(* *..<em>Service+.</em>(..))</p></li></ul><h4 id="切入点表达式的书写技巧"><a href="#切入点表达式的书写技巧" class="headerlink" title="切入点表达式的书写技巧"></a>切入点表达式的书写技巧</h4><ul><li>所有代码按照标准规范开发,否则以下技巧全部失效</li><li>描述切入点<strong>通常描述接口</strong>,而不描述实现类</li><li>访问控制修饰符针对接口开发均采用public描述(<strong>可省略访问控制修饰符描述</strong>)</li><li>返回值类型对于增删改类使用精准类型加速匹配,对于查询类使用*通配快速描述</li><li><strong>包名</strong>书写<strong>尽量不使用..匹配</strong>,效率过低,常用*做单个包描述匹配,或精准匹配</li><li><strong>接口名</strong>/类名书写名称与模块相关的<strong>采用*匹配</strong>,例如UserService书写成*Service,绑定业务层接口名</li><li><strong>方法名</strong>书写以<strong>动词进行精准匹配</strong>,名词采用<em>匹配,例如getByld书写成getBy</em>,selectAll书写成selectAll</li><li>参数规则较为复杂,根据业务方法灵活调整</li><li>通常<strong>不使用异常作为匹配规则</strong></li></ul><h1 id="AOP通知类型"><a href="#AOP通知类型" class="headerlink" title="AOP通知类型"></a>AOP通知类型</h1><p><strong>AOP通知描述了抽取的共性功能,根据共性功能抽取的位置不同,最终运行代码时要将其加入到合理的位置AOP通知共分为5种类型</strong></p><ul><li>前置通知 <strong>@Before</strong></li><li>后置通知 <strong>@After</strong></li><li>环绕通知(<strong>重点</strong>) <strong>@Around</strong> <strong>需要对原始操作进行调用</strong></li><li>返回后通知(了解) <strong>@AfterReturning</strong> 正常运行完才会运行</li><li>抛出异常后通知(了解) <strong>@AfterThrowing</strong> 抛出异常才会运行</li></ul><p>环绕通知对原始操作的调用样例(<strong>注意有返回值时需要返回原始操作的返回值</strong>):</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> Object <span class="hljs-title function_">around</span><span class="hljs-params">(ProceedingJoinPoint pjp)</span> <span class="hljs-keyword">throws</span> Throwable {<br> System.out.println(<span class="hljs-string">"around before advice ..."</span>);<br> <span class="hljs-type">Object</span> <span class="hljs-variable">ret</span> <span class="hljs-operator">=</span> pjp.proceed();<br> System.out.println(<span class="hljs-string">"around after advice ..."</span>);<br> <span class="hljs-keyword">return</span> ret;<br> }<br></code></pre></td></tr></table></figure><p><strong>@Around注意事项</strong></p><ol><li>环绕通知必须依赖形参ProceedingJoinPoint才能实现对原始方法的调用,进而实现原始方法调用前后同时添加通知</li><li>通知中如果未使用ProceedingJoinPoint对原始方法进行调用将跳过原始方法的执行</li><li>对原始方法的调用可以不接收返回值,通知方法设置成void即可,如果接收返回值,必须设定为Object类型</li><li>原始方法的返回值如果是void类型,通知方法的返回值类型可以设置成void,也可以设置成Object</li><li>由于无法预知原始方法运行后是否会抛出异常,因此环绕通知方法必须抛出Throwable对象</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-type">signature</span> <span class="hljs-variable">signature</span> <span class="hljs-operator">=</span> pjp.getsignature();<span class="hljs-comment">//获取代理类和被代理类的信息。</span><br>system.out.println(signature.getDeclaringTypeName());<span class="hljs-comment">//获取原始方法的类名</span><br>system.out.println(signature.getName());<span class="hljs-comment">//获取原始方法的方法名</span><br></code></pre></td></tr></table></figure><h1 id="AOP通知获取数据"><a href="#AOP通知获取数据" class="headerlink" title="AOP通知获取数据"></a>AOP通知获取数据</h1><h2 id="获取参数"><a href="#获取参数" class="headerlink" title="获取参数"></a>获取参数</h2><p>获取切入点方法的参数</p><ul><li><p>JoinPoint :适用于前置、后置、返回后、抛出异常后通知</p></li><li><p>ProceedJointPoint :适用于环绕通知</p></li></ul><h2 id="获取返回值"><a href="#获取返回值" class="headerlink" title="获取返回值"></a>获取返回值</h2><p>获取切入点方法返回值</p><ul><li><p>返回后通知</p></li><li><p>环绕通知</p></li></ul><h2 id="获取异常"><a href="#获取异常" class="headerlink" title="获取异常"></a>获取异常</h2><p>获取切入点方法运行异常信息</p><ul><li><p>抛出异常后通知</p></li><li><p>环绕通知</p></li></ul><p>样例代码如下(来自黑马程序员,侵删)</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> com.itheima.aop;<br><br><span class="hljs-keyword">import</span> org.aspectj.lang.JoinPoint;<br><span class="hljs-keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;<br><span class="hljs-keyword">import</span> org.aspectj.lang.annotation.*;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Component;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><br><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@Aspect</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">MyAdvice</span> {<br> <span class="hljs-meta">@Pointcut("execution(* com.itheima.dao.BookDao.findName(..))")</span><br> <span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">pt</span><span class="hljs-params">()</span>{}<br><br> <span class="hljs-comment">//JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数</span><br><span class="hljs-comment">// @Before("pt()")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">before</span><span class="hljs-params">(JoinPoint jp)</span> {<br> Object[] args = jp.getArgs();<br> System.out.println(Arrays.toString(args));<br> System.out.println(<span class="hljs-string">"before advice ..."</span> );<br> }<br><br><span class="hljs-comment">// @After("pt()")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">after</span><span class="hljs-params">(JoinPoint jp)</span> {<br> Object[] args = jp.getArgs();<br> System.out.println(Arrays.toString(args));<br> System.out.println(<span class="hljs-string">"after advice ..."</span>);<br> }<br><br> <span class="hljs-comment">//ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用</span><br><span class="hljs-comment">// @Around("pt()")</span><br> <span class="hljs-keyword">public</span> Object <span class="hljs-title function_">around</span><span class="hljs-params">(ProceedingJoinPoint pjp)</span> {<br> Object[] args = pjp.getArgs();<br> System.out.println(Arrays.toString(args));<br> args[<span class="hljs-number">0</span>] = <span class="hljs-number">666</span>;<br> <span class="hljs-type">Object</span> <span class="hljs-variable">ret</span> <span class="hljs-operator">=</span> <span class="hljs-literal">null</span>;<br> <span class="hljs-keyword">try</span> {<br> ret = pjp.proceed(args);<br> } <span class="hljs-keyword">catch</span> (Throwable t) {<br> t.printStackTrace();<br> }<br> <span class="hljs-keyword">return</span> ret;<br> }<br><br> <span class="hljs-comment">//设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同</span><br> <span class="hljs-meta">@AfterReturning(value = "pt()",returning = "ret")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">afterReturning</span><span class="hljs-params">(JoinPoint jp,String ret)</span> {<br> System.out.println(<span class="hljs-string">"afterReturning advice ..."</span>+ret);<br> }<br><br> <span class="hljs-comment">//设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同</span><br> <span class="hljs-meta">@AfterThrowing(value = "pt()",throwing = "t")</span><br> <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">afterThrowing</span><span class="hljs-params">(Throwable t)</span> {<br> System.out.println(<span class="hljs-string">"afterThrowing advice ..."</span>+t);<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>Spring</tag>
</tags>
</entry>
<entry>
<title>SSM06-Spring问题</title>
<link href="/2023/05/30/SSM06-spring%E9%97%AE%E9%A2%98/"/>
<url>/2023/05/30/SSM06-spring%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<p>了Spring中的问题。</p><span id="more"></span><h2 id="为什么会选择接口来注入而不是实现类直接注入?"><a href="#为什么会选择接口来注入而不是实现类直接注入?" class="headerlink" title="为什么会选择接口来注入而不是实现类直接注入?"></a>为什么会选择接口来注入而不是实现类直接注入?</h2><p>在 Spring 中,选择接口来注入而不是直接注入实现类有几个重要的原因:</p><ol><li><strong>松耦合</strong>:<ul><li>使用接口注入可以实现松耦合(loose coupling)。</li><li>通过依赖于接口,而不是具体的实现类,你的代码与特定的实现解耦。这意味着你可以在不改变使用接口的代码的情况下,随时切换不同的实现类。</li></ul></li><li><strong>可替换性</strong>:<ul><li>接口注入允许你在运行时选择不同的实现类。</li><li>这对于测试、模拟和扩展功能非常有用。例如,在测试阶段,你可以注入一个模拟实现,而在生产环境中使用真正的实现。</li></ul></li><li><strong>遵循面向接口编程的原则</strong>:<ul><li>面向接口编程是一种良好的实践,它强调使用接口定义合同,而不是依赖于具体的实现。</li><li>这有助于代码的可维护性、可测试性和可扩展性。</li></ul></li></ol><p>总之,尽管直接注入实现类也是可能的,但使用接口注入是一种更好的实践,有助于构建灵活、可扩展的应用程序</p><h2 id="什么是Restful风格?"><a href="#什么是Restful风格?" class="headerlink" title="什么是Restful风格?"></a>什么是Restful风格?</h2><p><strong>RESTful API</strong> 的设计风格确实通常使用 <strong>JSON</strong> 作为返回数据的格式,但这并不是绝对的规定。让我详细解释一下:</p><ol><li><strong>RESTful API</strong> 是一种软件架构风格,它强调了一组设计原则和约束条件,而不是具体的标准。这种风格主要用于客户端和服务器之间的交互</li><li><strong>RESTful API</strong> 的特点包括:<ul><li><strong>以资源为基础</strong>:资源可以是图片、音乐、XML、HTML、JSON 等网络上的实体。通常,普通文本资源更多以 <strong>JSON</strong> 为载体,而面向用户的数据通常从数据库中查询得到。</li><li><strong>统一接口</strong>:对资源的操作包括获取、创建、修改和删除,这些操作对应 HTTP 协议提供的 GET、POST、PUT 和 DELETE 方法。虽然 RESTful 风格的接口只能定位资源,但具体操作需要根据 HTTP 请求方法类型来判断。</li><li><strong>无状态</strong>:服务器不保存客户端状态,每次请求都需要携带必要的状态信息。会话信息由客户端保存,服务器根据这些状态信息处理请求。</li><li><strong>URI 指向资源</strong>:每个 URI 只指向一种资源,统一资源标识符 (URI) 用来标识抽象或物理资源的紧凑字符串。</li><li><strong>按需代码</strong>:允许发送一些特殊的代码给客户端,例如 JavaScript 代码</li></ul></li></ol><p> 3.至于 <strong>Tomcat</strong>,它是一个 <strong>Java Servlet</strong> 容器,用于部署 <strong>Java</strong> Web 应用程序。虽然 <strong>Tomcat</strong> 本身不是一个专门的 <strong>RESTful API</strong> 框架,但你可以在 <strong>Tomcat</strong> 上部署使用 <strong>RESTful</strong> 风格的应用程序。例如,你可以使用 <strong>Spring Boot</strong> 或其他支持 <strong>RESTful</strong> 风格的框架构建应用程序,并将其部署到 <strong>Tomcat</strong> 上。</p><h2 id="为什么使用Integer?"><a href="#为什么使用Integer?" class="headerlink" title="为什么使用Integer?"></a>为什么使用Integer?</h2><p>使用 <code>Integer</code> 类型(或其他包装类型)而不是原始数据类型(例如 <code>int</code>)在数据库操作中有一些好处:</p><ol><li><strong>处理空值(null)</strong>:<ul><li>包装类型允许你将属性设置为 <code>null</code>,而原始数据类型不支持这一点。在数据库中,某些字段可能没有值,或者需要表示空值。使用包装类型可以更好地处理这些情况。</li><li>例如,如果你的实体类属性是 <code>Integer</code>,那么你可以将其设置为 <code>null</code>,表示该属性没有值。</li></ul></li><li><strong>默认值</strong>:<ul><li>原始数据类型的默认值是0(例如,<code>int</code> 的默认值是0)。如果你在数据库中存储整数属性,并且没有显式设置值,那么它会被默认为0。</li><li>使用包装类型,你可以将默认值设置为 <code>null</code>,以便更好地区分是否有实际值。</li></ul></li><li><strong>映射到数据库列</strong>:<ul><li>数据库表中的列通常可以映射到实体类的属性。如果你的数据库表中的某个列允许 <code>null</code> 值,那么使用包装类型可以更好地映射这些值。</li><li>例如,如果你的数据库表中有一个整数列,你可以将其映射到实体类的 <code>Integer</code> 属性,以便正确处理 <code>null</code> 值。</li></ul></li></ol><p>总之,使用包装类型可以更好地处理空值、默认值和数据库映射。但在某些情况下,原始数据类型也可能更适合,具体取决于你的需求和设计选择。</p><h2 id="List使用"><a href="#List使用" class="headerlink" title="List使用"></a>List使用</h2><ol><li><p><strong>泛型和类型擦除</strong>:</p><ul><li><p>Java 中的泛型允许你在编译时指定集合中元素的类型。在你的 <code>PageBean</code> 类中,<code>rows</code> 属性没有指定具体的泛型类型,因此它可以接收任何类型的 <code>List</code>。</p></li><li><p>例如,你可以将以下类型的</p><p>属性:</p><ul><li><code>List<Integer></code></li><li><code>List<String></code></li><li><code>List<MyCustomObject></code>(自定义对象)</li></ul></li></ul></li></ol><h3 id="对比数组"><a href="#对比数组" class="headerlink" title="对比数组"></a>对比数组</h3><ol><li><strong>相似之处</strong>:<ul><li><strong>索引访问</strong>:与数组一样,<code>List</code> 也可以通过索引访问元素。例如,<code>myList.get(0)</code> 可以获取列表中的第一个元素。</li><li><strong>动态大小</strong>:与数组不同,<code>List</code> 的大小可以动态调整。你可以添加、删除或修改列表中的元素,而不需要提前指定大小。</li></ul></li><li><strong>不同之处</strong>:<ul><li><strong>类型</strong>:数组是原始数据类型的集合,而 <code>List</code> 是一个通用的集合类型。因此,<code>List</code> 可以包含任何类型的元素,例如整数、字符串、自定义对象等。</li><li><strong>自动装箱</strong>:<code>List</code> 中的元素是对象,而数组中的元素可以是原始数据类型。当你将原始数据类型添加到 <code>List</code> 中时,Java 会自动将其装箱为对应的包装类型(例如,将 <code>int</code> 装箱为 <code>Integer</code>)。</li><li><strong>灵活性</strong>:<code>List</code> 提供了更多的灵活性,例如添加、删除、查找元素,以及支持不同的列表实现(例如 <code>ArrayList</code>、<code>LinkedList</code> 等)。</li></ul></li></ol><h2 id="跨域问题"><a href="#跨域问题" class="headerlink" title="跨域问题"></a>跨域问题</h2><h3 id="什么是跨域"><a href="#什么是跨域" class="headerlink" title="什么是跨域"></a>什么是跨域</h3><p>简单而言,跨域请求就是当一台服务器资源从另一台服务器(不同 的域名或者端口)请求一个资源或者接口,就会发起一个跨域 HTTP 请求。举个简单的例子,从<a href="http://www.baidu.com,发送一个/">http://www.baidu.com,发送一个</a> Ajax 请求,请求地址是 <a href="http://www.taobao.com下面的一个接口,这就是发起了一个跨域请求,在不做任何处理的情况下,显然当前跨域请求是无法被成功请求,因为浏览器基于同源策略会对跨域请求做一定的限制./">http://www.taobao.com下面的一个接口,这就是发起了一个跨域请求,在不做任何处理的情况下,显然当前跨域请求是无法被成功请求,因为浏览器基于同源策略会对跨域请求做一定的限制。</a></p><h3 id="产生跨域问题的条件"><a href="#产生跨域问题的条件" class="headerlink" title="产生跨域问题的条件"></a><strong>产生跨域问题的条件</strong></h3><p>例如: <a href="http://192.168.38.438:8080">http://192.168.38.438:8080</a><br>当协议、IP、端口三部分中有任意一个不同时,即为跨域</p><h3 id="CORS请求问题"><a href="#CORS请求问题" class="headerlink" title="CORS请求问题"></a>CORS请求问题</h3><ul><li>跨域请求每次浏览器都会预先发送一次预检请求来去确认资源是否可以访问,方法为OPTIONS</li></ul><h2 id="DLL和LIB文件"><a href="#DLL和LIB文件" class="headerlink" title="DLL和LIB文件"></a>DLL和LIB文件</h2><ul><li>动态链接 vs. 静态链接:DLL 文件是动态链接库文件,它在<strong>运行时</strong>被加载到内存中,并通过<strong>动态链接的方式</strong>提供函数和资源给程序使用。LIB 文件是静态链接库文件,它在<strong>编译时</strong>将库代码<strong>直接复制到生成的可执行文件</strong>中,程序在运行时不需要外部的依赖文件。</li><li>共享的代码和资源:DLL 文件和 LIB 文件都包含可重用的代码和资源,通过它们,多个程序可以共享相同的代码实现。</li><li>函数声明和链接:无论是使用 DLL 文件还是 LIB 文件,都需要在程序中进行函数声明,以便在编译时或运行时正确链接和调用库中的函数。</li></ul><h2 id="什么是Servlet"><a href="#什么是Servlet" class="headerlink" title="什么是Servlet"></a>什么是Servlet</h2><ul><li>Servlet是Java Servlet API的一部分,是一个Java类,<strong>用于接收和响应客户端发送的请求</strong>。它是Java EE(现在称为Jakarta EE)平台的一部分,主要用于Web应用程序。Servlet作为服务器端软件,运行在支持Java的Web服务器上,如Apache Tomcat、Jetty或GlassFish。</li></ul><h3 id="Servlet的主要功能包括:"><a href="#Servlet的主要功能包括:" class="headerlink" title="Servlet的主要功能包括:"></a>Servlet的主要功能包括:</h3><ul><li>请求处理:Servlet可以接收来自客户端(如Web浏览器)的请求,并对这些请求进行处理。</li><li>响应生成:Servlet可以生成响应,并将它们发送回客户端。这些响应通常是HTML页面,但也可能是其他格式,如JSON、XML等。</li><li>数据转换:Servlet可以处理客户端发送的数据,如表单数据,并将这些数据转换为服务器端可以处理的形式。</li><li>会话管理:Servlet可以管理用户会话,跟踪用户的状态,这对于需要用户登录和个性化体验的Web应用程序非常重要。</li><li>安全性:Servlet可以实现访问控制,确保只有授权的用户才能访问特定的资源。</li><li>数据持久化:Servlet可以与数据库或其他数据存储系统交互,用于读取和更新数据。</li></ul><h3 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h3><ul><li><p>Servlet的生命周期由Servlet容器(如Web服务器)管理。当请求到达服务器时,容器创建Servlet实例,调用其service方法来处理请求,然后销毁实例。Servlet容器还负责加载、初始化和卸载Servlet。</p></li><li><p>Servlet API提供了一组标准的方法,<strong>如doGet、doPost、doPut、doDelete等,用于处理不同类型的HTTP方法(请求)</strong>。这些方法由Servlet容器调用,以响应用户的请求。</p></li><li><p>在Spring框架中,Servlet API仍然是一个重要的组成部分,但Spring MVC(Model-View-Controller)框架提供了一种更高级的Web应用程序开发方式,它使用Controller类来处理请求,而不是直接使用Servlet。尽管如此,Spring MVC框架底层仍然使用Servlet API来处理HTTP请求和响应。</p></li></ul><h2 id="什么是初始化器"><a href="#什么是初始化器" class="headerlink" title="什么是初始化器"></a>什么是初始化器</h2><ul><li>WebApplicationInitializer 的主要作用是简化Spring MVC应用程序的配置过程,提供了一种无需XML配置文件就能初始化Spring容器和Web应用程序的方式</li></ul><h3 id="springboot使用初始化器"><a href="#springboot使用初始化器" class="headerlink" title="springboot使用初始化器"></a>springboot使用初始化器</h3><ul><li>Spring Boot 本身是一个微框架(microframework),它旨在简化Spring应用的开发。Spring Boot 提供了一系列的自动配置和默认值,以减少开发者的配置工作。然而,在某些情况下,你可能需要自定义初始化器(Spring Boot Initializr)来满足特定的需求。以下是一些可能需要配置初始化器的情况:</li></ul><blockquote><p> 1.定制Banner:Spring Boot 启动时会显示一个banner,显示项目信息和版本号。如果你想要定制这个banner,可以通过SpringApplicationBuilder来创建一个自定义的SpringApplication。</p><p> 2.添加额外的properties文件:Spring Boot 默认会加载application.properties或application.yml文件。如果你需要在应用启动时加载其他的properties文件,可以通过SpringApplicationBuilder来添加。</p><p> 3.设置不同的应用类型:Spring Boot 支持不同的应用类型,如Web应用程序、命令行应用程序等。如果你需要设置不同的应用类型,可以通过SpringApplicationBuilder来指定。</p><p> 4.自定义运行时监听器:Spring Boot 应用可以使用SpringApplicationRunListeners来监听应用的启动和关闭过程。如果你需要添加自定义的监听器,可以通过编程的方式注册。</p><p> 5.自定义环境设置:Spring Boot 应用可以使用SpringApplicationBuilder来设置自定义的环境信息,例如设置系统属性或环境变量。</p><p> 6.禁用或启用自动配置:Spring Boot 的一大特点是自动配置,但在某些情况下,你可能需要禁用或启用特定的自动配置。这可以通过编程方式来控制。</p><p> 7.指定自定义的SpringApplication:如果你需要创建一个完全自定义的SpringApplication,可以通过编程方式来创建,而不是使用SpringApplicationBuilder。</p><p> 8.应用自定义的JavaAgent:在某些情况下,你可能需要向Spring Boot应用中添加自定义的JavaAgent,以修改JVM的行为。</p><p> 9.处理程序映射的定制:如果你需要定制Spring Boot应用中的处理程序映射,可以通过编程方式来控制。</p><p> 10.资源加载和转换:Spring Boot 应用可以使用SpringApplicationBuilder来指定自定义的资源加载器,以支持不同的资源加载和转换需求。</p></blockquote><ul><li>通常,这些情况都是针对Spring Boot应用的高级定制需求。对于大多数简单的Spring Boot应用,默认的初始化器和自动配置就足够了。只有在特殊情况下,才需要配置初始化器来自定义Spring Boot应用的行为。</li></ul><h2 id="序列化与反序列化"><a href="#序列化与反序列化" class="headerlink" title="序列化与反序列化"></a>序列化与反序列化</h2><p>在我们网络交流中,采用的通信格式多种多样,但是传输的的时候都是二进制码,将原本的信息序列化为二进制码叫序列化,反过来叫反序列化</p><ul><li>把对象转换为字节序列的过程称为对象的序列化</li><li>把字节序列恢复为对象的过程称为对象的反序列化</li><li>序列化对于面向对象的编程语言来说是非常重要的,因为无论什么编程语言,其底层涉及IO操作的部分还是由操作系统其帮其完成的,而底层IO操作都是以字节流的方式进行的,所以写操作都涉及将编程语言数据类型转换为字节流,而读操作则又涉及将字节流转化为编程语言类型的特定数据类型。</li></ul><h3 id="什么是Serializable接口"><a href="#什么是Serializable接口" class="headerlink" title="什么是Serializable接口"></a>什么是Serializable接口</h3><p>一个对象序列化的接口,一个类只有实现了Serializable接口,它的对象才能被序列化。</p><p>Serializable是java.io包中定义的、用于实现Java类的序列化操作而提供的一个语义级别的接口。</p><p>Serializable序列化接口没有任何方法或者字段,只是用于标识可序列化的语义。</p><blockquote><p>序列化通常用于将对象存储到文件、数据库或通过网络传输对象时。尽管可以直接将数据保存到MySQL、Oracle等数据库中而不需要序列化,这主要是因为这些数据库提供了直接操作数据结构(如表、列等)的能力,而不是操作对象本身。</p></blockquote><h2 id="什么是持久化?"><a href="#什么是持久化?" class="headerlink" title="什么是持久化?"></a>什么是持久化?</h2><p>持久化(Persistence)在计算机科学中指的是<strong>将数据保存到能够长期存储的存储介质中的过程</strong>,这样即使在电源关闭或系统崩溃之后,数据仍然可以保持并能够被恢复。持久化是确保数据不会因为应用程序或系统的结束而丢失的关键机制。<br>在不同的上下文中,持久化可能有不同的含义:</p><ul><li>对象持久化:在<strong>面向对象编程中,对象持久化是指将内存中的对象状态保存到诸如关系数据库、对象数据库、文件系统或其他形式的永久存储中的过程</strong>。这样,对象在程序不再运行时仍然存在,可以在未来的某个时间点被重新加载到内存中,通常具有与之前相同的状态。</li><li>数据持久化:数据持久化是一个更广泛的概念,它不仅包括对象持久化,还包括任何类型的数据的存储,如用户配置、文件、系统设置等。</li></ul><h3 id="持久化技术通常包括以下几种:"><a href="#持久化技术通常包括以下几种:" class="headerlink" title="持久化技术通常包括以下几种:"></a>持久化技术通常包括以下几种:</h3><ul><li>关系数据库:如MySQL、Oracle、SQL Server等,通过SQL语句将数据保存到表中。</li><li>NoSQL数据库:如MongoDB、Cassandra、Redis等,提供了更灵活的数据模型和高效的存储机制。</li><li>对象数据库:如ObjectDB、db4o等,可以直接存储对象图。</li><li>文件系统:将数据保存为文件,可以是文本文件、二进制文件等。</li><li>分布式存储系统:如Hadoop的HDFS、Amazon S3等,提供了高可用性和可扩展性的数据存储解决方案。<br>持久化是现代软件系统中不可或缺的一部分,它确保了数据的长期保存和可靠性。</li></ul><h2 id="xxxxxxxxxx57-1package-com-itheima-aop-23import-org-aspectj-lang-JoinPoint-4import-org-aspectj-lang-ProceedingJoinPoint-5import-org-aspectj-lang-annotation-6import-org-springframework-stereotype-Component-78import-java-util-Arrays-910-Component11-Aspect12public-class-MyAdvice-13-Pointcut-“execution-com-itheima-dao-BookDao-findName-”-14-private-void-pt-1516-JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数17-Before-“pt-”-18-public-void-before-JoinPoint-jp-19-Object-args-jp-getArgs-20-System-out-println-Arrays-toString-args-21-System-out-println-“before-advice-…”-22-2324-After-“pt-”-25-public-void-after-JoinPoint-jp-26-Object-args-jp-getArgs-27-System-out-println-Arrays-toString-args-28-System-out-println-“after-advice-…”-29-3031-ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用32-Around-“pt-”-33-public-Object-around-ProceedingJoinPoint-pjp-34-Object-args-pjp-getArgs-35-System-out-println-Arrays-toString-args-36-args-0-666-37-Object-ret-null-38-try-39-ret-pjp-proceed-args-40-catch-Throwable-t-41-t-printStackTrace-42-43-return-ret-44-4546-设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同47-AfterReturning-value-“pt-”-returning-“ret”-48-public-void-afterReturning-JoinPoint-jp-String-ret-49-System-out-println-“afterReturning-advice-…”-ret-50-5152-设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同53-AfterThrowing-value-“pt-”-throwing-“t”-54-public-void-afterThrowing-Throwable-t-55-System-out-println-“afterThrowing-advice-…”-t-56-57-java"><a href="#xxxxxxxxxx57-1package-com-itheima-aop-23import-org-aspectj-lang-JoinPoint-4import-org-aspectj-lang-ProceedingJoinPoint-5import-org-aspectj-lang-annotation-6import-org-springframework-stereotype-Component-78import-java-util-Arrays-910-Component11-Aspect12public-class-MyAdvice-13-Pointcut-“execution-com-itheima-dao-BookDao-findName-”-14-private-void-pt-1516-JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数17-Before-“pt-”-18-public-void-before-JoinPoint-jp-19-Object-args-jp-getArgs-20-System-out-println-Arrays-toString-args-21-System-out-println-“before-advice-…”-22-2324-After-“pt-”-25-public-void-after-JoinPoint-jp-26-Object-args-jp-getArgs-27-System-out-println-Arrays-toString-args-28-System-out-println-“after-advice-…”-29-3031-ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用32-Around-“pt-”-33-public-Object-around-ProceedingJoinPoint-pjp-34-Object-args-pjp-getArgs-35-System-out-println-Arrays-toString-args-36-args-0-666-37-Object-ret-null-38-try-39-ret-pjp-proceed-args-40-catch-Throwable-t-41-t-printStackTrace-42-43-return-ret-44-4546-设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同47-AfterReturning-value-“pt-”-returning-“ret”-48-public-void-afterReturning-JoinPoint-jp-String-ret-49-System-out-println-“afterReturning-advice-…”-ret-50-5152-设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同53-AfterThrowing-value-“pt-”-throwing-“t”-54-public-void-afterThrowing-Throwable-t-55-System-out-println-“afterThrowing-advice-…”-t-56-57-java" class="headerlink" title="xxxxxxxxxx57 1package com.itheima.aop;23import org.aspectj.lang.JoinPoint;4import org.aspectj.lang.ProceedingJoinPoint;5import org.aspectj.lang.annotation.;6import org.springframework.stereotype.Component;78import java.util.Arrays;910@Component11@Aspect12public class MyAdvice {13 @Pointcut(“execution( com.itheima.dao.BookDao.findName(..))”)14 private void pt(){}1516 //JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数17// @Before(“pt()”)18 public void before(JoinPoint jp) {19 Object[] args = jp.getArgs();20 System.out.println(Arrays.toString(args));21 System.out.println(“before advice …” );22 }2324// @After(“pt()”)25 public void after(JoinPoint jp) {26 Object[] args = jp.getArgs();27 System.out.println(Arrays.toString(args));28 System.out.println(“after advice …”);29 }3031 //ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用32// @Around(“pt()”)33 public Object around(ProceedingJoinPoint pjp) {34 Object[] args = pjp.getArgs();35 System.out.println(Arrays.toString(args));36 args[0] = 666;37 Object ret = null;38 try {39 ret = pjp.proceed(args);40 } catch (Throwable t) {41 t.printStackTrace();42 }43 return ret;44 }4546 //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同47 @AfterReturning(value = “pt()”,returning = “ret”)48 public void afterReturning(JoinPoint jp,String ret) {49 System.out.println(“afterReturning advice …”+ret);50 }5152 //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同53 @AfterThrowing(value = “pt()”,throwing = “t”)54 public void afterThrowing(Throwable t) {55 System.out.println(“afterThrowing advice …”+t);56 }57}java"></a>xxxxxxxxxx57 1package com.itheima.aop;23import org.aspectj.lang.JoinPoint;4import org.aspectj.lang.ProceedingJoinPoint;5import org.aspectj.lang.annotation.<em>;6import org.springframework.stereotype.Component;78import java.util.Arrays;910@Component11@Aspect12public class MyAdvice {13 @Pointcut(“execution(</em> com.itheima.dao.BookDao.findName(..))”)14 private void pt(){}1516 //JoinPoint:用于描述切入点的对象,必须配置成通知方法中的第一个参数,可用于获取原始方法调用的参数17// @Before(“pt()”)18 public void before(JoinPoint jp) {19 Object[] args = jp.getArgs();20 System.out.println(Arrays.toString(args));21 System.out.println(“before advice …” );22 }2324// @After(“pt()”)25 public void after(JoinPoint jp) {26 Object[] args = jp.getArgs();27 System.out.println(Arrays.toString(args));28 System.out.println(“after advice …”);29 }3031 //ProceedingJoinPoint:专用于环绕通知,是JoinPoint子类,可以实现对原始方法的调用32// @Around(“pt()”)33 public Object around(ProceedingJoinPoint pjp) {34 Object[] args = pjp.getArgs();35 System.out.println(Arrays.toString(args));36 args[0] = 666;37 Object ret = null;38 try {39 ret = pjp.proceed(args);40 } catch (Throwable t) {41 t.printStackTrace();42 }43 return ret;44 }4546 //设置返回后通知获取原始方法的返回值,要求returning属性值必须与方法形参名相同47 @AfterReturning(value = “pt()”,returning = “ret”)48 public void afterReturning(JoinPoint jp,String ret) {49 System.out.println(“afterReturning advice …”+ret);50 }5152 //设置抛出异常后通知获取原始方法运行时抛出的异常对象,要求throwing属性值必须与方法形参名相同53 @AfterThrowing(value = “pt()”,throwing = “t”)54 public void afterThrowing(Throwable t) {55 System.out.println(“afterThrowing advice …”+t);56 }57}java</h2><p>Swagger 3,也称为 OpenAPI Specification 3,是一种用于描述、消费和可视化 RESTful 网络服务的规范。</p><h3 id="Controller"><a href="#Controller" class="headerlink" title="Controller"></a>Controller</h3><p>@Tag</p><h3 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h3><p>@Operation</p><h3 id="entity或者pojo(对外可暴露实体类属性集合)"><a href="#entity或者pojo(对外可暴露实体类属性集合)" class="headerlink" title="entity或者pojo(对外可暴露实体类属性集合)"></a>entity或者pojo(对外可暴露实体类属性集合)</h3><p>@Schema</p><h2 id="全局异常处理器"><a href="#全局异常处理器" class="headerlink" title="全局异常处理器"></a>全局异常处理器</h2><p>不用try catch了</p><ul><li>新建GlobalExeptionHandle</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Slf4j</span><br><span class="hljs-meta">@RestControllerAdvice</span><span class="hljs-comment">//</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">GlobalExceptionHandler</span><br>{<br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 默认全局异常处理。</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> e the e</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span> ResultData</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@ExceptionHandler(RuntimeException.class)</span><br> <span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 在Spring框架中,当你在一个标注有 <span class="hljs-doctag">@ControllerAdvice</span> 或 <span class="hljs-doctag">@RestControllerAdvice</span> 的类中使用 <span class="hljs-doctag">@ExceptionHandler</span> 注解</span><br><span class="hljs-comment"> * 时,你是在定义一个全局的异常处理方法。这意味着,当你的应用程序中任何一个控制器(<span class="hljs-doctag">@Controller</span> 或 <span class="hljs-doctag">@RestController</span>)发生 </span><br><span class="hljs-comment"> * RuntimeException 或其子类的异常时,都会调用这个方法来处理异常</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-meta">@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)</span><br> <span class="hljs-comment">//返回http状态码</span><br> <span class="hljs-keyword">public</span> ResultData<String> <span class="hljs-title function_">exception</span><span class="hljs-params">(Exception e)</span> {<br> <span class="hljs-comment">//自定义返回情况,返回数据</span><br> System.out.println(<span class="hljs-string">"----come in GlobalExceptionHandler"</span>);<br> log.error(<span class="hljs-string">"全局异常信息exception:{}"</span>, e.getMessage(), e);<br> <span class="hljs-keyword">return</span> ResultData.fail(ReturnCodeEnum.RC500.getCode(),e.getMessage());<br> }<br>}<br></code></pre></td></tr></table></figure><blockquote><p>@RestControllerAdvice 是一个组合注解,它是 @ControllerAdvice 和 @ResponseBody 注解的快捷方式。</p><p>在Spring框架中,@RestControllerAdvice 用于定义全局的异常处理类,以及全局的数据绑定和预处理逻辑。</p><p>@ControllerAdvice 注解用于标识一个类,该类会包含一个或多个如下类型的组件:</p><ul><li><p>@ExceptionHandler 方法,用于处理全局的异常。</p></li><li><p>@InitBinder 方法,用于设置 WebDataBinder,比如在数据绑定之前将字符串转换为日期。</p></li><li><p>@ModelAttribute 方法,用于添加全局的模型属性。</p><p>@ResponseBody 注解则表示该类的所有方法返回值都会直接作为响应体返回给客户端,而不是被视图解析器处理。<br>因此,@RestControllerAdvice 结合了这两个注解的功能,允许你在一个地方集中处理整个应用程序的异常情况,并且能够直接返回异常信息给客户端,常用于RESTful API开发中。通过这种方式,你不需要在每个 @RestController 中重复编写异常处理逻辑,提高了代码的可维护性和复用性。</p></li></ul></blockquote><h2 id="什么是RestTemplate"><a href="#什么是RestTemplate" class="headerlink" title="什么是RestTemplate"></a>什么是RestTemplate</h2><p>RestTemplate 是 Spring 框架的一部分,它<strong>是 Spring 提供的一个同步的客户端 HTTP 访问模板工具</strong>,用于发起 HTTP 请求和接收响应。它不依赖于 Servlet API,因此不需要在 Web 容器或 Servlet 环境中运行。</p><p><strong>RestTemplate 是一个独立的类,可以在任何 Java 应用程序中使用</strong>,包括命令行应用程序、桌面应用程序、微服务或者其他任何不需要 Servlet API 的环境<strong>。它内部使用 Java 的 HTTP 客户端库来实现 HTTP 通信</strong>,比如 HttpURLConnection(JDK 自带的 HTTP 客户端)或者 Apache HttpComponents(Apache 提供的 HTTP 客户端库)。</p><p>相比之下,Spring MVC 中的 @RestController 和 @RequestMapping 等注解是建立在 Servlet API 之上的,它们通常用于构建 Web 服务端应用程序,需要在支持 Servlet 的 Web 容器(如 Apache Tomcat、Jetty 等)中运行。</p><p>总结来说,RestTemplate 是一个客户端工具,用于发起 HTTP 请求,它不依赖于 Servlet API,可以在任何 Java 应用程序中使用。而 Servlet 相关的技术通常用于构建 Web 服务端应用程序。</p><blockquote><p><strong>RestTemplate的post和get请求 都可以直接接收返回值。</strong></p><p><strong>但是put与delete方法为void,无法接收返回值,无法满足业务需求</strong></p><p><strong>若想接收返回值 使用restTemplate.exchange方法</strong></p></blockquote><h2 id="AMD和ARM"><a href="#AMD和ARM" class="headerlink" title="AMD和ARM"></a>AMD和ARM</h2><p>Windows AMD 和 ARM64 是两种不同的处理器架构,它们在多个方面有所区别:<br>处理器架构:<br>AMD:通常指的是基于 x86 或 x86-64(也称为 AMD64)架构的处理器。这是一种由 Intel 和 AMD 共同开发的复杂指令集计算(CISC)架构,它历史悠久,广泛用于台式机、笔记本电脑和服务器中。<br>ARM64:也称为 ARMv8-A,是一种基于 ARM 架构的处理器,使用的是精简指令集计算(RISC)。ARM 架构主要用于移动设备、嵌入式系统和一些特定的服务器。</p><blockquote><p>是的,Intel 80386(通常简称为386)是32位微处理器。</p></blockquote>]]></content>
<categories>
<category>SSM</category>
</categories>
<tags>
<tag>Spring</tag>
</tags>
</entry>
</search>