Spring 源码阅读–beans-(1)
2016-05-11 11:10
134 查看
最近买了本书,来大概学习写spring源码
一:先来段代码来测试一下。
照书上的先来试试
当照着书上面的代码样例写出来的时候,擦IDEA告诉我已经过时了,不禁让我心底一凉,这书是不是买的太久了。。。
好吧我们来学习新版本的源码,大概印证下吧,只有。
首先这个类来读取资源文件
然后我们可以通过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实例作为构造参数的方法:
下面的reader.loadBeanDefinitions就是资源加载的真正实现。
在这之前还有一个父类,我点进去瞅一瞅。
其中ignoreDependencyInterface的主要功能就是忽略给定接口的自动装配功能。
(3)加载Bean
上面我们走到这个this.reader.loadBeanDefinitions(resource),这里是整个资源加载的切入点。
大致顺序:
1.封装资源文件。当进入
大致的源码调用顺序。
首先看EncodedResource的作用,对于资源文件的编码进行处理。主要的方法在于其中的getResder方法,用来设置编码。
当构造好了encodeResource对象后,再次转入
都在头部有声明
(1)验证模式的读取
通过这个方法来读取getValidationModeForResource
如果手动指定了验证模式则使用指定的验证模式,如果未指定则使用自动检测
自动检测方法在
判断是否含有DOCTYPE来检测验证模式。
四:获取document
经过验证模式准备的步骤就可以进行document加载了,然后通过loadDocument方法来进行,主要是通过SAX解析XML文档,
然后其中要有个EntityResolver.
(1)EntityResolver用法
SAX应用需要实现自定义处理外部实体,则必须实现此接口向SAX注册一个实例,对于解析XML,SAX首先读取该XML的声明,根据声明去寻找相应的DTD定义。默认寻址通过网络,下载过程是个漫长的过程,而且网络中断或者不可用,这里会报错。这个的作用是项目本身就可以提供一个DTD声明的方法,避免了通过网络来寻找相应的声明。
五:解析及注册BeanDefinitions
上面我们已经把文件转化成Document,然后就是提取以及注册bean。
当拥有Document实例对象时候,引入下面的方法。
很符合面向对象的单一职责的原则。BeanDefinitionDocumentReader是一个接口,实例化是在
之前的所有都是XML加载解析的准备阶段doRegisterBeanDefinitions算是真正地开始进行解析。
那么处理流程,首先是对rpofile的处理,然后开始解析。
(1)profile属性的使用
注册bean的最开始是对PROFILE_ATTRIBUTE属性的解析,
profile用的最多是更换不同的数据库,用来生产环境和开发环境。
(2)解析并注册BeanDeifnition
处理了profile后就可以进行XML的读取了。进入
这个方法就是XML的读取,
一:先来段代码来测试一下。
照书上的先来试试
<?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的读取,
相关文章推荐
- java实现base64编码和解码
- Java类的初始化
- java File类
- Spring核心技术(二)——Spring的依赖及其注入
- Spring 核心技术IoC容器(二)
- 重构二叉树(Java实现)
- Java字符串格式化:数字前面加0
- spring的orm模块
- spring定时器配置
- 软件测试上机实验(一)
- java 练习-接收键盘输入的数据并输出
- java中set接口的用法
- java的System.getProperty()方法可以获取的值
- Eclipse快捷键
- 【第六章】 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3
- 深入理解Java的接口和抽象类
- Eclipse下mybatis配置文件自动提示
- struts2 中,如何覆盖默认的复杂格式的错误消息
- Java多线程与并发(三)之死锁
- SSM框架中以注解形式实现事务管理