Java Spring框架的核心原理详解
🤟致敬读者
- 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉
📘博主相关
- 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息
文章目录
- Java Spring框架的核心原理详解
- 一、核心基石:IoC (控制反转) / DI (依赖注入)
- 二、核心支柱:AOP (面向切面编程)
- 三、声明式事务管理
- 四、Spring MVC 核心流程
- 五、设计模式的应用
- 六、Bean 的生命周期(重要!)
- 七、常用扩展点
- 面试回答建议
📃文章前言
- 🔷文章均为学习工作中整理的笔记。
- 🔶如有错误请指正,共同学习进步。
Java Spring框架的核心原理详解
关于 Java 面试中 Spring 框架的核心原理,这是非常高频且重要的考察点。面试官不仅希望你了解基本概念,更期望你深入理解其设计思想、工作机制和关键实现细节。
以下是对 Spring 核心原理的详细解析,涵盖面试中最常被问及的关键点:
一、核心基石:IoC (控制反转) / DI (依赖注入)
-
核心思想:
- 传统方式: 对象自己创建和管理其依赖的对象(
new
关键字)。耦合度高,难以测试和扩展。 - IoC: 控制权反转。 对象的创建、依赖关系的组装不再由对象自身控制,而是交给一个外部容器(Spring IoC 容器)来负责。对象只需声明自己需要什么依赖。
- DI: 依赖注入。 是 IoC 的一种具体实现方式。容器在创建对象时,自动将其所依赖的对象(或值)“注入”给它(通过构造器、Setter 方法或字段)。
- 传统方式: 对象自己创建和管理其依赖的对象(
-
为什么重要?
- 解耦: 对象不关心依赖的具体实现和来源,只依赖于接口/抽象。提高了模块化和可维护性。
- 可测试性: 依赖可以被 Mock 或 Stub,便于单元测试。
- 可配置性和灵活性: 通过配置文件(XML)或注解(Java Config)就能改变依赖关系或实现类,无需修改代码。
- 生命周期管理: 容器统一管理对象的创建、初始化、销毁等生命周期。
-
关键接口与实现:
BeanFactory
: 基础 IoC 容器接口。提供基本的 Bean 管理和依赖查找功能。延迟加载(用到时才创建)。ApplicationContext
:BeanFactory
的子接口。提供了更丰富的企业级功能,是实际应用中最常用的容器:- 继承
BeanFactory
: 基础的 Bean 管理。 - 继承
ResourcePatternResolver
: 资源加载(类路径、文件系统等)。 - 继承
MessageSource
: 国际化(i18n)。 - 继承
ApplicationEventPublisher
: 事件发布机制。 - 继承
EnvironmentCapable
: 环境(Profile、Property)支持。 - 通常预加载所有单例 Bean: 启动时即创建(可通过配置修改),提高运行时性能。
- 继承
- 常见实现类:
ClassPathXmlApplicationContext
:从类路径加载 XML 配置文件。FileSystemXmlApplicationContext
:从文件系统加载 XML 配置文件。AnnotationConfigApplicationContext
:基于 Java 配置类(@Configuration
)加载 Bean。AnnotationConfigWebApplicationContext
:用于 Web 应用的基于注解的上下文。
-
Bean 的定义与注册:
- 方式: XML
<bean>
标签、@Component
及其衍生注解 (@Service
,@Repository
,@Controller
)、@Bean
注解(在@Configuration
类中)。 - 容器启动过程:
- 加载配置: 读取 XML 文件或扫描注解标记的类。
- 解析配置: 将配置信息解析成
BeanDefinition
对象(包含 Bean 的类名、作用域、是否延迟加载、依赖关系、初始化/销毁方法等元数据)。 - 注册
BeanDefinition
: 将BeanDefinition
注册到BeanDefinitionRegistry
(通常是ApplicationContext
内部实现)。 - 实例化 Bean: 根据
BeanDefinition
信息,通过反射机制创建 Bean 实例(调用构造器)。 - 填充属性 (DI): 解析 Bean 的依赖(查找或创建依赖的 Bean),并通过反射调用 Setter 方法或直接设置字段值注入依赖。
- 初始化: 调用 Bean 的初始化方法(如
@PostConstruct
注解的方法、InitializingBean
接口的afterPropertiesSet
方法、XML 中配置的init-method
)。 - 放入容器: 将初始化完成的 Bean 放入容器(通常是 ConcurrentHashMap)中管理。
- 使用: 应用程序通过
getBean()
方法(通常间接通过依赖注入)获取并使用 Bean。 - 销毁: 容器关闭时,调用 Bean 的销毁方法(如
@PreDestroy
注解的方法、DisposableBean
接口的destroy
方法、XML 中配置的destroy-method
)。
- 方式: XML
-
作用域 (Scope):
singleton
(默认): 容器中只存在一个该 Bean 的实例。prototype
: 每次请求(getBean()
或注入)都创建一个新的实例。request
(Web): 每个 HTTP 请求创建一个实例(仅在 Web 应用中有效)。session
(Web): 每个 HTTP Session 创建一个实例(仅在 Web 应用中有效)。application
(Web): 每个ServletContext
生命周期创建一个实例(仅在 Web 应用中有效)。websocket
(Web): 每个 WebSocket 会话创建一个实例(仅在 Web 应用中有效)。
-
循环依赖:
- 问题: A 依赖 B,B 又依赖 A。
- Spring 解决方案 (针对 Singleton Bean):
- 使用三级缓存 (3-level cache):
singletonObjects
(一级缓存): 存放完全初始化好的单例 Bean。earlySingletonObjects
(二级缓存): 存放提前暴露的(只实例化但未填充属性)Bean 的原始引用或代理对象。用于解决循环依赖。singletonFactories
(三级缓存): 存放创建 Bean 的工厂对象 (ObjectFactory
)。用于在需要时创建 Bean 的早期引用或代理。
- 解决过程 (以 A->B->A 为例):
- 创建 A:实例化 A (此时 A 还不完整),将创建 A 的工厂放入三级缓存。
- 为 A 注入属性 B:发现需要 B。
- 创建 B:实例化 B (此时 B 也不完整),将创建 B 的工厂放入三级缓存。
- 为 B 注入属性 A:去一级缓存找 A(没有) -> 去二级缓存找 A(没有) -> 去三级缓存找到 A 的工厂 -> 调用工厂获取 A 的早期引用(可能是原始对象或代理对象)-> 将这个早期引用放入二级缓存 -> 将这个早期引用注入给 B。
- B 完成属性注入,执行初始化,成为一个完整的 Bean,放入一级缓存,同时清除二级和三级缓存中关于 B 的信息。
- A 成功注入 B(此时 B 已在一级缓存),A 完成属性注入,执行初始化,成为一个完整的 Bean,放入一级缓存,同时清除二级和三级缓存中关于 A 的信息。
- 关键点: 通过提前暴露一个处于“中间状态”(只实例化,未初始化)的 Bean 引用(原始对象或代理)来解决循环依赖。构造器注入无法解决循环依赖(因为实例化就需要依赖,而这时 Bean 自己都还没创建出来放入缓存)。
- 使用三级缓存 (3-level cache):
二、核心支柱:AOP (面向切面编程)
-
核心思想:
- 将那些与核心业务逻辑无关,但又在多个地方重复出现的功能(如日志、事务、安全、性能监控)横向抽取出来,形成独立的模块(称为“切面” - Aspect)。
- 在程序运行的特定时机(连接点 - Joinpoint),通过动态代理技术,将切面定义的逻辑(通知 - Advice)“织入”(Weave)到目标对象的方法中,而不修改原有业务代码。
-
核心概念:
- 切面 (Aspect): 封装横切关注点的模块。包含通知和切点。通常是一个用
@Aspect
注解的类。 - 连接点 (Joinpoint): 程序执行过程中的一个点(如方法调用、方法执行、异常抛出、字段访问)。Spring AOP 只支持方法执行类型的连接点。
- 通知 (Advice): 切面在特定连接点上执行的动作。类型:
@Before
: 在目标方法执行前执行。@AfterReturning
: 在目标方法成功执行后执行。@AfterThrowing
: 在目标方法抛出异常后执行。@After
/@After(finally)
: 在目标方法执行完成后执行(无论成功或异常),类似于finally
。@Around
: 环绕通知。功能最强大。可以控制目标方法是否执行、何时执行、修改参数、修改返回值、处理异常等。本质是将目标方法封装在这个通知方法内部执行。
- 切点 (Pointcut): 一个表达式,用于匹配哪些连接点需要应用通知。
@Pointcut
注解定义可重用的切点表达式。 - 目标对象 (Target Object): 被一个或多个切面通知的对象。包含核心业务逻辑。
- AOP 代理 (AOP Proxy): Spring AOP 通过创建代理对象来实现切面功能。客户端代码实际调用的是代理对象的方法,代理对象在调用目标方法前后执行通知逻辑。
- 织入 (Weaving): 将切面应用到目标对象并创建代理对象的过程。Spring AOP 在运行时(通常是应用启动时)通过动态代理完成织入。
- 切面 (Aspect): 封装横切关注点的模块。包含通知和切点。通常是一个用
-
实现机制:动态代理
- JDK 动态代理 (基于接口):
- 要求目标对象至少实现一个接口。
- 核心类是
java.lang.reflect.Proxy
和java.lang.reflect.InvocationHandler
。 - 生成的代理对象实现了目标对象的接口。
- CGLIB 动态代理 (基于子类):
- 目标对象可以不实现任何接口。
- 通过生成目标对象的子类作为代理对象。
- 核心类是
net.sf.cglib.proxy.Enhancer
和MethodInterceptor
。 - 注意: 不能代理
final
类或final
/private
/static
方法。
- Spring 的选择:
- 默认情况下,如果目标对象实现了接口,则使用 JDK 动态代理。
- 如果目标对象没有实现接口,则使用 CGLIB 动态代理。
- 可以通过配置强制使用 CGLIB (
<aop:aspectj-autoproxy proxy-target-class="true"/>
或@EnableAspectJAutoProxy(proxyTargetClass = true)
)。
- JDK 动态代理 (基于接口):
-
@AspectJ
支持:- Spring AOP 集成了 AspectJ 的注解和切点表达式语言,使得定义切面更加简洁直观。
- 底层实现仍然是 Spring 自己的运行时动态代理,而非 AspectJ 的编译时/类加载时织入。
-
应用场景:
- 声明式事务管理(Spring 事务的核心实现方式)
- 日志记录
- 安全和权限检查
- 性能监控和统计
- 异常处理统一封装
- 缓存管理
三、声明式事务管理
-
核心思想:
- 将复杂的事务管理代码(如
beginTransaction()
,commit()
,rollback()
)从业务逻辑中剥离出来。 - 开发者只需通过注解 (
@Transactional
) 或 XML 配置声明哪些方法需要事务、事务的传播行为、隔离级别、超时、只读等属性。 - Spring 通过 AOP 代理在运行时根据声明自动管理事务的边界(开始、提交、回滚)。
- 将复杂的事务管理代码(如
-
关键接口:
PlatformTransactionManager
(平台事务管理器): Spring 事务策略的核心接口。不同的数据访问技术(JDBC, JPA, Hibernate, JTA 等)有不同的实现。DataSourceTransactionManager
:用于 JDBC 和 MyBatis(基于DataSource
)。HibernateTransactionManager
:用于 Hibernate。JpaTransactionManager
:用于 JPA。JtaTransactionManager
:用于 JTA(分布式事务)。
TransactionDefinition
: 定义事务的属性(传播行为、隔离级别、超时、只读)。TransactionStatus
: 表示事务的运行时状态(是否新事务、是否回滚、是否完成等)。
-
传播行为 (Propagation Behavior): 定义多个事务方法相互调用时,事务应该如何传播。
REQUIRED
(默认): 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新事务。REQUIRES_NEW
: 创建一个新事务,如果当前存在事务,则挂起当前事务。新事务独立提交或回滚。SUPPORTS
: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。NOT_SUPPORTED
: 以非事务方式执行操作。如果当前存在事务,则挂起当前事务。MANDATORY
: 必须在事务中运行。如果当前没有事务,则抛出异常。NEVER
: 必须在非事务方式下运行。如果当前存在事务,则抛出异常。NESTED
: 如果当前存在事务,则在嵌套事务内执行。嵌套事务可以独立于外层事务回滚。如果当前没有事务,则行为同REQUIRED
。注意: 并非所有事务管理器都支持嵌套事务(如 JDBCDataSourceTransactionManager
通过 Savepoint 机制支持)。
-
隔离级别 (Isolation Level): 定义事务在访问数据库时,如何与其他并发事务交互。
DEFAULT
:使用底层数据库默认的隔离级别(通常是READ_COMMITTED
)。READ_UNCOMMITTED
:最低级别。可能读到未提交的数据(脏读)。READ_COMMITTED
:避免脏读。可能发生不可重复读和幻读。REPEATABLE_READ
:避免脏读和不可重复读。可能发生幻读。SERIALIZABLE
:最高级别。避免所有并发问题(脏读、不可重复读、幻读)。性能最差。
-
@Transactional
工作原理:- 使用
@Transactional
注解的方法/类被 Spring AOP 代理。 - 当调用代理对象的方法时:
- 代理从
TransactionManager
获取(或创建)一个事务 (TransactionStatus
)。 - 将事务与当前线程绑定(通常是
ThreadLocal
)。 - 调用目标对象的实际业务方法。
- 如果方法正常返回,代理提交事务。
- 如果方法抛出未检查异常(默认
RuntimeException
和Error
的子类)或配置需要回滚的异常,代理回滚事务。 - 如果抛出已检查异常(默认不回滚),则提交事务(除非配置了
rollbackFor
)。
- 代理从
- 解除事务与当前线程的绑定。
- 使用
-
常见失效场景:
- 方法非
public
。 - 方法在同一个类内部调用(绕过代理,直接调用目标方法)。
- 异常被
catch
住且未重新抛出(代理感知不到异常)。 - 抛出的异常类型不是默认回滚的异常类型且未配置
rollbackFor
。 - 使用了错误的事务管理器或未配置事务管理器。
- 数据库引擎不支持事务(如 MySQL 的 MyISAM)。
- 方法非
四、Spring MVC 核心流程
- DispatcherServlet:前端控制器,核心枢纽。
- 请求处理流程:
- 用户发送请求 -> 到达
DispatcherServlet
。 DispatcherServlet
调用HandlerMapping
: 根据请求 URL 等信息,查找能处理该请求的Handler
(Controller + Method)。DispatcherServlet
调用HandlerAdapter
: 找到能执行该Handler
的适配器(如RequestMappingHandlerAdapter
)。HandlerAdapter
执行Handler
(Controller 方法):- 解析参数(
@RequestParam
,@PathVariable
,@RequestBody
,Model
等)。 - 执行业务逻辑。
- 返回一个
ModelAndView
对象(包含模型数据 Model 和视图名称 View Name)或直接返回数据(配合@ResponseBody
)。
- 解析参数(
DispatcherServlet
调用ViewResolver
: 根据Handler
返回的视图名称,解析出具体的View
对象(如 JSP, Thymeleaf, FreeMarker 模板)。DispatcherServlet
将模型数据Model
传递给View
进行渲染。View
渲染完成,生成响应内容(HTML, JSON, XML 等)。DispatcherServlet
将响应返回给用户。
- 用户发送请求 -> 到达
五、设计模式的应用
Spring 框架本身就是设计模式的典范教科书:
- 工厂模式 (Factory):
BeanFactory
,ApplicationContext
是 Bean 创建的工厂。 - 单例模式 (Singleton): Spring Bean 默认作用域就是单例。容器保证只创建一个实例。
- 代理模式 (Proxy): AOP 的核心实现方式(JDK 动态代理、CGLIB 代理)。
- 模板方法模式 (Template Method):
JdbcTemplate
,RestTemplate
,JmsTemplate
等。定义算法骨架,将易变步骤延迟到子类或通过回调实现。 - 观察者模式 (Observer / Listener): Spring 事件机制 (
ApplicationEvent
,ApplicationListener
)。 - 适配器模式 (Adapter):
HandlerAdapter
(MVC 中适配不同类型的 Controller 方法执行),AdvisorAdapter
(AOP 中适配不同类型的 Advice)。 - 策略模式 (Strategy):
PlatformTransactionManager
(不同事务管理策略),HandlerMapping
(不同请求映射策略)。 - 装饰器模式 (Decorator): 在 AOP 的
Advice
链中,多个通知对目标方法进行层层包装增强。 - 依赖注入模式 (DI): 本身就是 IoC 的核心实现模式。
六、Bean 的生命周期(重要!)
理解 Bean 从创建到销毁过程中经历的各个关键点及其扩展点非常重要:
- 实例化 (Instantiate): 调用构造器创建 Bean 实例。
- 填充属性 (Populate Properties): 依赖注入(DI),设置属性值。
- Bean 后置处理器 (
BeanPostProcessor
) 前置处理: 调用postProcessBeforeInitialization
方法。可在此修改 Bean 实例(如返回代理)。 - 初始化 (Initialization):
- 调用
@PostConstruct
注解的方法。 - 如果实现了
InitializingBean
接口,调用afterPropertiesSet()
方法。 - 调用 XML 或
@Bean(initMethod = "...")
指定的自定义初始化方法。
- 调用
- Bean 后置处理器 (
BeanPostProcessor
) 后置处理: 调用postProcessAfterInitialization
方法。可在此修改 Bean 实例(如返回代理)。 - Bean 可用 (Ready): Bean 完全初始化,可被使用。
- 销毁 (Destruction): (容器关闭时)
- 调用
@PreDestroy
注解的方法。 - 如果实现了
DisposableBean
接口,调用destroy()
方法。 - 调用 XML 或
@Bean(destroyMethod = "...")
指定的自定义销毁方法。
- 调用
七、常用扩展点
BeanFactoryPostProcessor
: 在BeanDefinition
加载之后、Bean 实例化之前,允许修改容器的BeanDefinition
(如修改属性值)。例如:PropertySourcesPlaceholderConfigurer
(解析${...}
占位符),ConfigurationClassPostProcessor
(处理@Configuration
类)。BeanPostProcessor
: 在 Bean 实例化、依赖注入完成之后,在初始化方法调用之前和之后,提供修改 Bean 实例的机会(见生命周期第 3、5 步)。例如: AOP 创建代理 (AbstractAutoProxyCreator
),AutowiredAnnotationBeanPostProcessor
(处理@Autowired
,@Value
),CommonAnnotationBeanPostProcessor
(处理@PostConstruct
,@PreDestroy
,@Resource
)。ApplicationContextAware
等*Aware
接口: 让 Bean 能感知到 Spring 容器中的特定对象(如ApplicationContext
,BeanFactory
,Environment
)。实现对应接口,Spring 会在初始化时注入这些对象。慎用,会引入 Spring 耦合。
面试回答建议
- 结构化: 清晰地分点阐述(如 IoC、AOP、事务、MVC、设计模式)。
- 深入关键点: 对核心概念(IoC/DI 思想、AOP 原理、事务传播/隔离、Bean 生命周期)要能讲清楚本质和细节(如三级缓存解决循环依赖、动态代理实现 AOP)。
- 结合实践: 能举例说明这些原理在实际项目中的应用(如用 AOP 做了什么,事务配置遇到过什么问题)。
- 理解设计模式: 能说出 Spring 中常用的几种设计模式及其体现。
- 知道限制: 了解 Spring AOP 的局限性(只支持方法级别)、事务失效的常见原因。
- 表达清晰: 用简洁专业的语言描述,避免过于口语化或啰嗦。
掌握这些核心原理,不仅能让你在 Java 面试中游刃有余地回答 Spring 相关问题,更能深刻理解 Spring 框架的设计之美,提升你的架构思维和开发能力。祝你面试顺利!
📜文末寄语
- 🟠关注我,获取更多内容。
- 🟡技术动态、实战教程、问题解决方案等内容持续更新中。
- 🟢《全栈知识库》技术交流和分享社区,集结全栈各领域开发者,期待你的加入。
- 🔵加入开发者的《专属社群》,分享交流,技术之路不再孤独,一起变强。
- 🟣点击下方名片获取更多内容🍭🍭🍭👇