Java学习第六天:Spring 核心原理

5 阅读14分钟

一、Spring 核心架构

  • 核心容器:Bean、Core、Context、SpEL
  • 核心能力:IOC 控制反转、DI 依赖注入、AOP 面向切面编程
  • 作用:解耦、统一对象生命周期、统一事务管理、简化开发

二、IOC 控制反转(最核心)

1. 什么是 IOC

  • 对象创建、依赖管理、生命周期交给 Spring 容器管理
  • 程序员不再 new 对象,由容器自动注入
  • 目的:解耦、便于统一管理、便于扩展、便于测试

2. IOC 容器顶层接口

  • BeanFactory:基础容器,延迟初始化(获取 Bean 时才创建)
  • ApplicationContext:高级容器(BeanFactory 子类),项目常用,启动时初始化所有单例 Bean,提供国际化、事件发布等扩展能力

3. Bean 生命周期(面试必背)

  1. 实例化(调用构造方法创建 Bean 实例)
  2. 填充属性(DI 依赖注入,给 @Autowired / @Resource 字段赋值)
  3. 执行 Aware 接口方法(BeanNameAware 获取 Bean 名称、BeanFactoryAware 获取容器、ApplicationContextAware 获取上下文)
  4. 执行 BeanPostProcessor 前置处理(postProcessBeforeInitialization,所有 Bean 初始化前统一增强)
  5. 执行初始化方法(先 @PostConstruct 注解方法 → 再 afterPropertiesSet 接口方法 → 最后 xml 配置的 init-method)
  6. 执行 BeanPostProcessor 后置处理(postProcessAfterInitialization,所有 Bean 初始化后统一增强,AOP 代理生成在此阶段)
  7. Bean 初始化完成,进入容器缓存,可被使用
  8. 销毁阶段(容器关闭时:先 @PreDestroy 注解方法 → 再 destroy 接口方法 → 最后 xml 配置的 destroy-method)

一句话总结:实例化 → 依赖注入 → Aware 回调 → 前置处理 → 初始化 → 后置处理 → 运行 → 销毁

4. Bean 作用域

  • singleton:单例(默认),整个 Spring 容器中只有一个实例,启动时创建(ApplicationContext)
  • prototype:多例,每次 getBean () 都创建新对象,容器只负责创建,不管理销毁
  • request:Web 环境,每个 HTTP 请求创建一个实例
  • session:Web 环境,每个 HTTP Session 一个实例
  • application:Web 环境,整个 Web 应用一个实例

5. Bean 循环依赖(面试核心)

什么是循环依赖

两个或多个 Bean 互相依赖:A → B → A,或 A → B → C → A

Spring 解决循环依赖的前提

  • 仅支持 单例 Bean(多例 Bean 每次获取都新建,无缓存,无法解决)
  • 仅支持 字段注入 / Setter 注入(构造器注入在实例化阶段就需要依赖,无法提前暴露)
  • 不支持 AOP 代理的构造器注入循环依赖

三级缓存解决循环依赖的核心逻辑(超详细)

Spring 容器内部维护三个缓存(本质是 Map),核心是「提前暴露半成品 Bean」:

缓存级别名称(源码变量)存储内容核心作用
一级缓存singletonObjects完全初始化完成的单例 Bean(成品)供外部获取最终可用的 Bean
二级缓存earlySingletonObjects早期暴露的半成品 Bean(已实例化、未填充属性 / 未执行初始化)避免重复创建代理,缓存提前暴露的 Bean
三级缓存singletonFactoriesObjectFactory 工厂对象(创建 Bean 实例 / 生成代理的工厂)延迟创建代理,解决「实例化」和「代理生成」的时序问题
完整解决流程(以 A → B → A 为例)
  1. 容器创建 Bean A:

    • 执行构造方法,完成实例化(此时仅创建空对象,无属性)
    • 将 A 的 ObjectFactory 放入三级缓存
    • 开始填充属性,发现依赖 Bean B,暂停 A 的创建,去创建 B
  2. 容器创建 Bean B:

    • 执行构造方法,完成实例化
    • 将 B 的 ObjectFactory 放入三级缓存
    • 开始填充属性,发现依赖 Bean A
    • 从一级缓存找 A → 无;从二级缓存找 A → 无;从三级缓存取 A 的 ObjectFactory → 执行工厂获取 A 的半成品实例
    • 将 A 的半成品实例放入二级缓存,删除三级缓存中的 A 工厂
    • 把 A 的半成品实例注入到 B 中,B 继续完成属性填充、初始化,成为成品
    • B 成品放入一级缓存,删除二级缓存中的 B(如有)
  3. 回到 Bean A 的创建流程:

    • 从一级缓存获取 B 的成品,注入到 A 中
    • A 继续完成属性填充、初始化、后置处理器(如生成 AOP 代理)
    • A 成品放入一级缓存,删除二级缓存中的 A
    • 最终一级缓存中保存 A、B 的成品 Bean,供外部使用
