Spring 源码解析 加载与初始化.docx

上传人:b****7 文档编号:10605098 上传时间:2023-02-21 格式:DOCX 页数:20 大小:152.67KB
下载 相关 举报
Spring 源码解析 加载与初始化.docx_第1页
第1页 / 共20页
Spring 源码解析 加载与初始化.docx_第2页
第2页 / 共20页
Spring 源码解析 加载与初始化.docx_第3页
第3页 / 共20页
Spring 源码解析 加载与初始化.docx_第4页
第4页 / 共20页
Spring 源码解析 加载与初始化.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

Spring 源码解析 加载与初始化.docx

《Spring 源码解析 加载与初始化.docx》由会员分享,可在线阅读,更多相关《Spring 源码解析 加载与初始化.docx(20页珍藏版)》请在冰豆网上搜索。

Spring 源码解析 加载与初始化.docx

Spring源码解析加载与初始化

Spring代码分析一:

加载与初始化

一般的Web项目都会在web.xml中加入Spring监听器,内容如下:

org.springframework.web.context.ContextLoaderListener

contextConfigLocationclasspath:

applicationContext-struts.xml,classpath:

spring/applicationContext.xml

我们的问题是,Spring是何时以及如何加载我们的配置文件来初始化Bean工厂的,带着这些问题,我们展开研究:

我们先来看看web.xml中配置的监听器的类,来回答我们的问题,Spring是何时来加载我们的配置文件的:

org.springframework.web.context.ContextLoaderListener

它继承了javax.servlet.ServletContextListener接口。

ServletContextListener是J2EEServletAPI中的一个标准接口,

它能够监听ServletContext对象的生命周期,实际上就是监听Web应用的生命周期。

当Servlet容器启动或终止Web应用时,会触发ServletContextEvent事件,该事件由ServletContextListener来处理。

这里面有两个方法我们比较感兴趣:

/**

*CreatetheContextLoadertouse.Canbeoverriddeninsubclasses.

*@returnthenewContextLoader

*/

ProtectedContextLoadercreateContextLoader(){

ReturnnewContextLoader();

}

这个方法构造一个默认的ContextLoader,ContextLoader可以理解为Spring上下文的加载器。

之所以这样去定义这样一个类,是为了开发人员进行重写此方法来使用一个自定义的Spring上下文的加载器。

/**

*Initializetherootwebapplicationcontext.

*/

PublicvoidcontextInitialized(ServletContextEventevent){

this.contextLoader=createContextLoader();

this.contextLoader.initWebApplicationContext(event.getServletContext());

}

这个方法很简单,仅仅只是调用了createContextLoader()构造了ContextLoader,并调用其初始化方法。

由此,我们可以得出结论,Spring是在Web项目启动时,通过ServletContextListener机制,来加载以及初始化Spring上下文的。

下面,我们好好研究一下Spring是如何加载其上下文的:

我们先定位ContextLoader类。

看看此类的initWebApplicationContext()方法(省略了不重要的语句)

/**

*InitializeSpring'swebapplicationcontextforthegivenservletcontext,

*accordingtothe"{@link#CONTEXT_CLASS_PARAMcontextClass}"and

*"{@link#CONFIG_LOCATION_PARAMcontextConfigLocation}"context-params.

*@paramservletContextcurrentservletcontext

*@returnthenewWebApplicationContext

*@throwsIllegalStateExceptionifthereisalreadyarootapplicationcontextpresent

*@throwsBeansExceptionifthecontextfailedtoinitialize

*@see#CONTEXT_CLASS_PARAM

*@see#CONFIG_LOCATION_PARAM

*/

publicWebApplicationContextinitWebApplicationContext(ServletContextservletContext)

