这一次搞懂Spring的XML解析原理说明
前言
Spring已经是我们JavaWeb开发必不可少的一个框架,其大大简化了我们的开发,提高了开发者的效率。同时,其源码对于开发者来说也是宝藏,从中我们可以学习到非常优秀的设计思想以及优雅的命名规范,但因其体系庞大、设计复杂对于刚开始阅读源码的人来说是非常困难的。所以在此之前首先你得下定决心,不管有多困难都得坚持下去;其次,最好先把设计模式掌握熟练;然后在开始阅读源码时一定要多画UML类图和时序图,多问自己为什么要这么设计?这样设计的好处是什么?还有没有更好的设计?当然,晕车是难免的,但还是那句话,一定要持之以恒(PS:源码版本5.1.3.RELEASE)。
正文
熟悉IOC体系结构
要学习Spring源码,我们首先得要找准入口,那这个入口怎么找呢?我们不妨先思考一下,在Spring项目启动时,Spring做了哪些事情。这里我以最原始的xml配置方式来分析,那么在项目启动时,首先肯定要先定位——找到xml配置文件,定位之后肯定是加载——将我们的配置加载到内存,最后才是根据我们的配置实例化(本篇文章只讲前两个过程)。那么Spring是如何定位和加载xml文件的呢?涉及到哪些类呢?我们先来看张类图:
该图是IOC的体系图,整体上你需要有一个大概的印象,可以看到所有的IOC都是有继承关系的,这样设计的好处就是任何一个子类IOC可以直接使用父类IOC加载的Bean,有点像JVM类加载的双亲委派机制;而红色方框圈起来的是本篇涉及到的重要类,需要着重记忆它们的关系。
图中最重要的两个类是BeanFactory和ApplicationContext,这是所有IOC的父接口。其中BeanFactory提供了最基本的对bean的操作:
而ApplicationContex继承了BeanFactory,同时还继承了MessageSource、ResourceLoader、ApplicationEventPublisher等接口以提供国际化、资源加载、事件发布等高级功能。我们应该想到平时Spring加载xml文件应该是ApplicationContext的子类,从图中我们可以看到一个叫ClassPathXmlApplicationContext的类,联想到我们平时都会将xml放到classPath下,所以我们直接从这个类开始就行,这就是优秀命名的好处。
探究配置加载的过程
在ClassPathXmlApplicationContext中有很多构造方法,其中有一个是传入一个字符串的(即配置文件的相对路径),但最终是调用的下面这个构造:
publicClassPathXmlApplicationContext(
String[]configLocations,booleanrefresh,@NullableApplicationContextparent)
throwsBeansException{
super(parent);
//创建解析器,解析configLocations
setConfigLocations(configLocations);
if(refresh){
refresh();
}
}
首先调用父类构造器设置环境:
publicAbstractApplicationContext(@NullableApplicationContextparent){
this();
setParent(parent);
}
publicvoidsetParent(@NullableApplicationContextparent){
this.parent=parent;
if(parent!=null){
EnvironmentparentEnvironment=parent.getEnvironment();
if(parentEnvironmentinstanceofConfigurableEnvironment){
getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
}
}
}
然后解析传入的相对路径保存到configLocations变量中,最后再调用父类AbstractApplicationContext的refresh方法刷新容器(启动容器都会调用该方法),我们着重来看这个方法:
publicvoidrefresh()throwsBeansException,IllegalStateException{
synchronized(this.startupShutdownMonitor){
//为容器初始化做准备
prepareRefresh();
//解析xml
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){
if(logger.isWarnEnabled()){
logger.warn("Exceptionencounteredduringcontextinitialization-"+
"cancellingrefreshattempt:"+ex);
}
//Destroyalreadycreatedsingletonstoavoiddanglingresources.
destroyBeans();
//Reset'active'flag.
cancelRefresh(ex);
//Propagateexceptiontocaller.
throwex;
}
finally{
//ResetcommonintrospectioncachesinSpring'score,sincewe
//mightnoteverneedmetadataforsingletonbeansanymore...
resetCommonCaches();
}
}
}
这个方法是一个典型的模板方法模式的实现,第一步是准备初始化容器环境,这一步不重要,重点是第二步,创建BeanFactory对象、加载解析xml并封装成BeanDefinition对象都是在这一步完成的。
protectedConfigurableListableBeanFactoryobtainFreshBeanFactory(){
refreshBeanFactory();
returngetBeanFactory();
}
点进去看是调用了refreshBeanFactory方法,但这里有两个实现,应该进哪一个类里面呢?
如果你还记得前面的继承体系,那你就会毫不犹豫的进入AbstractRefreshableApplicationContext类中,所以在阅读源码的过程中一定要记住类的继承体系。
protectedfinalvoidrefreshBeanFactory()throwsBeansException{
//如果BeanFactory不为空,则清除BeanFactory和里面的实例
if(hasBeanFactory()){
destroyBeans();
closeBeanFactory();
}
try{
//创建DefaultListableBeanFactory
DefaultListableBeanFactorybeanFactory=createBeanFactory();
beanFactory.setSerializationId(getId());
//设置是否可以循环依赖allowCircularReferences
//是否允许使用相同名称重新注册不同的bean实现.
customizeBeanFactory(beanFactory);
//解析xml,并把xml中的标签封装成BeanDefinition对象
loadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor){
this.beanFactory=beanFactory;
}
}
catch(IOExceptionex){
thrownewApplicationContextException("I/Oerrorparsingbeandefinitionsourcefor"+getDisplayName(),ex);
}
}
在这个方法中首先会清除掉上一次创建的BeanFactory和对象实例,然后创建了一个DefaultListableBeanFactory对象并传入到了loadBeanDefinitions方法中,这也是一个模板方法,因为我们的配置不止有xml,还有注解等,所以这里我们应该进入AbstractXmlApplicationContext类中:
protectedvoidloadBeanDefinitions(DefaultListableBeanFactorybeanFactory)throwsBeansException,IOException{
//创建xml的解析器,这里是一个委托模式
XmlBeanDefinitionReaderbeanDefinitionReader=newXmlBeanDefinitionReader(beanFactory);
//Configurethebeandefinitionreaderwiththiscontext's
//resourceloadingenvironment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
//这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(newResourceEntityResolver(this));
//Allowasubclasstoprovidecustominitializationofthereader,
//thenproceedwithactuallyloadingthebeandefinitions.
initBeanDefinitionReader(beanDefinitionReader);
//主要看这个方法
loadBeanDefinitions(beanDefinitionReader);
}
首先创建了一个XmlBeanDefinitionReader对象,见名知意,这个就是解析xml的类,需要注意的是该类的构造方法接收的是BeanDefinitionRegistry对象,而这里将DefaultListableBeanFactory对象传入了进去(别忘记了这个对象是实现了BeanDefinitionRegistry类的),如果你足够敏感,应该可以想到后面会委托给该类去注册。注册什么呢?自然是注册BeanDefintion。记住这个猜想,我们稍后来验证是不是这么回事。
接着进入loadBeanDefinitions方法获取之前保存的xml配置文件路径,并委托给XmlBeanDefinitionReader对象解析加载:
protectedvoidloadBeanDefinitions(XmlBeanDefinitionReaderreader)throwsBeansException,IOException{
Resource[]configResources=getConfigResources();
if(configResources!=null){
reader.loadBeanDefinitions(configResources);
}
//获取需要加载的xml配置文件
String[]configLocations=getConfigLocations();
if(configLocations!=null){
reader.loadBeanDefinitions(configLocations);
}
}
最后会进入到抽象父类AbstractBeanDefinitionReader中:
publicintloadBeanDefinitions(Stringlocation,@NullableSetactualResources)throwsBeanDefinitionStoreException{ //这里获取到的依然是DefaultListableBeanFactory对象 ResourceLoaderresourceLoader=getResourceLoader(); if(resourceLoader==null){ thrownewBeanDefinitionStoreException( "Cannotloadbeandefinitionsfromlocation["+location+"]:noResourceLoaderavailable"); } if(resourceLoaderinstanceofResourcePatternResolver){ //Resourcepatternmatchingavailable. try{ //把字符串类型的xml文件路径,形如:classpath*:user/**/*-context.xml,转换成Resource对象类型,其实就是用流 //的方式加载配置文件,然后封装成Resource对象 Resource[]resources=((ResourcePatternResolver)resourceLoader).getResources(location); //主要看这个方法 intcount=loadBeanDefinitions(resources); if(actualResources!=null){ Collections.addAll(actualResources,resources); } if(logger.isTraceEnabled()){ logger.trace("Loaded"+count+"beandefinitionsfromlocationpattern["+location+"]"); } returncount; } catch(IOExceptionex){ thrownewBeanDefinitionStoreException( "Couldnotresolvebeandefinitionresourcepattern["+location+"]",ex); } } else{ //CanonlyloadsingleresourcesbyabsoluteURL. Resourceresource=resourceLoader.getResource(location); intcount=loadBeanDefinitions(resource); if(actualResources!=null){ actualResources.add(resource); } if(logger.isTraceEnabled()){ logger.trace("Loaded"+count+"beandefinitionsfromlocation["+location+"]"); } returncount; } }
这个方法中主要将xml配置加载到存中并封装成为Resource对象,这一步不重要,可以略过,主要的还是loadBeanDefinitions方法,最终还是调用到子类XmlBeanDefinitionReader的方法:
publicintloadBeanDefinitions(EncodedResourceencodedResource)throwsBeanDefinitionStoreException{
try{
//获取Resource对象中的xml文件流对象
InputStreaminputStream=encodedResource.getResource().getInputStream();
try{
//InputSource是jdk中的saxxml文件解析对象
InputSourceinputSource=newInputSource(inputStream);
if(encodedResource.getEncoding()!=null){
inputSource.setEncoding(encodedResource.getEncoding());
}
//主要看这个方法
returndoLoadBeanDefinitions(inputSource,encodedResource.getResource());
}
finally{
inputStream.close();
}
}
}
protectedintdoLoadBeanDefinitions(InputSourceinputSource,Resourceresource)
throwsBeanDefinitionStoreException{
try{
//把inputSource封装成Document文件对象,这是jdk的API
Documentdoc=doLoadDocument(inputSource,resource);
//主要看这个方法,根据解析出来的document对象,拿到里面的标签元素封装成BeanDefinition
intcount=registerBeanDefinitions(doc,resource);
if(logger.isDebugEnabled()){
logger.debug("Loaded"+count+"beandefinitionsfrom"+resource);
}
returncount;
}
}
publicintregisterBeanDefinitions(Documentdoc,Resourceresource)throwsBeanDefinitionStoreException{
//创建DefaultBeanDefinitionDocumentReader对象,并委托其做解析注册工作
BeanDefinitionDocumentReaderdocumentReader=createBeanDefinitionDocumentReader();
intcountBefore=getRegistry().getBeanDefinitionCount();
//主要看这个方法,需要注意createReaderContext方法中创建的几个对象
documentReader.registerBeanDefinitions(doc,createReaderContext(resource));
returngetRegistry().getBeanDefinitionCount()-countBefore;
}
publicXmlReaderContextcreateReaderContext(Resourceresource){
//XmlReaderContext对象中保存了XmlBeanDefinitionReader对象和DefaultNamespaceHandlerResolver对象的引用,在后面会用到
returnnewXmlReaderContext(resource,this.problemReporter,this.eventListener,
this.sourceExtractor,this,getNamespaceHandlerResolver());
}
接着看看DefaultBeanDefinitionDocumentReader中是如何解析的:
protectedvoiddoRegisterBeanDefinitions(Elementroot){
//创建了BeanDefinitionParserDelegate对象
BeanDefinitionParserDelegateparent=this.delegate;
this.delegate=createDelegate(getReaderContext(),root,parent);
//如果是Spring原生命名空间,首先解析profile标签,这里不重要
if(this.delegate.isDefaultNamespace(root)){
StringprofileSpec=root.getAttribute(PROFILE_ATTRIBUTE);
if(StringUtils.hasText(profileSpec)){
String[]specifiedProfiles=StringUtils.tokenizeToStringArray(
profileSpec,BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
//WecannotuseProfiles.of(...)sinceprofileexpressionsarenotsupported
//inXMLconfig.SeeSPR-12458fordetails.
if(!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)){
if(logger.isDebugEnabled()){
logger.debug("SkippedXMLbeandefinitionfileduetospecifiedprofiles["+profileSpec+
"]notmatching:"+getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
//主要看这个方法,标签具体解析过程
parseBeanDefinitions(root,this.delegate);
postProcessXml(root);
this.delegate=parent;
}
在这个方法中重点关注preProcessXml、parseBeanDefinitions、postProcessXml三个方法,其中preProcessXml和postProcessXml都是空方法,意思是在解析标签前后我们自己可以扩展需要执行的操作,也是一个模板方法模式,体现了Spring的高扩展性。然后进入parseBeanDefinitions方法看具体是怎么解析标签的:
protectedvoidparseBeanDefinitions(Elementroot,BeanDefinitionParserDelegatedelegate){
if(delegate.isDefaultNamespace(root)){
NodeListnl=root.getChildNodes();
for(inti=0;i
这里有两种标签的解析:Spring原生标签和自定义标签。怎么区分这两种标签呢?
//自定义标签
//默认标签
如上,带前缀的就是自定义标签,否则就是Spring默认标签,无论哪种标签在使用前都需要在Spring的xml配置文件里声明NamespaceURI,这样在解析标签时才能通过NamespaceURI找到对应的NamespaceHandler。
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/beans
isDefaultNamespace判断是不是默认标签,点进去看看是不是跟我上面说的一致:
publicbooleanisDefaultNamespace(Nodenode){
returnisDefaultNamespace(getNamespaceURI(node));
}
publicstaticfinalStringBEANS_NAMESPACE_URI="http://www.springframework.org/schema/beans";
publicbooleanisDefaultNamespace(@NullableStringnamespaceUri){
return(!StringUtils.hasLength(namespaceUri)||BEANS_NAMESPACE_URI.equals(namespaceUri));
}
可以看到http://www.springframework.org/schema/beans所对应的就是默认标签。接着,我们进入parseDefaultElement方法:
privatevoidparseDefaultElement(Elementele,BeanDefinitionParserDelegatedelegate){
//import标签解析
if(delegate.nodeNameEquals(ele,IMPORT_ELEMENT)){
importBeanDefinitionResource(ele);
}
//alias标签解析
elseif(delegate.nodeNameEquals(ele,ALIAS_ELEMENT)){
processAliasRegistration(ele);
}
//bean标签
elseif(delegate.nodeNameEquals(ele,BEAN_ELEMENT)){
processBeanDefinition(ele,delegate);
}
elseif(delegate.nodeNameEquals(ele,NESTED_BEANS_ELEMENT)){
//recurse
doRegisterBeanDefinitions(ele);
}
}
这里面主要是对import、alias、bean标签的解析以及beans的字标签的递归解析,主要看看bean标签的解析:
protectedvoidprocessBeanDefinition(Elementele,BeanDefinitionParserDelegatedelegate){
//解析elment封装为BeanDefinitionHolder对象
BeanDefinitionHolderbdHolder=delegate.parseBeanDefinitionElement(ele);
if(bdHolder!=null){
//该方法功能不重要,主要理解设计思想:装饰者设计模式以及SPI设计思想
bdHolder=delegate.decorateBeanDefinitionIfRequired(ele,bdHolder);
try{
//完成document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,getReaderContext().getRegistry());
}
//Sendregistrationevent.
getReaderContext().fireComponentRegistered(newBeanComponentDefinition(bdHolder));
}
}
publicBeanDefinitionHolderparseBeanDefinitionElement(Elementele,@NullableBeanDefinitioncontainingBean){
//获取id和name属性
Stringid=ele.getAttribute(ID_ATTRIBUTE);
StringnameAttr=ele.getAttribute(NAME_ATTRIBUTE);
//获取别名属性,多个别名可用,;隔开
Listaliases=newArrayList<>();
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.isTraceEnabled()){
logger.trace("NoXML'id'specified-using'"+beanName+
"'asbeannameand"+aliases+"asaliases");
}
}
//检查beanName是否重复
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.isTraceEnabled()){
logger.trace("NeitherXML'id'nor'name'specified-"+
"usinggeneratedbeanname["+beanName+"]");
}
}catch(Exceptionex){
error(ex.getMessage(),ele);
returnnull;
}
}
String[]aliasesArray=StringUtils.toStringArray(aliases);
returnnewBeanDefinitionHolder(beanDefinition,beanName,aliasesArray);
}
returnnull;
}
//bean的解析
publicAbstractBeanDefinitionparseBeanDefinitionElement(
Elementele,StringbeanName,@NullableBeanDefinitioncontainingBean){
this.parseState.push(newBeanEntry(beanName));
//获取class名称和父类名称
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);
}
try{
//创建GenericBeanDefinition对象
AbstractBeanDefinitionbd=createBeanDefinition(className,parent);
//解析bean标签的属性,并把解析出来的属性设置到BeanDefinition对象中
parseBeanDefinitionAttributes(ele,beanName,containingBean,bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele,DESCRIPTION_ELEMENT));
//解析bean中的meta标签
parseMetaElements(ele,bd);
//解析bean中的lookup-method标签
parseLookupOverrideSubElements(ele,bd.getMethodOverrides());
//解析bean中的replaced-method标签
parseReplacedMethodSubElements(ele,bd.getMethodOverrides());
//解析bean中的constructor-arg标签
parseConstructorArgElements(ele,bd);
//解析bean中的property标签
parsePropertyElements(ele,bd);
parseQualifierElements(ele,bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
returnbd;
}
returnnull;
}
bean标签的解析步骤仔细理解并不复杂,就是将一个个标签属性的值装入到了BeanDefinition对象中,这里需要注意parseConstructorArgElements和parsePropertyElements方法,分别是对constructor-arg和property标签的解析,解析完成后分别装入了BeanDefinition对象的constructorArgumentValues和propertyValues中,而这两个属性在接下来c和p标签的解析中还会用到,而且还涉及一个很重要的设计思想——装饰器模式。
Bean标签解析完成后将生成的BeanDefinition对象、bean的名称以及别名一起封装到了BeanDefinitionHolder对象并返回,然后调用了decorateBeanDefinitionIfRequired进行装饰:
publicBeanDefinitionHolderdecorateBeanDefinitionIfRequired(
Elementele,BeanDefinitionHolderdefinitionHolder,@NullableBeanDefinitioncontainingBd){
BeanDefinitionHolderfinalDefinition=definitionHolder;
//根据bean标签属性装饰BeanDefinitionHolder,比如
NamedNodeMapattributes=ele.getAttributes();
for(inti=0;i
在这个方法中分别对Bean标签的属性和子标签迭代,获取其中的自定义标签进行解析,并装饰之前创建的BeanDefinition对象,如同下面的c和p:
//c:和p:表示通过构造器和属性的setter方法给属性赋值,是constructor-arg和property的简化写法
两个步骤是一样的,我们点进decorateIfRequired方法中:
publicBeanDefinitionHolderdecorateIfRequired(
Nodenode,BeanDefinitionHolderoriginalDef,@NullableBeanDefinitioncontainingBd){
//根据node获取到node的命名空间,形如:http://www.springframework.org/schema/p
StringnamespaceUri=getNamespaceURI(node);
if(namespaceUri!=null&&!isDefaultNamespace(namespaceUri)){
//根据配置文件获取namespaceUri对应的处理类,SPI思想
NamespaceHandlerhandler=this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if(handler!=null){
//调用NamespaceHandler处理类的decorate方法,开始具体装饰过程,并返回装饰完的对象
BeanDefinitionHolderdecorated=
handler.decorate(node,originalDef,newParserContext(this.readerContext,this,containingBd));
if(decorated!=null){
returndecorated;
}
}
elseif(namespaceUri.startsWith("http://www.springframework.org/")){
error("UnabletolocateSpringNamespaceHandlerforXMLschemanamespace["+namespaceUri+"]",node);
}
else{
//Acustomnamespace,nottobehandledbySpring-maybe"xml:...".
if(logger.isDebugEnabled()){
logger.debug("NoSpringNamespaceHandlerfoundforXMLschemanamespace["+namespaceUri+"]");
}
}
}
returnoriginalDef;
}
这里也和我们之前说的一样,首先获取到标签对应的namespaceUri,然后通过这个Uri去获取到对应的NamespceHandler,最后再调用NamespceHandler的decorate方法进行装饰。我们先来看看获取NamespceHandler的过程,这涉及到一个非常重要的高扩展性的思想——SPI(有关SPI,在我之前的文章Dubbo——SPI及自适应扩展原理中已经详细讲解过,这里不再赘述):
publicNamespaceHandlerresolve(StringnamespaceUri){
//获取spring中所有jar包里面的"META-INF/spring.handlers"文件,并且建立映射关系
MaphandlerMappings=getHandlerMappings();
//根据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的处理类
ObjecthandlerOrClassName=handlerMappings.get(namespaceUri);
if(handlerOrClassName==null){
returnnull;
}
elseif(handlerOrClassNameinstanceofNamespaceHandler){
return(NamespaceHandler)handlerOrClassName;
}
else{
StringclassName=(String)handlerOrClassName;
try{
Class>handlerClass=ClassUtils.forName(className,this.classLoader);
if(!NamespaceHandler.class.isAssignableFrom(handlerClass)){
thrownewFatalBeanException("Class["+className+"]fornamespace["+namespaceUri+
"]doesnotimplementthe["+NamespaceHandler.class.getName()+"]interface");
}
NamespaceHandlernamespaceHandler=(NamespaceHandler)BeanUtils.instantiateClass(handlerClass);
//调用处理类的init方法,在init方法中完成标签元素解析类的注册
namespaceHandler.init();
handlerMappings.put(namespaceUri,namespaceHandler);
returnnamespaceHandler;
}
}
}
//AOP标签对应的NamespaceHandler,可以发现NamespaceHandler的作用就是管理和注册与自己相关的标签解析器
publicvoidinit(){
//In2.0XSDaswellasin2.1XSD.
registerBeanDefinitionParser("config",newConfigBeanDefinitionParser());
registerBeanDefinitionParser("aspectj-autoproxy",newAspectJAutoProxyBeanDefinitionParser());
registerBeanDefinitionDecorator("scoped-proxy",newScopedProxyBeanDefinitionDecorator());
//Onlyin2.0XSD:movedtocontextnamespaceasof2.1
registerBeanDefinitionParser("spring-configured",newSpringConfiguredBeanDefinitionParser());
}
看到这里我们应该就清楚了Spring是如何解析xml里的标签了以及我们如果要扩展自己的标签该怎么做。只需要创建一个我们的自定义标签和解析类,并指定它的命名空间以及NamespaceHandler,最后在META-INF/spring.handlers文件中指定命名空间和NamespaceHandler的映射关系即可,就像Spring的c和p标签一样:
http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
像这样使用SPI的思想设计我们的项目的话,当需要扩展时,不需要改动任何的代码,非常的方便优雅。
接着,我们回到handler的decorate方法,这里有三个默认的实现类:NamespaceHandlerSupport、SimpleConstructorNamespaceHandler、SimplePropertyNamespaceHandler。第一个是一个抽象类,与我们这里的流程无关,感兴趣的可自行了解,第二个和第三个则分别是c和p标签对应的NamespaceHandler,两个装饰的处理逻辑基本上是一样的,我这里进入的是SimpleConstructorNamespaceHandler类:
publicBeanDefinitionHolderdecorate(Nodenode,BeanDefinitionHolderdefinition,ParserContextparserContext){
if(nodeinstanceofAttr){
Attrattr=(Attr)node;
StringargName=StringUtils.trimWhitespace(parserContext.getDelegate().getLocalName(attr));
StringargValue=StringUtils.trimWhitespace(attr.getValue());
ConstructorArgumentValuescvs=definition.getBeanDefinition().getConstructorArgumentValues();
booleanref=false;
//handle-refarguments
if(argName.endsWith(REF_SUFFIX)){
ref=true;
argName=argName.substring(0,argName.length()-REF_SUFFIX.length());
}
ValueHoldervalueHolder=newValueHolder(ref?newRuntimeBeanReference(argValue):argValue);
valueHolder.setSource(parserContext.getReaderContext().extractSource(attr));
//handle"escaped"/"_"arguments
if(argName.startsWith(DELIMITER_PREFIX)){
Stringarg=argName.substring(1).trim();
//fastdefaultcheck
if(!StringUtils.hasText(arg)){
cvs.addGenericArgumentValue(valueHolder);
}
//assumeanindexotherwise
else{
intindex=-1;
try{
index=Integer.parseInt(arg);
}
catch(NumberFormatExceptionex){
parserContext.getReaderContext().error(
"Constructorargument'"+argName+"'specifiesaninvalidinteger",attr);
}
if(index<0){
parserContext.getReaderContext().error(
"Constructorargument'"+argName+"'specifiesanegativeindex",attr);
}
if(cvs.hasIndexedArgumentValue(index)){
parserContext.getReaderContext().error(
"Constructorargument'"+argName+"'withindex"+index+"alreadydefinedusing."+
"Onlyoneapproachmaybeusedperargument.",attr);
}
cvs.addIndexedArgumentValue(index,valueHolder);
}
}
//noescaping->ctrname
else{
Stringname=Conventions.attributeNameToPropertyName(argName);
if(containsArgWithName(name,cvs)){
parserContext.getReaderContext().error(
"Constructorargument'"+argName+"'alreadydefinedusing."+
"Onlyoneapproachmaybeusedperargument.",attr);
}
valueHolder.setName(Conventions.attributeNameToPropertyName(argName));
cvs.addGenericArgumentValue(valueHolder);
}
}
returndefinition;
}
很简单,拿到c标签对应的值,封装成ValueHolder,再添加到BeanDefinition的ConstructorArgumentValues属性中去,这样就装饰完成了。
讲到这里你可能会觉得,这和平时看到装饰器模式不太一样。其实,设计模式真正想要表达的是各种模式所代表的思想,而不是死搬硬套的实现,只有灵活的运用其思想才算是真正的掌握了设计模式,而装饰器模式的精髓就是动态的将属性、功能、责任附加到对象上,这样你再看这里是否是运用了装饰器的思想呢?
装饰完成后返回BeanDefinitionHolder对象并调用BeanDefinitionReaderUtils.registerBeanDefinition方法将该对象缓存起来,等待容器去实例化。这里就是将其缓存到DefaultListableBeanFactory的beanDefinitionMap属性中,自己看看代码也就明白了,我就不贴代码了。至此,Spring的XML解析原理分析完毕,下面是我画的时序图,可以对照看看:
总结
本篇是Spring源码分析的第一篇,只是分析了refresh中的obtainFreshBeanFactory方法,我们可以看到仅仅是对XML的解析和bean定义的注册缓存,Spring就做了这么多事,并考虑到了各个可能会扩展的地方,那我们平时做的项目呢?看似简单的背后是否有深入思考过呢?
以上这篇这一次搞懂Spring的XML解析原理说明就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。