)
文档教程知识库【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址https://gitcode.com/doocs/source-code-hunter点击查看免费下载本文源自 doocs/source-code-hunter 仓库中的 Spring 源码解读系列围绕一个 Bean 是怎么被创建出来的这一主线用 16 张图梳理 Spring 的整体脉络从 BeanDefinition 的解析与注册、BeanFactory 与 FactoryBean 的分工到 ApplicationContext 的扩展能力、Bean 生命周期的 14 个标准步骤再到 BeanFactoryPostProcessor / BeanPostProcessor 两大扩展点与 AOP 代理的生效位置。读完本文你将能在源码层面建立 Spring 的整体坐标系知道一个 Bean 在什么时候经历了什么、在哪里可以被拦截和修改。概览一张图看懂 Spring 要讲什么本文会先按下面的知识地图展开Bean 解析流程、BeanDefinition、反射与 BeanFactory、FactoryBean、ApplicationContext、IoC 容器、BeanFactory 后置处理器、Bean 生命周期、Bean 后置处理器以及 AOP 的生效位置。印象中的 SpringIOC 工厂模式 XML 反射在接触源码之前可以先在脑海里建立这样一条公式IOC 工厂模式 XML 反射而 DI依赖注入、AOP、事务等能力在早期的 XML 配置中都能很直观地表现出来。虽然现在主流开发已改用注解Component、Autowired等但底层原理基本一致。对于学习而言建议先通过 XML 这种结构化的、有层次感的配置入手更容易留下深刻印象之后再迁移到注解视角就不难了。小小 Spring把容器浓缩成一张图如果把 Spring 浓缩一下就可以得到下面这张小小 SpringBeanDefinition 作为原料被解析、注册进容器BeanFactory 作为工厂负责生产 Bean生产过程中经历实例化、属性填充、初始化等工序并通过后置处理器提供扩展点。使用 Spring 最主要的一点就是让它帮我们管理、创建 Bean。那么先从源头看起——Bean 从哪来。Bean 解析流程BeanDefinition 的诞生与注册如图所示整个流程的核心是通过解析器对 XML 文件或注解进行解析把配置信息封装成BeanDefinition对象再通过BeanDefinitionRegistry接口将这些信息注册起来最终存放在beanDefinitionMap变量中key beanNamevalue BeanDefinition。这一步在仓库的 BeanDefinition 的资源定位过程、将 bean 解析封装成 BeanDefinition、将 BeanDefinition 注册进 IoC 容器 三篇文档中有完整源码走读核心调用链如下AbstractApplicationContext.refresh() └─ obtainFreshBeanFactory() // 刷新内部 beanFactory └─ refreshBeanFactory() // 模板方法子类实现 └─ loadBeanDefinitions(beanFactory) // 载入 BeanDefinition └─ XmlBeanDefinitionReader // XML 读取器 └─ BeanDefinitionDocumentReader // 解析 Document └─ BeanDefinitionParserDelegate // 具体解析 bean 元素 └─ BeanDefinitionReaderUtils.registerBeanDefinition() └─ DefaultListableBeanFactory.registerBeanDefinition() └─ beanDefinitionMap.put(beanName, beanDefinition)从源码结构看DefaultListableBeanFactory中beanDefinitionMap的实际形态是/** IoC 容器的实际体现key -- beanNamevalue -- BeanDefinition 对象 */ private final MapString, BeanDefinition beanDefinitionMap new ConcurrentHashMap(64);Component、Bean、bean/最终都会被解析成BeanDefinition只是解析入口不同注解扫描 vs XML 解析。BeanDefinition 的关键属性简单看看BeanDefinition中的核心属性属性含义beanClassBean 的类型实例化时使用scope作用范围有singleton、prototypeWeb 环境下还有request、session等isLazy懒加载。true时会在getBean()时才生成且此时scope prototype的每次新建语义无效false时在 Spring 启动过程中直接生成initMethodName初始化方法在 Bean 初始化阶段调用primary是否为主要候选者存在多个同类型 Bean 时优先注入它dependsOn依赖的 Bean必须等这些依赖 Bean 创建完成后才可以创建当前 Bean除上述属性外BeanDefinition还承载了构造参数constructorArgumentValues、属性值propertyValues、自动装配模式autowireMode、方法覆盖methodOverrides等信息——在 将 bean 解析封装成 BeanDefinition 中可以看到parseConstructorArgElements、parsePropertyElements、parseLookupOverrideSubElements等对constructor-arg、property、lookup-method等子元素的完整解析逻辑。反射工厂是怎么创建 Bean 的有了原料BeanDefinition之后就要看这个工厂——BeanFactory如何创建 Bean。答案就是反射。结合从原料中获取的重要属性beanClass容器就可以通过类信息反射实例化对象。这一过程在 依赖注入(DI).md) 中有更细的源码佐证SimpleInstantiationStrategy.instantiate()在 Bean 没有方法覆盖methodOverrides时通过clazz.getDeclaredConstructor()BeanUtils.instantiateClass(constructorToUse)走 JDK 反射实例化当存在方法覆盖时则交给子类CglibSubclassingInstantiationStrategy使用 CGLIB 的Enhancer生成子类实例。BeanFactoryIoC 容器的根接口先来看作为 IoC 容器根接口的BeanFactory提供了哪些方法核心是getBean()方法以及按别名获取、按类型获取的方法还有isSingleton是否单例、isPrototype是否多例、isTypeMatch类型匹配、containsBean是否包含 Bean等判断方法。子接口体系看类名对号入座看源码时有个小技巧直接看默认实现类比如这里的DefaultListableBeanFactory基本上看类名就能知道大致作用。先对号入座接口 / 类职责ListableBeanFactory遍历 Bean获取所有 beanNames、统计 BeanDefinition 等HierarchicalBeanFactory提供父子关系可以获取上一级的 BeanFactoryConfigurableBeanFactory实现了SingletonBeanRegistry主要是单例 Bean 的注册、生成AutowireCapableBeanFactory和自动装配有关AbstractBeanFactory单例缓存以及 FactoryBean 相关的处理ConfigurableListableBeanFactory预实例化单例 Bean、分析并修改 BeanDefinitionAbstractAutowireCapableBeanFactory创建 Bean、属性注入、实例化、调用初始化方法等DefaultListableBeanFactory支持单例 Bean、Bean 别名、父子 BeanFactory、Bean 类型转换、Bean 后置处理、FactoryBean、自动装配等可以看出从BeanFactory到DefaultListableBeanFactory是一层一层叠加能力的过程这正是 Spring 面向对象设计中接口隔离与模板方法的体现。FactoryBean归大工厂管理的小工厂FactoryBean本身也是一个 Bean算是小工厂归BeanFactory这个大工厂管理。它只有三个方法getObject()—— 获取对象生产出来的 BeanisSingleton()—— 生产的对象是否是单例getObjectType()—— 返回生产对象Bean的类型相比大工厂BeanFactory它少了严格的 Bean 生命周期流程。两者的关系可以这样区分BeanFactory是 Spring 容器的根接口是大工厂生产各种各样的 BeanFactoryBean对象本身也是一个 Bean是小工厂可以生产另外的 BeanbeanName获取到的是正常对象 beanName获取到的是实现了FactoryBean接口的工厂对象本身。这个前缀语义在AbstractBeanFactory.getObjectForBeanInstance()中落地当请求名以开头时返回 FactoryBean 本身否则返回factoryBean.getObject()的产物产物还会被缓存相关实现可见 Spring-beanFactory。ApplicationContext能力更全的容器门面再来看ApplicationContext。它扩展了很多功能除了 BeanFactory 的创建、获取 Bean 之外还可以处理国际化、事件、资源获取等EnvironmentCapable获取环境变量的功能可以获取操作系统变量和JVM 环境变量ListableBeanFactory获取所有 BeanNames、判断某个 BeanName 是否存在 BeanDefinition、统计 BeanDefinition、获取某个类型对应的所有 beanNames 等HierarchicalBeanFactory获取父 BeanFactory、判断某个 name 是否存在 bean 对象MessageSource国际化功能获取某个国际化资源ApplicationEventPublisher事件发布功能重点ResourcePatternResolver加载、获取资源的功能这里的资源可以是文件、图片等某个 URL 资源。还有三个重要的实现类日常开发中按需选择ClassPathXmlApplicationContext—— 从 classpath 加载 XML 配置AnnotationConfigApplicationContext—— 基于注解 / 配置类驱动FileSystemXmlApplicationContext—— 从文件系统路径加载 XML 配置其refresh()启动流程在 BeanDefinition 的资源定位过程 中有完整解读IoC 容器后置处理器无处不在到这里就该让核心角色出场了——IoC 容器。我们常说 IOC 是控制反转但别忘了容器这个词BeanFactory是容器的根接口ClassPathXmlApplicationContext、AnnotationConfigApplicationContext、FileSystemXmlApplicationContext是容器的实现。同时要注意这里无处不在的后置处理器xxxPostProcessor——这正是 Spring 扩展性强的根本原因。我们可以在各个阶段合理使用这些 PostProcessor 来扩展流程、修改 Bean 定义信息。可以看到在这个容器中完成了 Bean 的初始化。容器的完整启动流程由AbstractApplicationContext.refresh()方法驱动源码中的关键步骤见 BeanDefinition 的资源定位过程如下public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // 容器准备刷新获取当前时间设置同步标识 prepareRefresh(); // 告诉子类启动 refreshBeanFactory()BeanDefinition 的载入由此开始 ConfigurableListableBeanFactory beanFactory obtainFreshBeanFactory(); // 为 BeanFactory 配置容器特性类加载器、事件处理器等 prepareBeanFactory(beanFactory); try { // 为容器的某些子类指定特殊的 BeanPost 事件处理器 postProcessBeanFactory(beanFactory); // 调用所有已注册的 BeanFactoryPostProcessor含 BeanDefinitionRegistryPostProcessor invokeBeanFactoryPostProcessors(beanFactory); // 注册 BeanPostProcessorBean 后置处理器 registerBeanPostProcessors(beanFactory); // 初始化信息源国际化相关 initMessageSource(); // 初始化容器事件传播器 initApplicationEventMulticaster(); // 调用子类的某些特殊 Bean 初始化方法 onRefresh(); // 为事件传播器注册事件监听器 registerListeners(); // 初始化 Bean并对 lazy-init 属性进行处理预实例化单例 finishBeanFactoryInitialization(beanFactory); // 初始化容器的生命周期事件处理器发布容器的生命周期事件 finishRefresh(); } catch (BeansException ex) { destroyBeans(); // 销毁已创建的单例 Bean cancelRefresh(ex); // 取消 refresh 操作 throw ex; } } }其中 DI依赖注入发生在属性填充阶段详见下文。BeanFactory 后置处理器在原料阶段动手脚作为 IoC 容器根接口的BeanFactory有着非常高的扩展性。在最初获取原料BeanDefinition时就出现了两个针对BeanFactory的后置处理器BeanDefinitionRegistryPostProcessor通过该接口我们可以自己掌控原料通过BeanDefinitionRegistry接口去新增、删除、获取BeanDefinition。BeanFactoryPostProcessor通过该接口可以在实例化对象前对BeanDefinition进行修改、冻结、预实例化单例 Bean等。官方定义见 BeanFactoryPostProcessor 源码分析指出BeanFactoryPostProcessor操作 Bean 的元数据配置容器允许它在实例化除BeanFactoryPostProcessor实例之外的任何 Bean之前读取并修改配置元数据。其接口只有一个方法public interface BeanFactoryPostProcessor { void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; }两者的执行时机在refresh()的invokeBeanFactoryPostProcessors()中由于BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor的子接口它会先于普通BeanFactoryPostProcessor执行并且按PriorityOrdered→Ordered→ 无排序接口的顺序分批调用相关getBean调用同时会触发这些后置处理器自身的实例化。一个经典的BeanFactoryPostProcessor例子是CustomEditorConfigurer——通过自定义属性编辑器PropertyEditorSupport可以把 XML 中的字符串属性如四川,成都在属性填充时转换为Address对象其工作原理是在postProcessBeanFactory()中把PropertyEditorRegistrar注册进容器最终在populateBean() → applyPropertyValues() → convertForProperty()阶段生效。完整示例可参考 BeanFactoryPostProcessor 源码分析。经过上述层层处理之后最终会来到目标方法getBean()将原料投入生产拿到一个个 Bean 对象。getBean 的幕后单例缓存、dependsOn 与三级 scopegetBean()是所有重载的汇聚点最终进入AbstractBeanFactory.doGetBean()源码详见 依赖注入(DI).md)。它的核心逻辑可以概括为先将别名alias转换为唯一beanName尝试从单例缓存getSingleton(beanName)直接取已实例化的单例若未命中则检查父容器HierarchicalBeanFactory的父子关系在这里体现处理dependsOn递归调用getBean(dependsOnBean)从依赖链的末级开始逐个实例化按 scope 分支创建singleton配合ObjectFactory匿名内部类、prototype每次新建、request/session等自定义 scope返回前做类型校验必要时通过TypeConverter转换。Bean 生命周期标准化的 14 个步骤Bean 的创建和管理有标准化流程在BeanFactory的实现中写得清清楚楚总共14个步骤一目了然。看这部分源码时要多注意两个英文单词的区别实例化→Instantiation创建对象本身初始化→Initialization对对象做初始化处理如回调 Aware、执行 init-method仔细阅读这 14 个步骤会发现前面8个都是Aware接口它们的作用很简单获取xxAware这个单词前缀所代表的xx组件。比如ApplicationEventPublisherAware只要实现了它就能获取到事件发布器ApplicationEventPublisher。生命周期中的循环依赖三级缓存在实例化完成、初始化未完成这个窗口期Spring 还通过三级缓存解决了单例 Bean 的循环依赖问题详见 循环依赖// 一级缓存存放完整 Bean 对象实例化 初始化 private final MapString, Object singletonObjects new ConcurrentHashMap(256); // 三级缓存存放 lambda 表达式ObjectFactory private final MapString, ObjectFactory? singletonFactories new HashMap(16); // 二级缓存存放半成品 Bean只实例化还未初始化即提前暴露 private final MapString, Object earlySingletonObjects new ConcurrentHashMap(16);在doCreateBean()中earlySingletonExposure为 true 时会把提前暴露的工厂放入三级缓存addSingletonFactory(beanName, () - getEarlyBeanReference(beanName, mbd, bean))这样当 A 依赖 B、B 又依赖 A 时B 在属性填充阶段能拿到 A 的提前暴露引用从而打破闭环。Bean 后置处理器实例化与初始化阶段的扩展点在实例化和初始化流程中把 Bean 的后置处理器BeanPostProcessor安排上就得到下图其中需要留意实例化阶段的扩展点是InstantiationAwareBeanPostProcessor例如postProcessBeforeInstantiation、postProcessAfterInstantiation、postProcessPropertyValues可干预属性填充初始化阶段的扩展点BeanPostProcessor非常多我们主要关注其中的AOP。BeanPostProcessor接口本身只有两个方法源码见 BeanPostProcessor 源码分析public interface BeanPostProcessor { /** 实例化、依赖注入完毕在显式初始化之前执行 */ Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; /** 实例化、依赖注入、初始化完毕时执行 */ Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }注册方式有两种实现接口并注册为 Bean或通过ConfigurableBeanFactory.addBeanPostProcessor()手动注册。多个BeanPostProcessor可以通过实现Ordered/PriorityOrdered接口控制执行顺序。典型的实现是AutowiredAnnotationBeanPostProcessor——Autowired/Value的字段注入正是由它在属性填充阶段postProcessProperties→InjectionMetadata.inject()完成的。AOP代理对象在哪一步生成那么 AOP 是在哪个步骤对对象做代理的呢答案就在初始化阶段的 Bean 后置处理器中。可以在AbstractAutoProxyCreator类中看到从源码结构看AbstractAutoProxyCreator是SmartInstantiationAwareBeanPostProcessor的实现它会在 Bean 实例化之后、初始化前后主要是在postProcessAfterInitialization检查当前 Bean 是否匹配切点若匹配则用 JDK 动态代理或 CGLIB 生成代理对象返回。这也解释了为什么 AOP 代理的生成发生在Bean 后置处理器这一扩展点上。其生效入口是aop:aspectj-autoproxy/标签由AspectJAutoProxyBeanDefinitionParser解析通过AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary()注册名为internalAutoProxyCreator的AnnotationAwareAspectJAutoProxyCreatorAbstractAutoProxyCreator的子类并支持proxy-target-class是否强制使用 CGLIB 类代理与expose-proxy属性完整解析流程见 Spring AOP 如何生效代理的底层原理可进一步阅读 JDK 动态代理的实现原理解析 与 AOP 源码实现及分析。总结Spring 的整体脉络本文围绕一个 Bean 是怎么被创建出来的这条主线介绍了 Spring 的整体脉络原料阶段BeanDefinition的解析XML / 注解 → BeanDefinition与注册beanDefinitionMap工厂阶段BeanFactory及其子接口/实现类的分工FactoryBean小工厂与beanName的语义容器门面ApplicationContext在 BeanFactory 之上扩展的环境、国际化、事件、资源能力扩展点从原料阶段的BeanFactoryPostProcessor/BeanDefinitionRegistryPostProcessor到产物阶段的BeanPostProcessor/InstantiationAwareBeanPostProcessor流程细节实例化Instantiation与初始化Initialization的顺序、Bean 生命周期的 14 个标准步骤、循环依赖的三级缓存核心机制工厂 XML 反射以及 AOP 代理发生的位置初始化阶段的后置处理器。如果你希望继续深入仓库中还有配套的源码级走读IoC 初始化三步曲资源定位 → 解析封装 BeanDefinition → 注册进容器、依赖注入(DI).md)、BeanPostProcessor、BeanFactoryPostProcessor、循环依赖、Spring-beanFactory以及 AOP 相关的三篇分析可以按图索骥、逐层深入。赞分享文档教程知识库【免费下载链接】source-code-hunter 从源码层面剖析挖掘互联网行业主流技术的底层实现原理为广大开发者 “提升技术深度” 提供便利。目前开放 Spring 全家桶Mybatis、Netty、Dubbo 框架及 Redis、Tomcat 中间件等项目地址https://gitcode.com/doocs/source-code-hunter点击查看免费下载相关推荐16张图解锁Spring整体脉络从BeanDefinition到Bean生命周期的源码级全解析16张图解锁Spring整体脉络从BeanDefinition到Bean生命周期的源码级全解析 导读本文以 Spring 容器的整体运行脉络为主线从工厂文档教程技术博客知识库Spring IoC 源码解析将 BeanDefinition 注册进 IoC 容器source-code-hunter 实战解读Spring IoC 源码解析将 BeanDefinition 注册进 IoC 容器source code hunter 实战解读 导读 本文是 sour文档教程技术博客知识库源码猎人source-code-hunterSpring Bean生命周期完整解析源码猎人source code hunterSpring Bean生命周期完整解析 引言为什么需要深入理解Bean生命周期 在日常Spring开发中你是文档教程技术博客知识库上一篇NWB配置完全指南从零配置到高级自定义的终极教程下一篇Fiddler中文版数据导出与分析如何生成专业测试报告创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考