为什么需要三级缓存(而非两级)
  • 核心目的:延迟生成 AOP 代理,避免不必要的代理创建
  • 如果只有两级缓存:需要在实例化后立即生成代理并放入二级缓存,无论该 Bean 是否需要被循环依赖,都会提前生成代理,增加性能开销
  • 三级缓存通过 ObjectFactory 延迟执行代理创建逻辑:只有发生循环依赖时,才会执行工厂生成代理;无循环依赖时,代理在 Bean 初始化后的后置处理器阶段生成,符合正常流程

无法解决的循环依赖场景

  1. 构造器注入循环依赖(实例化阶段就需要依赖,无法提前暴露半成品)
  2. 多例 Bean 循环依赖(多例无缓存,每次 getBean () 都新建,无法复用)
  3. 单例 Bean + @Scope (proxyMode = ScopedProxyMode.TARGET_CLASS) 且构造器注入

三、DI 依赖注入

1. 注入方式(优先级 / 适用场景)

  • 构造器注入(Spring 4.x+ 推荐):优点:依赖明确、不可变(构造后字段无法修改)、天然避免循环依赖(编译期报错)适用:核心、必选依赖
  • Setter 注入:优点:支持可选依赖、可循环依赖适用:非核心、可选依赖
  • Field 注入(@Autowired):优点:代码简洁、开发效率高(最常用)缺点:依赖隐藏、无法通过构造器保证依赖非空、单元测试需手动注入

2. @Autowired 装配规则

  1. 按类型(byType)匹配容器中的 Bean
  2. 匹配到多个 → 按名称(byName)匹配字段名 / 方法参数名
  3. 匹配不到 → 抛 NoSuchBeanDefinitionException(required=true 时)
  4. 可通过 @Qualifier 指定 Bean 名称,精准匹配

3. @Resource vs @Autowired

特性@Autowired@Resource
所属规范Spring 自定义注解JSR-250 Java 标准注解
匹配规则默认 byType → byName默认 byName → byType
支持指定名称需配合 @Qualifier直接通过 name 属性指定
支持依赖非空控制required 属性(默认 true)无,找不到则抛异常
适用场景优先匹配类型,Spring 生态优先匹配名称,跨框架兼容

四、AOP 面向切面编程

1. 核心思想

在不修改目标方法源码的前提下,通过动态代理对方法进行横向增强(如日志、事务、权限、限流、监控),实现业务逻辑与横切逻辑解耦

2. AOP 核心术语(必须吃透)

  • 切面(Aspect) :封装横切逻辑的类(如 LogAspect、TransactionAspect),通常加 @Aspect 注解
  • 连接点(JoinPoint) :Spring 中特指可被增强的方法执行点(如 Controller 方法、Service 方法)
  • 切点(Pointcut) :匹配连接点的规则(如 execution (* com.xxx.service. . (..))),决定哪些方法被增强
  • 通知(Advice) :具体的增强逻辑,分为 5 种类型
  • 目标对象(Target) :被代理的原始业务对象(如 UserServiceImpl)
  • 代理对象(Proxy) :Spring 生成的包含增强逻辑的对象,外部调用的是代理而非原始对象
  • 织入(Weaving) :将通知应用到目标对象生成代理的过程(Spring 中织入发生在 Bean 初始化阶段)

3. 五种通知类型及执行顺序

通知类型注解执行时机核心作用
前置通知@Before目标方法执行前参数校验、日志记录(入参)
环绕通知@Around目标方法执行前后(最强)全流程控制(限流、超时、重试)
返回通知@AfterReturning目标方法正常返回后结果处理、日志记录(出参)
异常通知@AfterThrowing目标方法抛出异常后异常捕获、告警、日志记录
最终通知@After目标方法执行完成后(无论是否异常)资源释放、清理操作

完整执行顺序(无异常):Around 前置处理 → Before → 目标方法执行 → Around 后置处理 → AfterReturning → After

有异常时顺序:Around 前置处理 → Before → 目标方法抛出异常 → Around 异常处理 → AfterThrowing → After

4. AOP 底层实现(动态代理)

JDK 动态代理

  • 前提:目标对象实现至少一个接口
  • 原理:通过 Proxy 类生成接口的实现类,代理类持有 InvocationHandler,invoke 方法中执行增强逻辑 + 目标方法
  • 特点:基于接口,轻量,JDK 原生支持,无需额外依赖

CGLIB 动态代理

  • 前提:目标对象无实现接口(或强制指定)
  • 原理:通过字节码生成目标对象的子类,重写目标方法,子类中执行增强逻辑 + 父类方法
  • 特点:基于子类,需依赖 cglib 包(SpringBoot 已内置),可代理任意类,无法代理 final 方法 / 类

