服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|JavaScript|易语言|

服务器之家 - 编程语言 - Java教程 - Spring中自定义Schema如何解析生效详解

Spring中自定义Schema如何解析生效详解

2021-05-14 10:42侯树成 Java教程

Spring2.5在2.0的基于Schema的Bean配置的基础之上,再增加了扩展XML配置的机制。通过该机制,我们可以编写自己的Schema,并根据自定义的Schema用自定的标签配置Bean,下面这篇文章主要介绍了关于Spring中自定义Schema如何解析生效的相关资

前言

随着 spring boot 的日渐流行,应用里的大部分配置都被隐藏了起来,我们仅需要关心真正的业务内容, controller, service, repository,拿起键盘就是一通业务代码的coding,具体的 component scan,view,placeholder ... 都可以抛在脑后。但其实这种零配置在 java 应用开发中,还真不太久。 「由奢入俭难」,不少开发者都经历过 spring xml 配置的冗长,再回到这种配置确实不好受。

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

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

spring data es

Spring中自定义Schema如何解析生效详解

dubbo

Spring中自定义Schema如何解析生效详解

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

  • 都是通过schemalocation将对应的schema引入
  • 在对应的beans元素中增加更具体的自定义配置

那这些自定义的配置,是在什么时候工作的呢?如何校验是否配置正确?

我们来看 spring 中包含一个名为 spring.handlers的文件,所有的自定义扩展,都是通过这个文件生效的。spring 官方的aop, tx 也都是这个原理。

这个文件在哪呢?

Spring中自定义Schema如何解析生效详解

如上图所示,是meta-inf/spring.handlers。文件内容也超级简单:
http\://www.springframework.org/schema/data/elasticsearch=org.springframework.data.elasticsearch.config.elasticsearchnamespacehandler
前面是各个schema前缀,后面是schema 对应的解析类。这个spring.handlers文件是什么时候加载的呢?

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
public class elasticsearchnamespacehandler extends namespacehandlersupport {
 public elasticsearchnamespacehandler() {
 }
 
 public void init() {
 repositoryconfigurationextension extension = new elasticsearchrepositoryconfigextension();
 repositorybeandefinitionparser parser = new repositorybeandefinitionparser(extension);
 this.registerbeandefinitionparser("repositories", parser);
 this.registerbeandefinitionparser("node-client", new nodeclientbeandefinitionparser());
 this.registerbeandefinitionparser("transport-client", new transportclientbeandefinitionparser());
 }
}

首先是继承自namesapcehandlersupport

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

?
1
2
<elasticsearch:transport-client id="client"
 cluster-nodes="192.168.73.186:9300" cluster

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

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

?
1
private final map<string, beandefinitionparser> parsers = new hashmap();

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

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

所以,spring 在解析配置文件是,会有如下的判断,是否是defaultnamespace,不是的话就走customelementparse

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
protected void parsebeandefinitions(element root, beandefinitionparserdelegate delegate) {
  if(delegate.isdefaultnamespace(root)) {
  nodelist nl = root.getchildnodes();
 
  for(int i = 0; i < nl.getlength(); ++i) {
   node node = nl.item(i);
   if(node instanceof element) {
   element ele = (element)node;
   if(delegate.isdefaultnamespace(ele)) {
   this.parsedefaultelement(ele, delegate);
   } else {
   delegate.parsecustomelement(ele);
   }
  }
  }
 } else {
  delegate.parsecustomelement(root);
 }
 }

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

?
1
2
3
public boolean isdefaultnamespace(string namespaceuri) {
 return !stringutils.haslength(namespaceuri) || "http://www.springframework.org/schema/beans".equals(namespaceuri);
 }

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

