Spring 源码深度解析 – wiki基地


Spring 源码深度解析:揭开核心机制的神秘面纱

Spring 框架以其强大的功能、优雅的设计和高度的灵活性,成为了 Java 企业级应用开发的主流选择。我们在日常开发中熟练地使用着 IoC 容器管理 Bean、利用 AOP 实现横切关注点、通过事务注解简化数据操作等等。然而,仅仅停留在 API 的使用层面,我们就像在使用一个黑箱。

真正理解 Spring 如何工作的魔力,在于深入其源代码。阅读 Spring 源码不仅能帮助我们更好地排查问题、优化应用,更能提升我们的编程思想和设计能力。本文将带领读者踏上一段 Spring 源码的探索之旅,重点聚焦于其两大核心机制:控制反转(IoC)和面向切面编程(AOP)。

为什么要阅读 Spring 源码?

  1. 知其所以然: 深入理解 Spring 的设计理念和实现细节,不再是“魔法”而是“机制”。
  2. 高效调试与排障: 遇到问题时,能够根据异常堆栈快速定位到框架内部,理解出错的根本原因。
  3. 性能优化: 理解 Bean 的生命周期、AOP 的代理机制,可以更好地评估和优化应用性能。
  4. 高级应用与扩展: 能够利用 Spring 提供的扩展点(如 BeanPostProcessor、BeanFactoryPostProcessor)实现自定义功能。
  5. 提升设计能力: 学习 Spring 优秀的设计模式(如工厂模式、代理模式、模板方法模式)和代码组织方式。

开始前的准备

阅读 Spring 源码需要一些基础:

  • Java 基础: 扎实的 Java 功底,熟悉面向对象、反射、动态代理等核心概念。
  • Spring 基础: 了解 IoC、DI、AOP 的基本概念和常用 API 的使用。
  • 开发环境: 准备一个强大的 IDE(如 IntelliJ IDEA),并配置好 Spring 源码项目。通常需要从 GitHub 克隆 Spring Framework 仓库,并使用 Gradle 构建。

如何阅读 Spring 源码?

  1. 明确目标: 不要试图一次读完所有代码,选择一个核心功能(如 IoC 容器的初始化、Bean 的创建过程、AOP 代理的生成)作为切入点。
  2. 从入口开始: 找到核心功能的入口类或方法。例如,对于 IoC 容器,可以从 ApplicationContext 的实现类(如 ClassPathXmlApplicationContextAnnotationConfigApplicationContext)的构造方法或 refresh() 方法开始。
  3. 跟随主线: 顺着核心流程,通过断点调试、查看调用栈来理解代码的执行路径。
  4. 关注核心接口和类: Spring 大量使用了接口,理解接口的作用比记住具体实现类更重要。关注如 BeanFactory, ApplicationContext, BeanDefinition, BeanPostProcessor, MethodInterceptor 等核心组件。
  5. 跳过非主线逻辑: 初次阅读时,可以暂时跳过异常处理、日志、国际化等非核心业务逻辑。
  6. 结合文档和设计模式: 阅读代码时,思考它运用了哪些设计模式,查阅 Spring 官方文档或相关解析文章作为辅助。
  7. 画图: 梳理核心组件之间的关系和流程,画出流程图或类图帮助理解。

IoC 容器深度解析

IoC(Inversion of Control,控制反转)是 Spring 的基石。它负责管理对象的创建、组装和生命周期。理解 IoC 容器的源码,就是理解 Spring 如何管理 Bean 的过程。

核心接口与概念

  • BeanFactory Spring IoC 容器的最顶层接口,提供了最基本的 Bean 管理功能,如获取 Bean (getBean())。
  • ApplicationContext BeanFactory 的子接口,提供了更丰富的功能,如国际化、事件发布、资源加载,是我们在实际开发中主要使用的容器接口。其主要实现类如 ClassPathXmlApplicationContext, FileSystemXmlApplicationContext, AnnotationConfigApplicationContext 等。
  • BeanDefinition Bean 的定义信息,包括 Bean 的类名、作用域(singleton, prototype等)、属性值、依赖的 Bean、初始化方法、销毁方法等。容器根据 BeanDefinition 来创建 Bean。
  • BeanDefinitionReader 负责读取不同格式的 Bean 定义资源(如 XML、注解、Groovy)并将其转换为 BeanDefinition
  • BeanDefinitionRegistry 负责注册和管理 BeanDefinition 对象。DefaultListableBeanFactory 是一个重要的实现类,它同时实现了 BeanFactoryBeanDefinitionRegistry 接口,是 Spring 容器底层实现的核心。

