【Spring】Spring ioc源码学习以及关于阅读源码方式的一些小笔记
创始人
2025-05-31 00:41:11
0

Spring ioc源码学习以及关于阅读源码方式的一些小笔记

学习源码的方式

最好的方法是带着问题去学习。
如果拿到的是一个新的框架源码,首先应当去搞懂如何使用,这个框架能做什么事。接下来可以采取两种方式:
第一种:

  1. 第一步了解它的核心概念有哪些,确定你要学习的目标
  2. 由整体到局部的分析,先拆分目标,这个框架为了达到这个效果做了哪些步骤
  3. 找到每个步骤对应的入口,先理清楚主干流程,再详细看具体细节
  4. 理清楚之后针对其可扩展的部分,自己编写一些代码去验证是否能实现(多折腾)

第二种:

  1. 第一步了解它的核心概念有哪些,确定你要学习的目标
  2. 自己动手去实现
  3. 自己实现完之后再对比框架源码,思考为什么源码此处要这么写

由于本人自认为水平欠佳,在阅读过程中采取的是第一种方式。

做了些什么

ioc容器最核心的功能就是帮助用户管理bean,把用户自己创建的模式改成由容器创建的模式,所以至少需要两个步骤:创建bean和获取bean。

实现的步骤

主要有如下几个步骤:

  1. 用户配置bean定义
  2. 容器读取用户配置的bean定义
  3. 容器创建bean定义
  4. 用户从容器中读取bean

首先找到加载的入口,此处以xml配置方式为例,注解方式后续补充:
xml配置方式的入口为:ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lalala.xml");

把握整体

ApplicationContext是Spring中的高级容器,它内嵌了一个BeanFactoryClassPathXmlApplicationContext也是ApplicaitonContext的实现类。下面是ApplicationContext的类图。

在这里插入图片描述

这里采用了设计模式中的装饰模式(有疑问,算装饰模式吗):

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory, MessageSource, ApplicationEventPublisher, ResourcePatternResolver {@NullableString getId();String getApplicationName();String getDisplayName();long getStartupDate();@NullableApplicationContext getParent();AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}

可以看到ApplicationContext 继承的功能有:

  • EnvironmentCapable: 获取环境相关变量
  • ListableBeanFactory: 提供BeanFactory容器
  • HierarchicalBeanFactory: 提供父子容器相关的内容
  • MessageSource: 提供国际化的内容
  • ApplicationEventPublisher: 提供事件发布机制
  • ResourcePatternResolver : 提供资源解析器

具体实现

第一次读源码的时候首先要找准一个你熟悉的过程,debug打断点,获取到它的调用栈并截图保存。 需要关注的重点是入参在这些方法中是如何变化的,从而知道每一个类是干什么用的。

接下来再将步骤拆分的细一点:

  1. 读取用户配置的xml文件
  2. 将xml树解析为beanDefinition
  3. 创建beanFactory
  4. beanDefinition注册到beanFactory

首先我们观察到启动时候的入参只有一个xml文件的地址:

/*** xml文件配置方式*/private static void testClassPathXmlApplicationContext() {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("lalala.xml");for (String beanName : context.getBeanDefinitionNames()) {System.out.println(beanName);}System.out.println(context.getBean(Bean2.class).getBean1());}

那第一步就要先将这个xml文件配置的内容解析成beanDefinition。上面提到过ResourcePatternResolver 是用来解析资源的,点开ClassPathXmlApplicationContext的类图,找到上层哪个类持有这个解析器->AbstractApplicationContext
在这里插入图片描述
然后定位到对应的方法,再一步一步往下跟踪,能够得到如下的调用栈:

在这里插入图片描述
下面拿出一些个人认为重要的步骤进行说明:

  1. org.springframework.context.support.AbstractRefreshableApplicationContext#refreshBeanFactory()方法中新建了一个BeanFactory.
    它的默认类型是DefaultListableBeanFactory
    在这里插入图片描述
  2. org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 中新建了一个XmlBeanDefinitionReader,容器在此处将xml文件的解析过程委托给了这个XmlBeanDefinitionReader进行下一步的解析:

在这里插入图片描述
3. 在org.springframework.beans.factory.support.AbstractBeanDefinitionReader 中读取xml的内容并将其转换为Resource 对象。对于多个配置文件是进行循环读取。
在这里插入图片描述

  1. org.springframework.beans.factory.xml.XmlBeanDefinitionReader中有一处转换是将Resource 转化为EncodedResource
