本文分析的Spring源码是5.2.2版本,使用Gradle进行管理。

一、Bean的注册,先来看通过XML配置Bean的方式

1.配置applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"><bean id ="person" class="com.eragon.Person"><constructor-arg name="student" ref="student"/></bean><bean id = "student" class="com.eragon.Student" ><constructor-arg name="person" ref="person"/></bean><bean id="studentFactory" class="com.eragon.StudentFactory"/></beans>

2.创建BeanFactory:

BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));

3.当通过读取XML配置文件的方式创建一个BeanFactory的时候实际上已经完成了bean的注册,我们跟进源码看看具体spring是如何做到的。

public XmlBeanFactory(Resource resource) throws BeansException {this(resource, null);}
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {//调父类的构造函数,会额外执行一些方法,比如忽略BeanNameAware、BeanFactoryAware、BeanClassLoaderAware这3个类的依赖super(parentBeanFactory);//调用XmlBeanDefinitionReader解析资源this.reader.loadBeanDefinitions(resource);}

调用loadBeanDefinitions()解析xml

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {/*** 装饰者设计模式,实际上EncodeResource也是Resource,内部持有一个resource对象,主要是对编码的加强* new EncodedResource(resource).getCharset();//返回编码方式* new EncodedResource(resource).getReader(); //返回InputStreamReader*/return loadBeanDefinitions(new EncodedResource(resource));}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {// 断言判断非空Assert.notNull(encodedResource, "EncodedResource must not be null");if (logger.isTraceEnabled()) {logger.trace("Loading XML bean definitions from " + encodedResource);}// 从ThreadLocal中获取已经加载的资源,主要考虑在多线程下,避免对相同资源的重复加载Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();if (currentResources == null) {// 表示没有任何资源被加载,将当前资源放进集合currentResources = new HashSet<>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}// add失败表示该资源已经加载过,直接异常if (!currentResources.add(encodedResource)) {throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");}try {//获得InputStreamInputStream inputStream = encodedResource.getResource().getInputStream();try {// 因为SAX解析XML,需要InputSource对象,所以将InputStream封装成InputSourceInputSource inputSource = new InputSource(inputStream);if (encodedResource.getEncoding() != null) {inputSource.setEncoding(encodedResource.getEncoding());}/*** 前面是准备工作,这里应该是真正开始加载(doXXX方法一般是真正开始做,而XXX一般是做些准备工作)*/return doLoadBeanDefinitions(inputSource, encodedResource.getResource());}finally {inputStream.close();}}catch (IOException ex) {throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), ex);}finally {currentResources.remove(encodedResource);if (currentResources.isEmpty()) {this.resourcesCurrentlyBeingLoaded.remove();}}}

doLoadBeanDefinitions()方法真正去解析XML

try {/*** 用SAX将资源解析成Document*/Document doc = doLoadDocument(inputSource, resource);/***  通过解析XML得到的doc注册BeanDefinitions*/int count = registerBeanDefinitions(doc, resource);if (logger.isDebugEnabled()) {logger.debug("Loaded " + count + " bean definitions from " + resource);}return count;}

这里发现只有2个步骤,第一步去将xml通过SAX方式解析成Document对象

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {/*** 使用DefaultDocumentLoader对资源进行解析* getEntityResolver():主要是解析DTD/XSD约束文件的,如果没有自定义会使用默认的* this.errorHandler:SAX异常操作器(带日志)* getValidationModeForResource(resource):校验规则* isNamespaceAware():指示此解析器是否被配置为可识别名称空间*/return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());}

至于解析的过程,spring用的SAX解析,没有自己编写代码去解析,这里省略。

第二步,registerBeanDefinitions()方法进行bean的注册

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {// 通过反射创建解析doc的解析器BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();//已经注册的BeanDefinition数量int countBefore = getRegistry().getBeanDefinitionCount();/*** 解析Document并注册BeanDefinition* createReaderContext(resource)*/documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 返回实际此次实际注册BeanDefinition数量return getRegistry().getBeanDefinitionCount() - countBefore;}public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;/*** 真正注册BeanDefinitionss的方法*/doRegisterBeanDefinitions(doc.getDocumentElement());}
protected void doRegisterBeanDefinitions(Element root) {// 父Delegate设为nullBeanDefinitionParserDelegate parent = this.delegate;// 创建当前delegatethis.delegate = createDelegate(getReaderContext(), root, parent);// 判断是否为spring默认的namespaceif (this.delegate.isDefaultNamespace(root)) {// 如果profile标签存在,需要判断当前配置的activeProfile在不在其中,如果不在直接skipString 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;}}}// 注册之前的方法,hook,留给子类实现preProcessXml(root);// 解析标签成BeanDefinitionsparseBeanDefinitions(root, this.delegate);// 注册之后的方法,hook,留给子类实现postProcessXml(root);this.delegate = parent;}

parseBeanDefintions()方法就是真正去解析标签然后生成BeanDefinitions

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 默认的spring标签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;// spring标签if (delegate.isDefaultNamespace(ele)) {/*** 解析spring标签*/parseDefaultElement(ele, delegate);}// 自定义标签else {delegate.parseCustomElement(ele);}}}}// 自定义标签else {delegate.parseCustomElement(root);}}

这里我们只关注对spring标签的解析 parseDefaultElement()

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {/*** 这里主要是针对4个标签的解析 import、alias、bean、beans*/if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {importBeanDefinitionResource(ele);}else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {processAliasRegistration(ele);}else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {/*** 解析<Bean>标签*/processBeanDefinition(ele, delegate);}else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {// recurse// 如果是<beans>标签,那么直接递归调用doRegisterBeanDefinitionsdoRegisterBeanDefinitions(ele);}}

着重跟进解析bean标签的方法processBeanDefinition()

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {// TODO 为什么spring不直接解析生成BeanDefinition,而是先生成Holder?/*** 解析成BeanDefinitionHolder* 回答上面的疑问:这里实际也是装饰者模式,BeanDefinitionHolder内部持有BeanDefinition对象和它没有的beanName、alias*/BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);if (bdHolder != null) {// 解析自定义标签bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);try {// Register the final decorated instance.// 将BeanDefinition注册到容器中BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());}catch (BeanDefinitionStoreException ex) {getReaderContext().error("Failed to register bean definition with name '" +bdHolder.getBeanName() + "'", ele, ex);}// Send registration event.// 通知容器getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));}}

delegate.parseBeanDefinitionElement(ele)方法会生成BeanDefinition到BeanDefinitionHolder里

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {return parseBeanDefinitionElement(ele, null);}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {// 获得<id>String id = ele.getAttribute(ID_ATTRIBUTE);// 获得<name>,name可以是多个,相当于别名String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);// 将所有别名放入数组List<String> aliases = new ArrayList<>();if (StringUtils.hasLength(nameAttr)) {String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}// 获得beanName,如果id为null,那么取别名的第一个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");}}// 检查beanName是否唯一if (containingBean == null) {checkNameUniqueness(beanName, aliases, ele);}// 生成BeanDefinitionAbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);if (beanDefinition != null) {if (!StringUtils.hasText(beanName)) {// 如果beanName没值,比如id没有填,name也没填,会生成一个默认的nametry {if (containingBean != null) {beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);}else {beanName = this.readerContext.generateBeanName(beanDefinition);// Register an alias for the plain bean class name, if still possible,// if the generator returned the class name plus a suffix.// This is expected for Spring 1.2/2.0 backwards compatibility.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);// 返回BeanDefinitionHolderreturn new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);}return null;}
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())将bean注册到容器中
public static void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)throws BeanDefinitionStoreException {// Register bean definition under primary name.String beanName = definitionHolder.getBeanName();// key-value 形式注册到容器里(这里容器既可以是BeanFactory也可以是ApplicationContext)registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.String[] aliases = definitionHolder.getAliases();if (aliases != null) {for (String alias : aliases) {registry.registerAlias(beanName, alias);}}
}

registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 注册

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 {((AbstractBeanDefinition) beanDefinition).validate();}catch (BeanDefinitionValidationException ex) {throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", ex);}}BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);if (existingDefinition != null) {//如果允许覆盖if (!isAllowBeanDefinitionOverriding()) {throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);}// 根据role分类不同,日志不同else if (existingDefinition.getRole() < beanDefinition.getRole()) {// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTUREif (logger.isInfoEnabled()) {logger.info("Overriding user-defined bean definition for bean '" + beanName +"' with a framework-generated bean definition: replacing [" +existingDefinition + "] with [" + beanDefinition + "]");}}else if (!beanDefinition.equals(existingDefinition)) {if (logger.isDebugEnabled()) {logger.debug("Overriding bean definition for bean '" + beanName +"' with a different definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}else {if (logger.isTraceEnabled()) {logger.trace("Overriding bean definition for bean '" + beanName +"' with an equivalent definition: replacing [" + existingDefinition +"] with [" + beanDefinition + "]");}}// 放入容器this.beanDefinitionMap.put(beanName, beanDefinition);}else {/*** 是否已经有创建的bean,表示已经有线程在进行bean的构建,那么就需要同步机制确保多线程安全*/if (hasBeanCreationStarted()) {// Cannot modify startup-time collection elements anymore (for stable iteration)/***  TODO 这里是如何确保线程安全的呢?为什么这里不能直接this.beanDefinitionNames.add(beanName)*     List在进行迭代的时候是不允许对集合进行修改或删除的,否则会立马抛出ConcurrentModificationException异常*    所以这里使用一个临时集合来做add操作*/synchronized (this.beanDefinitionMap) {this.beanDefinitionMap.put(beanName, beanDefinition);List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);updatedDefinitions.addAll(this.beanDefinitionNames);updatedDefinitions.add(beanName);this.beanDefinitionNames = updatedDefinitions;// 如果该beanName在单例集合里,从单例集合里删除removeManualSingletonName(beanName);}}else {// Still in startup registration phasethis.beanDefinitionMap.put(beanName, beanDefinition);this.beanDefinitionNames.add(beanName);removeManualSingletonName(beanName);}this.frozenBeanDefinitionNames = null;}// 清除缓存if (existingDefinition != null || containsSingleton(beanName)) {resetBeanDefinition(beanName);}}

我们可以看到最终会将beanName放到集合beanDefinitionNames里,而BeanDefinition则会以key-value的形式,和baneName一一对应的放入集合beanDefintionMap里。

然后会解析自定义标签,最后通知容器注册完成。

getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));

总结:

通过解析xml配置文件或注解方式的配置将bean的各种属性封装到BeanDefintion里,然后保存到BeanFactory的集合里,用来给创建Bean的时候做准备。注意,生成BeanDefintion并不代表Bean的生成,两者不能划等号。

Spring源码之Bean的注册(使用XML配置的方式)相关推荐

  1. Spring源码之Bean的注册(注解方式)

    1.创建AnnotationConfigApplicationContext AnnotationConfigApplicationContext context = new AnnotationCo ...

  2. Spring源码解析-bean实例化

    Spring源码解析-bean实例化 ​ 本文介绍Spring创建 bean 过程中的第一个步骤:实例化 bean. 1. Bean实例化源码 ​ 虽然实例化Bean有多种方式(包括静态工厂和工厂实例 ...

  3. Spring 源码解析 - Bean创建过程 以及 解决循环依赖

    一.Spring Bean创建过程以及循环依赖 上篇文章对 Spring Bean资源的加载注册过程进行了源码梳理和解析,我们可以得到结论,资源文件中的 bean 定义信息,被组装成了 BeanDef ...

  4. Spring源码剖析——Bean的配置与启动

    IOC介绍   相信大多数人在学习Spring时 IOC 和 Bean 算得上是最常听到的两个名词,IOC在学习Spring当中出现频率如此之高必然有其原因.如果我们做一个比喻的话,把Bean说成Sp ...

  5. Spring源码深度解析,Spring源码以及Bean的生命周期(五)(附代码示例:)

    五)Bean 的生命周期,创建---初始化---销毁的过程 目录 五)Bean 的生命周期,创建---初始化---销毁的过程 一 ,  指定初始化方法 init-method 方法​ 二 ,指定销毁 ...

  6. Spring源码学习第七天==>解析配置注解类与BPP

    关键词: Spring解析配置类注解Bean Spring注册Bean后置增强器(BPP) Spring消息资源和监听器的初始化 一:Spring解析配置类注解Bean==>Configurat ...

  7. Spring源码分析——Bean的生命周期

    文章目录 说明 测试代码 说明 本文从源码的角度分析Spring中Bean的加载过程,本文使用的Spring版本为4.3.25.RELEASE 测试代码 测试代码如下,根据这段简单的测试代码,一步步跟 ...

  8. Spring源码学习:BeanPostProcessor注册和执行时机

    目录 前言 1 BeanPostProcessors作用 2 源码分析 2.1 BeanPostProcessors注册时机 2.1.1 注册beanPostProcessorChecker 2.1. ...

  9. 三 spring源码解析--- Bean解析接口结构分析

    2019独角兽企业重金招聘Python工程师标准>>> 解析Bean是通过定义抽象的bean reader来解析,结构图如下 1.AbstractBeanDefinitionRead ...

最新文章

  1. MIT 更新最大自然灾害图像数据集,囊括 19 种灾害事件
  2. C++中逗号操作符的重载
  3. WinCE中的paging pool
  4. CompletableFuture详解~getNow
  5. 4918字,详解商品系统的存储架构设计
  6. 2.10 是否要使用端到端的深度学习
  7. .net framework开发winform_.NET架构开发应知应会
  8. asp毕业设计——基于asp+access的公司门户网站设计与实现(毕业论文+程序源码)——公司门户网站
  9. APP测试基本流程以及APP测试要点
  10. 计算机电缆国家标准是什么,计算机电缆执行标准是什么
  11. 传统企业如何开启O2O营销模式?
  12. 【EXLIBRIS】关于我现有的大部分PDF书目以及共享
  13. Web页面无法执行CGI的exe程序
  14. python黑白棋设计思路_[黑白棋]规则、大食策略及AI转化思考
  15. typedef int()(int,int)
  16. CodeForces - 1436D Bandit in a City
  17. 付费系列 6 - 离散型障碍和触碰期权 PDE 有限差分
  18. Centos7.9最小化安装与初始化环境配置
  19. docker访问宿主机端口
  20. pythonweb视频播放器_干货分享,Python与PyQT制作视频播放器

热门文章

  1. TX Text Control ActiveX 29.0 sp3-支持32/64位
  2. http://www.cnblogs.com/chio/archive/2007/09/10/888260.html
  3. 微信小程序预览常见问题 未找到app.json这个怎么解决
  4. 导入微信小程序显示未选择环境或未指定环境,解决办法
  5. Q10:Java中输出如下直角图形
  6. 统一修改word中的英文字体
  7. 【蜀山诛仙传】仙梦奇缘一键服务端
  8. Python学习---day35
  9. ML中几种常见熵学习笔记
  10. Docker-端口映射与容器互联