您的位置:首页 > 编程语言 > Java开发

Spring 源码阅读–beans-(1)

2016-05-11 11:10 134 查看
最近买了本书,来大概学习写spring源码

一:先来段代码来测试一下。

照书上的先来试试

<?xmlversion="1.0"encoding="UTF-8"?>
<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.xsd">
<beanid="myTestBean"class="com.nico.TestClient.SpringTest.BeanTest.MyTestBean"/>

</beans>


packagecom.nico.TestClient.SpringTest.BeanTest;

importorg.junit.Test;
importorg.springframework.beans.factory.BeanFactory;
importorg.springframework.beans.factory.xml.XmlBeanFactory;
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
importorg.springframework.core.io.ClassPathResource;

/**
*CreatedbyAdministratoron2016/5/9.
*/
publicclassSpringBean{

@Test
publicvoidtestSimpleLoad(){
//已经过时的方法
BeanFactorybf=newXmlBeanFactory(newClassPathResource("BeanFactoryTest.xml"));

ApplicationContextcontext=newClassPathXmlApplicationContext("BeanFactoryTest.xml");
MyTestBeanmyTestBean=(MyTestBean)context.getBean("myTestBean");

System.out.println("testStr:"+myTestBean.getTestStr());
}
}


当照着书上面的代码样例写出来的时候,擦IDEA告诉我已经过时了,不禁让我心底一凉,这书是不是买的太久了。。。

好吧我们来学习新版本的源码,大概印证下吧,只有。

首先这个类来读取资源文件

ApplicationContextcontext=newClassPathXmlApplicationContext("BeanFactoryTest.xml");


然后我们可以通过context.getbean就可以获取各种类型的bean。

大致学习源码还是按照书中的步骤来,节省时间!!只有以后在有机会一个版本一个版本的看看区别。

二:载入xml文件,以及读取。

上面测试代码的大致流程:

1.读取配置文件

2.根据配置文件中的配置找到对应类的配置,并实例化

3.调用实例化后的实例

三:分析代码以及实现

首先我们先从第一段代码分析:

(1)BeanFactorybf=newXmlBeanFactory(newClassPathResource("BeanFactoryTest.xml"));

大致流程:

1.newClassPathResource(xml文件)返回ClassPathResource

2.newXmlBeanFactory()返回beanfactiory对象

(2)配置文件封装

spring的配置文件是通过ClassPathResource来进行封装的,将不用来源的资源抽象成RL,然后我们可以查看Resource来看看都有哪几种方法:

看下类图





相比书中所写,已经少了很多,将就看吧。。大致脉络看看。

我们进去看看Resource,其中的方法有:

contentLength
createRelative
exists
getDescription
getFile
getFilename
getURI
getURL
isOpen
isReadable
lastModified

其中抽象了Spring内部使用到的底层资源,file,url,classpath等。并且存在三个判断当前资源状态的三个方法:存在性,刻度性,是否可以打开状态

Resource接口方便对资源进行统一管理,实现方式是通过class或者classLoader提供的底层方法进行调用。

当resource相关类完成了对配置文件的封装后配置文件的读取工作就就交给xmlBeanDefinitionReader来处理了。

XmlBeanFactory的初始化有若干方法,我看下使用Resource实例作为构造参数的方法:

/**
*CreateanewXmlBeanFactorywiththegivenresource,
*whichmustbeparsableusingDOM.
*@paramresourceXMLresourcetoloadbeandefinitionsfrom
*@throwsBeansExceptionincaseofloadingorparsingerrors
*/
publicXmlBeanFactory(Resourceresource)throwsBeansException{
this(resource,null);
}

/**
*CreateanewXmlBeanFactorywiththegiveninputstream,
*whichmustbeparsableusingDOM.
*@paramresourceXMLresourcetoloadbeandefinitionsfrom
*@paramparentBeanFactoryparentbeanfactory
*@throwsBeansExceptionincaseofloadingorparsingerrors
*/
publicXmlBeanFactory(Resourceresource,BeanFactoryparentBeanFactory)throwsBeansException{
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}


下面的reader.loadBeanDefinitions就是资源加载的真正实现。

在这之前还有一个父类,我点进去瞅一瞅。

/**
*CreateanewAbstractAutowireCapableBeanFactory.
*/
publicAbstractAutowireCapableBeanFactory(){
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}


其中ignoreDependencyInterface的主要功能就是忽略给定接口的自动装配功能。

(3)加载Bean

上面我们走到这个this.reader.loadBeanDefinitions(resource),这里是整个资源加载的切入点。

大致顺序:

1.封装资源文件。当进入

loadBeanDefinitions方法时候使用EncodeResource类进行封装

2.获取输入流。从Resource中获取对应的InputStream并且构造InputSource

3.通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。

源码流程:


