Spring & Spring Boot 高级面试题补充(续)
五、Spring 事务管理深度解析
9. 声明式事务实现原理
Q9:Spring声明式事务的底层实现机制是什么?@Transactional注解在类和方法上的优先级如何?
答案:
实现机制:
1. 代理创建:
- 通过BeanPostProcessor(InfrastructureAdvisorAutoProxyCreator)创建代理
- 根据@Transactional注解决定是否创建代理
2. 拦截器链:
- TransactionInterceptor作为MethodInterceptor
- 包含PlatformTransactionManager和TransactionAttributeSource
3. 事务执行流程:
a. 获取事务属性(@Transactional配置)
b. 获取TransactionManager
c. 创建TransactionStatus
d. 执行业务方法
e. 提交/回滚事务
注解优先级规则:
1. 方法注解优先于类注解
2. 子类注解优先于父类注解
3. 接口注解默认不生效(需配置@EnableTransactionManagement(proxyTargetClass=true))
源码关键路径:
1. 代理创建:
AbstractAutoProxyCreator#postProcessAfterInitialization
→ createProxy
→ DefaultAopProxyFactory
2. 事务拦截:
TransactionInterceptor#invoke
→ TransactionAspectSupport#invokeWithinTransaction
3. 事务管理:
DataSourceTransactionManager
AbstractPlatformTransactionManager
特殊场景处理:
1. 自调用问题:
- 同类方法调用不会经过代理
- 解决方案:
a. 自我注入
b. 使用AopContext.currentProxy()
2. 事务传播行为:
- REQUIRED(默认):加入当前事务,没有则新建
- REQUIRES_NEW:新建事务,挂起当前事务
- NESTED:嵌套事务
10. 事务传播机制
Q10:Spring事务的7种传播行为在实际应用中如何选择?NESTED和REQUIRES_NEW有什么区别?
答案:
7种传播行为及适用场景:
1. REQUIRED(默认):
- 当前有事务则加入,没有则新建
- 适用场景:大多数业务方法
2. SUPPORTS:
- 当前有事务则加入,没有则以非事务运行
- 适用场景:查询方法,可适应事务环境
3. MANDATORY:
- 必须在事务中调用,否则抛异常
- 适用场景:必须参与事务的核心操作
4. REQUIRES_NEW:
- 新建事务,挂起当前事务
- 适用场景:独立业务日志记录
5. NOT_SUPPORTED:
- 以非事务方式执行,挂起当前事务
- 适用场景:发送消息等非核心操作
6. NEVER:
- 必须在非事务环境执行,否则抛异常
- 适用场景:性能敏感的非事务操作
7. NESTED:
- 在当前事务中嵌套子事务
- 适用场景:可部分回滚的业务流程
NESTED vs REQUIRES_NEW:
┌──────────────────┬──────────────────────────────┬──────────────────────────────┐
│ 特性 │ NESTED │ REQUIRES_NEW │
├──────────────────┼──────────────────────────────┼──────────────────────────────┤
│ 事务关系 │ 嵌套子事务 │ 完全独立的新事务 │
│ 保存点 │ 使用保存点实现 │ 不使用保存点 │
│ 回滚影响 │ 外部事务可选择是否回滚 │ 不影响外部事务 │
│ 数据库支持 │ 依赖数据库保存点支持 │ 广泛支持 │
│ 连接占用 │ 共用同一连接 │ 需要新连接 │
└──────────────────┴──────────────────────────────┴──────────────────────────────┘
实战示例:
// 订单服务主流程
@Transactional
public void processOrder(Order order) {
orderRepository.save(order); // REQUIRED
inventoryService.reduceStock(order.getItems()); // REQUIRED
logService.addLog(order); // REQUIRES_NEW
recommendationService.updatePref(order); // NESTED
}
六、Spring Boot 性能优化
11. 启动性能优化
Q11:如何优化Spring Boot应用的启动速度?有哪些具体可实施的方案?
答案:
启动耗时分析:
1. 使用Spring Boot Actuator:
management.endpoints.web.exposure.include=startup
curl http://localhost:8080/actuator/startup
2. 添加启动监听:
spring.application.admin.enabled=true
JConsole连接查看StartupDate
优化方案:
1. 类加载优化:
- 配置JVM参数:
-XX:TieredStopAtLevel=1
-noverify
- 使用AppCDS(Application Class-Data Sharing)
2. 组件扫描优化:
- @ComponentScan指定具体包路径
- 排除不必要的自动配置:
@SpringBootApplication(exclude={...})
3. 延迟初始化:
spring.main.lazy-initialization=true
(注意:可能导致首次请求延迟)
4. 反射优化:
- 添加Spring Native支持
- 使用GraalVM编译原生镜像
5. 其他优化:
- 减少@Bean方法复杂度
- 使用spring-context-indexer
- 避免静态代码块中的耗时操作
基准测试结果(示例):
优化前:8.2秒
优化后:3.5秒
- 类加载优化:减少1.2秒
- 组件扫描优化:减少2.1秒
- 延迟初始化:减少1.4秒
进阶工具:
1. Spring Boot Startup Report:
java -jar app.jar --spring.application.admin.enabled=true
2. JVM启动参数分析:
-XX:+PrintCompilation
-XX:+UnlockDiagnosticVMOptions
3. Arthas诊断:
trace org.springframework.context.support.AbstractApplicationContext refresh
12. 运行时性能优化
Q12:Spring Boot应用在生产环境出现性能瓶颈,如何定位和解决问题?
答案:
诊断步骤:
1. 监控指标收集:
- Actuator端点:/metrics, /health, /prometheus
- JVM指标:GC频率、堆内存、线程数
- 外部系统:数据库连接池、Redis响应时间
2. 性能分析工具:
- Arthas:trace/watch/monitor命令
- JProfiler:CPU和内存热点分析
- Async Profiler:低开销采样
常见瓶颈及解决方案:
1. 数据库访问:
- 问题表现:慢查询、连接池耗尽
- 解决方案:
a. 添加Druid监控
b. 优化SQL和索引
c. 配置连接池:
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.connection-timeout=3000
2. JSON序列化:
- 问题表现:CPU使用率高
- 解决方案:
a. 启用Jackson afterburner模块
b. 使用protobuf替代JSON
3. 缓存穿透:
- 问题表现:大量请求直达数据库
- 解决方案:
a. 空值缓存
b. 布隆过滤器
4. 线程阻塞:
- 问题表现:线程池满、响应延迟
- 解决方案:
a. 分析线程dump
b. 优化锁粒度
c. 使用异步处理
实战案例:
问题现象:API平均响应从50ms突增到1200ms
分析过程:
1. Arthas监控发现Controller方法耗时正常
2. 网络抓包发现数据库响应慢
3. 检查MyBatis日志发现全表扫描
解决方案:
1. 添加缺失索引
2. 优化查询语句
3. 引入二级缓存
结果:响应时间恢复至60ms
持续优化:
1. 性能基准测试:
JMeter定期回归测试
2. 容量规划:
根据QPS估算资源需求
3. 渐进式优化:
每次发布只做一个性能优化点