IoC 容器的启动与初始化流程(refresh() 方法)

ApplicationContext 的核心初始化逻辑都在 refresh() 方法中。这个方法是一个模板方法,定义了容器初始化的标准流程,具体步骤由子类实现。以下是 refresh() 方法中的关键步骤(以 AbstractApplicationContext 为例):

  1. prepareRefresh() 准备刷新,设置容器的启动时间、状态,校验环境等。
  2. obtainFreshBeanFactory() 获取并初始化 BeanFactory。对于 ApplicationContext 实现类,这一步通常会创建一个 DefaultListableBeanFactory
  3. prepareBeanFactory(beanFactory) 配置 BeanFactory 的标准设置,如类加载器、属性编辑器,并注册一些重要的内置 BeanPostProcessor(后文详述)和忽略的依赖接口。
  4. postProcessBeanFactory(beanFactory) 留给子类扩展,允许在 BeanFactory 标准配置完成后进行自定义修改,例如注册自定义的 BeanPostProcessor
  5. invokeBeanFactoryPostProcessors(beanFactory) 调用 BeanFactoryPostProcessor 这是 Spring 扩展机制的重要一环。BeanFactoryPostProcessor 可以在 Bean 实例化 之前 修改 Bean 定义信息 (BeanDefinition)。例如,PropertySourcesPlaceholderConfigurer 用于处理属性文件中的占位符 ${...},它就是通过修改 BeanDefinition 中的属性值来实现的。配置类解析 (ConfigurationClassPostProcessor) 也是在这里完成的,它负责解析 @Configuration, @Component, @ComponentScan, @Import, @Bean 等注解,生成相应的 BeanDefinition
  6. registerBeanPostProcessors(beanFactory) 注册 BeanPostProcessor BeanPostProcessor 用于在 Bean 实例化和初始化 之后 对 Bean 进行增强或替换。Spring 的很多核心功能,如 AOP、事务、依赖注入等,都是通过内置的 BeanPostProcessor 实现的。这一步只是将实现了 BeanPostProcessor 接口的 Bean 注册到容器中,等待后续使用。
  7. initMessageSource() 初始化国际化资源。
  8. initApplicationEventMulticaster() 初始化应用事件多播器,用于发布和监听应用事件。
  9. onRefresh() 留给子类扩展,在 BeanFactory 准备就绪后,实例化非延迟加载的单例 Bean 之前执行。Web 应用中,这里会创建并初始化 DispatcherServlet
  10. registerListeners() 注册监听器 Bean,并将早期事件发布给它们。
  11. finishBeanFactoryInitialization(beanFactory) 实例化所有非延迟加载的单例 Bean。 这是 IoC 容器初始化最耗时的一步。容器会遍历所有 BeanDefinition,对于单例且非延迟加载的 Bean,开始 Bean 的创建过程。
  12. finishRefresh() 完成刷新过程,发布容器刷新完成事件,清空缓存等。

Bean 的创建过程(doCreateBean 等方法)

这是 IoC 容器的核心中的核心。当需要创建 Bean 时(例如容器初始化时创建单例 Bean,或者调用 getBean() 方法时),会执行以下主要步骤(简化版流程,实际过程非常复杂,涉及多个方法和后置处理器):

  1. 获取 BeanDefinition 根据 BeanName 从注册中心获取对应的 BeanDefinition
  2. 实例化前处理: 调用 InstantiationAwareBeanPostProcessorpostProcessBeforeInstantiation 方法。如果任一后置处理器返回非 null 的 Bean 实例,则跳过后续的实例化和属性填充步骤,直接进入初始化后处理。这是 AOP 实现代理的常见方式之一。
  3. 实例化: 根据 BeanDefinition 创建 Bean 实例。可以通过构造器注入或无参构造器。使用 BeanWrapper 包装 Bean 实例,方便后续操作。
  4. 实例化后处理: 调用 InstantiationAwareBeanPostProcessorpostProcessAfterInstantiation 方法。可以决定是否继续进行属性填充。
  5. 属性填充前处理: 调用 InstantiationAwareBeanPostProcessorpostProcessPropertiespostProcessPropertyValues 方法。用于在属性填充前修改属性值,例如处理 @Autowired, @Value 等注解,实现依赖注入。
  6. 属性填充: 根据 BeanDefinition 中的属性值和前一步处理结果,利用反射或 BeanWrapper 设置 Bean 实例的属性。
  7. 初始化前处理: 调用 BeanPostProcessorpostProcessBeforeInitialization 方法。许多 Spring 内置功能(如 AOP 代理的创建、处理 @PostConstruct 注解)都在这里实现。
  8. 执行初始化方法: 如果 Bean 实现了 InitializingBean 接口,调用 afterPropertiesSet() 方法;如果 BeanDefinition 指定了 init-method,调用指定方法。
  9. 初始化后处理: 调用 BeanPostProcessorpostProcessAfterInitialization 方法。 这是 AOP 代理对象最终返回的地方!如果一个 Bean 需要被代理,某个 BeanPostProcessor 会在这里返回 Bean 的代理对象,而不是原始 Bean 对象。后续获取到的就是这个代理对象。
  10. 注册可销毁 Bean: 对于单例 Bean,如果定义了销毁方法(如实现 DisposableBean 接口或指定 destroy-method),将其注册到容器中,以便在容器关闭时调用。