@Overridepublic int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource));}
  1. org.springframework.beans.factory.xml.XmlBeanDefinitionReader 中开始进行注册beanDefinition的行为
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {Document doc = doLoadDocument(inputSource, resource);int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}catch (BeanDefinitionStoreException ex) {throw ex;}catch (SAXParseException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);}catch (SAXException ex) {throw new XmlBeanDefinitionStoreException(resource.getDescription(),"XML document from " + resource + " is invalid", ex);}catch (ParserConfigurationException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Parser configuration exception parsing XML from " + resource, ex);}catch (IOException ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"IOException parsing XML document from " + resource, ex);}catch (Throwable ex) {throw new BeanDefinitionStoreException(resource.getDescription(),"Unexpected exception parsing XML document from " + resource, ex);}}

注册BeanDefinition的行为实际是被委托给了BeanDefinitionDocumentReader 去完成。

  1. org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader 中进行实际的BeanDefinition 注册过程。
protected void doRegisterBeanDefinitions(Element root) {BeanDefinitionParserDelegate parent = this.delegate;// 处理嵌套的标签this.delegate = createDelegate(getReaderContext(), root, parent);// 判断是否是默认命名空间  http://www.springframework.org/schema/beansif (this.delegate.isDefaultNamespace(root)) {// 判断运行的环境 profile属性String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);if (StringUtils.hasText(profileSpec)) {String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {if (logger.isDebugEnabled()) {logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +"] not matching: " + getReaderContext().getResource());}return;}}}// xml的前置处理preProcessXml(root);// 处理BeanDefinitionparseBeanDefinitions(root, this.delegate);// xml的后置处理postProcessXml(root);this.delegate = parent;}

进入parseBeanDefinitions方法:

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {if (delegate.isDefaultNamespace(root)) {// 遍历默认命名空间下的每一个节点NodeList nl = root.getChildNodes();for (int i = 0; i < nl.getLength(); i++) {Node node = nl.item(i);if (node instanceof Element) {Element ele = (Element) node;// 是否是默认空间下的if (delegate.isDefaultNamespace(ele)) {// 处理默认元素 import alias bean beansparseDefaultElement(ele, delegate);}else {delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}

下面来看看它怎么解析bean标签的:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {// 获取id属性的值String id = ele.getAttribute(ID_ATTRIBUTE);// 获取name属性的值String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);// 获取别名List aliases = new ArrayList<>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}String beanName = id;if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {beanName = aliases.remove(0);if (logger.isTraceEnabled()) {logger.trace("No XML 'id' specified - using '" + beanName +"' as bean name and " + aliases + " as aliases");}}// 校验名称的唯一性if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {try {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);String beanClassName = beanDefinition.getBeanClassName();if (beanClassName != null &&beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {aliases.add(beanClassName);}}if (logger.isTraceEnabled()) {logger.trace("Neither XML 'id' nor 'name' specified - " +"using generated bean name [" + beanName + "]");}}catch (Exception ex) {error(ex.getMessage(), ele);return null;}}String[] aliasesArray = StringUtils.toStringArray(aliases);return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;}
@Nullablepublic AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {this.parseState.push(new BeanEntry(beanName));String className = null;// 获取class属性的值if (ele.hasAttribute(CLASS_ATTRIBUTE)) {className = ele.getAttribute(CLASS_ATTRIBUTE).trim();}String parent = null;// 获取parent属性的值if (ele.hasAttribute(PARENT_ATTRIBUTE)) {parent = ele.getAttribute(PARENT_ATTRIBUTE);}try {// 创建beanDefinition 默认的beanDefinition类型是GenericBeanDefinitionAbstractBeanDefinition bd = createBeanDefinition(className, parent);// 解析scope abstract lazy-init(默认为false) autowire depends-on autowire-candidate primary init-method destory-method factory-method factory-bean 属性 这里有部分属性会在populateDefaults中设置一遍parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));// 解析meta标签parseMetaElements(ele, bd);// 解析lookup-methodparseLookupOverrideSubElements(ele, bd.getMethodOverrides());// 解析replaced-methodparseReplacedMethodSubElements(ele, bd.getMethodOverrides());// 解析constructor-argparseConstructorArgElements(ele, bd);// 解析propertyparsePropertyElements(ele, bd);// 解析qualifierparseQualifierElements(ele, bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));return bd;}catch (ClassNotFoundException ex) {error("Bean class [" + className + "] not found", ele, ex);}catch (NoClassDefFoundError err) {error("Class that bean class [" + className + "] depends on not found", ele, err);}catch (Throwable ex) {error("Unexpected failure during bean definition parsing", ele, ex);}finally {this.parseState.pop();}return null;}

org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.populateDefaults方法中会先处理如下属性内容:

protected void populateDefaults(DocumentDefaultsDefinition defaults, @Nullable DocumentDefaultsDefinition parentDefaults, Element root) {String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);if (isDefaultValue(lazyInit)) {lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);}defaults.setLazyInit(lazyInit);String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);if (isDefaultValue(merge)) {merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);}defaults.setMerge(merge);String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);if (isDefaultValue(autowire)) {autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);}defaults.setAutowire(autowire);if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));}else if (parentDefaults != null) {defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());}if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));}else if (parentDefaults != null) {defaults.setInitMethod(parentDefaults.getInitMethod());}if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));}else if (parentDefaults != null) {defaults.setDestroyMethod(parentDefaults.getDestroyMethod());}defaults.setSource(this.readerContext.extractSource(root));}