throwsIllegalStateException,BeansException{

if(servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)!

=null){

thrownewIllegalStateException(

"Cannotinitializecontextbecausethereisalreadyarootapplicationcontextpresent-"+

"checkwhetheryouhavemultipleContextLoader*definitionsinyourweb.xml!

");

}

try{

//Determineparentforrootwebapplicationcontext,ifany.

ApplicationContextparent=loadParentContext(servletContext);

//Storecontextinlocalinstancevariable,toguaranteethat

//itisavailableonServletContextshutdown.

this.context=createWebApplicationContext(servletContext,parent);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,this.context);

currentContextPerThread.put(Thread.currentThread().getContextClassLoader(),this.context);

returnthis.context;

}catch(RuntimeExceptionex){

logger.error("Contextinitializationfailed",ex);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,ex);

throwex;

}catch(Errorerr){

logger.error("Contextinitializationfailed",err);

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,err);

throwerr;

}

}

其中的有两句比较重要,我们来看看:

ApplicationContextparent=loadParentContext(servletContext);

这个方法的用途主要是用来解决Spring共享环境的,即,如果我们有多个WAR包部署在同一个服务器上,而且这些WAR都共享某一套业务逻辑层。

如何共享一套业务逻辑包配置而不要每个WAR都单独配置,这时我们就可能需要Spring的共享环境了。

ProtectedApplicationContextloadParentContext(ServletContextservletContext)throwsBeansException{

ApplicationContextparentContext=null;

//从web.xml中读取父工厂的配置文件,默认为:

"classpath*:

beanRefContext.xml"

StringlocatorFactorySelector=servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM);

//从web.xml中读取父类工厂的名称

StringparentContextKey=servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM);

if(parentContextKey!

=null){

//locatorFactorySelectormaybenull,indicatingthedefault"classpath*:

beanRefContext.xml"

BeanFactoryLocatorlocator=ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

this.parentContextRef=locator.useBeanFactory(parentContextKey);

parentContext=(ApplicationContext)this.parentContextRef.getFactory();

}

returnparentContext;

}

现在我们引入BeanFactoryLocator,它是Spring配置文件的一个定位器,Spring官方给它的定义是用来查找,使用和释放一个BeanFactory或其子类的接口。

下面我们看看此图:

ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

是根据参数locatorFactorySelector去一个单例工厂中去拿一个对应的BeanFactoryLocator,也即,如果工厂中没有对应于locatorFactorySelector的BeanFactoryLocator对象,那就返回一个新的BeanFactoryLocator实例(这里是ContextSingletonBeanFactoryLocator的实例),否则,就从工厂里取现有的BeanFactoryLocator对象。

ContextSingletonBeanFactoryLocator里维护了一个静态的Map对象instances,每次需要新增BeanFactoryLocator实例时都会更新这个Map对象,这个Map对象是以配置文件名为KEY,BeanFactoryLocator对象为值。

原因很简单,就是希望同一个配置文件只被初始化一次。

如果没有在web.xml中定义locatorFactorySelector这个参数,父环境的配置文件默认使用:

"classpath*:

beanRefContext.xml"

this.parentContextRef=locator.useBeanFactory(parentContextKey);

此方法定义在SingletonBeanFactoryLocator类中,同样是一个单例工厂模式,判断传入的参数parentContextKey对应的BeanFactory是否有被初始化,经过上面的ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector)指定Spring父环境配置文件,这个方法判断指定的父环境是否被初始化,如果有则返回,没有就进行初始化。

看看此方法的实现:

