
这是一个关于 Spring 框架核心原理和常见面试问题的全面总结。无论是准备面试还是深入理解 Spring,这份指南都会对你大有裨益。
Part 1: Spring 核心原理深度解析
Spring 的核心可以归结为两大基石:IoC (控制反转) 和 AOP (面向切面编程)。几乎所有其他特性都是建立在它们之上的。
一、IoC (控制反转) / DI (依赖注入)
1. 核心思想:
-
控制反转 (IoC): 将对象的创建、组装、管理的控制权从应用程序代码反转到外部容器(Spring IoC Container)。简单说,以前是
new Object(),现在是由 Spring 容器给你对象。 -
依赖注入 (DI): 是 IoC 的一种实现方式。对象之间的依赖关系由容器在运行时注入(通过构造函数、Setter方法等),而不是由对象自己创建或查找。
2. 核心组件:
-
BeanDefinition: Spring 容器内部用于描述一个 Bean 的元数据对象,它定义了如何创建这个 Bean(类名、作用域、是否懒加载、依赖关系等)。
-
BeanFactory: IoC 容器的最底层接口,提供了基础的 DI 功能。(功能简单,一般用于资源受限的环境)
-
ApplicationContext:
BeanFactory的子接口,是更高级的容器,提供了更多企业级功能,如:-
国际化的消息访问(MessageSource)
-
资源访问(如URL和文件)
-
事件发布机制(ApplicationEventPublisher)
-
与 Web 应用的集成(WebApplicationContext)
-
它是我们最常用的容器。(例如:
ClassPathXmlApplicationContext,AnnotationConfigApplicationContext)
-
3. 容器工作流程 (Bean 的生命周期):
这是面试的超高频问题!一个 Bean 从无到有要经历很多步骤。
-
实例化 (Instantiate): 容器通过反射调用构造方法创建 Bean 的实例。
-
属性填充 (Populate properties): 容器注入 Bean 所依赖的其他 Bean(依赖注入)。
-
设置 BeanName/Aware: 如果 Bean 实现了
BeanNameAware等Aware接口,容器会回调相关方法,让 Bean 感知到自身在容器中的 ID 和资源。 -
BeanPostProcessor (前置处理): 调用所有
BeanPostProcessor的postProcessBeforeInitialization方法。 -
初始化 (Initialization):
-
如果 Bean 实现了
InitializingBean接口,执行afterPropertiesSet()方法。 -
如果配置了自定义的
init-method方法,执行该方法。
-
-
BeanPostProcessor (后置处理): 调用所有
BeanPostProcessor的postProcessAfterInitialization方法。AOP 代理就是在此阶段生成的! -
使用 (Ready): Bean 已经完全创建,存放在容器中,可以被应用程序使用。
-
销毁 (Destruction):
-
容器关闭时,如果 Bean 实现了
DisposableBean接口,执行destroy()方法。 -
如果配置了自定义的
destroy-method方法,执行该方法。
-
二、AOP (面向切面编程)
1. 核心思想:
将那些与核心业务逻辑无关但又遍布在代码各处的功能(称为横切关注点,如日志、事务、安全等)抽取出来,形成独立的切面(Aspect),然后在需要的地方织入(Weaving) 这些切面。从而实现关注点的分离,提高代码的复用性和可维护性。
2. 核心概念:
-
Aspect (切面): 横切关注点的模块化,它是一个类,包含 Advice 和 Pointcut。
-
Joinpoint (连接点): 程序执行过程中一个明确的点,如方法调用、异常抛出。在 Spring AOP 中,连接点总是代表方法的执行。
-
Advice (通知): 切面在特定连接点上执行的动作。主要类型有:
-
@Before: 方法执行前 -
@AfterReturning: 方法成功返回后 -
@AfterThrowing: 方法抛出异常后 -
@After(finally): 方法执行后(无论成功与否) -
@Around: 最强大的通知,环绕连接点,可以控制是否执行方法。
-
-
Pointcut (切点): 一个表达式,用于匹配哪些连接点需要被切入。
Advice和一个Pointcut表达式关联。 -
Weaving (织入): 将切面应用到目标对象并创建新的代理对象的过程。Spring AOP 在运行时通过动态代理完成织入。
3. 实现原理 (动态代理):
Spring AOP 默认使用动态代理技术。根据目标类是否实现接口,选择不同的代理策略:
-
JDK 动态代理: 如果目标类实现了接口,Spring 会使用 JDK 的
java.lang.reflect.Proxy来创建代理对象。 -
CGLIB 代理: 如果目标类没有实现任何接口,Spring 会使用 CGLIB 库生成目标类的子类作为代理对象。(可以通过配置强制使用 CGLIB)
Part 2: 常见 Spring 面试问题与解答思路
一、IoC/DI 相关
1. 什么是 Spring IoC 和 DI?它们有什么关系?
-
IoC 是一种设计思想,将控制权交给容器。
-
DI 是实现 IoC 的主要技术手段。
-
关系: DI 是实现 IoC 的方式,IoC 是 DI 的目标。
2. Spring 中 Bean 的作用域(Scope)有哪些?
-
singleton (默认): 每个 IoC 容器中只有一个 Bean 实例。
-
prototype: 每次请求都创建一个新的 Bean 实例。
-
request: 每次 HTTP 请求创建一个实例(仅 Web 环境)。
-
session: 每个 HTTP Session 创建一个实例(仅 Web 环境)。
-
application: 每个
ServletContext生命周期内一个实例(仅 Web 环境)。 -
websocket: 每个 WebSocket 会话内一个实例(仅 Web 环境)。
3. Spring 如何解决循环依赖?
循环依赖:A 依赖 B,B 也依赖 A。
Spring 通过三级缓存来解决单例模式下的Setter注入/字段注入的循环依赖。
-
一级缓存 (singletonObjects): 存放已经完全初始化好的单例 Bean。(成品)
-
二级缓存 (earlySingletonObjects): 存放提前暴露的、未完成初始化的 Bean 的引用(早期引用),用于解决循环依赖。(半成品)
-
三级缓存 (singletonFactories): 存放生成 Bean 早期引用的 ObjectFactory。(工厂)
解决过程(A 和 B 循环依赖):
-
创建 A,实例化 A,将 A 的 ObjectFactory 放入三级缓存。
-
为 A 注入属性时发现依赖 B,于是去创建 B。
-
创建 B,实例化 B,将 B 的 ObjectFactory 放入三级缓存。
-
为 B 注入属性时发现依赖 A,于是从三级缓存中找到 A 的 ObjectFactory,获取 A 的早期引用(并将此引用放入二级缓存,同时从三级缓存删除)。将早期引用 A 注入给 B。
-
B 完成属性填充、初始化,成为一个完整的 Bean,放入一级缓存。同时清理二、三级缓存。
-
A 从一级缓存中拿到完整的 B,注入成功,A 也完成后续步骤,放入一级缓存。
注意:构造函数循环依赖无法解决! 因为 Java 要求在构造对象时必须先成功构造其依赖,而对方也处于构造阶段,导致死锁。
二、AOP 相关
1. Spring AOP 和 AspectJ 有什么区别?
| 特性 | Spring AOP | AspectJ |
|---|---|---|
| 实现方式 | 基于动态代理(运行时织入) | 基于字节码操作(编译时/后织入) |
| 功能 | 仅支持方法级别的连接点 | 支持所有连接点(方法、构造器、字段等) |
| 性能 | 运行时稍有开销 | 编译时织入,运行时无代理,性能更高 |
| 依赖 | 轻量,仅需 Spring Core | 需要引入 AspectJ 编译器/织入器 |
| 适用场景 | 解决大多数企业应用的横切关注点(事务、日志等) | 功能更强大,适用于需要更复杂切面编程的场景 |
2. 介绍一下 Spring 的事务管理?
-
编程式事务: 在代码中手动使用
TransactionTemplate或PlatformTransactionManager管理事务。(灵活性高,但代码侵入性强) -
声明式事务(推荐): 通过
@Transactional注解或 XML 配置,由 Spring AOP 在背后自动管理事务。-
原理: 声明式事务基于 AOP 实现。
@Transactional注解的方法会被事务通知 (TransactionInterceptor) 环绕,在方法执行前开启事务,执行后提交或回滚事务。 -
传播行为 (Propagation): 定义了一个事务方法被另一个事务方法调用时,事务应该如何传播。常用
REQUIRED(默认,如果当前没有事务,就新建一个;如果已存在,则加入)。 -
隔离级别 (Isolation): 定义事务与其他并发事务的隔离程度(如
READ_COMMITTED)。
-
三、其他综合问题
1. Spring 中常用注解有哪些?
-
声明 Bean:
@Component,@Service,@Controller,@Repository,@Configuration -
注入依赖:
@Autowired(按类型),@Resource(按名称,JSR-250),@Inject(JSR-330),@Value -
配置相关:
@Configuration,@Bean,@ComponentScan,@PropertySource,@Profile -
AOP/事务:
@Aspect,@Transactional -
MVC:
@RequestMapping,@GetMapping,@PostMapping,@RequestBody,@ResponseBody
2. Spring 中的设计模式?
-
工厂模式:
BeanFactory -
单例模式: Bean 的默认作用域
-
代理模式: Spring AOP 的实现
-
模板方法模式:
JdbcTemplate,RestTemplate等,定义了操作骨架,具体步骤由子类实现。 -
观察者模式: 事件监听机制 (
ApplicationEvent和ApplicationListener) -
适配器模式: Spring MVC 中的
HandlerAdapter
3. Spring 如何整合 MyBatis?
核心是通过 SqlSessionFactoryBean 和 Mapper 扫描。
-
配置
DataSource。 -
配置
SqlSessionFactoryBean,注入DataSource和 Mapper XML 文件的位置。 -
配置
MapperScannerConfigurer,指定要扫描的 Mapper 接口所在的包。Spring 会为这些接口自动生成代理实现类,并注入到需要的 Service 中。
面试技巧与建议
-
不要死记硬背: 理解原理背后的为什么。比如,为什么要有三级缓存?两级行不行?理解每一步的设计意图。
-
结合代码: 在回答时,如果能结合简单的代码示例(比如用
@Autowired注入,或者定义一个@Aspect),会显得你经验丰富。 -
主动深入: 当被问到基础问题时,可以主动向深处引申。例如,被问到
@Autowired,可以接着说它的注入原理、@Qualifier的作用、与@Resource的区别等。 -
诚实守信: 如果遇到不会的问题,坦诚承认,并说明自己的学习思路,比胡乱猜测要好得多。
希望这份总结能帮助你更好地理解和准备 Spring 相关的面试!祝你成功!
1万+

被折叠的 条评论
为什么被折叠?



