Spring 核心面试题:Bean 生命周期、AOP 与事务管理
本文详细解析了 Spring 框架的四大核心知识点:Bean 的生命周期步骤(实例化、属性赋值、初始化、销毁)、AOP 实现原理及 JDK/CGLIB 代理区别、@Transactional 事务传播机制与失效场景、以及 Spring MVC 工作流程与 DispatcherServlet 职责。内容涵盖理论说明、代码示例及最佳实践,适合 Java 开发者复习面试或深入理解 Spring 底层机制。

本文详细解析了 Spring 框架的四大核心知识点:Bean 的生命周期步骤(实例化、属性赋值、初始化、销毁)、AOP 实现原理及 JDK/CGLIB 代理区别、@Transactional 事务传播机制与失效场景、以及 Spring MVC 工作流程与 DispatcherServlet 职责。内容涵盖理论说明、代码示例及最佳实践,适合 Java 开发者复习面试或深入理解 Spring 底层机制。

Spring Bean 的生命周期涉及多个步骤,从实例化到销毁。在 Spring 容器中,Bean 的生命周期由 Spring IoC 容器管理。
(1)DisposableBean.destroy():
实现该接口的 Bean 会调用 destroy() 方法。
(2)自定义销毁方法(destroy-method):
通过 XML 的 destroy-method 或 @Bean(destroyMethod="...") 指定的方法。
容器关闭时触发销毁(仅单例 Bean,原型 Bean 需手动管理)。
Bean 完全初始化,可被应用程序使用。
(1)Aware 接口回调(按顺序):
ApplicationContextAware → setApplicationContext()
BeanFactoryAware → setBeanFactory()
BeanNameAware → setBeanName()
(2)BeanPostProcessor.postProcessBeforeInitialization():
初始化前处理(例如 @PostConstruct 注解的处理逻辑)。
(3)InitializingBean.afterPropertiesSet():
属性注入完成后执行自定义初始化逻辑。
(4)自定义初始化方法(init-method):
通过 XML 的 init-method 或 @Bean(initMethod="...") 指定的方法。
(5)BeanPostProcessor.postProcessAfterInitialization():
初始化后处理(例如生成 AOP 代理对象)。
注入 Bean 的依赖(通过 @Autowired、setter 或 XML 配置)。
容器通过构造函数或工厂方法创建 Bean 的实例(对象)。
注:Spring 容器在启动时默认会初始化单例 Bean,但可以通过设置懒加载(lazy-init)来延迟初始化。对于原型(prototype)Bean,容器不会管理其完整生命周期,初始化后不会调用销毁方法。
Spring Bean 的生命周期通过 容器回调、接口实现、注解驱动 的方式提供了高度扩展性,开发者可在关键节点插入自定义逻辑,但需注意不同作用域 Bean 的生命周期差异。
Spring AOP 的实现原理是基于动态代理的。在运行时,Spring AOP 会为目标对象创建代理对象,代理对象会拦截对目标方法的调用,并在方法执行前后插入切面逻辑。
Spring AOP 有两种动态代理方式:
| 特性 | JDK 动态代理 | CGLIB 代理 |
|---|---|---|
| 实现方式 | 通过实现目标接口来创建代理类 | 通过继承目标类来创建子类代理 |
| 要求 | 目标类必须实现至少一个接口 | 目标类不能是 final 的,方法也不能是 final 的(因为需要被重写) |
| 性能 | 在早期版本中,JDK 动态代理比 CGLIB 慢,但 JDK 1.8 以后,性能有所提升 | 早期版本中,CGLIB 创建代理对象速度较慢,但运行速度快。现在两者性能差异不大 |
| 依赖 | 仅依赖 JDK,无需额外库 | 需要引入 CGLIB 库(Spring 内部已集成) |
| 代理对象 | 代理对象实现目标接口,因此只能调用接口中定义的方法 | 代理对象继承目标类,可以调用目标类的所有非 final 方法 |
| 默认策略 | 当目标对象实现接口时,默认使用 JDK 动态代理 | 当目标对象没有实现接口时,默认使用 CGLIB 代理。也可以通过配置强制使用 CGLIB 代理 |
在 Spring Boot 2.x 之后,默认使用 CGLIB 代理,因为这样可以对类进行代理,而不只是接口。但可以通过配置 spring.aop.proxy-target-class=false 改为使用 JDK 动态代理(当有接口时)。
由于 CGLIB 通过继承实现代理,所以不能代理 final 类或 final 方法。
Spring AOP 通过动态代理实现,根据目标对象是否有接口选择使用 JDK 动态代理或 CGLIB 代理。两者各有特点,选择哪种代理方式可以根据具体需求和场景来决定。
@Transactional(rollbackFor = Exception.class) 来设置。@EnableTransactionManagement 来启用事务管理(Spring Boot 中默认已开启)。Spring 事务失效的根本原因通常是 代理机制 和 异常处理。记住关键点:
public通过合理的配置和遵循最佳实践,可以避免大多数事务失效问题。
同一个类中方法调用导致事务失效:
@Service
public class UserService {
public void updateUser() {
// 这里调用同一个类中的事务方法,事务不会生效
this.updateUserWithTransaction(); // 错误方式
}
@Transactional
public void updateUserWithTransaction() {
// 数据库更新操作
}
}
修正方法:
@Service
public class UserService {
@Autowired
private ApplicationContext applicationContext;
public void updateUser() {
// 通过 AopContext 获取当前代理对象,然后调用事务方法
UserService proxy = (UserService) applicationContext.getBean("userService");
proxy.updateUserWithTransaction();
}
@Transactional
public void updateUserWithTransaction() {
// 数据库更新操作
}
}
Spring MVC 的工作流程可以概括为客户端请求被 DispatcherServlet 接收,然后由 DispatcherServlet 将请求分发给相应的处理器进行处理,并最终返回响应。DispatcherServlet 是 Spring MVC 的核心,它充当了前端控制器的角色,负责协调整个请求处理流程。
DispatcherServlet 的作用是:
接收客户端请求,并将请求分发给合适的处理器。
协调各个组件(HandlerMapping、HandlerAdapter、ViewResolver 等)完成请求处理。
作为 Spring MVC 的入口,整合 Spring 容器。
// DispatcherServlet 类定义
public class DispatcherServlet extends FrameworkServlet {
// 作为前端控制器(Front Controller),统一处理所有请求
}
// web.xml 配置(传统方式)
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
<!-- 拦截所有请求 -->
</servlet-mapping>
| 职责 | 说明 | 相关组件 |
|---|---|---|
| 请求接收与分发 | 拦截所有 HTTP 请求,统一入口 | DispatcherServlet 自身 |
| 处理器映射 | 查找请求对应的处理器 | HandlerMapping |
| 处理器适配 | 适配不同处理器类型 | HandlerAdapter |
| 视图解析 | 解析逻辑视图到物理视图 | ViewResolver |
| 异常处理 | 统一异常处理机制 | HandlerExceptionResolver |
| 组件协调 | 协调各组件工作流程 | 所有 MVC 组件 |

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online