Spring 核心原理: IOC、DI、AOP、Spring 事务的总结(聚焦原理、实战、面试高频点)

58 阅读13分钟

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. 核心类(面试必讲)

核心类 / 接口核心作用
BeanFactoryIOC 容器顶层接口,定义 Bean 的获取、存在性判断等基础方法(懒加载,轻量级)
ApplicationContext继承 BeanFactory,提供高级特性(预加载 Bean、国际化、事件发布、资源加载)
BeanDefinition存储 Bean 的元信息(类名、依赖、作用域、初始化方法等),是 IOC 容器的核心数据结构
BeanDefinitionRegistry注册 BeanDefinition 的接口,DefaultListableBeanFactory是核心实现
AbstractApplicationContextApplicationContext 抽象实现,封装refresh()方法(容器启动核心流程)
DefaultListableBeanFactory核心 IOC 容器实现,整合 BeanDefinition 注册、Bean 实例化、依赖注入等所有功能
BeanPostProcessorBean 后置处理器接口,用于 Bean 初始化前后增强(AOP、依赖注入的核心扩展点)

5. 高频面试题(8 年经验级)

  1. IOC 的本质是什么?解决了什么问题?(核心:解耦,控制权转移,生命周期托管)
  2. BeanFactory 和 ApplicationContext 的区别?(加载方式、功能范围、适用场景)
  3. Spring 如何解决单例 Bean 的循环依赖?(三级缓存:singletonObjects/earlySingletonObjects/singletonFactories,提前暴露半成品 Bean)
  4. Bean 的完整生命周期?(实例化→属性填充→初始化→销毁,结合 Aware 接口、BeanPostProcessor)
  5. Bean 的作用域有哪些?singleton 和 prototype 的区别?(重点:singleton 是容器级单例,prototype 每次 getBean 都新建)
  6. 什么是延迟加载 Bean?如何配置?(@Lazy,默认 singleton 非延迟加载,prototype 默认延迟)
  7. FactoryBean 和 BeanFactory 的区别?(FactoryBean 是 "Bean 的工厂",可自定义 Bean 创建逻辑;BeanFactory 是 IOC 容器顶层接口)
  8. 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 年经验级)

  1. DI 和 IOC 的关系?(DI 是 IOC 的实现手段,IOC 是设计思想,DI 是落地方式)

  2. @Autowired@Resource@Inject的区别?(来源、注入规则、支持的属性、适用场景)

    • @Autowired:Spring 原生,按类型注入,支持required=false,可配合@Qualifier按名称注入;
    • @Resource:JDK 原生(javax.annotation),按名称注入,名称不存在则按类型,支持name/lookup属性;
    • @Inject:JSR-330 标准,按类型注入,需导入依赖,无required属性;
  3. 构造器注入、Setter 注入、字段注入的优缺点?(推荐构造器注入:强制依赖、不可变、利于测试)

  4. 为什么字段注入不推荐?(无法初始化 final 字段、依赖隐藏、不利于单元测试(无法 mock 注入)、可能导致循环依赖)

  5. Spring 如何处理依赖注入时的类型歧义?(@Qualifier指定名称、@Primary指定首选 Bean、自定义BeanPostProcessor

  6. @Value如何注入复杂对象(如 List、Map)?(配置文件中用,分隔,或通过@ConfigurationProperties绑定)

  7. 依赖注入的核心扩展点是什么?(BeanPostProcessor,尤其是AutowiredAnnotationBeanPostProcessor

  8. 循环依赖的产生条件和解决方式?(单例 + 构造器注入无法解决,单例 + 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代理对象创建的核心工厂类,整合切入点、通知、目标对象,选择代理方式
JdkDynamicAopProxyJDK 动态代理实现类,基于InvocationHandler接口
CglibAopProxyCGLIB 动态代理实现类,基于MethodInterceptor接口(需导入 CGLIB 依赖)
AbstractAdvisorAutoProxyCreator自动代理创建器,扫描切面和目标 Bean,触发代理对象创建(AOP 自动生效的核心)
MethodInvocation通知链执行的核心接口,负责遍历通知并执行目标方法
TransactionInterceptor事务切面的核心通知类(@Transactional的底层实现,环绕通知)

6. 高频面试题(8 年经验级)

  1. AOP 的核心概念有哪些?(切面、切入点、通知、连接点、织入、目标对象、代理对象)

  2. Spring AOP 的动态代理实现方式有哪些?区别是什么?

    维度JDK 动态代理CGLIB 动态代理
    依赖目标类实现接口无(基于类继承)
    原理实现目标接口,重写方法生成目标类子类,重写方法
    限制无法代理类方法(非接口)无法代理 final 类 / 方法
    性能代理创建快,执行慢代理创建慢,执行快
    Spring 选择目标类有接口则优先使用无接口或指定proxyTargetClass=true
  3. 通知的执行顺序是什么?(环绕前置→前置→目标方法→后置返回 / 异常→后置→环绕后置)

  4. 切入点表达式的语法?(execution(modifier? returnType declaringType? methodName(paramType*) throws?),举例:execution(* com.xxx.service.*.add*(..))(匹配 service 包下所有 add 开头的方法)

  5. 环绕通知(@Around)的作用是什么?如何使用?(可控制目标方法执行、修改参数 / 返回值、捕获异常,必须调用proceed()执行目标方法)

  6. Spring AOP 和 AspectJ 的关系?(Spring AOP 借鉴 AspectJ 的注解和切入点表达式,底层是自己的动态代理;AspectJ 是独立 AOP 框架,支持编译时织入,功能更强)

  7. 如何自定义一个 AOP 切面?(步骤:1. 加@Aspect@Component;2. 用@Pointcut定义切入点;3. 用通知注解定义逻辑;4. 加@EnableAspectJAutoProxy

  8. AOP 在 Spring 事务中的应用?(@Transactional本质是 AOP 环绕通知,通过TransactionInterceptor拦截目标方法,实现事务的开启、提交 / 回滚)

  9. 如何解决 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()
DataSourceTransactionManagerJDBC 事务管理器(最常用),管理 JDBC Connection 的事务状态
TransactionDefinition事务定义接口,封装事务属性(传播机制、隔离级别、超时时间、只读)
TransactionStatus事务状态接口,记录事务当前状态(是否新事务、是否已提交 / 回滚、是否有保存点)
TransactionInterceptor事务切面的核心通知类,环绕通知实现事务的开启、提交 / 回滚
@Transactional声明式事务注解,标注在类 / 方法上,指定事务属性
TransactionTemplate编程式事务模板类,简化编程式事务的代码(execute()方法接收 TransactionCallback)

6. 高频面试题(8 年经验级)

  1. 事务的 ACID 特性是什么?分别如何保证?(原子性 / 一致性 / 隔离性:数据库锁机制;持久性:WAL 日志)

  2. Spring 事务的传播机制有哪些?重点区别是什么?(面试必问)

    • REQUIRED(默认):如果当前有事务,加入;无则创建新事务(最常用,如用户下单);
    • REQUIRES_NEW:创建新事务,暂停当前事务(如日志记录,无论主事务成功与否都提交);
    • NESTED:嵌套事务(基于保存点),主事务回滚则嵌套事务回滚,嵌套事务回滚不影响主事务(如订单创建 + 库存扣减,库存扣减失败可单独回滚);
    • 其他:SUPPORTS(有则加入,无则无事务)、NOT_SUPPORTED(无事务,暂停当前事务)、MANDATORY(必须有事务,否则抛异常)、NEVER(禁止事务,否则抛异常);
  3. 事务的隔离级别有哪些?Spring 默认隔离级别是什么?

    • 数据库隔离级别:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)、串行化(Serializable);
    • Spring 默认隔离级别:ISOLATION_DEFAULT(继承数据库默认,MySQL 默认可重复读);
    • 隔离级别解决的问题:脏读(读未提交)、不可重复读(读已提交)、幻读(可重复读 / 串行化);
  4. @Transactional注解为什么默认只对 RuntimeException 回滚?(可通过rollbackFor=Exception.class修改)

  5. 哪些场景下@Transactional会失效?(8 年经验必知,面试高频)

    • 方法不是 public(Spring AOP 只拦截 public 方法);
    • 注解标注在内部方法(自身调用,未通过代理对象);
    • 目标类未被 Spring 管理(未加@Component等注解);
    • 事务管理器未配置(如未引入数据源事务管理器);
    • 异常被 catch 捕获且未重新抛出;
    • 传播机制配置错误(如NOT_SUPPORTED);
    • 多线程场景(子线程事务与主线程事务独立);
  6. 编程式事务和声明式事务的区别?

    维度编程式事务(TransactionTemplate)声明式事务(@Transactional)
    代码侵入性高(需手动写事务逻辑)低(注解配置)
    灵活性高(可精确控制事务边界)低(依赖注解属性)
    易用性低(代码繁琐)高(配置简单)
    适用场景复杂业务逻辑(如多步提交 / 回滚)简单 / 通用业务场景
  7. Spring 事务如何实现分布式事务?(Spring 本身不支持分布式事务,需整合 Seata、Saga 等框架;@Transactional仅支持单库事务)

  8. 如何优化事务性能?(缩小事务范围、设置合理的超时时间、只读事务标记readOnly=true、避免事务中执行耗时操作(如 RPC 调用、IO))

求职加分提醒

  1. 所有知识点需结合  "原理 + 实战 + 问题排查"  表述,比如讲事务时,不仅说传播机制,还要说 "项目中曾遇到 @Transactional 失效,排查后发现是方法为 private,修改为 public 后解决";
  2. 流程图可简化手绘,面试时重点讲核心步骤(如 IOC 的 BeanDefinition 注册→实例化→依赖注入→初始化);
  3. 核心类无需死记,重点理解 "为什么需要这个类"(如AutowiredAnnotationBeanPostProcessor是处理 @Autowired 的核心,所以是 DI 的关键类);
  4. 结合 8 年经验,可适当延伸到框架设计思想(如 IOC 的工厂模式、AOP 的代理模式、事务的策略模式),体现架构思维。

按以上内容复习,既能覆盖面试高频点,又能体现深度,符合 8 年 Java 开发者的求职竞争力!