publicXmlBeanFactory(Resourceresource)throwsBeansException{
this(resource,null);
}

publicXmlBeanFactory(Resourceresource,BeanFactoryparentBeanFactory)throwsBeansException{
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}

@Override
publicintloadBeanDefinitions(Resourceresource)throwsBeanDefinitionStoreException{
returnloadBeanDefinitions(newEncodedResource(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();
}
}
}

protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)
throwsBeanDefinitionStoreException{
try{
Documentdoc=doLoadDocument(inputSource,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);
}
}


大致的源码调用顺序。

首先看EncodedResource的作用,对于资源文件的编码进行处理。主要的方法在于其中的getResder方法,用来设置编码。

当构造好了encodeResource对象后,再次转入

loadBeanDefinitions(EncodedResourceencodedResource),这个方法内部就是真正的数据准备阶段。

然后

doLoadBeanDefinitions方法就是核心算法。

然后它大概做了三件事:

1.获取对xml文件的验证模式

2.加载xml文件,并得到对应的document

3.根据返回的document注册bean信息


三:获取XML的验证模式

xml文件的验证模式保证了xml文件的正确性,比较常用的是两种:DTD和XSD。

DTD和XSD的区别

都在头部有声明

(1)验证模式的读取

通过这个方法来读取getValidationModeForResource

如果手动指定了验证模式则使用指定的验证模式,如果未指定则使用自动检测

intvalidationModeToUse=getValidationMode();
if(validationModeToUse!=VALIDATION_AUTO){
returnvalidationModeToUse;
}
intdetectedMode=detectValidationMode(resource);
if(detectedMode!=VALIDATION_AUTO){
returndetectedMode;
}


自动检测方法在

detectValidationMode中实现,最后来


/**
*DoesthecontentcontainthetheDTDDOCTYPEdeclaration?
*/
privatebooleanhasDoctype(Stringcontent){
return(content.indexOf(DOCTYPE)>-1);
}


判断是否含有DOCTYPE来检测验证模式。

四:获取document

经过验证模式准备的步骤就可以进行document加载了,然后通过loadDocument方法来进行,主要是通过SAX解析XML文档,

@Override
publicDocumentloadDocument(InputSourceinputSource,EntityResolverentityResolver,
ErrorHandlererrorHandler,intvalidationMode,booleannamespaceAware)throwsException{

DocumentBuilderFactoryfactory=createDocumentBuilderFactory(validationMode,namespaceAware);
if(logger.isDebugEnabled()){
logger.debug("UsingJAXPprovider["+factory.getClass().getName()+"]");
}
DocumentBuilderbuilder=createDocumentBuilder(factory,entityResolver,errorHandler);
returnbuilder.parse(inputSource);
}


然后其中要有个EntityResolver.

(1)EntityResolver用法

SAX应用需要实现自定义处理外部实体,则必须实现此接口向SAX注册一个实例,对于解析XML,SAX首先读取该XML的声明,根据声明去寻找相应的DTD定义。默认寻址通过网络,下载过程是个漫长的过程,而且网络中断或者不可用,这里会报错。这个的作用是项目本身就可以提供一个DTD声明的方法,避免了通过网络来寻找相应的声明。

五:解析及注册BeanDefinitions

上面我们已经把文件转化成Document,然后就是提取以及注册bean。

当拥有Document实例对象时候,引入下面的方法。

publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinitionStoreException{
BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
intcountBefore=getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));
returngetRegistry().getBeanDefinitionCount()-countBefore;
}


很符合面向对象的单一职责的原则。BeanDefinitionDocumentReader是一个接口,实例化是在

createBeanDefinitionDocumentReader完成,通过这个方法,然后进入DefaultBeanDefinitionDocumentReader,这个方法的重要目的之一就是提取ROOT以便于再次将root作为参数继续BeanDefinition的注册


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


protectedvoiddoRegisterBeanDefinitions(Elementroot){
StringprofileSpec=root.getAttribute(PROFILE_ATTRIBUTE);
if(StringUtils.hasText(profileSpec)){
Assert.state(this.environment!=null,"Environmentmustbesetforevaluatingprofiles");
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=createDelegate(this.readerContext,root,parent);

preProcessXml(root);
parseBeanDefinitions(root,this.delegate);
postProcessXml(root);

this.delegate=parent;
}


之前的所有都是XML加载解析的准备阶段doRegisterBeanDefinitions算是真正地开始进行解析。

那么处理流程,首先是对rpofile的处理,然后开始解析。

(1)profile属性的使用

注册bean的最开始是对PROFILE_ATTRIBUTE属性的解析,

profile用的最多是更换不同的数据库,用来生产环境和开发环境。

(2)解析并注册BeanDeifnition

处理了profile后就可以进行XML的读取了。进入

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);
}
}


这个方法就是XML的读取,
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: