Spring 核心原理: IOC、DI、AOP、Spring 事务 的总结(聚焦原理、实战、面试高频点),所有流程图均采用 Mermaid 语法(可直接复制到工具渲染,面试时可手绘核心逻辑):
一、IOC(Inverse of Control:控制反转)
1. 深度描述
IOC 是 Spring 的核心设计思想,本质是 "将对象的创建、生命周期管理、依赖装配的控制权,从应用程序代码转移到 Spring 容器" —— 开发者无需手动通过new关键字实例化对象,也无需关注对象的依赖关系如何维护,只需通过配置(注解 / XML / 配置类)定义对象的 "元信息"(如类路径、依赖关系、作用域),Spring 容器会自动完成对象的创建、组装、销毁,核心目标是 解耦组件间的依赖关系,提升代码的可扩展性和可测试性。
2. 应用场景
- 注解驱动(主流):
@Component/@Service/@Repository/@Controller标注 Bean,@Configuration+@Bean注册第三方组件(如数据源、RedisTemplate); - 配置类:
@ComponentScan扫描指定包下的 Bean,@Import导入外部 Bean,@Conditional条件化注册 Bean; - XML 配置(传统,面试高频):
<bean id="xxx" class="xxx"/>定义 Bean,<property>配置依赖; - 动态注册:通过
BeanDefinitionRegistry编程式注册 Bean(如 SPI 扩展、自定义 starter)。
3. 实现过程(流程图)
flowchart TD
A["Spring容器启动"] --> B["资源加载:读取配置(注解/XML/配置类)"]
B --> C["BeanDefinition解析:将配置转换为BeanDefinition"]
C --> D["BeanDefinition注册:存入BeanDefinitionRegistry"]
D --> E["Bean实例化触发:refresh()或getBean()"]
E --> F{"是否单例?"}
F -->|是| G["从三级缓存获取:singletonObjects→earlySingletonObjects→singletonFactories"]
F -->|否| H["实例化Bean:createBeanInstance(构造器反射)"]
G -->|存在| Z["返回Bean实例"]
G -->|不存在| H
H --> I["提前暴露半成品Bean:存入singletonFactories"]
I --> J["依赖注入:populateBean(解析@Autowired/@Resource)"]
J --> K["Bean初始化:initializeBean"]
K --> K1["Aware接口回调(BeanNameAware/ApplicationContextAware等)"]
K1 --> K2["BeanPostProcessor前置处理(postProcessBeforeInitialization)"]
K2 --> K3["初始化方法执行(@PostConstruct/init-method/InitializingBean)"]
K3 --> K4["BeanPostProcessor后置处理(postProcessAfterInitialization)"]
K4 --> L{"是否单例?"}
L -->|是| M["存入singletonObjects(一级缓存),移除三级/二级缓存"]
L -->|否| N["直接返回原型Bean"]
M --> Z
N --> Z
Z --> O["Bean使用中"]
O --> P["容器关闭:destroyBean(@PreDestroy/destroy-method/DisposableBean)"]
4. 核心类(面试必讲)
| 核心类 / 接口 | 核心作用 |
|---|---|
BeanFactory | IOC 容器顶层接口,定义 Bean 的获取、存在性判断等基础方法(懒加载,轻量级) |
ApplicationContext | 继承 BeanFactory,提供高级特性(预加载 Bean、国际化、事件发布、资源加载) |
BeanDefinition | 存储 Bean 的元信息(类名、依赖、作用域、初始化方法等),是 IOC 容器的核心数据结构 |
BeanDefinitionRegistry | 注册 BeanDefinition 的接口,DefaultListableBeanFactory是核心实现 |
AbstractApplicationContext | ApplicationContext 抽象实现,封装refresh()方法(容器启动核心流程) |
DefaultListableBeanFactory | 核心 IOC 容器实现,整合 BeanDefinition 注册、Bean 实例化、依赖注入等所有功能 |
BeanPostProcessor | Bean 后置处理器接口,用于 Bean 初始化前后增强(AOP、依赖注入的核心扩展点) |
5. 高频面试题(8 年经验级)
- IOC 的本质是什么?解决了什么问题?(核心:解耦,控制权转移,生命周期托管)
BeanFactory和ApplicationContext的区别?(加载方式、功能范围、适用场景)- Spring 如何解决单例 Bean 的循环依赖?(三级缓存:singletonObjects/earlySingletonObjects/singletonFactories,提前暴露半成品 Bean)
- Bean 的完整生命周期?(实例化→属性填充→初始化→销毁,结合 Aware 接口、BeanPostProcessor)
- Bean 的作用域有哪些?singleton 和 prototype 的区别?(重点:singleton 是容器级单例,prototype 每次 getBean 都新建)
- 什么是延迟加载 Bean?如何配置?(
@Lazy,默认 singleton 非延迟加载,prototype 默认延迟) FactoryBean和BeanFactory的区别?(FactoryBean 是 "Bean 的工厂",可自定义 Bean 创建逻辑;BeanFactory 是 IOC 容器顶层接口)- Spring 容器启动的核心方法是什么?(
AbstractApplicationContext#refresh(),包含 12 个核心步骤)
二、DI(Dependency Injection:依赖注入)
1. 深度描述
DI 是 IOC 的 具体实现方式,本质是 "容器在创建 Bean 时,自动将其依赖的其他 Bean(或基础数据类型)注入到 Bean 的属性 / 构造器 / 方法中" —— 开发者无需手动通过setXxx()或构造器传入依赖,只需通过注解(@Autowired)或配置声明依赖关系,Spring 容器会通过 "依赖查找"(从 IOC 容器中查找依赖 Bean)完成自动装配,核心目标是 彻底解耦组件间的依赖引用,让组件更专注于自身业务逻辑。
2. 应用场景
- 构造器注入(推荐):
public UserService(UserMapper userMapper) {}(强制依赖,避免 NPE,利于测试); - Setter 注入:
@Autowired public void setUserMapper(UserMapper userMapper) {}(可选依赖); - 字段注入:
@Autowired private UserMapper userMapper;(简洁,但不利于测试和依赖可视化); - 注解驱动:
@Autowired(Spring 原生,按类型注入)、@Resource(JDK 原生,按名称 + 类型注入)、@Value(注入配置项,如@Value("${server.port}")); - 集合注入:
@Autowired private List<UserService> userServices;(自动注入所有 UserService 类型的 Bean)。
3. 实现过程(流程图)
flowchart TD
A["Bean实例化完成(createBeanInstance)"] --> B["触发属性填充:populateBean()"]
B --> C["解析Bean的依赖元信息:查找@Autowired/@Resource/@Value注解"]
C --> D{"依赖类型?"}
D -->|Bean依赖| E["依赖查找:从BeanFactory获取依赖Bean(getBean())"]
D -->|基础类型/配置项| L["通过PropertySourcesPlaceholderConfigurer解析@Value表达式"]
E --> F{"依赖是否存在?"}
F -->|否| G{"是否允许为空?"}
F -->|是| J["处理循环依赖:通过三级缓存获取半成品Bean"]
G -->|否| H["抛出NoSuchBeanDefinitionException异常"]
G -->|是| I["注入null"]
J --> K["注入依赖Bean到目标Bean(构造器/setter/字段)"]
L --> M["注入解析后的值到目标Bean"]
K --> N["依赖注入完成"]
I --> N
M --> N
4. 核心类(面试必讲)
| 核心类 / 接口 | 核心作用 |
|---|---|
AutowiredAnnotationBeanPostProcessor | 处理@Autowired和@Value注解的依赖注入(BeanPostProcessor 的实现类) |
CommonAnnotationBeanPostProcessor | 处理@Resource、@PostConstruct、@PreDestroy注解(JDK 原生注解支持) |
BeanDefinitionValueResolver | 解析 BeanDefinition 中的属性值,处理依赖 Bean 的引用(ref 属性) |
ConstructorResolver | 处理构造器注入,解析构造器参数并查找对应依赖 Bean |
PropertySourcesPlaceholderConfigurer | 解析@Value中的占位符(${xxx}),关联配置文件(application.properties) |
5. 高频面试题(8 年经验级)
-
DI 和 IOC 的关系?(DI 是 IOC 的实现手段,IOC 是设计思想,DI 是落地方式)
-
@Autowired、@Resource、@Inject的区别?(来源、注入规则、支持的属性、适用场景)@Autowired:Spring 原生,按类型注入,支持required=false,可配合@Qualifier按名称注入;@Resource:JDK 原生(javax.annotation),按名称注入,名称不存在则按类型,支持name/lookup属性;@Inject:JSR-330 标准,按类型注入,需导入依赖,无required属性;
-
构造器注入、Setter 注入、字段注入的优缺点?(推荐构造器注入:强制依赖、不可变、利于测试)
-
为什么字段注入不推荐?(无法初始化 final 字段、依赖隐藏、不利于单元测试(无法 mock 注入)、可能导致循环依赖)
-
Spring 如何处理依赖注入时的类型歧义?(
@Qualifier指定名称、@Primary指定首选 Bean、自定义BeanPostProcessor) -
@Value如何注入复杂对象(如 List、Map)?(配置文件中用,分隔,或通过@ConfigurationProperties绑定) -
依赖注入的核心扩展点是什么?(
BeanPostProcessor,尤其是AutowiredAnnotationBeanPostProcessor) -
循环依赖的产生条件和解决方式?(单例 + 构造器注入无法解决,单例 + setter / 字段注入通过三级缓存解决)
三、AOP(Aspect-Oriented Programming:面向切面编程)
1. 深度描述
AOP 是一种 "横切逻辑分离" 的编程思想,核心是 "在不修改目标代码的前提下,通过动态代理技术为目标对象添加统一的横切逻辑" —— 将日志、权限、事务、缓存等通用功能(横切逻辑)抽离为 "切面",通过切入点定义 "在哪里执行",通过通知定义 "执行时机和逻辑",最终由 Spring 容器自动将切面织入目标对象,实现 "业务逻辑与横切逻辑的解耦" ,提升代码复用性和可维护性。
2. 应用场景
- 日志记录:接口请求参数、响应结果、执行耗时日志;
- 权限控制:接口访问前校验用户权限;
- 事务管理:声明式事务(
@Transactional本质是 AOP 环绕通知); - 缓存处理:方法执行前查缓存,执行后写缓存;
- 异常处理:全局异常捕获与统一响应;
- 性能监控:统计方法执行耗时、资源占用;
- 日志脱敏:敏感数据(手机号、身份证)输出脱敏。
3. 核心概念(面试必背)
- 切面(Aspect):横切逻辑的封装(如
@Aspect标注的类); - 切入点(Pointcut):定义切面织入的目标位置(如 "所有 controller 包下的方法");
- 通知(Advice):切面的执行逻辑 + 时机(前置 @Before、后置 @After、环绕 @Around、异常 @AfterThrowing、最终 @AfterReturning);
- 连接点(JoinPoint):目标对象中可能被织入切面的点(如方法执行、字段赋值);
- 织入(Weaving):将切面逻辑融入目标对象的过程(Spring 采用动态织入,运行时创建代理对象);
- 目标对象(Target):被切面增强的原始对象;
- 代理对象(Proxy):织入切面后生成的对象(Spring 通过 JDK/CGLIB 创建)。
4. 实现过程(流程图)
flowchart TD
A["Spring容器启动"] --> B["扫描切面:识别@Aspect标注的Bean"]
B --> C["解析切面:提取切入点(@Pointcut)和通知(@Before/@Around等)"]
C --> D["解析切入点表达式:通过AspectJExpressionPointcut匹配目标Bean的方法"]
D --> E["创建代理对象:对匹配切入点的目标Bean,通过ProxyFactory生成代理"]
E --> F{"目标类是否实现接口?"}
F -->|是| G["JdkDynamicAopProxy(JDK动态代理,基于接口)"]
F -->|否| H["CglibAopProxy(CGLIB动态代理,基于类继承)"]
G --> I["代理对象创建完成"]
H --> I
I --> J["目标方法被调用(通过代理对象)"]
J --> K["触发通知链执行:MethodInvocation遍历所有匹配的通知"]
K --> L["执行顺序:@Around前置 → @Before → 目标方法执行 → @AfterReturning/@AfterThrowing → @After → @Around后置"]
L --> M{"目标方法执行成功?"}
M -->|是| N["执行@AfterReturning通知"]
M -->|否| O["执行@AfterThrowing通知"]
N --> P["执行@After通知 → 环绕通知后置逻辑"]
O --> P
P --> Q["返回结果(环绕通知可修改返回值)"]
5. 核心类(面试必讲)
| 核心类 / 接口 | 核心作用 |
|---|---|
@Aspect | 切面类注解,标记当前类为切面(需配合@EnableAspectJAutoProxy启用) |
AspectJExpressionPointcut | 解析 AspectJ 风格的切入点表达式(如execution(* com.xxx.controller.*(..))) |
ProxyFactory | 代理对象创建的核心工厂类,整合切入点、通知、目标对象,选择代理方式 |
JdkDynamicAopProxy | JDK 动态代理实现类,基于InvocationHandler接口 |
CglibAopProxy | CGLIB 动态代理实现类,基于MethodInterceptor接口(需导入 CGLIB 依赖) |
AbstractAdvisorAutoProxyCreator | 自动代理创建器,扫描切面和目标 Bean,触发代理对象创建(AOP 自动生效的核心) |
MethodInvocation | 通知链执行的核心接口,负责遍历通知并执行目标方法 |
TransactionInterceptor | 事务切面的核心通知类(@Transactional的底层实现,环绕通知) |
6. 高频面试题(8 年经验级)
-
AOP 的核心概念有哪些?(切面、切入点、通知、连接点、织入、目标对象、代理对象)
-
Spring AOP 的动态代理实现方式有哪些?区别是什么?
维度 JDK 动态代理 CGLIB 动态代理 依赖 目标类实现接口 无(基于类继承) 原理 实现目标接口,重写方法 生成目标类子类,重写方法 限制 无法代理类方法(非接口) 无法代理 final 类 / 方法 性能 代理创建快,执行慢 代理创建慢,执行快 Spring 选择 目标类有接口则优先使用 无接口或指定 proxyTargetClass=true -
通知的执行顺序是什么?(环绕前置→前置→目标方法→后置返回 / 异常→后置→环绕后置)
-
切入点表达式的语法?(
execution(modifier? returnType declaringType? methodName(paramType*) throws?),举例:execution(* com.xxx.service.*.add*(..))(匹配 service 包下所有 add 开头的方法) -
环绕通知(
@Around)的作用是什么?如何使用?(可控制目标方法执行、修改参数 / 返回值、捕获异常,必须调用proceed()执行目标方法) -
Spring AOP 和 AspectJ 的关系?(Spring AOP 借鉴 AspectJ 的注解和切入点表达式,底层是自己的动态代理;AspectJ 是独立 AOP 框架,支持编译时织入,功能更强)
-
如何自定义一个 AOP 切面?(步骤:1. 加
@Aspect和@Component;2. 用@Pointcut定义切入点;3. 用通知注解定义逻辑;4. 加@EnableAspectJAutoProxy) -
AOP 在 Spring 事务中的应用?(
@Transactional本质是 AOP 环绕通知,通过TransactionInterceptor拦截目标方法,实现事务的开启、提交 / 回滚) -
如何解决 AOP 切面失效的问题?(常见原因:1. 切面类未加
@Component(未被 Spring 管理);2. 未启用@EnableAspectJAutoProxy;3. 目标方法是 private/final(无法代理);4. 自身调用(this. 方法,未通过代理对象);5. 切入点表达式错误)
四、Spring 事务(Transaction)
1. 深度描述
Spring 事务是 "基于 AOP 的声明式事务管理框架" ,核心是 " 封装底层事务管理器(如 JDBC TransactionManager、Hibernate TransactionManager),通过声明式注解(@Transactional)或 XML 配置,将事务控制逻辑(开启、提交、回滚)与业务逻辑解耦 " —— 开发者无需手动编写conn.setAutoCommit(false)、conn.commit()等代码,Spring 通过 AOP 环绕通知自动完成事务管理,同时支持灵活的事务属性(传播机制、隔离级别、超时时间等),适配不同业务场景。
2. 核心特性(ACID)
- 原子性(Atomicity):事务是最小执行单元,要么全成功,要么全回滚;
- 一致性(Consistency):事务执行前后,数据状态保持合法(如转账后总金额不变);
- 隔离性(Isolation):多个事务并发执行时,相互隔离,避免脏读、不可重复读、幻读;
- 持久性(Durability):事务提交后,数据修改永久生效(写入磁盘)。
3. 应用场景
-
声明式事务(主流):
@Transactional注解标注类 / 方法(类级别的注解对所有 public 方法生效);- 核心属性:
propagation(传播机制)、isolation(隔离级别)、timeout(超时时间,默认 - 1 无限制)、readOnly(只读事务,禁止修改操作)、rollbackFor(指定回滚的异常类型)、noRollbackFor(指定不回滚的异常类型);
- 核心属性:
-
编程式事务:
TransactionTemplate(手动控制事务,适用于复杂业务逻辑); -
XML 配置事务(传统):
<tx:advice>+<aop:config>配置切面和事务属性。
4. 实现过程(流程图)
flowchart TD
A["Spring容器启动"] --> B["扫描切面:识别@Aspect标注的Bean"]
B --> C["解析切面:提取切入点(@Pointcut)和通知(@Before/@Around等)"]
C --> D["解析切入点表达式:通过AspectJExpressionPointcut匹配目标Bean的方法"]
D --> E["创建代理对象:对匹配切入点的目标Bean,通过ProxyFactory生成代理"]
E --> F{"目标类是否实现接口?"}
F -->|是| G["JdkDynamicAopProxy(JDK动态代理,基于接口)"]
F -->|否| H["CglibAopProxy(CGLIB动态代理,基于类继承)"]
G --> I["代理对象创建完成"]
H --> I
I --> J["目标方法被调用(通过代理对象)"]
J --> K["触发通知链执行:MethodInvocation遍历所有匹配的通知"]
K --> L["执行顺序:@Around前置 → @Before → 目标方法执行 → @AfterReturning/@AfterThrowing → @After → @Around后置"]
L --> M{"目标方法执行成功?"}
M -->|是| N["执行@AfterReturning通知"]
M -->|否| O["执行@AfterThrowing通知"]
N --> P["执行@After通知 → 环绕通知后置逻辑"]
O --> P
P --> Q["返回结果(环绕通知可修改返回值)"]
5. 核心类(面试必讲)
| 核心类 / 接口 | 核心作用 |
|---|---|
PlatformTransactionManager | 事务管理器顶层接口(策略模式),定义getTransaction()、commit()、rollback() |
DataSourceTransactionManager | JDBC 事务管理器(最常用),管理 JDBC Connection 的事务状态 |
TransactionDefinition | 事务定义接口,封装事务属性(传播机制、隔离级别、超时时间、只读) |
TransactionStatus | 事务状态接口,记录事务当前状态(是否新事务、是否已提交 / 回滚、是否有保存点) |
TransactionInterceptor | 事务切面的核心通知类,环绕通知实现事务的开启、提交 / 回滚 |
@Transactional | 声明式事务注解,标注在类 / 方法上,指定事务属性 |
TransactionTemplate | 编程式事务模板类,简化编程式事务的代码(execute()方法接收 TransactionCallback) |
6. 高频面试题(8 年经验级)
-
事务的 ACID 特性是什么?分别如何保证?(原子性 / 一致性 / 隔离性:数据库锁机制;持久性:WAL 日志)
-
Spring 事务的传播机制有哪些?重点区别是什么?(面试必问)
REQUIRED(默认):如果当前有事务,加入;无则创建新事务(最常用,如用户下单);REQUIRES_NEW:创建新事务,暂停当前事务(如日志记录,无论主事务成功与否都提交);NESTED:嵌套事务(基于保存点),主事务回滚则嵌套事务回滚,嵌套事务回滚不影响主事务(如订单创建 + 库存扣减,库存扣减失败可单独回滚);- 其他:
SUPPORTS(有则加入,无则无事务)、NOT_SUPPORTED(无事务,暂停当前事务)、MANDATORY(必须有事务,否则抛异常)、NEVER(禁止事务,否则抛异常);
-
事务的隔离级别有哪些?Spring 默认隔离级别是什么?
- 数据库隔离级别:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable);
- Spring 默认隔离级别:
ISOLATION_DEFAULT(继承数据库默认,MySQL 默认可重复读); - 隔离级别解决的问题:脏读(读未提交)、不可重复读(读已提交)、幻读(可重复读 / 串行化);
-
@Transactional注解为什么默认只对 RuntimeException 回滚?(可通过rollbackFor=Exception.class修改) -
哪些场景下
@Transactional会失效?(8 年经验必知,面试高频)- 方法不是 public(Spring AOP 只拦截 public 方法);
- 注解标注在内部方法(自身调用,未通过代理对象);
- 目标类未被 Spring 管理(未加
@Component等注解); - 事务管理器未配置(如未引入数据源事务管理器);
- 异常被 catch 捕获且未重新抛出;
- 传播机制配置错误(如
NOT_SUPPORTED); - 多线程场景(子线程事务与主线程事务独立);
-
编程式事务和声明式事务的区别?
维度 编程式事务(TransactionTemplate) 声明式事务(@Transactional) 代码侵入性 高(需手动写事务逻辑) 低(注解配置) 灵活性 高(可精确控制事务边界) 低(依赖注解属性) 易用性 低(代码繁琐) 高(配置简单) 适用场景 复杂业务逻辑(如多步提交 / 回滚) 简单 / 通用业务场景 -
Spring 事务如何实现分布式事务?(Spring 本身不支持分布式事务,需整合 Seata、Saga 等框架;
@Transactional仅支持单库事务) -
如何优化事务性能?(缩小事务范围、设置合理的超时时间、只读事务标记
readOnly=true、避免事务中执行耗时操作(如 RPC 调用、IO))
求职加分提醒
- 所有知识点需结合 "原理 + 实战 + 问题排查" 表述,比如讲事务时,不仅说传播机制,还要说 "项目中曾遇到 @Transactional 失效,排查后发现是方法为 private,修改为 public 后解决";
- 流程图可简化手绘,面试时重点讲核心步骤(如 IOC 的 BeanDefinition 注册→实例化→依赖注入→初始化);
- 核心类无需死记,重点理解 "为什么需要这个类"(如
AutowiredAnnotationBeanPostProcessor是处理 @Autowired 的核心,所以是 DI 的关键类); - 结合 8 年经验,可适当延伸到框架设计思想(如 IOC 的工厂模式、AOP 的代理模式、事务的策略模式),体现架构思维。
按以上内容复习,既能覆盖面试高频点,又能体现深度,符合 8 年 Java 开发者的求职竞争力!