通过上述流程可以看到,BeanPostProcessor 在 Bean 的生命周期中扮演了至关重要的角色,它提供了在 Bean 实例化、属性填充、初始化等阶段插入自定义逻辑的能力,是 Spring 实现 AOP、声明式事务、依赖注入等核心功能的关键扩展点。

AOP 深度解析

AOP(Aspect-Oriented Programming,面向切面编程)是 Spring 的另一大核心功能,用于解决横切关注点(如日志、事务、安全)的分离问题。Spring AOP 的实现是基于代理的。

核心概念

  • Aspect(切面): 跨多个对象关注点的模块化。例如,事务管理可以被视为一个切面。在 Spring AOP 中,切面通常是带有 @Aspect 注解的类。
  • Join Point(连接点): 程序执行过程中的一个特定点,如方法的调用、异常的抛出等。Spring AOP 主要支持方法执行连接点。
  • Advice(通知/增强): 在特定的连接点执行的操作。例如,在方法调用 之前 执行的日志记录操作就是一个 Before Advice。Spring 支持多种类型的 Advice:
    • @Before (前置通知)
    • @AfterReturning (后置通知,方法正常返回后执行)
    • @AfterThrowing (异常通知,方法抛出异常后执行)
    • @After (最终通知,无论方法是否抛异常都执行)
    • @Around (环绕通知,包围一个连接点,可以控制连接点是否执行,是功能最强大的 Advice)
  • Pointcut(切入点): 匹配连接点的表达式。Advice 与 Pointcut 关联,表示 Advice 要作用于哪些连接点。
  • Advisor(通知器): Advisor 是 Pointcut 和 Advice 的组合。在 Spring 内部,它代表了一个完整的切面配置单元。
  • Target Object(目标对象): 包含被通知代码的对象,即业务对象。
  • AOP Proxy(AOP 代理): Spring AOP 生成的代理对象,它包含目标对象的引用,并在调用目标方法时织入 Advice。

Spring AOP 的实现机制

Spring AOP 默认使用 JDK 动态代理CGLIB 来创建代理对象。

  • 如果目标对象实现了接口,并且配置了代理接口而非类,Spring 默认使用 JDK 动态代理。
  • 如果目标对象没有实现接口,或者配置了代理类,Spring 使用 CGLIB 生成子类代理。