接下来回到: BeanDefinitionReaderUtils.registerBeanDefinition 方法,它内层是这样的:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {try {// 验证beanDefinition的完整性((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);// 判断容器中是否已经存在当前beanDefinitionif (existingDefinition != null) {if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}else if (existingDefinition.getRole() < beanDefinition.getRole()) {}else if (!beanDefinition.equals(existingDefinition)) {}else {}this.beanDefinitionMap.put(beanName, beanDefinition);}else {if (hasBeanCreationStarted()) {synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;removeManualSingletonName(beanName);}}else {this.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}else if (isConfigurationFrozen()) {clearByTypeCache();}}

从此看出ioc容器中的BeanDefinition都存放在beanDefinitionMap中。
此时beanDefinition已经加载完成。

ps: 嵌套bean标签注入的用法:

如果某个Bean所依赖的Bean不想被Spring容器直接访问,可以使用嵌套Bean。元素用来定义嵌套Bean,嵌套Bean只对嵌套它的外部Bean有效,Spring容器无法直接访问嵌套Bean,因此定义嵌套Bean时无须指定id属性。




时序图如下:

在这里插入图片描述

遗留的问题

  1. EncodedResource 的作用?
  2. BeanDefinitionHolder 的作用?
  3. 解析自定义标签的时候,readContext里面的内容是从哪里来的?
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {String namespaceUri = getNamespaceURI(ele);if (namespaceUri == null) {return null;}// 这里NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);if (handler == null) {error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);return null;}return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));}

参考资料

1.小不点啊的spring源码阅读系列文
2.注入嵌套bean的用法

相关内容

热门资讯

走进小城看消费丨江西资溪:低碳...   夏日时节下午4点,江西省抚州市资溪县大觉山景区漂流终点依然热闹。来自南昌的游客余鑫漂流结束后没有...
【中原晨会0625】市场分析专... 来源:市场资讯 (来源:中原证券研究所) 本期重点研报目录 【中原策略】市场分析:电子半导体领涨 ...
南向资金连买4日!低费率+可月... 6月25日早盘,港股红利资产震荡整理。截至11时14分,港股红利低波ETF招商(520550)下跌0...
618成交破百万!紫荆花用一套... 一年一度的618年中大促,是消费市场的晴雨表,也是品牌间最激烈的角力场。当各大品牌在直播间里铆足了劲...
原创 黄... 2026年6月25日的国际金价已经从前期的5500美元高点跌到4200美元下方,累计跌幅超过22%,...
英伟达CEO:Vera Rub... 截至9:38,中证半导体材料设备主题指数(931743)涨2.36%创新高;权重股中,中微公司涨3....
再被催债16亿!“钢铁大王”戴... 澎湃新闻记者 贺梨萍 因“铁本事件”入狱五年的戴国芳重返钢铁行业,但他并没有完成从阶下囚再到“钢铁大...
周三原油价格下跌 随着美国和伊朗在和平谈判中取得进展,越来越多的油轮公开穿越霍尔木兹海峡,原油在战时的价格上涨已经蒸发...
这种蛋白是大脑衰老的开关 这种蛋白是大脑衰老的开关 清晨,假设一位五十岁左右的王女士发现自己常常把手机放在熟悉的抽屉里又找不到...
信通院牵头算力Token出海生... 盘面上,截至11:04,中证科创创业50指数(931643)涨1.68%,创历史新高;权重股中,芯原...
海外 774 亿营收背后:日本... 文 | 游戏价值论 6月23日,彭博社报道了腾讯正在围绕出售多家日本游戏工作室少数股权开展谈判,包...
餐饮“抢人”大战:把店开到公交... 作者 |餐饮老板内参 内参君 医院、公交站、演唱会…餐饮品牌,正在无孔不入 在北京儿童医院,肯德基...
快讯 | 外资扫货!陈翊庭:港... 港交所行政总裁陈翊庭在接受《中国证券报》专访时指出,国际资本对中国资产的看法已彻底扭转,布局中国市场...
2777.77元!A股“股王”... 25日早盘,昨天创下历史新高的A股“股王”联讯仪器,今天上午继续走强,盘中股价再度刷新历史新高。 截...
原创 今... 欧洲自己的媒体直接下结论,欧盟衰退躲不掉,内部分裂拦不住,现在就连欧洲顶尖工业巨头,都偷偷在用中国的...
黄仁勋股东大会放言:本轮AI基... 在当地时间6月24日的英伟达(NVDA.O)2026年度股东大会上,股东批准了该公司全部10名董事会...
国际油价大跌 新华社消息, 纽约原油期货主力合约价格24日盘中跌破每桶70美元,为伊朗战事爆发以来首次。 市场分析...
马云带队插秧,什么信号? 一场别开生面的“务农”,让外界看到了一个不一样的阿里巴巴。 近日,阿里巴巴合伙人、高德董事长刘振飞在...
全球最大产能,最高丰度达99.... 本文转自【科技日报】; 6月23日,高丰度硼-10同位素技术暨产业化成果发布会在山东省东营市举办,全...
黄金大跳水!金饰克价年内暴跌近... 25日,现货黄金盘中震荡,截至发稿,报3985.070美元/盎司,跌0.17%。 当地时间24日,...