-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathspring.md
More file actions
3951 lines (2788 loc) · 205 KB
/
spring.md
File metadata and controls
3951 lines (2788 loc) · 205 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
---
title: Spring面试题,41道Spring八股文(3.3万字180张手绘图),面渣逆袭必看👍
shortTitle: 面渣逆袭-Spring
description: 下载次数超 1 万次,3.3 万字 180 张手绘图,详解 41 道 Spring 面试高频题(让天下没有难背的八股),面渣背会这些 Spring 八股文,这次吊打面试官,我觉得稳了(手动 dog)。
author: 三分恶&沉默王二
date: 2026-03-29
category:
- 面渣逆袭
tag:
- 面渣逆袭
- Spring
- 面试题
head:
- - meta
- name: keywords
content: Spring面试题,Spring,面试题,八股文,java,spring全家桶,spring八股
---

## 前言
3.3 万字 180 张手绘图,详解 41 道 Spring 面试高频题(让天下没有难背的八股),面渣背会这些 Spring 八股文,这次吊打面试官,我觉得稳了(手动 dog)。整理:沉默王二,戳[转载链接](https://mp.weixin.qq.com/s/EQge6DmgIqYITM3mAxkatg),作者:三分恶,戳[原文链接](https://mp.weixin.qq.com/s/Y17S85ntHm_MLTZMJdtjQQ)。
亮白版本更适合拿出来打印,这也是很多学生党喜欢的方式,打印出来背诵的效率会更高。

2025 年 06 月 15 日开始着手第二版更新,历经两个月,主要是中间有段时间把精力放到了[派聪明 RAG 这个项目](https://javabetter.cn/zhishixingqiu/paismart.html)的教程撰写上,第二版,我们升级了很多内容。
- 对于高频题,会标注在《[Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)》中出现的位置,哪家公司,原题是什么,并且会加🌟,目录一目了然;如果你想节省时间的话,可以优先背诵这些题目,尽快做到知彼知己,百战不殆。
- 区分八股精华回答版本和原理底层解释,让大家知其然知其所以然,同时又能做到面试时的高效回答。
- 结合项目([技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)、[pmhub](https://javabetter.cn/zhishixingqiu/pmhub.html))来组织语言,让面试官最大程度感受到你的诚意,而不是机械化的背诵。
- 修复第一版中出现的问题,包括球友们的私信反馈,网站留言区的评论,以及 [GitHub 仓库](https://github.com/itwanger/toBeBetterJavaer/issues)中的 issue,让这份面试指南更加完善。
- 增加[二哥编程星球](https://javabetter.cn/zhishixingqiu/)的球友们拿到的一些 offer,对面渣逆袭的感谢,以及对简历修改的一些认可,以此来激励大家,给大家更多信心。
- 优化排版,增加手绘图,重新组织答案,使其更加口语化,从而更贴近面试官的预期。

由于 PDF 没办法自我更新,所以需要最新版的小伙伴,可以微信搜【**沉默王二**】,或者扫描/长按识别下面的二维码,关注二哥的公众号,回复【**222**】即可拉取最新版本。
<div style="text-align: center; margin: 20px 0;">
<img src="https://cdn.paicoding.com/tobebetterjavaer/images/gongzhonghao.png" alt="微信扫码或者长按识别,或者微信搜索“沉默王二”" style="max-width: 100%; height: auto; border-radius: 10px;" />
</div>
当然了,请允许我的一点点私心,那就是星球的 PDF 版本会比公众号早一个月时间,毕竟星球用户都付费过了,我有必要让他们先享受到一点点福利。相信大家也都能理解,毕竟在线版是免费的,CDN、服务器、域名、OSS 等等都是需要成本的。
更别说我付出的时间和精力了,大家觉得有帮助还请给个口碑,让你身边的同事、同学都能受益到。

我把二哥的 Java 进阶之路、JVM 进阶之路、并发编程进阶之路,以及所有面渣逆袭的版本都放进来了,涵盖 Java基础、Java集合、Java并发、JVM、Spring、MyBatis、计算机网络、操作系统、MySQL、Redis、RocketMQ、分布式、微服务、设计模式、Linux 等 16 个大的主题,共有 40 多万字,2000+张手绘图,可以说是诚意满满。
展示一下暗黑版本的 PDF 吧,排版清晰,字体优雅,更加适合夜服,晚上看会更舒服一点。

## 基础
### 1.Spring是什么?
Spring 是一个 Java 后端开发框架,其最核心的作用是帮我们管理 Java 对象。

其最重要的特性就是 IoC,也就是控制反转。以前我们要使用一个对象时,都要自己先 new 出来。但有了 Spring 之后,我们只需要告诉 Spring 我们需要什么对象,它就会自动帮我们创建好并注入到 Spring 容器当中。
比如我在一个 Service 类里需要用到 Dao 对象,只需要加个 `@Autowired` 注解,Spring 就会自动把 Dao 对象注入到 Spring 容器当中,这样就不需要我们手动去管理这些对象之间的依赖关系了。

另外,Spring 还提供了 AOP,也就是面向切面编程,在我们需要做一些通用功能的时候特别有用,比如说日志记录、权限校验、事务管理这些,我们不用在每个方法里都写重复的代码,直接用 AOP 就能统一处理。

Spring 的生态也特别丰富,像 Spring Boot 能让我们快速搭建项目,Spring MVC 能帮我们处理 web 请求,Spring Data 能帮我们简化数据库操作,Spring Cloud 能帮我们做微服务架构等等。
#### Spring有哪些特性?
Spring 的特性还是挺多的,我按照在实际工作/学习中用得最多的几个来说吧。

首先最核心的就是 IoC 控制反转和 DI 依赖注入,让 Spring 有能力帮我们管理对象的创建和依赖关系。
比如我写一个 UserService,需要用到 UserDao,以前得自己 new 一个 UserDao 出来,现在只要在 UserService 上加个 `@Service` 注解,在 UserDao 字段上加个 `@Autowired`,Spring 就会自动帮我们处理好这些依赖关系。
这样代码的耦合度就大大降低了,测试的时候也更容易 mock。
第二个就是 AOP 面向切面编程。这个在我们处理一些横切关注点的时候特别有用,比如说我们要给某些 Controller 方法都加上权限控制,如果没有 AOP 的话,每个方法都要写一遍加权代码,维护起来很麻烦。

用了 AOP 之后,我们只需要写一个切面类,定义好切点和通知,就能统一处理了。事务管理也是同样的道理,加个 `@Transactional` 注解就搞定了。
还有就是 Spring 对各种企业级功能的集成支持也特别好。比如数据库访问,不管我们用 JDBC、MyBatis-Plus 还是 Hibernate,Spring 都能很好地集成。消息队列、缓存、安全认证这些, Spring 都有对应的模块来支持。

另外 Spring 的配置也很灵活,既支持 XML 配置,也支持注解配置,现在我们基本都用注解了,写起来更简洁。Spring Boot 出来之后就更方便了,约定大于配置,很多东西都是开箱即用的。
#### 简单说一下什么是AOP和IoC?
AOP 面向切面编程,简单点说就是把一些通用的功能从业务代码里抽取出来,统一处理。比如说[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)中的 `@MdcDot` 注解的作用是配合 AOP 在日志中加入 MDC 信息,方便进行日志追踪。

IoC 控制反转是一种设计思想,它的主要作用是将对象的创建和对象之间的调用过程交给 Spring 容器来管理。比如说在[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)项目当中,`@PostConstruct` 注解表明这个方法由 Spring 容器在 Bean 初始化完成后自动调用,我们不需要手动调用 init 方法。

#### Spring源码看过吗?
看过一些,主要是带着问题去看的,比如遇到一些技术难点或者想深入理解某个功能的时候。
我重点看过的是 IoC 容器的初始化过程,特别是 ApplicationContext 的启动流程。从 `refresh()` 方法开始,包括 Bean 的定义和加载、Bean 工厂的准备、Bean 的实例化和初始化这些关键步骤。

看源码的时候发现 Spring 用了很多设计模式,比如工厂模式、单例模式、模板方法模式等等,这对我平时写代码也很有启发。
还有就是 Spring 的 Bean 生命周期,从 BeanDefinition 的创建到 Bean 的实例化、属性注入、初始化回调,再到最后的销毁,整个过程还是挺复杂的。看了源码之后对 `@PostConstruct`、`@PreDestroy` 这些注解的执行时机就更清楚了。
不过说实话,Spring 的源码确实比较难啃,涉及的概念和技术点太多了。我一般是结合一些技术博客和 Claude 一起看,这样理解起来会相对容易一些。
PS:关于这份小册的 PDF 版本,目前只有[星球](https://javabetter.cn/zhishixingqiu/)的用户可以获取,后续会考虑开放给大家。

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 5 Java 后端技术一面面试原题:IOC与AOP
memo:2025 年 6 月 15 日修改至此,今天在帮[球友们修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,碰到一个中山大学本硕的球友,校园荣誉基本上拉满了,非常优秀,那我也希望能够帮助到更多的球友们,帮他们拿到更好的 offer。

### 2.Spring有哪些模块呢?
我按照平时工作/学习中接触的频率来说一下。
首先是 Spring Core 模块,这是整个 Spring 框架的基础,包含了 IoC 容器和依赖注入等核心功能。还有 Spring Beans 模块,负责 Bean 的配置和管理。这两个模块基本上是其他所有模块的基础,不管用 Spring 的哪个功能都会用到。

然后是 Spring Context 上下文模块,它在 Core 的基础上提供了更多企业级的功能,比如国际化、事件传播、资源加载这些。ApplicationContext 就是在这个模块里面的。
```java
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Spring Boot会自动创建ApplicationContext
ApplicationContext context = SpringApplication.run(Application.class, args);
}
}
```
Spring AOP 模块提供了面向切面编程的支持,我们用的 `@Transactional`、自定义切面这些都是基于这个模块。
Web 开发方面,Spring Web 模块提供了基础的 Web 功能,Spring WebMVC 就是我们常用的 MVC 框架,用来处理 HTTP 请求和响应。现在还有 Spring WebFlux,支持响应式编程。
比如说[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)项目中,`GlobalExceptionHandler` 类就使用了 `@RestControllerAdvice` 来实现统一的异常处理。
```java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = ForumAdviceException.class)
public ResVo<String> handleForumAdviceException(ForumAdviceException e) {
return ResVo.fail(e.getStatus());
}
}
```
数据访问方面,Spring JDBC 简化了 JDBC 的使用,在[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)项目中,我们就 `JdbcTemplate` 来检查表是否存在、执行数据库初始化脚本。

Spring ORM 提供了对 MyBatis-Plus 等 ORM 框架的集成支持。在[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)项目中,我们就用了 `@TableName`、`@TableField` 等注解进行对象关系映射,通过继承 BaseMapper 来获取通用的 CRUD 能力。

Spring Test 模块提供了测试支持,可以很方便地进行单元测试和集成测试。我们写测试用例的时候经常用 `@SpringBootTest` 这些注解。比如说在[技术派](https://javabetter.cn/zhishixingqiu/paicoding.html)项目中,我们就用 `@SpringBootTest` 注解来加载 Spring 上下文,进行集成测试。
```java
@Slf4j
@SpringBootTest(classes = QuickForumApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class BasicTest {
}
```
还有一些其他的模块,比如 Spring Security 负责安全认证,Spring Batch 处理批处理任务等等。
现在我们基本都是用 Spring Boot 来开发,它把这些模块都整合好了,用起来更方便。

memo:2025 年 6 月 16 日修改至此,今天在帮[球友们修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,碰到一个大连海事大学硕河南理工大学本的球友,他荣誉奖项里提到的优秀研究生、奖学金、英语四六级,我希望看到的同学也都能争取一下,不要把这些荣誉拱手让人,或者压根就不知道,或者不屑于去参加,到时候你简历上这一栏就会比较苍白。

### 3.Spring有哪些常用注解呢?
Spring 的注解挺多的,我按照不同的功能分类来说一下平时用得最多的那些。
首先是 Bean 管理相关的注解。`@Component` 是最基础的,用来标识一个类是 Spring 组件。像 `@Service`、`@Repository`、`@Controller` 这些都是 `@Component` 的特化版本,分别用在服务层、数据访问层和控制器层。

依赖注入方面,`@Autowired` 是用得最多的,可以标注在字段、setter 方法或者构造方法上。`@Qualifier` 在有多个同类型 Bean 的时候用来指定具体注入哪一个。`@Resource` 和 `@Autowired` 功能差不多,不过它是按名称注入的。

配置相关的注解也很常用。`@Configuration` 标识配置类,`@Bean` 用来定义 Bean,`@Value` 用来注入配置文件中的属性值。我们项目里的数据库连接信息、Redis 配置这些都是用 `@Value` 来注入的。`@PropertySource` 用来指定配置文件的位置。

Web 开发的注解就更多了。`@RestController` 相当于 `@Controller` 加 `@ResponseBody`,用来做 RESTful 接口。

`@RequestMapping` 及其变体`@GetMapping`、`@PostMapping`、`@PutMapping`、`@DeleteMapping` 用来映射 HTTP 请求。`@PathVariable` 获取路径参数,`@RequestParam` 获取请求参数,`@RequestBody` 接收 JSON 数据。
AOP 相关的注解,`@Aspect` 定义切面,`@Pointcut` 定义切点,`@Before`、`@After`、`@Around` 这些定义通知类型。

不过我们用得最多的还是`@Transactional`,基本上 Service 层需要保证事务原子性的方法都会加上这个注解。
生命周期相关的,`@PostConstruct` 在 Bean 初始化后执行,`@PreDestroy` 在 Bean 销毁前执行。测试的时候 `@SpringBootTest` 也经常用到。

还有一些 Spring Boot 特有的注解,比如 `@SpringBootApplication` 这个启动类注解,`@ConditionalOnProperty` 做条件装配,`@EnableAutoConfiguration` 开启自动配置等等。

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的微众银行同学 1 Java 后端一面的原题:说说 Spring 常见的注解?
memo:2025 年 6 月 17 日修改至此,今天在帮[球友们修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,碰到一个学院本的球友,他的荣誉奖项还是 OK的,态度也非常好,之前有学院本球友拿到滴滴 SP offer 的,希望这位球友也能够成为星球里新的榜样。

### 4.🌟Spring用了哪些设计模式?
Spring 框架里面确实用了很多设计模式,我从平时工作中能观察到的几个来说说。
首先是工厂模式,这个在 Spring 里用得非常多。BeanFactory 就是一个典型的工厂,它负责创建和管理所有的 Bean 对象。我们平时用的 ApplicationContext 其实也是 BeanFactory 的一个实现。当我们通过 `@Autowired` 获取一个 Bean 的时候,底层就是通过工厂模式来创建和获取对象的。

单例模式也是 Spring 的默认行为。默认情况下,Spring 容器中的 Bean 都是单例的,整个应用中只会有一个实例。这样可以节省内存,提高性能。当然我们也可以通过 `@Scope` 注解来改变 Bean 的作用域,比如设置为 prototype 就是每次获取都创建新实例。

代理模式在 AOP 中用得特别多。Spring AOP 的底层实现就是基于动态代理的,对于实现了接口的类用 JDK 动态代理,没有实现接口的类用 CGLIB 代理。比如我们用 `@Transactional` 注解的时候,Spring 会为我们的类创建一个代理对象,在方法执行前后添加事务处理逻辑。
模板方法模式在 Spring 里也很常见,比如 JdbcTemplate。它定义了数据库操作的基本流程:获取连接、执行 SQL、处理结果、关闭连接,但是具体的 SQL 语句和结果处理逻辑由我们来实现。

观察者模式在 Spring 的事件机制中有所体现。我们可以通过 ApplicationEvent 和 ApplicationListener 来实现事件的发布和监听。比如用户注册成功后,我们可以发布一个用户注册事件,然后有多个监听器来处理后续的业务逻辑,比如发送邮件、记录日志等。

这些设计模式的应用让 Spring 框架既灵活又强大,也让我在实际的开发中学到很多经典的设计思想。
#### Spring如何实现单例模式?
传统的单例模式是在类的内部控制只能创建一个实例,比如用 private 构造方法加 `static getInstance()` 这种方式。但是 Spring 的单例是容器级别的,同一个 Bean 在整个 Spring 容器中只会有一个实例。
具体的实现机制是这样的:Spring 在启动的时候会把所有的 Bean 定义信息加载进来,然后在 DefaultSingletonBeanRegistry 这个类里面维护了一个叫 singletonObjects 的 ConcurrentHashMap,这个 Map 就是用来存储单例 Bean 的。key 是 Bean 的名称,value 就是 Bean 的实例对象。

当我们第一次获取某个 Bean 的时候,Spring 会先检查 singletonObjects 这个 Map 里面有没有这个 Bean,如果没有就会创建一个新的实例,然后放到 Map 里面。后面再获取同一个 Bean 的时候,直接从 Map 里面取就行了,这样就保证了单例。

还有一个细节就是 Spring 为了解决循环依赖的问题,还用了三级缓存。除了 singletonObjects 这个一级缓存,还有 earlySingletonObjects 二级缓存和 singletonFactories 三级缓存。这样即使有循环依赖,Spring 也能正确处理。
而且 Spring 的单例是线程安全的,因为用的是 ConcurrentHashMap,多线程访问不会有问题。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的携程面经同学 10 Java 暑期实习一面面试原题:Spring IoC 的设计模式,AOP 的设计模式
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小公司面经合集同学 1 Java 后端面试原题:Spring 框架使用到的设计模式?
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的同学 1 贝壳找房后端技术一面面试原题:Spring用了什么设计模式?
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手同学 4 一面原题:Spring中使用了哪些设计模式,以其中一种模式举例说明?Spring如何实现单例模式?
memo:2025 年 6 月 20 日修改至此,今天[帮球友修改简历](https://javabetter.cn/zhishixingqiu/jianli.html)的时候,有碰到重庆邮电大学本,电子科技大学硕的球友,期间还有过清华大学科研项目的经历,基本上也是把学历这块拉的满中满了,那希望星球能帮助到更多院校的同学,不管是工作党还是学生党,都希望大家都拿到更好的 offer。

### 5.Spring容器和Web容器之间的区别知道吗?(补充)
>2024 年 7 月 11 日增补
首先从概念上来说,Spring 容器是一个 IoC 容器,主要负责管理 Java 对象的生命周期和依赖关系。而 Web 容器,比如 Tomcat、Jetty 这些,是用来运行 Web 应用的容器,负责处理 HTTP 请求和响应,管理 Servlet 的生命周期。
```java
/**
* SpringUtil.java
* 用于获取 Spring 容器中的 Bean,技术派源码:https://github.com/itwanger/paicoding
*/
@Component
public class SpringUtil implements ApplicationContextAware {
private volatile static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringUtil.context = applicationContext;
}
public static <T> T getBean(Class<T> bean) {
return context.getBean(bean);
}
}
```
从功能上看,Spring 容器专注于业务逻辑层面的对象管理,比如我们的 Service、Dao、Controller 这些 Bean 都是由 Spring 容器来创建和管理的。而 Web 容器主要处理网络通信,比如接收 HTTP 请求、解析请求参数、调用相应的 Servlet,然后把响应返回给客户端。

在实际项目中,这两个容器是相辅相成的。我们的 Web 项目部署在 Tomcat 上的时候,Tomcat 会负责接收 HTTP 请求,然后把请求交给 DispatcherServlet 处理,而 DispatcherServlet 又会去 Spring 容器中查找相应的 Controller 来处理业务逻辑。
```java
/**
* GlobalViewInterceptor.java
* 用于全局拦截器,技术派源码:https://github.com/itwanger/paicoding
*/
@Component
public class GlobalViewInterceptor implements HandlerInterceptor {
@Autowired
private GlobalInitService globalInitService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
// Web 容器的 HTTP 请求 + Spring 容器的业务服务
}
}
```
还有一个重要的区别是生命周期。Web 容器的生命周期跟 Web 应用程序的部署和卸载相关,而 Spring 容器的生命周期是在 Web 应用启动的时候初始化,应用关闭的时候销毁。
现在我们都用 Spring Boot 了,Spring Boot 内置了 Tomcat,把 Web 容器和 Spring 容器都整合在一起了,我们只需要运行一个 jar 包就可以了。
```java
@SpringBootApplication
public class QuickForumApplication {
public static void main(String[] args) {
SpringApplication.run(QuickForumApplication.class, args);
}
}
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的去哪儿同学 1 技术二面原题:spring的容器、web容器、springmvc的容器之间的区别
memo:2025 年 6 月 27 日修改至此,今天看到[有球友发的 offer 选择提问贴](https://javabetter.cn/zhishixingqiu/),其中一个是杭州六小龙群核科技,我个人认为还是非常值得去的,毕竟是杭州的独角兽公司,薪资待遇都不错。

### 6.你是怎么理解Bean的?
在我看来,Bean 本质上就是由 Spring 容器管理的 Java 对象,但它和普通的 Java 对象有很大区别。普通的 Java 对象我们是通过 new 关键字创建的。而 Bean 是交给 Spring 容器来管理的,从创建到销毁都由容器负责。

从实际使用的角度来说,我们项目里的 Service、Dao、Controller 这些都是 Bean。比如 UserService 被标注了 `@Service` 注解,它就成了一个 Bean,Spring 会自动创建它的实例,管理它的依赖关系,当其他地方需要用到 UserService 的时候,Spring 就会把这个实例注入进去。

这种依赖注入的方式让对象之间的关系变得松耦合。
Spring 提供了多种 Bean 的配置方式,基于注解的方式是最常用的。

基于 XML 配置的方式在 Spring Boot 项目中已经不怎么用了。Java 配置类的方式则可以用来解决一些比较复杂的场景,比如说主从数据源,我们可以用 `@Primary` 注解标注主数据源,用 `@Qualifier` 来指定备用数据源。
```java
@Configuration
public class AppConfig {
@Bean
@Primary // 主要候选者
public DataSource primaryDataSource() {
return new HikariDataSource();
}
@Bean
@Qualifier("secondary")
public DataSource secondaryDataSource() {
return new BasicDataSource();
}
}
```
那在使用的时候,当我们直接用 `@Autowired` 注解注入 DataSource 时,Spring 默认会使用 HikariDataSource;当加上 `@Qualifier("secondary")` 注解时,Spring 则会注入 BasicDataSource。
```java
@Autowired
private DataSource dataSource; // 会注入 primaryDataSource(因为有 @Primary)
@Autowired
@Qualifier("secondary")
private DataSource secondaryDataSource;
```
#### @Component 和 @Bean 有什么区别?
首先从使用上来说,`@Component` 是标注在类上的,而 `@Bean` 是标注在方法上的。`@Component` 告诉 Spring 这个类是一个组件,请把它注册为 Bean,而 `@Bean` 则告诉 Spring 请将这个方法返回的对象注册为 Bean。
```java
@Component // Spring自动创建UserService实例
public class UserService {
@Autowired
private UserDao userDao;
}
@Configuration
public class AppConfig {
@Bean // 我们手动创建DataSource实例
public DataSource dataSource() {
HikariDataSource ds = new HikariDataSource();
ds.setJdbcUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("123456");
return ds; // 返回给Spring管理
}
}
```
从控制权的角度来说,`@Component` 是由 Spring 自动创建和管理的。

而 `@Bean` 则是由我们手动创建的,然后再交给 Spring 管理,我们对对象的创建过程有完全的控制权。

> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 9 面试原题:怎么理解spring的bean,@Component 和 @Bean 的区别
memo:2025 年 6 月 28 日修改至此,今天在帮球友[修改简历](https://javabetter.cn/zhishixingqiu/)的时候,又碰到一个杭电本硕的球友。我这里想说的一点是,杭电的计算机专业非常强,虽然他只是一所双非,如果能把项目经历、专业技能好好写的话,拿个大厂的顶级 offer 是完全没问题的。

### 7.🌟能说一下Bean的生命周期吗?
推荐阅读:[三分恶:Spring Bean 生命周期,好像人的一生](https://mp.weixin.qq.com/s/zb6eA3Se0gQoqL8PylCPLw)
好的。
Bean 的生命周期可以分为 5 个主要阶段,我按照实际的执行顺序来说一下。

第一个阶段是实例化。Spring 容器会根据 BeanDefinition,通过反射调用 Bean 的构造方法创建对象实例。如果有多个构造方法,Spring 会根据依赖注入的规则选择合适的构造方法。

第二阶段是属性赋值。这个阶段 Spring 会给 Bean 的属性赋值,包括通过 `@Autowired`、`@Resource` 这些注解注入的依赖对象,以及通过 `@Value` 注入的配置值。

第三阶段是初始化。这个阶段会依次执行:
- `@PostConstruct` 标注的方法
- InitializingBean 接口的 afterPropertiesSet 方法
- 通过 `@Bean` 的 initMethod 指定的初始化方法

我在项目中经常用 `@PostConstruct` 来做一些初始化工作,比如缓存预加载、DB 配置等等。
```java
// CategoryServiceImpl中的缓存初始化
@PostConstruct
public void init() {
categoryCaches = CacheBuilder.newBuilder().maximumSize(300).build(new CacheLoader<Long, CategoryDTO>() {
@Override
public CategoryDTO load(@NotNull Long categoryId) throws Exception {
CategoryDO category = categoryDao.getById(categoryId);
// ...
}
});
}
// DynamicConfigContainer中的配置初始化
@PostConstruct
public void init() {
cache = Maps.newHashMap();
bindBeansFromLocalCache("dbConfig", cache);
}
```
初始化后,Spring 还会调用所有注册的 BeanPostProcessor 后置处理方法。这个阶段经常用来创建代理对象,比如 AOP 代理。
第五阶段是使用 Bean。比如我们的 Controller 调用 Service,Service 调用 DAO。
```java
// UserController中的使用示例
@Autowired
private UserService userService;
@GetMapping("/users/{id}")
public UserDTO getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
// UserService中的使用示例
@Autowired
private UserDao userDao;
public UserDTO getUserById(Long id) {
return userDao.getById(id);
}
// UserDao中的使用示例
@Autowired
private JdbcTemplate jdbcTemplate;
public UserDTO getById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new Object[]{id}, new UserRowMapper());
}
```
最后是销毁阶段。当容器关闭或者 Bean 被移除的时候,会依次执行:
- `@PreDestroy` 标注的方法
- DisposableBean 接口的 destroy 方法
- 通过 `@Bean` 的 destroyMethod 指定的销毁方法

#### Aware 类型的接口有什么作用?
Aware 接口在 Spring 中是一个很有意思的设计,它们的作用是让 Bean 能够感知到 Spring 容器的一些内部组件。
从设计理念来说,Aware 接口实现了一种“回调”机制。正常情况下,Bean 不应该直接依赖 Spring 容器,这样可以保持代码的独立性。但有些时候,Bean 确实需要获取容器的一些信息或者组件,Aware 接口就提供了这样一个能力。
我最常用的 Aware 接口是 ApplicationContextAware,它可以让 Bean 获取到 ApplicationContext 容器本身。

在[技术派项目](https://javabetter.cn/zhishixingqiu/paicoding.html)中,我就通过实现 ApplicationContextAware 和 EnvironmentAware 接口封装了一个 SpringUtil 工具类,通过 getBean 和 getProperty 方法来获取 Bean 和配置属性。
```java
// 静态方法获取Bean,方便在非Spring管理的类中使用
public static <T> T getBean(Class<T> clazz) {
return context.getBean(clazz);
}
// 获取配置属性
public static String getProperty(String key) {
return environment.getProperty(key);
}
```
#### 如果配置了 init-method 和 destroy-method,Spring 会在什么时候调用其配置的方法?
init-method 指定的初始化方法会在 Bean 的初始化阶段被调用,具体的执行顺序是:
- 先执行 `@PostConstruct` 标注的方法
- 然后执行 InitializingBean 接口的 `afterPropertiesSet()` 方法
- 最后再执行 init-method 指定的方法
也就是说,init-method 是在所有其他初始化方法之后执行的。
```java
@Component
public class MyService {
@Autowired
private UserDao userDao;
@PostConstruct
public void postConstruct() {
System.out.println("1. @PostConstruct执行");
}
public void customInit() { // 通过@Bean的initMethod指定
System.out.println("3. init-method执行");
}
}
@Configuration
public class AppConfig {
@Bean(initMethod = "customInit")
public MyService myService() {
return new MyService();
}
}
```
destroy-method 会在 Bean 销毁阶段被调用。
```java
@Component
public class MyService {
@PreDestroy
public void preDestroy() {
System.out.println("1. @PreDestroy执行");
}
public void customDestroy() { // 通过@Bean的destroyMethod指定
System.out.println("3. destroy-method执行");
}
}
```
不过在实际开发中,通常用 `@PostConstruct` 和 `@PreDestroy` 就够了,它们更简洁。
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的小米 25 届日常实习一面原题:说说 Bean 的生命周期
> 2. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的百度面经同学 1 文心一言 25 实习 Java 后端面试原题:Spring中bean生命周期
> 3. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的8 后端开发秋招一面面试原题:讲一下Spring Bean的生命周期
> 4. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的同学 1 贝壳找房后端技术一面面试原题:bean生命周期
> 5. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的快手同学 4 一面原题:介绍下Bean的生命周期?Aware类型接口的作用?如果配置了init-method和destroy-method,Spring会在什么时候调用其配置的方法?
memo:2025 年 6 月 30 日修改至此。昨天有[读者发消息说有三个 offer 要选择](https://javabetter.cn/zhishixingqiu/),中科大读博、中海油、商飞北研,问我该怎么选择?说实话,这三个都是非常优质的选择,我个人的建议是优先考虑中科大读博,毕竟是国内顶尖学府,博士毕业后可以选择在高校任教,会更符合他的家庭条件,当然了,我深知,读博的产出压力非常大。

### 8.Bean的作用域有哪些?
Bean 的作用域决定了 Bean 实例的生命周期和创建策略,singleton 是默认的作用域。整个 Spring 容器中只会有一个 Bean 实例。不管在多少个地方注入这个 Bean,拿到的都是同一个对象。
```java
@Component // 默认就是singleton
public class UserService {
// 整个应用中只有一个UserService实例
}
```
生命周期和 Spring 容器相同,容器启动时创建,容器销毁时销毁。
实际开发中,像 Service、Dao 这些业务组件基本都是单例的,因为单例既能节省内存,又能提高性能。
当把 scope 设置为 prototype 时,每次从容器中获取 Bean 的时候都会创建一个新的实例。
```java
@Component
@Scope("prototype")
public class OrderProcessor {
// 每次注入或获取都是新的实例
}
```
当需要处理一些有状态的 Bean 时会用到 prototype,比如每个订单处理器需要维护不同的状态信息。
需要注意的是,在 singleton Bean 中注入 prototype Bean 时要小心,因为 singleton Bean 只创建一次,所以 prototype Bean 也只会注入一次。这时候可以用 `@Lookup` 注解或者 ApplicationContext 来动态获取。
```java
@Component
public class SingletonService {
// 错误的做法,prototypeBean只会注入一次
@Autowired
private PrototypeBean prototypeBean;
// 正确的做法,每次调用都获取新实例
@Lookup
public PrototypeBean getPrototypeBean() {
return null; // Spring会重写这个方法
}
}
```
除了 singleton 和 prototype,Spring 还支持其他作用域,比如 request、session、application 和 websocket。

如果作用于是 request,表示在 Web 应用中,每个 HTTP 请求都会创建一个新的 Bean 实例,请求结束后 Bean 就被销毁。
```java
@Component
@Scope("request")
public class RequestContext {
// 每个HTTP请求都有自己的实例
}
```
如果作用于是 session,表示在 Web 应用中,每个 HTTP 会话都会创建一个新的 Bean 实例,会话结束后 Bean 被销毁。
```java
@Component
@Scope("session")
public class UserSession {
// 每个用户会话都有自己的实例
}
```
典型的使用场景是购物车、用户登录状态这些需要在整个会话期间保持的信息。
application 作用域表示在整个应用中只有一个 Bean 实例,类似于 singleton,但它的生命周期与 ServletContext 绑定。
```java
@Component
@Scope("application")
public class AppConfig {
// 整个应用中只有一个实例
}
```
websocket 作用域表示在 WebSocket 会话中每个连接都有自己的 Bean 实例。WebSocket 连接建立时创建,连接关闭时销毁。
```java
@Component
@Scope("websocket")
public class WebSocketHandler {
// 每个WebSocket连接都有自己的实例
}
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的同学 1 贝壳找房后端技术一面面试原题:bean是单例还是多例的,具体怎么修改
memo:2025 年 7 月 3 日修改至此,今天在[帮球友修改简历](https://javabetter.cn/zhishixingqiu/)的时候,碰到一个郑州大学硕,河北师范大学本的球友,整体在校的经历非常出色,奖学金、论文期刊基本上都拉满了。那这么多优秀的球友选择来到这里,也是对星球的又一次认可和肯定,我也一定会继续努力,提供更多优质的内容和服务。

### 9.Spring中的单例Bean会存在线程安全问题吗?
首先要明确一点。Spring 容器本身保证了 Bean 创建过程的线程安全,也就是说不会出现多个线程同时创建同一个单例 Bean 的情况。但是 Bean 创建完成后的使用过程,Spring 就不管了。
换句话说,单例 Bean 在被创建后,如果它的内部状态是可变的,那么在多线程环境下就可能会出现线程安全问题。

比如说在[技术派项目](https://javabetter.cn/zhishixingqiu/paicoding.html)中,有一个敏感词过滤的 Bean,我们就需要使用 volatile 关键字来保证多线程环境下的可见性。
```java
@Service
public class SensitiveService {
private volatile SensitiveWordBs sensitiveWordBs; // 使用volatile保证可见性
@PostConstruct
public void refresh() {
// 重新初始化sensitiveWordBs
}
}
```
如果 Bean 中没有成员变量,或者成员变量都是不可变的,final 修饰的,那么就不存在线程安全问题。
```java
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Autowired
private CountService countService;
// 只有依赖注入的无状态字段
}
@Service
public class ConfigService {
private final String appName; // final修饰,不可变
public ConfigService(@Value("${app.name}") String appName) {
this.appName = appName;
}
}
```
#### 单例Bean的线程安全问题怎么解决呢?
第一种,使用局部变量,也就是使用无状态的单例 Bean,把所有状态都通过方法参数传递:
```java
@Service
public class UserService {
@Autowired
private UserDao userDao;
// 无状态方法,所有数据通过参数传递
public User processUser(Long userId, String operation) {
User user = userDao.findById(userId);
// 处理逻辑...
return user;
}
}
```
第二种,当确实需要维护线程相关的状态时,可以使用 [ThreadLocal](https://javabetter.cn/thread/ThreadLocal.html) 来保存状态。ThreadLocal 可以保证每个线程都有自己的变量副本,互不干扰。
```java
@Service
public class UserContextService {
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
public void setCurrentUser(User user) {
userThreadLocal.set(user);
}
public User getCurrentUser() {
return userThreadLocal.get();
}
public void clear() {
userThreadLocal.remove(); // 防止内存泄漏
}
}
```
第三种,如果需要缓存数据或者计数,使用 JUC 包下的线程安全类,比如说 [AtomicInteger](https://javabetter.cn/thread/atomic.html)、[ConcurrentHashMap](https://javabetter.cn/thread/ConcurrentHashMap.html)、[CopyOnWriteArrayList](https://javabetter.cn/thread/CopyOnWriteArrayList.html) 等。
```java
@Service
public class CacheService {
// 使用线程安全的集合
private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<>();
private final AtomicLong counter = new AtomicLong(0);
public void put(String key, Object value) {
cache.put(key, value);
counter.incrementAndGet();
}
}
```
第四种,对于复杂的状态操作,可以使用 synchronized 或 Lock:
```java
@Service
public class CacheService {
private final Map<String, Object> cache = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock();
public void put(String key, Object value) {
lock.lock();
try {
cache.put(key, value);
} finally {
lock.unlock();
}
}
}
```
第五种,如果 Bean 确实需要维护状态,可以考虑将其改为 prototype 作用域,这样每次注入都会创建一个新的实例,避免了多线程共享同一个实例的问题。
```java
@Service
@Scope("prototype") // 每次注入都创建新实例
public class StatefulService {
private String state; // 现在每个实例都有独立状态
public void setState(String state) {
this.state = state;
}
}
```
或者使用 request 作用域,这样每个 HTTP 请求都会创建一个新的实例。
```java
@Service
@Scope("request")
public class RequestScopedService {
private String requestData;
// 每个请求都有独立的实例
}
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的阿里面经同学 1 闲鱼后端一面的原题:spring的bean的并发安全问题
memo:2025 年 7 月 4 日修改至此,今天在[帮球友修改简历](https://javabetter.cn/zhishixingqiu/)的时候,碰到一个武汉理工大学本硕的球友。说真的,和武汉理工大学挺有缘的,2023 年去武汉,就线下见了一名武理的球友,[他当时签约的是小米](https://t.zsxq.com/LfG3B),非常优秀。

### 10.为什么IDEA不推荐使用@Autowired注解注入Bean?
前情提要:当使用 `@Autowired` 注解注入 Bean 时,IDEA 会提示“Field injection is not recommended”。

面试回答:
主要有几个原因。
第一个是字段注入不利于单元测试。字段注入需要使用反射或 Spring 容器才能注入依赖,测试更复杂;而构造方法注入可以直接通过构造方法传入 Mock 对象,测试起来更简单。
```java
// 字段注入的测试困难
@Test
public void testUserService() {
UserService userService = new UserService();
// 无法直接设置userRepository,需要反射或Spring容器
// userService.userRepository = Mockito.mock(UserRepository.class);
// 需要手动设置依赖,测试不方便
ReflectionTestUtils.setField(userService, "userRepository", Mockito.mock(UserRepository.class));
userService.doSomething();
// ...
}
// 构造方法注入的测试简单
@Test
public void testUserService() {
UserRepository mockRepository = Mockito.mock(UserRepository.class);
UserService userService = new UserService(mockRepository); // 直接注入
}
```
第二个是字段注入会隐藏循环依赖问题,而构造方法注入会在项目启动时就去检查依赖关系,能更早发现问题。
第三个是构造方法注入可以使用 final 字段确保依赖在对象创建时就被初始化,避免了后续修改的风险。
在[技术派项目](https://javabetter.cn/zhishixingqiu/paicoding.html)中,我们已经在使用构造方法注入的方式来管理依赖关系。

不过话说回来,`@Autowired` 的字段注入方式在一些简单的场景下还是可以用的,主要看团队的编码规范吧。
#### @Autowired 和 @Resource 注解的区别?
首先从来源上说,`@Autowired` 是 Spring 框架提供的注解,而 `@Resource` 是 Java EE 标准提供的注解。换句话说,`@Resource` 是 JDK 自带的,而 `@Autowired` 是 Spring 特有的。
虽然 IDEA 不推荐使用 `@Autowired`,但对 `@Resource` 注解却没有任何提示。

从注入方式上说,`@Autowired` 默认按照类型,也就是 byType 进行注入,而 `@Resource` 默认按照名称,也就是 byName 进行注入。
当容器中存在多个相同类型的 Bean, 比如说有两个 UserRepository 的实现类,直接用 `@Autowired` 注入 UserRepository 时就会报错,因为 Spring 容器不知道该注入哪个实现类。
```java
@Component
public class UserRepository21 implements UserRepository2 {}
@Component
public class UserRepository22 implements UserRepository2 {}
@Component
public class UserService2 {
@Autowired
private UserRepository2 userRepository; // 冲突
}
```
这时候,有两种解决方案,第一种是使用 `@Autowired` + `@Qualifier` 指定具体的 Bean 名称来解决冲突。
```java
@Component("userRepository21")
public class UserRepository21 implements UserRepository2 {
}
@Component("userRepository22")
public class UserRepository22 implements UserRepository2 {
}
@Autowired
@Qualifier("userRepository22")
private UserRepository2 userRepository22;
```
第二种是使用 `@Resource` 注解按名称进行注入。
```java
@Resource(name = "userRepository21")
private UserRepository2 userRepository21;
```
> 1. [Java 面试指南(付费)](https://javabetter.cn/zhishixingqiu/mianshi.html)收录的京东面经同学 9 面试原题:依赖注入的时候,直接Autowired比较直接,为什么推荐构造方法注入呢
memo:2025 年 7 月 1 日修改至此,今天在[帮球友修改简历](https://javabetter.cn/zhishixingqiu/)的时候,碰到一个郑州大学本硕的球友,这也是我们河南省最好的大学了,但也仅仅是一所 211,所以希望所有河南的同学都能加把劲,证明自己的实力,去拿到更好的 offer,为校争光。

### 11.@Autowired的实现原理了解吗?
`@Autowired` 是 Spring 实现依赖注入的核心注解,其实现原理基于反射机制和 BeanPostProcessor 接口。
整个过程分为两个主要阶段。第一个阶段是依赖收集阶段,发生在 Bean 实例化之后、属性赋值之前。`Autowired` 的 Processor 会扫描 Bean 的所有字段、方法和构造方法,找出标注了 `@Autowired` 注解的地方,然后把这些信息封装成 `Injection` 元数据对象缓存起来。这个过程用到了大量的反射操作,需要分析类的结构、注解信息等等。

第二个阶段是依赖注入阶段,Spring 会取出之前缓存的 `Injection` 元数据对象,然后逐个处理每个注入点。对于每个 `@Autowired` 标注的字段或方法,Spring 会根据类型去容器中查找匹配的 Bean。
```java
// 1. 按类型查找(byType)
Map<String, Object> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(
this.beanFactory, type);
// 2. 如果找到多个候选者,按名称筛选(byName)
String autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
// 3. 考虑@Primary和@Priority注解
// 4. 最后按照字段名或参数名匹配
```
在具体的注入过程中,Spring 会使用反射来设置字段的值或者调用 setter 方法。比如对于字段注入,会调用 `Field.set()` 方法;对于 setter 注入,会调用 `Method.invoke()` 方法。
### 12.什么是自动装配?
自动装配的本质就是让 Spring 容器自动帮我们完成 Bean 之间的依赖关系注入,而不需要我们手动去指定每个依赖。简单来说,就是“我们不用告诉 Spring 具体怎么注入,Spring 自己会想办法找到合适的 Bean 注入进来”。
自动装配的工作原理简单来说就是,Spring 容器在启动时自动扫描 `@ComponentScan` 指定包路径下的所有类,然后根据类上的注解,比如 `@Autowired`、`@Resource` 等,来判断哪些 Bean 需要被自动装配。
```java
@Configuration
@ComponentScan("com.github.paicoding.forum.service")
@MapperScan(basePackages = {
"com.github.paicoding.forum.service.article.repository.mapper",
"com.github.paicoding.forum.service.user.repository.mapper"
// ... 更多包路径
})
public class ServiceAutoConfig {