Spring 代理选择规则(SpringBoot 2.0+)

  1. 目标对象有接口 → 默认用 JDK 动态代理(可通过 spring.aop.proxy-target-class=true 强制 CGLIB)
  2. 目标对象无接口 → 用 CGLIB 代理
  3. 标注 @EnableAspectJAutoProxy (proxyTargetClass = true) → 全局强制 CGLIB

五、Spring 事务核心

1. 事务管理核心组件

  • PlatformTransactionManager:事务管理器(核心),不同数据源对应不同实现:

    • DataSourceTransactionManager:JDBC/MyBatis 事务
    • JpaTransactionManager:JPA 事务
    • RedisTransactionManager:Redis 事务(非关系型)
  • TransactionDefinition:事务定义,包含 4 大属性:

    • 传播行为:事务如何嵌套
    • 隔离级别:解决并发问题
    • 超时时间:事务最长执行时间
    • 是否只读:优化数据库执行(只读事务不写数据)
  • TransactionStatus:事务运行状态,记录事务是否激活、是否回滚、是否新建等

2. @Transactional 生效条件(缺一不可)

  1. 注解标注在 public 方法上(Spring 代理仅拦截 public 方法)
  2. 目标类被 Spring 容器管理(加 @Component/@Service 等)
  3. 开启事务注解驱动(SpringBoot 自动开启,纯 Spring 需加 @EnableTransactionManagement)
  4. 异常未被手动捕获(需抛出到事务切面,否则无法触发回滚)
  5. 异常类型符合回滚规则(默认回滚 RuntimeException/Error,非检查异常需指定 rollbackFor)

3. 事务传播机制(7 种,重点背 3 个)

传播行为注解值核心逻辑典型场景
REQUIRED(默认)REQUIRED有事务则加入,无则新建普通业务方法(大部分场景)
REQUIRES_NEWREQUIRES_NEW强制新建事务,挂起当前事务日志记录、消息发送(不影响主事务)
NESTEDNESTED嵌套事务,基于保存点回滚批量操作(失败仅回滚当前批次)
SUPPORTSSUPPORTS有事务则加入,无则无事务查询方法(可选事务)
NOT_SUPPORTEDNOT_SUPPORTED无事务,挂起当前事务耗时非事务操作(避免锁占用)
MANDATORYMANDATORY必须在事务中执行,无则抛异常核心写操作(强制事务)
NEVERNEVER必须无事务,有则抛异常纯查询(禁止事务)

4. 事务隔离级别(解决并发问题)

隔离级别注解值解决的问题存在的问题数据库默认
READ_UNCOMMITTED读未提交无(最低级别)脏读、不可重复读、幻读无主流数据库默认
READ_COMMITTED读已提交脏读不可重复读、幻读Oracle、SQL Server、MySQL 8.0(部分)
REPEATABLE_READ可重复读脏读、不可重复读幻读(InnoDB 已解决)MySQL(InnoDB)默认
SERIALIZABLE串行化所有问题(最高级别)性能极低(表级锁)无主流数据库默认

并发问题说明

  • 脏读:读取到其他事务未提交的数据(如 A 改了数据未提交,B 读到了 A 的修改)
  • 不可重复读:同一事务内多次读取同一数据,结果不一致(如 A 读数据后,B 修改提交,A 再读不同)
  • 幻读:同一事务内多次查询同一条件,结果条数不一致(如 A 查所有用户,B 新增用户提交,A 再查多了一条)

5. 事务失效场景(高频面试)

  1. 非 public 方法:Spring 代理仅拦截 public 方法,private/protected 方法注解无效
  2. 同类内部调用:如 A.service1 () 调用 A.service2 ()(@Transactional),未走代理,失效
  3. 异常被吞:方法内 try-catch 异常,未抛出到切面,无法触发回滚
  4. 异常类型不匹配:抛出 Checked 异常(如 IOException),未指定 rollbackFor = Exception.class
  5. 多线程调用:主线程开启事务,子线程执行数据库操作,子线程无事务(事务不跨线程)
  6. 类未被 Spring 管理:未加 @Service/@Component,容器无代理对象
  7. 数据源未配置事务管理器:如未注入 DataSourceTransactionManager
  8. 只读事务写数据:@Transactional (readOnly = true) 执行 insert/update/delete,失效

六、SpringMVC 执行流程(必背)