?
1
2
3
4
5
6
7
8
9
10
public beandefinition parsecustomelement(element ele, beandefinition containingbd) {
 string namespaceuri = this.getnamespaceuri(ele);
 namespacehandler handler = this.readercontext.getnamespacehandlerresolver().resolve(namespaceuri);
 if(handler == null) {
  this.error("unable to locate spring namespacehandler for xml schema namespace [" + namespaceuri + "]", ele);
  return null;
 } else {
  return handler.parse(ele, new parsercontext(this.readercontext, this, containingbd));
 }
 }

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public beandefinition parse(element element, parsercontext parsercontext) {
 return this.findparserforelement(element, parsercontext).parse(element, parsercontext);
}
 
private beandefinitionparser findparserforelement(element element, parsercontext parsercontext) {
 string localname = parsercontext.getdelegate().getlocalname(element);
 beandefinitionparser parser = (beandefinitionparser)this.parsers.get(localname);
 if(parser == null) {
 parsercontext.getreadercontext().fatal("cannot locate beandefinitionparser for element [" + localname + "]", element);
}
 
return parser;
}

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

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

?
1
2
3
4
5
6
7
8
9
10
public class transportclientbeandefinitionparser extends abstractbeandefinitionparser {
 public transportclientbeandefinitionparser() {
 }
 
 protected abstractbeandefinition parseinternal(element element, parsercontext parsercontext) {
 beandefinitionbuilder builder = beandefinitionbuilder.rootbeandefinition(transportclientfactorybean.class);
 this.setconfigurations(element, builder);
 return this.getsourcedbeandefinition(builder, element, parsercontext);
 }
}

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

具体获取到beandefinition之后,放到整个context中如何生成 spring bean的内容,以后我们再做分析。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对服务器之家的支持。

原文链接:http://zhuanlan.51cto.com/art/201807/578129.htm

延伸 · 阅读

精彩推荐
  • Java教程详解微信开发之Author网页授权

    详解微信开发之Author网页授权

    微信开发中,经常有这样的需求:获得用户头像、绑定微信号给用户发信息,那么实现这些的前提就是授权!本文对此进行系统介绍,需要的朋友一起来看...

    liliangel2752020-07-11
  • Java教程Java微信支付之公众号支付、扫码支付实例

    Java微信支付之公众号支付、扫码支付实例

    微信支付已经成为生活中必不可少的付款方式,本篇文章主要介绍了Java微信支付之公众号支付、扫码支付,有需要的小伙伴可以了解一下。 ...

    青蛙小王子3142020-06-30
  • Java教程Struts中使用validate()输入校验方法详解

    Struts中使用validate()输入校验方法详解

    这篇文章主要介绍了Struts中使用validate()输入校验方法,本文介绍的非常详细,具有参考借鉴价值,感兴趣的朋友一起看看吧 ...

    MRR3422020-06-10
  • Java教程Java中的阻塞队列详细介绍

    Java中的阻塞队列详细介绍

    这篇文章主要介绍了Java中的阻塞队列详细介绍的相关资料,需要的朋友可以参考下 ...

    java教程网3232020-06-29
  • Java教程Java连接并操作Sedna XML数据库的方法

    Java连接并操作Sedna XML数据库的方法

    这篇文章主要介绍了Java连接并操作Sedna XML数据库的方法,较为详细的说明了Sedna XML数据库的原理与功能,并给出了基于java操作Sedna XML数据库的方法,需要的朋...

    红薯5252019-12-24
  • Java教程Java异常处理实例分析

    Java异常处理实例分析

    这篇文章主要介绍了Java异常处理,实例分析了java异常处理的常见用法,具有一定参考借鉴价值,需要的朋友可以参考下 ...

    司青2962019-12-16
  • Java教程volatile可见性的一些认识和论证

    volatile可见性的一些认识和论证

    volatile的关键词的使用在JVM内存模型中已是老生常谈了,这篇文章主要结合自己对可见性的一些认识和一些直观的例子来谈谈volatile,感兴趣的朋友一起看看...

    陈其苗5152020-12-20
  • Java教程Java NIO深入分析

    Java NIO深入分析

    本篇技术文章主要对Java新api(New IO)做了详细深入的讲解,有助于程序对NIO有更加深入的理解。...

    segmentfault10932021-02-28