spring ioc 容器的加载流程

**1.目标:**熟练使用spring,并分析其源码,了解其中的思想。这篇主要介绍spring ioc 容器的加载

**2.前提条件:**会使用debug

**3.源码分析方法:**Intellj idea debug 模式下源码追溯
通过ClassPathXmlApplicationContext 进行xml 件的读取,从每个堆栈中读取程序的运行信息

**4.注意:**由于Spring的类继承体系比较复杂,不能全部贴图,所以只将分析源码之后发现的最主要的类继承结构类图贴在下方。

**5.关于Spring Ioc
Demo:**我们从demo入手一步步进行代码追溯。

Spring Ioc Demo

1.定义数据访问接口IUserDao.java

publicinterfaceIUserDao{publicvoidInsertUser(Stringusername,Stringpassword);}

2.定义IUserDao.java实现类IUserDaoImpl.java

publicclassUserDaoImplimplementsIUserDao{@OverridepublicvoidInsertUser(Stringusername,Stringpassword){System.out.println("----UserDaoImpl--addUser----");}}

3.定义业务逻辑接口UserService.java

publicinterfaceUserService{publicvoidaddUser(Stringusername,Stringpassword);}

4.定义UserService.java实现类UserServiceImpl.java

publicclassUserServiceImplimplementsUserService{privateIUserDaouserDao;//set方法publicvoidsetUserDao(IUserDaouserDao){this.userDao=userDao;}@OverridepublicvoidaddUser(Stringusername,Stringpassword){userDao.InsertUser(username,password);}}

bean.xml配置文件

<beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd"><!--id名字自己取,class表示他代表的类,如果在包里的话需要加上包名--><beanid="userService"class="UserServiceImpl"><!--property代表是通过set方法注入,ref的值表示注入的内容--><propertyname="userDao"ref="userDao"/></bean><beanid="userDao"class="UserDaoImpl"/></beans>ApplicationContext 继承结构


1.顶层接口:ApplicationContext
2.ClassPathXmlApplicationContext实现类继承AbstractXmlApplication 抽象类
3.AbstractXmlApplication 继承AbstractRefreshableConfigApplicationContext
4.AbstractRefreshableConfigApplicationContext抽象类继承AbstractRefreshableApplicationContext
5.AbstractRefreshableApplicationContext 继承 AbstractApplicationContext
6.AbstractApplicationContext 实现ConfigurableApplicationContext 接口
7.ConfigurableApplicationContext 接口继承
ApplicationContext接口
总体来说继承实现结构较深,内部使用了大量适配器模式。
以ClassPathXmlApplicationContext为例,继承类图如下图所示:

Spring Ioc容器加载过程源码详解

在开始之前,先介绍一个整体的概念。即spring ioc容器的加载,大体上经过以下几个过程:
资源文件定位、解析、注册、实例化

1.资源文件定位
其中资源文件定位,一般是在ApplicationContext的实现类里完成的,因为ApplicationContext接口继承ResourcePatternResolver 接口,ResourcePatternResolver接口继承ResourceLoader接口,ResourceLoader其中的getResource()方法,可以将外部的资源,读取为Resource类。


2.解析DefaultBeanDefinitionDocumentReader,
解析主要是在BeanDefinitionReader中完成的,最常用的实现类是XmlBeanDefinitionReader,其中的loadBeanDefinitions()方法,负责读取Resource,并完成后续的步骤。ApplicationContext完成资源文件定位之后,是将解析工作委托给XmlBeanDefinitionReader来完成的
解析这里涉及到很多步骤,最常见的情况,资源文件来自一个XML配置文件。首先是BeanDefinitionReader,将XML文件读取成w3c的Document文档。
DefaultBeanDefinitionDocumentReader对Document进行进一步解析。然后DefaultBeanDefinitionDocumentReader又委托给BeanDefinitionParserDelegate进行解析。如果是标准的xml namespace元素,会在Delegate内部完成解析,如果是非标准的xml namespace元素,则会委托合适的NamespaceHandler进行解析最终解析的结果都封装为BeanDefinitionHolder,至此解析就算完成。
后续会进行细致讲解。


3.注册
然后bean的注册是在BeanFactory里完成的,BeanFactory接口最常见的一个实现类是DefaultListableBeanFactory,它实现了BeanDefinitionRegistry接口,所以其中的registerBeanDefinition()方法,可以对BeanDefinition进行注册这里附带一提,最常见的XmlWebApplicationContext不是自己持有BeanDefinition的,它继承自AbstractRefreshableApplicationContext,其持有一个DefaultListableBeanFactory的字段,就是用它来保存BeanDefinition
所谓的注册,其实就是将BeanDefinition的name和实例,保存到一个Map中。刚才说到,最常用的实现DefaultListableBeanFactory,其中的字段就是beanDefinitionMap,是一个ConcurrentHashMap。
代码如下:
>1.DefaultListableBeanFactory继承实现关系

publicclassDefaultListableBeanFactoryextendsAbstractAutowireCapableBeanFactoryimplementsConfigurableListableBeanFactory,BeanDefinitionRegistry,Serializable{//DefaultListableBeanFactory的实例中最终保存了所有注册的beanbeanDefinitionMap/**Mapofbeandefinitionobjects,keyedbybeanname*/privatefinalMap<String,BeanDefinition>beanDefinitionMap=newConcurrentHashMap<String,BeanDefinition>(64);//实现BeanDefinitionRegistry中定义的registerBeanDefinition()抽象方法publicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)throwsBeanDefinitionStoreException{}

>2.BeanDefinitionRegistry接口

publicinterfaceBeanDefinitionRegistryextendsAliasRegistry{//定义注册BeanDefinition实例的抽象方法voidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)throwsBeanDefinitionStoreException;

4.实例化


注册也完成之后,在BeanFactory的getBean()方法之中,会完成初始化,也就是依赖注入的过程
大体上的流程就是这样。

refresh()方法

1.目标:
这篇记录debug 追溯源码的过程,大概分三个篇幅,这是第一篇,现整体了解一下运行流程,定位资源加载,资源解析,bean 注册发生的位置。
2.记录结构:
1.调试栈截图
2.整体流程
3.bean.xml的处理
每段代码下面有相应的讲解

调试栈截图

每个栈帧中方法的行号都有标明,按照行号追溯源码,然后配合教程能够快速学习。

整体流程

ioc容器实例化代码

ApplicationContextapplicationContext=newClassPathXmlApplicationContext("bean.xml");

进入代码中一步步追溯,发现重要方法:refresh();
如下所示:

publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this.startupShutdownMonitor){//Preparethiscontextforrefreshing.prepareRefresh();//beanFactory实例化方法单步调试入口//Tellthesubclasstorefreshtheinternalbeanfactory.ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();//Preparethebeanfactoryforuseinthiscontext.prepareBeanFactory(beanFactory);try{//Allowspost-processingofthebeanfactoryincontextsubclasses.postProcessBeanFactory(beanFactory);//Invokefactoryprocessorsregisteredasbeansinthecontext.invokeBeanFactoryPostProcessors(beanFactory);//Registerbeanprocessorsthatinterceptbeancreation.registerBeanPostProcessors(beanFactory);//Initializemessagesourceforthiscontext.initMessageSource();//Initializeeventmulticasterforthiscontext.initApplicationEventMulticaster();//Initializeotherspecialbeansinspecificcontextsubclasses.onRefresh();//Checkforlistenerbeansandregisterthem.registerListeners();//Instantiateallremaining(non-lazy-init)singletons.finishBeanFactoryInitialization(beanFactory);//Laststep:publishcorrespondingevent.finishRefresh();}catch(BeansExceptionex){//Destroyalreadycreatedsingletonstoavoiddanglingresources.destroyBeans();//Reset'active'flag.cancelRefresh(ex);//Propagateexceptiontocaller.throwex;}}}

首先这个方法是同步的,以避免重复刷新。然后刷新的每个步骤,都放在单独的方法里,比较清晰,可以按顺序一个个看

首先是prepareRefresh()方法

protectedvoidprepareRefresh(){this.startupDate=System.currentTimeMillis();synchronized(this.activeMonitor){this.active=true;}if(logger.isInfoEnabled()){logger.info("Refreshing"+this);}//InitializeanyplaceholderpropertysourcesinthecontextenvironmentinitPropertySources();//Validatethatallpropertiesmarkedasrequiredareresolvable//seeConfigurablePropertyResolver#setRequiredPropertiesthis.environment.validateRequiredProperties();}

这个方法里做的事情不多,记录了开始时间,输出日志,另外initPropertySources()方法和validateRequiredProperties()方法一般都没有做什么事。

然后是核心的obtainFreshBeanFactory()方法,这个方法是初始化BeanFactory,是整个refresh()方法的核心,其中完成了配置文件的加载、解析、注册,后面会专门详细说 。

这里要说明一下,ApplicationContext实现了BeanFactory接口,并实现了ResourceLoader、MessageSource等接口,可以认为是增强的BeanFactory。但是ApplicationContext并不自己重复实现BeanFactory定义的方法,而是委托给DefaultListableBeanFactory来实现。这种设计思路也是值得学习的。
后面的 prepareBeanFactory()、postProcessBeanFactory()、invokeBeanFactoryPostProcessors()、registerBeanPostProcessors()、initMessageSource()、initApplicationEventMulticaster()、onRefresh()、registerListeners()、finishBeanFactoryInitialization()、finishRefresh()等方法,是添加一些后处理器、广播、拦截器等,就不一个个细说了

其中的关键方法是finishBeanFactoryInitialization(),在这个方法中,会对刚才注册的Bean(不延迟加载的),进行实例化,所以也是一个核心方法。

bean.xml的处理

从整体上介绍完了流程,接下来就重点看obtainFreshBeanFactory()方法,上文说到,在这个方法里,完成了配置文件的加载、解析、注册

protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){refreshBeanFactory();ConfigurableListableBeanFactorybeanFactory=getBeanFactory();if(logger.isDebugEnabled()){logger.debug("Beanfactoryfor"+getDisplayName()+":"+beanFactory);}returnbeanFactory;}

这个方法做了2件事,首先通过refreshBeanFactory()方法,创建了DefaultListableBeanFactory的实例,并进行初始化。

protectedfinalvoidrefreshBeanFactory()throwsBeansException{if(hasBeanFactory()){destroyBeans();closeBeanFactory();}try{DefaultListableBeanFactorybeanFactory=createBeanFactory();beanFactory.setSerializationId(getId());customizeBeanFactory(beanFactory);loadBeanDefinitions(beanFactory);synchronized(this.beanFactoryMonitor){this.beanFactory=beanFactory;}}catch(IOExceptionex){thrownewApplicationContextException("I/Oerrorparsingbeandefinitionsourcefor"+getDisplayName(),ex);}}

首先如果已经有BeanFactory实例,就先清空。然后通过createBeanFactory()方法,创建一个DefaultListableBeanFactory的实例

protectedDefaultListableBeanFactorycreateBeanFactory(){returnnewDefaultListableBeanFactory(getInternalParentBeanFactory());}

接下来设置ID唯一标识

beanFactory.setSerializationId(getId());

然后允许用户进行一些自定义的配置

protectedvoidcustomizeBeanFactory(DefaultListableBeanFactorybeanFactory){if(this.allowBeanDefinitionOverriding!=null){beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}if(this.allowCircularReferences!=null){beanFactory.setAllowCircularReferences(this.allowCircularReferences);}beanFactory.setAutowireCandidateResolver(newQualifierAnnotationAutowireCandidateResolver());}

最后,就是核心的loadBeanDefinitions()方法

protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{//CreateanewXmlBeanDefinitionReaderforthegivenBeanFactory.XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);//Configurethebeandefinitionreaderwiththiscontext's//resourceloadingenvironment.beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));//Allowasubclasstoprovidecustominitializationofthereader,//thenproceedwithactuallyloadingthebeandefinitions.initBeanDefinitionReader(beanDefinitionReader);loadBeanDefinitions(beanDefinitionReader);}

这里首先会创建一个XmlBeanDefinitionReader的实例,然后进行初始化。这个XmlBeanDefinitionReader中其实传递的BeanDefinitionRegistry类型的实例,为什么可以传递一个beanFactory呢,因为DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,这里是多态的使用。

protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{//CreateanewXmlBeanDefinitionReaderforthegivenBeanFactory.XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);//Configurethebeandefinitionreaderwiththiscontext's//resourceloadingenvironment.beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));//Allowasubclasstoprovidecustominitializationofthereader,//thenproceedwithactuallyloadingthebeandefinitions.initBeanDefinitionReader(beanDefinitionReader);}

这里要说明一下,ApplicationContext并不自己负责配置文件的加载、解析、注册,而是将这些工作委托给XmlBeanDefinitionReader来做。

loadBeanDefinitions(beanDefinitionReader);

这行代码,就是Bean定义读取实际发生的地方。这里的工作,主要是XmlBeanDefinitionReader来完成的,下一篇博客会详细介绍这个过程。

loadBeanDefinitionsloadBeanDefinitions: 源码阅读

入口是loadBeanDefinitions方法

protectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsIOException{String[]configLocations=getConfigLocations();if(configLocations!=null){for(StringconfigLocation:configLocations){reader.loadBeanDefinitions(configLocation);}}}

这是解析过程最外围的代码,首先要获取到配置文件的路径,这在之前已经完成了。
然后将每个配置文件的路径,作为参数传给BeanDefinitionReader的loadBeanDefinitions方法里

publicintloadBeanDefinitions(Stringlocation)throwsBeanDefinitionStoreException{returnloadBeanDefinitions(location,null);}

这个方法又调用了重载方法

publicintloadBeanDefinitions(Stringlocation,Set<Resource>actualResources)throwsBeanDefinitionStoreException{ResourceLoaderresourceLoader=getResourceLoader();if(resourceLoader==null){thrownewBeanDefinitionStoreException("Cannotimportbeandefinitionsfromlocation["+location+"]:noResourceLoaderavailable");}if(resourceLoaderinstanceofResourcePatternResolver){//Resourcepatternmatchingavailable.try{Resource[]resources=((ResourcePatternResolver)resourceLoader).getResources(location);intloadCount=loadBeanDefinitions(resources);if(actualResources!=null){for(Resourceresource:resources){actualResources.add(resource);}}if(logger.isDebugEnabled()){logger.debug("Loaded"+loadCount+"beandefinitionsfromlocationpattern["+location+"]");}returnloadCount;}catch(IOExceptionex){thrownewBeanDefinitionStoreException("Couldnotresolvebeandefinitionresourcepattern["+location+"]",ex);}}else{//CanonlyloadsingleresourcesbyabsoluteURL.Resourceresource=resourceLoader.getResource(location);intloadCount=loadBeanDefinitions(resource);if(actualResources!=null){actualResources.add(resource);}if(logger.isDebugEnabled()){logger.debug("Loaded"+loadCount+"beandefinitionsfromlocation["+location+"]");}returnloadCount;}}

首先getResourceLoader()的实现的前提条件是因为XmlBeanDefinitionReader在实例化的时候已经确定了创建了实例ResourceLoader实例, 代码位于 AbstractBeanDefinitionReader

protectedAbstractBeanDefinitionReader(BeanDefinitionRegistryregistry){Assert.notNull(registry,"BeanDefinitionRegistrymustnotbenull");this.registry=registry;//DetermineResourceLoadertouse.if(this.registryinstanceofResourceLoader){this.resourceLoader=(ResourceLoader)this.registry;}else{this.resourceLoader=newPathMatchingResourcePatternResolver();}//InheritEnvironmentifpossibleif(this.registryinstanceofEnvironmentCapable){this.environment=((EnvironmentCapable)this.registry).getEnvironment();}else{this.environment=newStandardEnvironment();}}

这个方法比较长,BeanDefinitionReader不能直接加载配置文件,需要把配置文件封装成Resource,然后才能调用重载方法loadBeanDefinitions()。所以这个方法其实就是2段,第一部分是委托ResourceLoader将配置文件封装成Resource,第二部分是调用loadBeanDefinitions(),对Resource进行解析

而这里的ResourceLoader,就是前面的XmlWebApplicationContext,因为ApplicationContext接口,是继承自ResourceLoader接口的

Resource也是一个接口体系,在web环境下,这里就是ServletContextResource

接下来进入重载方法loadBeanDefinitions()

publicintloadBeanDefinitions(Resource...resources)throwsBeanDefinitionStoreException{Assert.notNull(resources,"Resourcearraymustnotbenull");intcounter=0;for(Resourceresource:resources){counter+=loadBeanDefinitions(resource);}returncounter;}

这里就不用说了,就是把每一个Resource作为参数,继续调用重载方法。读spring源码,会发现重载方法特别多。

publicintloadBeanDefinitions(Resourceresource)throwsBeanDefinitionStoreException{returnloadBeanDefinitions(newEncodedResource(resource));}

还是重载方法,不过这里对传进来的Resource又进行了一次封装,变成了编码后的Resource。

publicintloadBeanDefinitions(EncodedResourceencodedResource)throwsBeanDefinitionStoreException{Assert.notNull(encodedResource,"EncodedResourcemustnotbenull");if(logger.isInfoEnabled()){logger.info("LoadingXMLbeandefinitionsfrom"+encodedResource.getResource());}Set<EncodedResource>currentResources=this.resourcesCurrentlyBeingLoaded.get();if(currentResources==null){currentResources=newHashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if(!currentResources.add(encodedResource)){thrownewBeanDefinitionStoreException("Detectedcyclicloadingof"+encodedResource+"-checkyourimportdefinitions!");}try{InputStreaminputStream=encodedResource.getResource().getInputStream();try{InputSourceinputSource=newInputSource(inputStream);if(encodedResource.getEncoding()!=null){inputSource.setEncoding(encodedResource.getEncoding());}returndoLoadBeanDefinitions(inputSource,encodedResource.getResource());}finally{inputStream.close();}}catch(IOExceptionex){thrownewBeanDefinitionStoreException("IOExceptionparsingXMLdocumentfrom"+encodedResource.getResource(),ex);}finally{currentResources.remove(encodedResource);if(currentResources.isEmpty()){this.resourcesCurrentlyBeingLoaded.remove();}}}

这个就是loadBeanDefinitions()的最后一个重载方法,比较长,可以拆看来看。

Assert.notNull(encodedResource,"EncodedResourcemustnotbenull");if(logger.isInfoEnabled()){logger.info("LoadingXMLbeandefinitionsfrom"+encodedResource.getResource());}Set<EncodedResource>currentResources=this.resourcesCurrentlyBeingLoaded.get();if(currentResources==null){currentResources=newHashSet<EncodedResource>(4);this.resourcesCurrentlyBeingLoaded.set(currentResources);}if(!currentResources.add(encodedResource)){thrownewBeanDefinitionStoreException("Detectedcyclicloadingof"+encodedResource+"-checkyourimportdefinitions!");}

这第一部分,是处理线程相关的工作,把当前正在解析的Resource,设置为当前Resource。

try{InputStreaminputStream=encodedResource.getResource().getInputStream();try{InputSourceinputSource=newInputSource(inputStream);if(encodedResource.getEncoding()!=null){inputSource.setEncoding(encodedResource.getEncoding());}returndoLoadBeanDefinitions(inputSource,encodedResource.getResource());}finally{inputStream.close();}}

这里是第二部分,是核心,首先把Resource还原为InputStream,然后调用实际解析的方法doLoadBeanDefinitions()。可以看到,这种命名方式是很值得学习的,一种业务方法,比如parse(),可能需要做一些外围的工作,然后实际解析的方法,可以命名为doParse()。这种doXXX()的命名方法,在很多开源框架中都有应用,比如logback等。
接下来就看一下这个doLoadBeanDefinitions()方法

protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)throwsBeanDefinitionStoreException{try{Documentdoc=doLoadDocument(inputSource,resource);returnregisterBeanDefinitions(doc,resource);returnregisterBeanDefinitions(doc,resource);}catch(BeanDefinitionStoreExceptionex){throwex;}catch(SAXParseExceptionex){thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"Line"+ex.getLineNumber()+"inXMLdocumentfrom"+resource+"isinvalid",ex);}catch(SAXExceptionex){thrownewXmlBeanDefinitionStoreException(resource.getDescription(),"XMLdocumentfrom"+resource+"isinvalid",ex);}catch(ParserConfigurationExceptionex){thrownewBeanDefinitionStoreException(resource.getDescription(),"ParserconfigurationexceptionparsingXMLfrom"+resource,ex);}catch(IOExceptionex){thrownewBeanDefinitionStoreException(resource.getDescription(),"IOExceptionparsingXMLdocumentfrom"+resource,ex);}catch(Throwableex){thrownewBeanDefinitionStoreException(resource.getDescription(),"UnexpectedexceptionparsingXMLdocumentfrom"+resource,ex);}}

抛开异常处理:核心代码如下:

Documentdoc=doLoadDocument(inputSource,resource);returnregisterBeanDefinitions(doc,resource);

doLoadDocument方法将InputStream读取成标准的Document对象,然后调用registerBeanDefinitions(),进行解析工作。

protectedDocumentdoLoadDocument(InputSourceinputSource,Resourceresource)throwsException{returnthis.documentLoader.loadDocument(inputSource,getEntityResolver(),this.errorHandler,getValidationModeForResource(resource),isNamespaceAware());}

接下来就看一下这个核心方法registerBeanDefinitions

publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinitionStoreException{//创建的其实是DefaultBeanDefinitionDocumentReader的实例,利用反射创建的。BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader();documentReader.setEnvironment(this.getEnvironment());intcountBefore=getRegistry().getBeanDefinitionCount();documentReader.registerBeanDefinitions(doc,createReaderContext(resource));returngetRegistry().getBeanDefinitionCount()-countBefore;}

这里注意两点 :

1.Document对象
首先这个Document对象,是W3C定义的标准XML对象,跟spring无关。其次这个registerBeanDefinitions方法,我觉得命名有点误导性。因为这个时候实际上解析还没有开始,怎么直接就注册了呢。比较好的命名,我觉得可以是parseAndRegisterBeanDefinitions()。
2.documentReader的创建时使用反射创建的,代码如下

protectedBeanDefinitionDocumentReadercreateBeanDefinitionDocumentReader(){returnBeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));}

instantiateClass方法中传入了一个Class类型的参数。追溯发现下述代码:

privateClass<?>documentReaderClass=DefaultBeanDefinitionDocumentReader.class;

所以创建的documentReaderClass是DefaultBeanDefinitionDocumentReader类的实例。
接下来就进入BeanDefinitionDocumentReader 中定义的registerBeanDefinitions()方法看看

publicvoidregisterBeanDefinitions(Documentdoc,XmlReaderContextreaderContext){this.readerContext=readerContext;logger.debug("Loadingbeandefinitions");Elementroot=doc.getDocumentElement();doRegisterBeanDefinitions(root);}

处理完外围事务之后,进入doRegisterBeanDefinitions()方法,这种命名规范,上文已经介绍过了

protectedvoiddoRegisterBeanDefinitions(Elementroot){StringprofileSpec=root.getAttribute(PROFILE_ATTRIBUTE);if(StringUtils.hasText(profileSpec)){Assert.state(this.environment!=null,"environmentpropertymustnotbenull");String[]specifiedProfiles=StringUtils.tokenizeToStringArray(profileSpec,BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if(!this.environment.acceptsProfiles(specifiedProfiles)){return;}}//anynested<beans>elementswillcauserecursioninthismethod.In//ordertopropagateandpreserve<beans>default-*attributescorrectly,//keeptrackofthecurrent(parent)delegate,whichmaybenull.Create//thenew(child)delegatewithareferencetotheparentforfallbackpurposes,//thenultimatelyresetthis.delegatebacktoitsoriginal(parent)reference.//thisbehavioremulatesastackofdelegateswithoutactuallynecessitatingone.BeanDefinitionParserDelegateparent=this.delegate;this.delegate=createHelper(readerContext,root,parent);preProcessXml(root);parseBeanDefinitions(root,this.delegate);postProcessXml(root);this.delegate=parent;}

这个方法也比较长,拆开来看

StringprofileSpec=root.getAttribute(PROFILE_ATTRIBUTE);if(StringUtils.hasText(profileSpec)){Assert.state(this.environment!=null,"environmentpropertymustnotbenull");String[]specifiedProfiles=StringUtils.tokenizeToStringArray(profileSpec,BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);if(!this.environment.acceptsProfiles(specifiedProfiles)){return;}}

如果配置文件中元素,配有profile属性,就会进入这一段,不过一般都是不会的

BeanDefinitionParserDelegateparent=this.delegate;this.delegate=createHelper(readerContext,root,parent);preProcessXml(root);parseBeanDefinitions(root,this.delegate);postProcessXml(root);this.delegate=parent;

然后这里创建了BeanDefinitionParserDelegate对象,preProcessXml()和postProcessXml()都是空方法,核心就是parseBeanDefinitions()方法。这里又把BeanDefinition解析和注册的工作,委托给了BeanDefinitionParserDelegate对象,在parseBeanDefinitions()方法中完成
总的来说,解析工作的委托链是这样的:ClassPathXmlApplicationContext,XmlBeanDefinitionReader,DefaultBeanDefinitionDocumentReader,BeanDefinitionParserDelegate
ClassPathXmlApplicationContext作为最外围的组件,发起解析的请求
XmlBeanDefinitionReader将配置文件路径封装为Resource,读取出w3c定义的Document对象,然后委托给DefaultBeanDefinitionDocumentReader
DefaultBeanDefinitionDocumentReader就开始做实际的解析工作了,但是涉及到bean的具体解析,它还是会继续委托给BeanDefinitionParserDelegate来做。
接下来在parseBeanDefinitions()方法中发生了什么,以及BeanDefinitionParserDelegate类完成的工作,在下一篇博客中继续介绍。

loadBeanDefinitions

BeanDefinition的解析,已经走到了DefaultBeanDefinitionDocumentR
eader里,这时候配置文件已经被加载,并解析成w3c的Document对象。这篇博客就接着介绍,DefaultBeanDefinitionDocumentReader和BeanDefinitionParserDelegate类,是怎么协同完成bean的解析和注册的。

BeanDefinitionParserDelegateparent=this.delegate;this.delegate=createHelper(readerContext,root,parent);preProcessXml(root);parseBeanDefinitions(root,this.delegate);postProcessXml(root);this.delegate=parent;

这段代码,创建了一个BeanDefinitionParserDelegate组件,然后就是preProcessXml()、parseBeanDefinitions()、postProcessXml()方法
其中preProcessXml()和postProcessXml()默认是空方法,接下来就看下parseBeanDefinitions()方法

protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){if(delegate.isDefaultNamespace(root)){NodeListnl=root.getChildNodes();for(inti=0;i<nl.getLength();i++){Nodenode=nl.item(i);if(nodeinstanceofElement){Elementele=(Element)node;if(delegate.isDefaultNamespace(ele)){parseDefaultElement(ele,delegate);}else{delegate.parseCustomElement(ele);}}}}else{delegate.parseCustomElement(root);}}

从这个方法开始,BeanDefinitionParserDelegate就开始发挥作用了,判断当前解析元素是否属于默认的命名空间,如果是的话,就调用parseDefaultElement()方法,否则调用delegate上parseCustomElement()方法

publicbooleanisDefaultNamespace(StringnamespaceUri){return(!StringUtils.hasLength(namespaceUri)||BEANS_NAMESPACE_URI.equals(namespaceUri));}publicbooleanisDefaultNamespace(Nodenode){returnisDefaultNamespace(getNamespaceURI(node));}

只有**http://www.springframework.org/schema/beans**,会被认为是默认的命名空间。也就是说,beans、bean这些元素,会认为属于默认的命名空间,而像task:scheduled这些,就认为不属于默认命名空间。
根节点beans的一个子节点bean,是属于默认命名空间的,所以会进入parseDefaultElement()方法

privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){if(delegate.nodeNameEquals(ele,IMPORT_ELEMENT)){importBeanDefinitionResource(ele);}elseif(delegate.nodeNameEquals(ele,ALIAS_ELEMENT)){processAliasRegistration(ele);}elseif(delegate.nodeNameEquals(ele,BEAN_ELEMENT)){processBeanDefinition(ele,delegate);}elseif(delegate.nodeNameEquals(ele,NESTED_BEANS_ELEMENT)){//recursedoRegisterBeanDefinitions(ele);}}

这里可能会有4种情况,import、alias、bean、beans,分别有一个方法与之对应,这里解析的是bean元素,所以会进入processBeanDefinition()方法

protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);if(bdHolder!=null){bdHolder=delegate.decorateBeanDefinitionIfRequired(ele,bdHolder);try{//Registerthefinaldecoratedinstance.BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());}catch(BeanDefinitionStoreExceptionex){getReaderContext().error("Failedtoregisterbeandefinitionwithname'"+bdHolder.getBeanName()+"'",ele,ex);}//Sendregistrationevent.getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));}}

这里主要有3个步骤,先是委托delegate对bean进行解析,然后委托delegate对bean进行装饰,最后由一个工具类来完成BeanDefinition的注册
可以看出来,DefaultBeanDefinitionDocumentReader不负责任何具体的bean解析,它面向的是xml Document对象,根据其元素的命名空间和名称,起一个类似路由的作用(不过,命名空间的判断,也是委托给delegate来做的)。所以这个类的命名,是比较贴切的,突出了其面向Document的特性。具体的工作,是由BeanDefinitionParserDelegate来完成的
下面就看下parseBeanDefinitionElement()方法

publicBeanDefinitionHolderparseBeanDefinitionElement(Elementele,BeanDefinitioncontainingBean){Stringid=ele.getAttribute(ID_ATTRIBUTE);StringnameAttr=ele.getAttribute(NAME_ATTRIBUTE);List<String>aliases=newArrayList<String>();if(StringUtils.hasLength(nameAttr)){String[]nameArr=StringUtils.tokenizeToStringArray(nameAttr,MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}StringbeanName=id;if(!StringUtils.hasText(beanName)&&!aliases.isEmpty()){beanName=aliases.remove(0);if(logger.isDebugEnabled()){logger.debug("NoXML'id'specified-using'"+beanName+"'asbeannameand"+aliases+"asaliases");}}if(containingBean==null){checkNameUniqueness(beanName,aliases,ele);}AbstractBeanDefinitionbeanDefinition=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);//Registeranaliasfortheplainbeanclassname,ifstillpossible,//ifthegeneratorreturnedtheclassnameplusasuffix.//ThisisexpectedforSpring1.2/2.0backwardscompatibility.StringbeanClassName=beanDefinition.getBeanClassName();if(beanClassName!=null&&beanName.startsWith(beanClassName)&&beanName.length()>beanClassName.length()&&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)){aliases.add(beanClassName);}}if(logger.isDebugEnabled()){logger.debug("NeitherXML'id'nor'name'specified-"+"usinggeneratedbeanname["+beanName+"]");}}catch(Exceptionex){error(ex.getMessage(),ele);returnnull;}}String[]aliasesArray=StringUtils.toStringArray(aliases);returnnewBeanDefinitionHolder(beanDefinition,beanName,aliasesArray);}returnnull;}

这个方法很长,可以分成三段来看

Stringid=ele.getAttribute(ID_ATTRIBUTE);StringnameAttr=ele.getAttribute(NAME_ATTRIBUTE);List<String>aliases=newArrayList<String>();if(StringUtils.hasLength(nameAttr)){String[]nameArr=StringUtils.tokenizeToStringArray(nameAttr,MULTI_VALUE_ATTRIBUTE_DELIMITERS);aliases.addAll(Arrays.asList(nameArr));}StringbeanName=id;if(!StringUtils.hasText(beanName)&&!aliases.isEmpty()){beanName=aliases.remove(0);if(logger.isDebugEnabled()){logger.debug("NoXML'id'specified-using'"+beanName+"'asbeannameand"+aliases+"asaliases");}}if(containingBean==null){checkNameUniqueness(beanName,aliases,ele);}

这一段,主要是处理一些跟alias,id等标识相关的东西

AbstractBeanDefinitionbeanDefinition=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);//Registeranaliasfortheplainbeanclassname,ifstillpossible,//ifthegeneratorreturnedtheclassnameplusasuffix.//ThisisexpectedforSpring1.2/2.0backwardscompatibility.StringbeanClassName=beanDefinition.getBeanClassName();if(beanClassName!=null&&beanName.startsWith(beanClassName)&&beanName.length()>beanClassName.length()&&!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)){aliases.add(beanClassName);}}if(logger.isDebugEnabled()){logger.debug("NeitherXML'id'nor'name'specified-"+"usinggeneratedbeanname["+beanName+"]");}}catch(Exceptionex){error(ex.getMessage(),ele);returnnull;}}String[]aliasesArray=StringUtils.toStringArray(aliases);returnnewBeanDefinitionHolder(beanDefinition,beanName,aliasesArray);}

这段是后置处理,对beanName进行处理
前置处理和后置处理,不是核心,就不细看了,重点看下核心的那一行调用

publicAbstractBeanDefinitionparseBeanDefinitionElement(Elementele,StringbeanName,BeanDefinitioncontainingBean){this.parseState.push(newBeanEntry(beanName));StringclassName=null;if(ele.hasAttribute(CLASS_ATTRIBUTE)){className=ele.getAttribute(CLASS_ATTRIBUTE).trim();}try{Stringparent=null;if(ele.hasAttribute(PARENT_ATTRIBUTE)){parent=ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinitionbd=createBeanDefinition(className,parent);parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));parseMetaElements(ele,bd);parseLookupOverrideSubElements(ele,bd.getMethodOverrides());parseReplacedMethodSubElements(ele,bd.getMethodOverrides());parseConstructorArgElements(ele,bd);parsePropertyElements(ele,bd);parseQualifierElements(ele,bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));returnbd;}catch(ClassNotFoundExceptionex){error("Beanclass["+className+"]notfound",ele,ex);}catch(NoClassDefFoundErrorerr){error("Classthatbeanclass["+className+"]dependsonnotfound",ele,err);}catch(Throwableex){error("Unexpectedfailureduringbeandefinitionparsing",ele,ex);}finally{this.parseState.pop();}returnnull;}

这个方法也挺长的,拆开看看

this.parseState.push(newBeanEntry(beanName));StringclassName=null;if(ele.hasAttribute(CLASS_ATTRIBUTE)){className=ele.getAttribute(CLASS_ATTRIBUTE).trim();}

这段是从配置中抽取出类名。接下来的长长一段,把异常处理先抛开,看看实际的业务

Stringparent=null;if(ele.hasAttribute(PARENT_ATTRIBUTE)){parent=ele.getAttribute(PARENT_ATTRIBUTE);}AbstractBeanDefinitionbd=createBeanDefinition(className,parent);parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));parseMetaElements(ele,bd);parseLookupOverrideSubElements(ele,bd.getMethodOverrides());parseReplacedMethodSubElements(ele,bd.getMethodOverrides());parseConstructorArgElements(ele,bd);parsePropertyElements(ele,bd);parseQualifierElements(ele,bd);bd.setResource(this.readerContext.getResource());bd.setSource(extractSource(ele));returnbd;

这里每个方法的命名,就说明了是要干什么,可以一个个跟进去看,本文就不细说了。总之,经过这里的解析,就得到了一个完整的BeanDefinitionHolder。只是说明一下,如果在配置文件里,没有对一些属性进行设置,比如autowire-candidate等,那么这个解析生成的BeanDefinition,都会得到一个默认值
然后,对这个Bean做一些必要的装饰

publicBeanDefinitionHolderdecorateBeanDefinitionIfRequired(Elementele,BeanDefinitionHolderdefinitionHolder,BeanDefinitioncontainingBd){BeanDefinitionHolderfinalDefinition=definitionHolder;//Decoratebasedoncustomattributesfirst.NamedNodeMapattributes=ele.getAttributes();for(inti=0;i<attributes.getLength();i++){Nodenode=attributes.item(i);finalDefinition=decorateIfRequired(node,finalDefinition,containingBd);}//Decoratebasedoncustomnestedelements.NodeListchildren=ele.getChildNodes();for(inti=0;i<children.getLength();i++){Nodenode=children.item(i);if(node.getNodeType()==Node.ELEMENT_NODE){finalDefinition=decorateIfRequired(node,finalDefinition,containingBd);}}returnfinalDefinition;}

持续单步调试,代码继续运行到DefaultBeanDefinitionDocumentReader中的processBeanDefinition中的registerBeanDefinition()

BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());

单步进入代码发现BeanDefinitionReaderUtils静态方法registerBeanDefinition()

publicstaticvoidregisterBeanDefinition(BeanDefinitionHolderdefinitionHolder,BeanDefinitionRegistryregistry)throwsBeanDefinitionStoreException{//Registerbeandefinitionunderprimaryname.StringbeanName=definitionHolder.getBeanName();//其实调用的是DefaultListableBeanFactory中的registerBeanDefinition方法registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());//Registeraliasesforbeanname,ifany.String[]aliases=definitionHolder.getAliases();if(aliases!=null){for(Stringaliase:aliases){registry.registerAlias(beanName,aliase);}}}

解释一下其实调用的是DefaultListableBeanFactory中的registerBeanDefinition方法这句话,因为DefaultListableBeanFactory实现BeanDefinitionRegistry接口,BeanDefinitionRegistry接口中定义了registerBeanDefinition()方法
看下DefaultListableBeanFactory中registerBeanDefinition()实例方法的具体实现:

publicvoidregisterBeanDefinition(StringbeanName,BeanDefinitionbeanDefinition)throwsBeanDefinitionStoreException{Assert.hasText(beanName,"Beannamemustnotbeempty");Assert.notNull(beanDefinition,"BeanDefinitionmustnotbenull");if(beanDefinitioninstanceofAbstractBeanDefinition){try{((AbstractBeanDefinition)beanDefinition).validate();}catch(BeanDefinitionValidationExceptionex){thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,"Validationofbeandefinitionfailed",ex);}}synchronized(this.beanDefinitionMap){ObjectoldBeanDefinition=this.beanDefinitionMap.get(beanName);if(oldBeanDefinition!=null){if(!this.allowBeanDefinitionOverriding){thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,"Cannotregisterbeandefinition["+beanDefinition+"]forbean'"+beanName+"':Thereisalready["+oldBeanDefinition+"]bound.");}else{if(this.logger.isInfoEnabled()){this.logger.info("Overridingbeandefinitionforbean'"+beanName+"':replacing["+oldBeanDefinition+"]with["+beanDefinition+"]");}}}else{this.beanDefinitionNames.add(beanName);this.frozenBeanDefinitionNames=null;}this.beanDefinitionMap.put(beanName,beanDefinition);resetBeanDefinition(beanName);}}

代码追溯之后发现这个方法里,最关键的是以下2行:

this.beanDefinitionNames.add(beanName);this.beanDefinitionMap.put(beanName,beanDefinition);

前者是把beanName放到队列里,后者是把BeanDefinition放到map中,到此注册就完成了。在后面实例化的时候,就是把beanDefinitionMap中的BeanDefinition取出来,逐一实例化
BeanFactory准备完毕之后,代码又回到了ClassPathXmlApplicationContext里

publicvoidrefresh()throwsBeansException,IllegalStateException{synchronized(this.startupShutdownMonitor){//Preparethiscontextforrefreshing.prepareRefresh();//Tellthesubclasstorefreshtheinternalbeanfactory.ConfigurableListableBeanFactorybeanFactory=obtainFreshBeanFactory();//Preparethebeanfactoryforuseinthiscontext.prepareBeanFactory(beanFactory);try{//Allowspost-processingofthebeanfactoryincontextsubclasses.postProcessBeanFactory(beanFactory);//Invokefactoryprocessorsregisteredasbeansinthecontext.invokeBeanFactoryPostProcessors(beanFactory);//Registerbeanprocessorsthatinterceptbeancreation.registerBeanPostProcessors(beanFactory);//Initializemessagesourceforthiscontext.initMessageSource();//Initializeeventmulticasterforthiscontext.initApplicationEventMulticaster();//Initializeotherspecialbeansinspecificcontextsubclasses.onRefresh();//Checkforlistenerbeansandregisterthem.registerListeners();//Instantiateallremaining(non-lazy-init)singletons.finishBeanFactoryInitialization(beanFactory);//Laststep:publishcorrespondingevent.finishRefresh();}catch(BeansExceptionex){//Destroyalreadycreatedsingletonstoavoiddanglingresources.destroyBeans();//Reset'active'flag.cancelRefresh(ex);//Propagateexceptiontocaller.throwex;}}}

也就是obtainFreshBeanFactory()方法执行之后,再进行下面的步骤。
总结来说,ApplicationContext将解析配置文件的工作委托给BeanDefinitionReader,然后BeanDefinitionReader将配置文件读取为xml的Document文档之后,又委托给BeanDefinitionDocumentReader
BeanDefinitionDocumentReader这个组件是根据xml元素的命名空间和元素名,起到一个路由的作用,实际的解析工作,是委托给BeanDefinitionParserDelegate来完成的
BeanDefinitionParserDelegate的解析工作完成以后,会返回BeanDefinitionHolder给BeanDefinitionDocumentReader,在这里,会委托给DefaultListableBeanFactory完成bean的注册
XmlBeanDefinitionReader(计数、解析XML文档),BeanDefinitionDocumentReader(依赖xml文档,进行解析和注册),BeanDefinitionParserDelegate(实际的解析工作)。可以看出,在解析bean的过程中,这3个组件的分工是比较清晰的,各司其职,这种设计思想值得学习
到此为止,bean的解析、注册、spring ioc 容器的实例化过程就基本分析结束了。

微信公众号【黄小斜】作者是蚂蚁金服 JAVA 工程师,专注于 JAVA
后端技术栈:SpringBoot、SSM全家桶、MySQL、分布式、中间件、微服务,同时也懂点投资理财,坚持学习和写作,相信终身学习的力量!关注公众号后回复”架构师“即可领取
Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源