核心执行流程(10 步详细版)

  1. 客户端发送 HTTP 请求 → 前端控制器 DispatcherServlet(中央入口)
  2. DispatcherServlet 调用 HandlerMapping(处理器映射器)→ 根据 URL 匹配对应的 Handler(Controller 方法)
  3. HandlerMapping 返回 HandlerExecutionChain(包含 Handler + 拦截器)给 DispatcherServlet
  4. DispatcherServlet 调用 HandlerAdapter(处理器适配器)→ 适配并执行 Handler
  5. HandlerAdapter 执行前置处理(如参数解析、@RequestParam 绑定)
  6. 调用 Controller 业务方法,执行核心业务逻辑
  7. Controller 返回 ModelAndView(数据模型 + 视图名)或 JSON 数据
  8. DispatcherServlet 调用 ViewResolver(视图解析器)→ 解析视图名,生成 View 对象(如 JSP/Thymeleaf)
  9. View 对象渲染数据(Model 填充到视图),生成响应内容(JSON 则直接返回)
  10. DispatcherServlet 将响应返回给客户端,完成请求处理

核心组件作用

  • DispatcherServlet:核心控制器,统筹所有流程,相当于 “大脑”
  • HandlerMapping:URL → Controller 方法的映射器(如 RequestMappingHandlerMapping)
  • HandlerAdapter:适配不同类型的 Handler,统一执行逻辑(如 RequestMappingHandlerAdapter)
  • ViewResolver:视图解析器,将逻辑视图名转为物理视图(如 InternalResourceViewResolver)
  • HandlerInterceptor:拦截器,可在请求处理前后增强(如登录校验、日志)

七、SpringBoot 核心原理

1. 核心注解 @SpringBootApplication 拆解

plaintext

@SpringBootApplication = 
  @SpringBootConfiguration  // 等价于 @Configuration,标识配置类
  + @EnableAutoConfiguration  // 核心:开启自动配置
  + @ComponentScan  // 扫描当前包及子包的 @Component 注解类

2. 自动配置原理(核心)

  1. 触发:@EnableAutoConfiguration 注解通过 @Import (AutoConfigurationImportSelector.class) 导入自动配置类

  2. 加载:AutoConfigurationImportSelector 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件,该文件包含所有自动配置类全限定名(如 DataSourceAutoConfiguration、RedisAutoConfiguration)

  3. 条件生效:自动配置类通过 条件注解 控制是否生效:

    • @ConditionalOnClass:类路径下存在指定类才生效(如 RedisTemplate 存在则 Redis 自动配置生效)
    • @ConditionalOnBean:容器中存在指定 Bean 才生效
    • @ConditionalOnMissingBean:容器中不存在指定 Bean 才生效(用户自定义优先)
    • @ConditionalOnProperty:配置文件中指定属性满足条件才生效(如 spring.redis.enabled=true)
  4. 属性绑定:通过 @ConfigurationProperties 绑定 application.yml/properties 中的配置,覆盖默认值

3. Starter 启动器原理

  • Starter 本质:依赖聚合 + 自动配置 的组合包,无需手动导入多个依赖

  • 核心结构:

    1. pom.xml:聚合相关依赖(如 spring-boot-starter-web 包含 tomcat、spring-web、spring-webmvc 等)
    2. 自动配置类:META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中声明自动配置类
    3. 配置属性:META-INF/spring-configuration-metadata.json 定义配置项提示
  • 典型示例:引入 spring-boot-starter-redis → 自动导入 Redis 依赖 + 自动配置 RedisTemplate、StringRedisTemplate

4. SpringBoot 启动流程(关键步骤)

  1. 执行启动类 main 方法 → 调用 SpringApplication.run ()

  2. 初始化 SpringApplication:设置应用类型(Web / 非 Web)、加载初始化器、监听器

  3. 准备环境(Environment):加载配置文件(application.yml/properties)、系统变量、命令行参数

  4. 创建 ApplicationContext(应用上下文):根据应用类型创建 AnnotationConfigServletWebServerApplicationContext(Web 应用)

  5. 刷新上下文(核心):

    • 扫描 Bean 定义(@Component/@Service 等)
    • 执行自动配置逻辑
    • 初始化所有单例 Bean(IOC 容器初始化)
    • 启动内置 Web 服务器(Tomcat/Jetty/Undertow)
  6. 发布启动完成事件,回调 CommandLineRunner/ApplicationRunner

  7. 应用启动完成,监听端口等待请求


八、第 6 天高频面试题(必背)

  1. Spring Bean 完整生命周期?
  2. 三级缓存解决循环依赖的原理?为什么需要三级缓存而非两级?
  3. 构造器注入、Setter 注入、Field 注入的区别及适用场景?
  4. AOP 五种通知的执行顺序?JDK 代理与 CGLIB 代理的区别?
  5. @Transactional 生效条件?常见失效场景及原因?
  6. 事务传播机制中 REQUIRED、REQUIRES_NEW、NESTED 的区别?
  7. SpringMVC 完整执行流程?核心组件的作用?
  8. SpringBoot 自动配置原理?Starter 是什么?
  9. Spring 如何解决事务的脏读、不可重复读、幻读?
  10. 为什么 Spring 事务不支持多线程?