Spring源码分析

Spring源码分析
mengnankkzhouIOC
BeanDefinition
BeanDefinition 是 Spring IoC 容器的基石,它是 Bean 的一切元数据(类名、作用域、构造函数参数、属性等)的载体,是面向接口编程思想的体现。容器操作的是 BeanDefinition,而非直接操作 Class。
我们最常用的就是DefaultListableBeanFactory 中 beanDefinitionMap 这个 ConcurrentHashMap,所有加载的 BeanDefinition 都存储于此。
然后我们的IOC容器ApplicationContext 根据其实现(XML, Annotation)选择不同的策略(Reader, Scanner)来加载 BeanDefinition。模板方法模式在 AbstractBeanDefinitionReader 等类中被广泛使用。
XML 路径: 从 AbstractXmlApplicationContext 的构造函数出发 -> loadBeanDefinitions(new XmlBeanDefinitionReader(this)) -> XmlBeanDefinitionReader.loadBeanDefinitions() -> doLoadDocument() (将XML转为DOM) -> registerBeanDefinitions() (解析DOM并注册 BeanDefinition)。
注解路径: 从 AnnotationConfigApplicationContext 构造函数出发 -> this.scanner.scan(...) -> ClassPathBeanDefinitionScanner.doScan() -> findCandidateComponents() (扫描路径,找到符合条件的类) -> isCandidateComponent() (检查是否包含 @Component 等注解) -> registerBeanDefinition()。
IOC容器就是BeanFactory 是 IoC 容器的“核心引擎”,负责 Bean 的生命周期管理。而 ApplicationContext 是一个更高级的“企业级容器”,它继承了 BeanFactory 的所有功能,并在此基础上集成了AOP、事件发布、国际化等众多高级服务。
BeanFactory: 顶层接口,定义了 getBean() 等核心方法。
ListableBeanFactory: 扩展接口,提供枚举所有 Bean 的能力。
ConfigurableListableBeanFactory: BeanFactory 体系的终极接口,几乎包含了所有功能。
ApplicationContext: 继承了 ListableBeanFactory 和其他多个高级接口。
AbstractApplicationContext: 核心中的核心,其 refresh() 方法定义了容器启动的完整流程。
追踪AbstractApplicationContext.refresh() 方法:
refresh()方法可以说是Spring IoC容器的“启动引擎”。它的核心使命就是根据我们的配置,创建并初始化一个功能完备的ApplicationContext,最终让所有Bean都准备就绪,随时可以被应用程序使用。准备与定义加载
prepareRefresh(): 做一些启动前的准备工作,比如设置启动时间、激活状态等。obtainFreshBeanFactory(): 这是奠定基础的一步。它会创建一个DefaultListableBeanFactory(IoC容器的底层核心),然后去解析我们的配置文件(无论是XML还是注解),把Bean的定义信息,也就是BeanDefinition,加载到容器中。需要强调的是,这个阶段只是加载了“蓝图”,还没有创建任何Bean的实例。prepareBeanFactory(): 对创建好的BeanFactory进行一些基础配置,比如设置类加载器、添加一些内置的后置处理器等。postProcessBeanFactory(): 这是一个模板方法,留给子类去扩展,体现了Spring设计的灵活性。第二阶段:核心处理与扩展
这个阶段是
refresh()的精髓所在,体现了Spring强大的扩展能力。这里有两个最重要的扩展点:
invokeBeanFactoryPostProcessors(): 调用所有BeanFactoryPostProcessor。这个扩展点允许我们在所有Bean实例化之前,去修改Bean的定义信息(BeanDefinition)。比如,我们常用的${...}属性占位符替换,就是在这里完成的。@Configuration和@ComponentScan等注解的解析也是由这个阶段的后置处理器完成的。registerBeanPostProcessors(): 注册所有BeanPostProcessor。这个扩展点则是在Bean实例化之后、初始化前后对Bean实例本身进行“加工”。Spring的AOP和声明式事务就是通过这个机制实现的。它会在这里为符合条件的Bean创建动态代理对象,对Bean实例进行增强。第三阶段:实例化与完成
initMessageSource()和initApplicationEventMult-icaster(): 分别初始化国际化支持和事件广播器,为事件驱动模型做准备。onRefresh(): 又一个模板方法,像Spring Boot内嵌的Tomcat就是在这里启动的。registerListeners(): 把我们定义的事件监听器注册到事件广播器上。finishBeanFactoryInitialization(): 这是整个流程中最重量级的一步。Spring会在这里实例化所有非懒加载的单例Bean。这个过程包括了实例化、属性填充(依赖注入)、以及执行各种初始化回调,比如@PostConstruct和BeanPostProcessor的前后置处理方法。finishRefresh(): 所有工作完成后,它会发布一个ContextRefreshedEvent事件,通知应用程序容器已经准备就绪。
bean的生命周期
AbstractAutowireCapableBeanFactory: Bean 生命周期的主要执行者。
DefaultListableBeanFactory.preInstantiateSingletons() -> getBean() -> doGetBean()开始
AbstractAutowireCapableBeanFactory.createBean()包含以下扩展点:
resolveBeforeInstantiation(): 第一次扩展点,允许 InstantiationAwareBeanPostProcessor 返回一个代理对象,直接短路后续流程(AOP 早期代理化的机会)。
doCreateBean():
createBeanInstance(): 【实例化】 通过构造函数反射创建 Bean 的空对象。populateBean(): 【属性填充】 依赖注入发生于此。会调用InstantiationAwareBeanPostProcessor的postProcessProperties()方法。initializeBean(): 【初始化】 这是最复杂的阶段,顺序如下:invokeAwareMethods(): 调用BeanNameAware,BeanFactoryAware等接口。applyBeanPostProcessorsBeforeInitialization(): 调用所有BeanPostProcessor的postProcessBeforeInitialization方法。invokeInitMethods(): 调用afterPropertiesSet()(InitializingBean) 或自定义的init-method。applyBeanPostProcessorsAfterInitialization(): 【AOP 关键点】 调用所有BeanPostProcessor的postProcessAfterInitialization方法。AOP 就是在这里通过AnnotationAwareAspectJAutoProxyCreator将原始对象包装成代理对象的。
循环依赖的解决
Spring 用来解决单例 Bean Setter 注入循环依赖的“三级缓存”机制
DefaultSingletonBeanRegistry这个来解决
从doGetBean() -> getSingleton(String beanName, ObjectFactory<?> singletonFactory)开始
singletonObjects (一级缓存): Map<String, Object>,存放完全初始化好的 Bean,是最终的成品。
earlySingletonObjects (二级缓存): Map<String, Object>,存放提前暴露的、未完成初始化的 Bean。这个 Bean 可能是原始对象,也可能是已经过 AOP 代理的对象。
singletonFactories (三级缓存): Map<String, ObjectFactory<?>>,存放一个能产生 Bean 的工厂 ObjectFactory(通常是一个 Lambda 表达式)。工厂的目的是推迟代理对象的创建。
例子:
A 正在创建,实例化后,将一个 () -> getEarlyBeanReference(beanName, mbd, bean) 的 ObjectFactory 放入三级缓存 singletonFactories。
A 在属性填充时发现需要 B,于是去创建 B。
B 在创建过程中发现需要 A,于是去 getBean(A)。
getBean(A) 时,先查一级缓存(无),再查二级缓存(无),最后查到三级缓存中有 A 的工厂。
执行 A 的 ObjectFactory,调用 getEarlyBeanReference(),如果 A 需要被代理,就在此时创建代理对象,否则返回原始对象。然后将结果放入二级缓存 earlySingletonObjects,并移除三级缓存中的工厂。B 获得了 A 的(代理)对象,完成创建。
B 创建完毕,A 获得了 B 的对象,也完成创建,最终放入一级缓存。
- 空间换时间和延迟计算。通过增加缓存来解决对象创建过程中的时序问题。三级缓存的核心思想是,只有在真正发生循环依赖时,才通过
ObjectFactory去提前创建(可能存在的)代理对象。
AOP
动态代理
AOP 是如何通过“运行时织入”的方式,在不侵入业务代码的前提下,为其附加新功能的。掌握两种动态代理技术的底层原理及其在 Spring 中的应用。
动态代理模式:
JDK: java.lang.reflect.Proxy, java.lang.reflect.InvocationHandler。
CGLIB: net.sf.cglib.proxy.Enhancer, net.sf.cglib.proxy.MethodInterceptor。
Spring 封装: AopProxyFactory, JdkDynamicAopProxy, ObjenesisCglibAopProxy。
跟踪 DefaultAopProxyFactory.createAopProxy() 方法:
DefaultAopProxyFactory.createAopProxy()这个方法是Spring AOP创建代理对象的决策中心。它的核心职责就是根据目标对象的特征,决定到底使用哪种动态代理技术来创建代理。如果目标对象实现了一个或多个接口,Spring默认会选择使用 JDK动态代理。它会在运行时动态创建一个新的代理类,这个代理类实现了目标对象所实现的所有接口,并将方法调用转发给一个
InvocationHandler进行处理。如果目标对象没有实现任何接口,那么JDK动态代理就无法工作了。此时,Spring会选择使用 CGLIB代理,CGLIB通过在运行时动态创建一个目标类的子类来作为代理。因为它使用的是继承,所以不要求目标类实现接口。
还有一个关键配置会影响这个决策,那就是
proxyTargetClass属性。
- 如果
proxy-target-class被设置为true(在Spring Boot中这是默认设置),那么createAopProxy()方法会跳过接口检查,直接选择使用CGLIB代理。这样做的好处是统一了代理行为,避免了因目标对象是否有接口而导致代理方式不同的问题,并且可以直接代理类本身的方法,而不仅仅是接口方法。
代理链路
Spring AOP 从解析切面 (@Aspect) 到创建代理,再到执行通知 (Advice)
重要的类:
AnnotationAwareAspectJAutoProxyCreator: 最核心的 BeanPostProcessor,驱动整个 AOP 流程。
Advisor: Spring AOP 的内部最小单元,包含一个 Pointcut 和一个 Advice。
Advised: 代理对象的配置信息持有者。
ReflectiveMethodInvocation: 方法调用链的实现,负责依次执行拦截器。
步骤:
代理创建: 如前所述,发生在 AnnotationAwareAspectJAutoProxyCreator 的 postProcessAfterInitialization 方法中。它会调用 wrapIfNecessary()。
寻找匹配的 Advisor: 在 wrapIfNecessary() 内部,会调用 findCandidateAdvisors() 找到所有切面,然后通过 findAdvisorsThatCanApply() 根据切入点表达式(Pointcut)筛选出适用于当前 Bean 的所有 Advisor。
创建代理: 如果找到 Advisor,则调用 createProxy() 创建代理对象。
代理执行: 当调用代理对象的方法时,会进入 JdkDynamicAopProxy.invoke() 或 CglibAopProxy.DynamicAdvisedInterceptor.intercept()。
构建拦截器链: 代理逻辑会从 Advised 配置中获取所有适用的 Advisor,并将它们转换成一个拦截器链(List<Object> chain)。
链式调用: 创建一个 ReflectiveMethodInvocation 实例,并调用其 proceed() 方法。proceed() 方法会依次调用链中的每一个拦截器(即 Advice),最后调用目标方法。
事务
Spring 如何巧妙地利用 AOP 将命令式的、与业务逻辑高度耦合的事务代码(try-catch-finally, commit, rollback)“剥离”出去,转化为声明式的、以注解驱动的优雅实现。核心在于理解“围绕方法调用的事务边界控制”
原理
Spring 事务管理的核心是 AOP 代理,并且其实现依赖于一个与具体数据访问技术解耦的抽象层。
核心类:
PlatformTransactionManager: 事务管理器的核心策略接口。不同的数据访问技术有不同的实现
TransactionDefinition: 定义事务的属性,如隔离级别 (Isolation)、传播行为 (Propagation)、超时、是否只读。
TransactionInterceptor: AOP 通知 (Advice) 的具体实现,是驱动事务管理的核心逻辑所在。
TransactionSynchronizationManager: 事务同步管理器。它使用 ThreadLocal 来保存和管理当前线程的事务状态(如连接、TransactionStatus),是确保事务在单线程内正确运行和线程安全的关键。
步骤:
入口:当一个被 @Transactional 注解的方法被调用时,AOP 代理会拦截这个调用。
拦截器介入:调用被转发到 TransactionInterceptor 的 invoke() 方法。
核心处理:invoke() 方法直接委托给父类 TransactionAspectSupport 的 invokeWithinTransaction() 方法。这是整个事务模块的心脏。
invokeWithinTransaction() 内部流程:
a. 获取事务属性:通过 TransactionAttributeSource 解析方法上的 @Transactional 注解,获取 TransactionDefinition。
b. 获取事务管理器:确定要使用的 PlatformTransactionManager。
c. 开启事务:调用 transactionManager.getTransaction(txAttr)。这是传播行为生效的地方。管理器会查询 TransactionSynchronizationManager,检查当前线程是否已存在事务: 若无事务且传播行为为 REQUIRED / REQUIRES_NEW / NESTED,则开启一个新事务,并将数据库连接和事务状态绑定到当前线程。 若有事务且传播行为为 REQUIRED / SUPPORTS,则加入当前事务。 * 若有事务且传播行为为 REQUIRES_NEW,则挂起当前事务,开启一个全新的事务。
d. 执行业务逻辑:将事务状态 TransactionStatus 准备好后,在一个 try-catch 块中,调用 invocation.proceed(),执行原始的业务方法。
e. 提交事务:如果业务方法正常执行完毕,则调用 commitTransactionAfterReturning(),内部最终会调用 transactionManager.commit(status)。
f. 回滚事务:如果业务方法抛出异常,catch 块会捕获它。completeTransactionAfterThrowing() 会被调用,它会检查抛出的异常是否满足回滚规则(默认是 RuntimeException 和 Error),如果满足,则调用 transactionManager.rollback(status)。
根据我们的spring事务的底层原理我们可以知道,我们事务失效的场景有哪些?
跳过了AOP代理
- this直接调用,跳过了AOP代理
- 权限错误,Spring AOP默认使用CGLIB创建代理,它通过继承目标类并重写
public方法来实现。 - 方法被
final修饰,一个被final修饰的方法无法被子类重写,因此AOP无法对其进行代理。 - 方法被
static修饰,static方法属于类,而不属于任何实例。Spring的AOP是基于实例的代理,无法代理静态方法。
异常
- 异常被
try-catch块捕获且未抛出,AOP的回滚是依赖捕获到了从事务方法中抛出的异常 - 抛出的异常类型不被默认回滚规则覆盖,异常的类型错误,在遇到
RuntimeException(非受检异常)或Error时才会触发回滚
- 异常被
事务传播
- 事务传播行为(Propagation)配置不当,比如NOT_SUPPORTED,NEVER
外部环境
- 数据库引擎不支持事务,比如MyISAM引擎的Mysql
- Spring相关配置问题,
@SpringBootApplication所在的启动类无法扫描到你的Service包。 2. 忘记在启动类上添加@EnableTransactionManagement注解
MVC
流程:
入口:所有 HTTP 请求进入 DispatcherServlet 的 service() 方法,最终由 doDispatch() 方法处理。doDispatch() 是 Spring MVC 的“主循环”。
doDispatch()
内部流程:
a. getHandler(): 遍历所有注册的 HandlerMapping,调用它们的 getHandler() 方法,直到找到一个匹配当前请求的 HandlerExecutionChain。这个 Chain 对象中包含了真正的处理器(HandlerMethod)和应用于它的所有拦截器。
b. getHandlerAdapter(): 遍历所有注册的 HandlerAdapter,找到一个支持上一步得到的 Handler 的适配器。
c. applyPreHandle(): 按顺序执行 HandlerExecutionChain 中所有拦截器的 preHandle() 方法。如果任何一个返回 false,则请求处理中断。
d. handlerAdapter.handle(): 核心调用。RequestMappingHandlerAdapter 的 handle() 方法会委托给 invokeHandlerMethod()。此方法内部:
i. 通过 HandlerMethodArgumentResolver 链,解析并准备 Controller 方法所需的所有参数。
ii. 通过反射调用真正的 Controller 方法。
iii. 通过 `HandlerMethodReturnValueHandler` 链,处理 Controller 方法的返回值,并将其封装到 `ModelAndView` 对象中。
e. applyPostHandle(): 倒序执行所有拦截器的 postHandle() 方法。
f. processDispatchResult(): 处理 ModelAndView。调用 render() 方法,render() 方法会使用 ViewResolver 找到对应的 View 对象,然后调用 view.render() 将模型数据渲染到响应中。
g. triggerAfterCompletion(): 在 finally 块中,无论成功与否,都会倒序执行拦截器的 afterCompletion() 方法,用于资源清理。
当然根据这个我们也可以将MVC的设计应用到一些重复性的工作之中,prehandel进行前置处理,finally进行后置处理
Boot
Spring Boot “约定大于配置” 的神秘面纱。理解其本质并非“魔法”,而是一套基于 Classpath 内容和环境配置的、条件化的、可插拔的 Bean 注册机制。
掌握 Spring Boot 是如何通过 @EnableAutoConfiguration 扫描、加载、并根据条件(@Conditional)过滤配置类,最终完成 Bean 自动注册的全过程。
流程:
起点:SpringApplication.run() 启动应用,创建 ApplicationContext。
触发:在容器 refresh 过程中,Spring 会解析主配置类上的 @SpringBootApplication 注解,进而处理其元注解 @EnableAutoConfiguration。
导入选择器:@EnableAutoConfiguration 通过 @Import(AutoConfigurationImportSelector.class) 引入了 AutoConfigurationImportSelector。
收集候选配置:Spring 会调用 AutoConfigurationImportSelector 的 selectImports() 方法。该方法会:
a. 调用 getCandidateConfigurations(),它使用 SpringFactoriesLoader (或新的 AutoConfiguration 机制) 去扫描所有 classpath 下的 JAR 包,读取 spring.factories 或 .imports 文件中 org.springframework.boot.autoconfigure.EnableAutoConfiguration key 下的自动配置类全名列表。
过滤与评估:得到这个庞大的候选列表后,并不会全部加载,而是进行严格的过滤:
a. 首先,移除用户通过 exclude 属性明确排除的配置类。
b. 然后,最重要的步骤:遍历每一个候选配置类,使用 ConditionEvaluator 检查该类上的所有 @Conditional... 注解。
c. 例如,DataSourceAutoConfiguration 上有 @ConditionalOnClass(DataSource.class),评估器会检查 DataSource.class 是否存在于当前 Classpath。RabbitAutoConfiguration 上的 @ConditionalOnClass(RabbitTemplate.class) 会检查 RabbitMQ 的客户端库是否存在。
d. 只有所有条件都满足的配置类,才会被认为是“激活”的。
最终加载:selectImports() 方法最终返回一个“激活”的配置类名称数组。Spring 会像处理普通的 @Configuration 类一样,将它们加载到 IoC 容器中,执行其中的 @Bean 方法,从而完成 Bean 的自动配置。
理解
SPI (Service Provider Interface):spring.factories / .imports 文件机制是一种 SPI 的实现,它允许第三方库(starters)向 Spring Boot “注册”自己的自动配置能力,实现了完美的解耦和可插拔性。
约定大于配置 (Convention over Configuration):Spring Boot 替你做了最佳实践的预设。例如,只要 spring-boot-starter-web 在 classpath 中,它就“约定”你可能需要一个内嵌的 Tomcat 和一个配置好的 DispatcherServlet。
条件化配置 (Conditional Configuration):@Conditional 注解是整个自动配置体系的基石,它让配置变得智能和按需加载,避免了不必要的资源消耗和配置冲突。












