【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的用法

相关内容

热门资讯

王凤英入职小鹏3年终获股权,此... 5月7日消息,小鹏汽车披露的监管及年报信息显示,公司总裁王凤英已正式进入股东名册,入职小鹏3年后股权...
五块钱红酒卖断货,便宜红酒为何... 最近一段时间,中国的酒类消费市场可以说是显得格外奇怪,一方面,各种高端酒特别是白酒的消费量出现了明显...
财联社C50风向指数调查:4月... 财联社5月8日讯(记者 夏淑媛)新一期财联社“C50风向指数”结果显示,市场机构对4月新增人民币贷款...
央视硬刚国际足联拒掏20亿,背... 作者| 史大郎&猫哥 来源| 是史大郎&大猫财经Pro 央视这次太刚了,离世界杯开幕还有1个月,死活...
新CEO上任直接放大招!Air... 快科技5月8日消息,苹果即将上任的CEO John Ternus对未来一系列新产品充满信心,称这些设...
“特朗普拟邀英伟达、波音等CE... 据路透社当地时间5月7日报道,特朗普政府正邀请英伟达、苹果、埃克森美孚、波音等大公司首席执行官,于下...
世界杯,还能看到直播吗? 2026年美加墨世界杯距离开幕,仅剩一个多月时间。多方信息显示,中央广播电视总台(以下简称“央视”)...
机构警告AI芯片热潮风险,超威... 5月7日,据央视财经,隔夜超威半导体公司(AMD)股价飙升近19%,带动AI芯片热潮持续升温。AMD...
银行员工转走储户1800万最新... 银行员工转走储户1800万最新进展:2名储户已收到银行全部款项
原创 中... 1994年,安徽省的经济格局曾发生过一次戏剧性的转折。在那一年,一座名为安庆的城市,其国内生产总值(...
昆都仑区:政策“蓄力”消费焕新 “一台5000多元的空调,叠加‘国补’和商场的以旧换新活动,能优惠1000元左右,旧机还能免费上门拆...
乐悦置业竞得佛山顺德乐从镇一商... 观点网讯:5月6日,佛山市顺德区乐从镇一商业地块成功出让,由广东省乐悦置业有限公司竞得,乐从南区·邻...
原创 亦... 《爱情没有神话》这部剧,一开始的命运颇为多舛,经历了几次撤档的波折后,终于在观众面前亮相,但其首播的...
美联储34年最大分歧叠加油价飙... 美联储按预期维持利率不变,但内部出现34年来最严重分歧,叠加布油创2022年6月以来新高,美债遭抛售...
支付宝消费券回收后,资金是否支... 摘要: 支付宝消费券回收变现后,资金能否直接转入信用卡?本文解答到账方式的相关规则,帮助用户了解资金...
中医介绍5个化痰穴位!收藏这篇... 很多人忽略了“痰”的危害,觉得咳几下就没事,殊不知,肺里的痰长期堆积,只会一步步加重身体负担。 中医...
黄金平台“杰我睿”涉嫌经济犯罪... 红星资本局5月7日消息,深圳水贝知名金店“杰我睿”兑付困难事件有了新进展。日前,深圳市公安局罗湖分局...
多地出台购房新政促楼市升温 记... 今年的“五一”假期,伴随着多个城市楼市新政密集落地,在叠加市场信心持续修复的作用下,房地产市场热度持...
谁是五一“吸金王”?这5座城市... 来源:市场资讯 (来源:21城市观) 哪座城市成为“五一”假期的大赢家? 图源:摄图网 作者|赵晓...
“低招低裁”格局稳固劳动力市场... 智通财经APP获悉,美国上周初请失业金人数在经历前一周回落至近几十年来最低水平后出现小幅反弹,表明尽...