扣丁学堂解析Spring中自定义Schema如何解析生效详解

2018-07-09 13:21:33 398浏览

随着SpringBoot的日渐流行,应用里的大部分配置都被隐藏了起来,Spring2.5在2.0的基于Schema的Bean配置的基础之上,再增加了扩展XML配置的机制。通过该机制,我们可以编写自己的Schema,并根据自定义的Schema用自定的标签配置Bean,下面这篇文章主要介绍了关于Java培训中Spring自定义Schema如何解析生效的详细介绍。



但有些时候,由于配置的内容在SpringBoot这种零配置中并不能很好的实现,就需要我们仍使用XML的配置形式,然后再ImportSource进来。或者一些项目受环境等影响,未使用Boot进行开发,此时也需要对配置有一定的了解。

那这次让我们往回看一些,看看在XML配置中,一些自定义的Schema内容,是如何融合到Spring中进行配置的。例如:

Springdataes
dubbo

还有许多这样的例子,我们不再一一罗列。但通过上述两个图,我们发现一个共同的特点:

都是通过schemaLocation将对应的schema引入

在对应的beans元素中增加更具体的自定义配置

这个也是发生在解析自定义配置文件过程中,有一个resolve的过程,此时会将当前classLoader对应的所有包含spring.handlers文件加载过来。

我们再来看这个解析类,内容如下:

  publicclassElasticsearchNamespaceHandlerextendsNamespaceHandlerSupport{
  publicElasticsearchNamespaceHandler(){
  }
  publicvoidinit(){
  RepositoryConfigurationExtensionextension=newElasticsearchRepositoryConfigExtension();
  RepositoryBeanDefinitionParserparser=newRepositoryBeanDefinitionParser(extension);
  this.registerBeanDefinitionParser("repositories",parser);
  this.registerBeanDefinitionParser("node-client",newNodeClientBeanDefinitionParser());
  this.registerBeanDefinitionParser("transport-client",newTransportClientBeanDefinitionParser());
  }
  }

首先是继承自NamesapceHandlerSupport

然后在重写的init方法中注册了一系列的parser,每个parser对应一个字符串,就是我们在xml配置文件是使用的自定义内容,比如上面的es的配置

<elasticsearch:transport-clientid="client"
cluster-nodes="192.168.73.186:9300"cluster

这里的配置最终就会通过TransportClientBeanDefinitionParser来进行解析

而上面提到的各个parser,在init方法中,保存在了一个Map中

privatefinalMap<String,BeanDefinitionParser>parsers=newHashMap();

所谓注册parser,就是在向这个parsers的map进行put操作。

那回过头来,在Spring中,最核心的内容是什么呢?是Bean。而实际上我们配置到XML里的这些内容,最终也会生在一个对应的Bean,所有的配置,只是为了生成Bean,这些自定义的配置,都称之为BeanDefinition。

所以,Spring在解析配置文件是,会有如下的判断,是否是defaultNamespace,不是的话就走customElementParse

  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)){
  this.parseDefaultElement(ele,delegate);
  }else{
  delegate.parseCustomElement(ele);
  }
  }
  }
  }else{
  delegate.parseCustomElement(root);
  }
  }

而是不是defaultNameSpace的判断更直接:namespace的uri有没有内容,或者是不是spring的beans的声明

publicbooleanisDefaultNamespace(StringnamespaceUri){
return!StringUtils.hasLength(namespaceUri)||"http://www.codingke.com/schema/beans".equals(namespaceUri);
}

所以请求都走到了parseCustomElement里,这里开始进行配置的解析,parse的时候,通过uriResolver查到对应的Handler

  publicBeanDefinitionparseCustomElement(Elementele,BeanDefinitioncontainingBd){
  StringnamespaceUri=this.getNamespaceURI(ele);
  NamespaceHandlerhandler=this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
  if(handler==null){
  this.error("UnabletolocateSpringNamespaceHandlerforXMLschemanamespace["+namespaceUri+"]",ele);
  returnnull;
  }else{
  returnhandler.parse(ele,newParserContext(this.readerContext,this,containingBd));
  }
  }

那此时返回的仅仅是spring.handlers里配置的Handler,而每个Handler又注册了不少的parse,还得需要一个获取parser的过程

  publicBeanDefinitionparse(Elementelement,ParserContextparserContext){
  returnthis.findParserForElement(element,parserContext).parse(element,parserContext);
  }
  privateBeanDefinitionParserfindParserForElement(Elementelement,ParserContextparserContext){
  StringlocalName=parserContext.getDelegate().getLocalName(element);
  BeanDefinitionParserparser=(BeanDefinitionParser)this.parsers.get(localName);
  if(parser==null){
  parserContext.getReaderContext().fatal("CannotlocateBeanDefinitionParserforelement["+localName+"]",element);
  }
  returnparser;
  }

这个获取的过程,就是通过传入的string,在我们开始声明的Map里get对应的parser,再使用它进行配置的解析啦。

有了parser,后面就是生成BeanDefinition的过程。

我们看,这些parser,都是继承自AbstraceBeanDefinitionParser,或者实现了BeanDefinitionParser的接口,统一解析的入口处,是接口的parse方法。

  publicclassTransportClientBeanDefinitionParserextendsAbstractBeanDefinitionParser{
  publicTransportClientBeanDefinitionParser(){
  }
  protectedAbstractBeanDefinitionparseInternal(Elementelement,ParserContextparserContext){
  BeanDefinitionBuilderbuilder=BeanDefinitionBuilder.rootBeanDefinition(TransportClientFactoryBean.class);
  this.setConfigurations(element,builder);
  returnthis.getSourcedBeanDefinition(builder,element,parserContext);
  }
  }

在重写的parseInternal方法中,返回解析配置后对应的BeanDefinition。这里也是各个框架自由抽象的地方。比如有些框架是简单直接一个类,而有些在此处会应用一些类似策略、装饰器等设计模式,提供更多的灵活性。

具体获取到BeanDefinition之后,放到整个Context中如何生成SpringBean的内容,以后我们再做分析。

以上就是关于扣丁学堂Java开发Spring自定义Schema如何解析生效的详细介绍,希望对大家学习有所帮助,想要了解更多内容的小伙伴可以登录扣丁学堂官网咨询。扣丁学堂不仅有专业的老师和与时俱进的课程体系,还有大量的Java视频教程供学员观看学习,想要快速学习Java开发技术的小伙伴快快行动吧。扣丁学堂H5技术交流群:559883758。

扣丁学堂微信公众号



【关注微信公众号获取更多学习资料】



查看更多关于“Java开发资讯的相关文章>>

标签: Java培训 Java开发程序员 Java视频教程 Spring视频教程

热门专区

暂无热门资讯

课程推荐

微信
微博
15311698296

全国免费咨询热线

邮箱:codingke@1000phone.com

官方群:148715490

北京千锋互联科技有限公司版权所有   北京市海淀区宝盛北里西区28号中关村智诚科创大厦4层
京ICP备12003911号-6   Copyright © 2013 - 2019

京公网安备 11010802030908号