PublicBeanFactoryReferenceuseBeanFactory(StringfactoryKey)throwsBeansException{

synchronized(this.bfgInstancesByKey){

BeanFactoryGroupbfg=(BeanFactoryGroup)this.bfgInstancesByKey.get(this.resourceLocation);

if(bfg!

=null){

bfg.refCount++;

}else{

//CreatetheBeanFactorybutdon'tinitializeit.

BeanFactorygroupContext=createDefinition(this.resourceLocation,factoryKey);

//Recorditsexistencenow,beforeinstantiatinganysingletons.

bfg=newBeanFactoryGroup();

bfg.definition=groupContext;

bfg.refCount=1;

this.bfgInstancesByKey.put(this.resourceLocation,bfg);

this.bfgInstancesByObj.put(groupContext,bfg);

//NowinitializetheBeanFactory.Thismaycauseare-entrantinvocation

//ofthismethod,butsincewe'vealreadyaddedtheBeanFactorytoour

//mappings,thenexttimeitwillbefoundandsimplyhaveits

//referencecountincremented.

try{

initializeDefinition(groupContext);

}catch(BeansExceptionex){

this.bfgInstancesByKey.remove(this.resourceLocation);

this.bfgInstancesByObj.remove(groupContext);

thrownewBootstrapException("Unabletoinitializegroupdefinition."+

"Groupresourcename["+this.resourceLocation+"],factorykey["+factoryKey+"]",ex);

}

}

try{

BeanFactorybeanFactory=null;

if(factoryKey!

=null){

beanFactory=(BeanFactory)bfg.definition.getBean(factoryKey,BeanFactory.class);

}elseif(bfg.definitioninstanceofListableBeanFactory){

beanFactory=(BeanFactory)BeanFactoryUtils.beanOfType((ListableBeanFactory)bfg.definition,BeanFactory.class);

}else{

thrownewIllegalStateException(

"Factorykeyisnull,andunderlyingfactoryisnotaListableBeanFactory:

"+bfg.definition);

}

returnnewCountingBeanFactoryReference(beanFactory,bfg.definition);

}catch(BeansExceptionex){

thrownewBootstrapException("UnabletoreturnspecifiedBeanFactoryinstance:

factorykey["+

factoryKey+"],fromgroupwithresourcename["+this.resourceLocation+"]",ex);

}

}

}

此方法分为两作了两件事,

第一,初始化上下文,注意这里初始化的是从web.xml配置参数里的Spring配置文件,也是上面讲loadParentContext方法里的

BeanFactoryLocatorlocator=ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

这句指定的参数。

这里初始化的是这个配置文件所有Bean。

我们指定的factoryKey对应的Bean也是其中之一。

第二,从已经初始化的Spring上下文环境中获取Spring父环境。

sharebean.xml

sharebean2.xml

—=========================web.xml=========================-->

locatorFactorySelector

beanRefFactory.xml

parentContextKey

factoryBeanId

这个一个典型的构造父环境的配置,web项目在启动的时候就会发现里面有Spring父环境的配置,那么Spring首先就会生成一个对应的配置文件为beanRefFactory.xml的BeanFactory(web.xml中的locatorFactorySelector参数指定),同时Spring在解析的时候,会发现factoryBeanId的配置同样为BeanFacotry(beanRefFactory.xml中factoryBeanId对应的Bean),所以Spring在拿父环境时就会写成:

beanFactory=(BeanFactory)bfg.definition.getBean(factoryKey,BeanFactory.class);

方法实现里引入了BeanFactoryGroup类。

类的结构很简单

refCount:

用来记录实例被外部引用的记数,当调用locator.useBeanFactory(parentContextKey)方法时,引用数就会加1,当调用CountingBeanFactoryReference#release方法时,引用数就会减1,当它变成0时,Spring就会释放掉它占用的内存,同时也会销毁掉它definition变量引用的BeanFactory。

下次再调用locator.useBeanFactory(parentContextKey)就会重新初始化BeanFactory。

说到release,请同学们参考ContextLoader中如下的两条语句:

//在调用CountingBeanFactoryReference#release后,即使对象已经销毁,这个Map仍然可以返回locator对象。

BeanFactoryLocatorlocator=ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

//如果对象已经销毁,再调用此方法会再一次初始化BeanFactory

this.parentContextRef=locator.useBeanFactory(parentContextKey);

bfgInstancesByKey:

一个Map对象,以配置文件名为Key,配置文件解析后生成的BeanFactory构成的BeanFactoryGroup为值。

bfgInstancesByObj:

一个Map对象,以BeanFactoryGroup.definitiion为Key,以BeanFactoryGroup为值。

这个对象主要还是在CountingBeanFactoryReference#release时使用。

下面,我看再看看另一个地方:

if(parentContextKey!

=null){

//locatorFactorySelectormaybenull,indicatingthedefault"classpath*:

beanRefContext.xml"

BeanFactoryLocatorlocator=ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector);

this.parentConte

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 经济学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1