核心流程与 IoC 容器紧密集成:

  1. 配置解析: 当 IoC 容器启动并调用 invokeBeanFactoryPostProcessors() 方法时,ConfigurationClassPostProcessor 会解析 @EnableAspectJAutoProxy@Aspect 等注解。
  2. 注册 BeanPostProcessor 如果启用了 AspectJ 自动代理(例如通过 @EnableAspectJAutoProxy),容器会注册一个重要的 BeanPostProcessorAnnotationAwareAspectJAutoProxyCreator (或其父类 AbstractAutoProxyCreator)。
  3. 识别需要代理的 Bean: 当容器创建 Bean 并执行到 BeanPostProcessorpostProcessBeforeInitializationpostProcessAfterInitialization 方法时,AnnotationAwareAspectJAutoProxyCreator 会介入。
  4. 查找适用的 Advisor: AnnotationAwareAspectJAutoProxyCreator 会扫描所有的 Bean,查找带有 @Aspect 注解的 Bean,并将其中的 Advice 和 Pointcut 封装成一个个的 Advisor
  5. 判断是否需要代理: 对于当前正在创建的 Bean (Target Object),AnnotationAwareAspectJAutoProxyCreator 会遍历所有找到的 Advisor,检查它们的 Pointcut 是否匹配当前 Bean 的类或方法。如果匹配,说明这个 Bean 需要被代理。
  6. 创建代理对象: 如果 Bean 需要被代理,AnnotationAwareAspectJAutoProxyCreator 会调用代理创建逻辑(例如 DefaultAopProxyFactory)。根据目标对象是否实现接口以及配置,选择使用 JdkDynamicAopProxyCglibAopProxy 来创建代理对象。
  7. 织入 Advice: 代理对象内部维护了一个 MethodInterceptor 链。当通过代理对象调用目标方法时,请求会被拦截。这个拦截器链会按顺序执行与该方法匹配的 Advice(通过 Pointcut 匹配)。对于 @Around 通知,它会包装对目标方法的调用;对于其他通知,则在调用目标方法之前或之后执行相应的逻辑。
  8. 返回代理对象:postProcessAfterInitialization 方法中,AnnotationAwareAspectJAutoProxyCreator 返回创建好的代理对象,替换了原始的 Bean 实例。后续从容器获取到的就是这个代理对象。

可以看到,AOP 的实现深度依赖于 IoC 容器提供的 BeanPostProcessor 扩展点。代理的创建和织入都是在 Bean 的生命周期中完成的。

AOP 的调用链(Around Advice 为例)

当调用一个被 @Around 通知环绕的方法时,实际的调用流程大致如下:

代理对象的方法调用 -> 拦截器链(通过 CglibMethodInvocationReflectiveMethodInvocation 封装调用信息) -> 第一个 Advice (MethodInterceptor) 执行 -> 调用 invocation.proceed() -> 下一个 Advice 执行 -> … -> 最后一个 Advice 执行 -> 调用 invocation.proceed() -> 调用目标对象的原始方法 -> 结果返回,Advice 逆序执行剩余逻辑。

这种链式调用机制使得 AOP 非常灵活,可以方便地叠加多个 Advice。

探索其他模块

除了 IoC 和 AOP,Spring 还有许多重要的模块值得深入探索:

  • 事务管理: @Transactional 注解的背后是 Spring 声明式事务。它的实现同样依赖于 AOP 和 BeanPostProcessor(例如 BeanFactoryTransactionAttributeSourceAdvisorTransactionInterceptor)。深入源码可以理解事务如何与数据库连接、提交/回滚逻辑关联。
  • Spring MVC:DispatcherServlet 的初始化开始,理解请求如何被接收、映射到 Controller、参数如何绑定、结果如何处理、视图如何解析。涉及 HandlerMapping, HandlerAdapter, ViewResolver 等核心组件。
  • Spring Boot 自动配置: Spring Boot 基于 Spring Framework 构建,其强大的自动配置能力是其魅力所在。阅读源码(如 EnableAutoConfigurationImportSelector 和各种 *AutoConfiguration 类)可以理解它是如何通过 SPI 机制 (spring.factories) 加载配置类、如何利用条件注解 (@Conditional*) 按需创建 Bean 的。
  • 事件机制: 理解 Spring 事件发布/订阅模型(ApplicationEventPublisher, ApplicationListener, ApplicationEventMulticaster)。
  • 资源管理: 理解 Spring 如何抽象和加载各种资源(文件、URL、Classpath 等),以及 ResourceResourceLoader 接口。

总结与进阶

阅读 Spring 源码是一个持续学习的过程。开始时可能会感到困难,但随着对核心流程的熟悉,你会发现 Spring 的代码组织非常清晰,大量使用了接口和设计模式,易于理解和扩展。

通过源码学习,你将对以下方面有更深刻的认识:

  • 依赖注入的本质实现。
  • AOP 代理的生成和调用机制。
  • 声明式事务的工作原理。
  • Spring 如何处理扩展点。
  • 大型框架的设计和模块化思想。

掌握了这些,你在面对 Spring 相关问题时将更加从容,也能写出更符合 Spring 理念的高质量代码。不妨从一个你最感兴趣或者在工作中常遇到的问题入手,开始你的 Spring 源码探索之旅吧!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部