死磕Spring系列之三,XML解析相关

简介:
原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://dba10g.blog.51cto.com/764602/1728020

通过第2章的介绍,应该知道Spring如何从XML一步步解析成BD对象并注册到容器中,这一过程有个概要认识了。

接下来开始详细分析与XML相关的那些事。

首先看一下使用的XML文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<? 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/beans
               http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
               < beanid = "beijingCard" class = "com.spring.examples.ioc.bean.Card"   >
                      < propertyname = "cardNo" value = "6220-52103-4123-456" ></ property >
                      < propertyname = "bank" value = "北京银行" />
               </ bean >
               < beanid = "jiansheCard" class = "com.spring.examples.ioc.bean.Card" >
                      < propertyname = "cardNo" value = "6227-52103-4123-456" ></ property >
                      < propertyname = "bank" value = "建设银行" />
               </ bean >
              
               < beanid = "miyue"   class = "com.spring.examples.ioc.bean.User" primary = "true"   scope = "singleton" >
                      < propertyname = "userName" value = "芈月" ></ property >
                      < propertyname = "email" value = "miyue@daqin.com" ></ property >
                      < propertyname = "cardList" >
                             < list >
                                           < refbean = "beijingCard" />
                                           < refbean = "jiansheCard" />
                             </ list >
                      </ property >
               </ bean >
</ beans >


首先需要知道,

XML 指可扩展标记语言.

第一行是 XML 声明。它定义 XML 的版本 (1.0) 和所使用的编码 UTF-8)。


下一行描述文档的根元素<beans>,(像在说:“本文档包含一个多个Bean “):

接下来就是子元素<bean>了。具体标签含义就不说了。

我想说的是文档头部的xmlns=".*",  xmlns:xsi=".*",xsi:schemaLocation=".*"类似字样的一坨代码含义。

这些就是命名空间。

命名空间


a)为什么要有命名空间?

XML 命名空间提供避免元素命名冲突的方法。

在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突。

b)默认的命名空间(Default Namespaces)

为元素定义默认的命名空间可以让我们省去在所有的子元素中使用前缀的工作。

格式:xmlns="namespaceURI"

c)xsi:schemaLocation

<xsi:schemaLocation="list of anyURI" >



schemaLocation 属性引用具有目标命名空间的 XML 架构文档。

xsi:schemaLocation属性的值由一个URI引用对组成,两个URI之间以空白符分隔。第一个URI是名称空间的名字,第二个URI给出模式文档的位置,模式处理器将从这个位置读取模式文档,该模式文档的目标名称空间必须与第一个URI相匹配


XSD (xml Schema Definition)



Xml Schema的用途

1.  定义一个Xml文档中都有什么元素

2.  定义一个Xml文档中都会有什么属性

3.  定义某个节点的都有什么样的子节点,可以有多少个子节点,子节点出现的顺序

4.  定义元素或者属性的数据类型

5.  定义元素或者属性的默认值或者固定值



具体语法,就不讲了,有兴趣的可以搜索。



DTD(Document Type Definition文档类型定义)的作用是定义 XML 文档的合法构建模块。

功能和XSD类似。具体使用如下:

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" " http://www.springframework.org/dtd/spring-beans.dtd ">  

XSD,知道即可,spring2.0中使用XSD验证。


解析XML需要关注点

1.XML文档如何转换为系统资源

2.使用什么引擎解析

3.解析文档元素,解析为数据载体对象


1.XML文档如何转换为系统资源

1
2
3
    public  int  loadBeanDefinitions(Resource resource)  throws  BeanDefinitionStoreException {
       return  loadBeanDefinitions( new  EncodedResource(resource));
    }

 首先看到这方,咱们关注到参数Resource,没错Spring提供Resource接口,将XML文件封装为资源对象。


wKiom1Z77ObyQeOxAAC7F39Q8Ko407.png

上图是Resource类图。通过Resouce接口,可以知道文件(xml)的名称,是否制度,内容长度,是否存在.....。

我们非常熟悉的获取资源的几种方式,都能找到。

ClasspathResource:根据类加载获取

UrlResource:URL路径(远程)获取

FileSystemResource:文件路径获取

大家有兴趣可以仔细看一下。getInputStream()方法要重点关注。

简单说一下 EncodedResource,他持有Resource接口引用,添加字符集支持。


2.使用什么引擎解析


2.1.准备输入(org.xml.sax.InputSource)

int org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  public  int  loadBeanDefinitions(EncodedResource encodedResource)  throws  BeanDefinitionStoreException {
       Assert.notNull(encodedResource,  "EncodedResource must not be null" );
       if  (logger.isInfoEnabled()) {
          logger.info( "Loading XML bean definitions from "  + encodedResource.getResource());
       }
  
       Set<EncodedResource> currentResources =  this .resourcesCurrentlyBeingLoaded.get();
       if  (currentResources ==  null ) {
          currentResources =  new  HashSet<EncodedResource>( 4 );
          this .resourcesCurrentlyBeingLoaded.set(currentResources);
       }
       if  (!currentResources.add(encodedResource)) {
          thrownew BeanDefinitionStoreException(
                 "Detected cyclic loading of "  + encodedResource +  " - check your import definitions!" );
       }
       try  {
          InputStream inputStream = encodedResource.getResource().getInputStream();
          try  {
             InputSource inputSource =  new  InputSource(inputStream);
             if  (encodedResource.getEncoding() !=  null ) {
                 inputSource.setEncoding(encodedResource.getEncoding());
             }
             return  doLoadBeanDefinitions(inputSource, encodedResource.getResource());
          }
          finally  {
             inputStream.close();
          }
       }
       catch  (IOException ex) {
          thrownew BeanDefinitionStoreException(
                 "IOException parsing XML document from "  + encodedResource.getResource(), ex);
       }
       finally  {
          currentResources.remove(encodedResource);
          if  (currentResources.isEmpty()) {
             this .resourcesCurrentlyBeingLoaded.remove();
          }
       }
    }

开始构造XML InputSource.

2.2 生成Document

1
2
3
4
5
6
7
8
9
10
  public  Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
          ErrorHandler errorHandler,  int  validationMode,  boolean  namespaceAware)  throws  Exception {
  
       DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
       if  (logger.isDebugEnabled()) {
          logger.debug( "Using JAXP provider ["  + factory.getClass().getName() +  "]" );
       }
       DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
       return  builder.parse(inputSource);
    }

2.3构造Document建造工厂

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   protected  DocumentBuilderFactory createDocumentBuilderFactory( int  validationMode,  boolean  namespaceAware)
          throws  ParserConfigurationException {
  
       DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
       factory.setNamespaceAware(namespaceAware);
       //根据获取的验证模式,决定为factory开启xsd开关
       if  (validationMode != XmlValidationModeDetector.VALIDATION_NONE) {
          factory.setValidating( true );
  
          if  (validationMode == XmlValidationModeDetector.VALIDATION_XSD) {
             // Enforce namespace aware for XSD...
             factory.setNamespaceAware( true );
             try  {
                 factory.setAttribute(SCHEMA_LANGUAGE_ATTRIBUTE, XSD_SCHEMA_LANGUAGE);
             }
             catch  (IllegalArgumentException ex) {
                 ParserConfigurationException pcex =  new  ParserConfigurationException(
                       "Unable to validate using XSD: Your JAXP provider ["  + factory +
                       "] does not support XML Schema. Are you running on Java 1.4 with Apache Crimson? "  +
                       "Upgrade to Apache Xerces (or Java 1.5) for full XSD support." );
                 pcex.initCause(ex);
                 throw  pcex;
             }
          }
       }
  
       return  factory;
    }

这时候一目了然了。

通过查看日志,最后我们得出我们想要的结果。

Using JAXP provider [com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl]

com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl

3.解析文档元素,解析为数据载体对象

到了这里,Document对象我们已经拿到了,开始进行解析。

void org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions(Element root)

3.1根据环境中profile参数,解析需要元素<beans>。


1
2
3
4
5
6
7
8
9
10
protected  BeanDefinitionParserDelegate createDelegate(
          XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
  
       BeanDefinitionParserDelegate delegate = createHelper(readerContext, root, parentDelegate);
       if  (delegate ==  null ) {
          delegate =  new  BeanDefinitionParserDelegate(readerContext, getEnvironment());
          delegate.initDefaults(root, parentDelegate);
       }
       return  delegate;
    }
1
2
3
4
5
6
7
8
9
    /**
     * 初始化默认参数:lazy-init, autowire, dependency check settings,
     * init-method, destroy-method and merge settings. 
     *如果没有显示设置,使用上级默认参数。
     */
    publicvoid initDefaults(Element root, BeanDefinitionParserDelegate parent) {
       populateDefaults( this .defaults, (parent !=  null  ? parent.defaults :  null ), root);
       this .readerContext.fireDefaultsRegistered( this .defaults);
    }

下面正式进入正题,子元素的解析(如bean)

3.2 分别解析bean标签和自定义标签(对应于XML中元素)

void org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
   protectedvoid 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)) {
                    parseDefaultElement(ele, delegate);
                 }
                 else  {
                    delegate.parseCustomElement(ele);
                 }
             }
          }
       }
       else  {
          delegate.parseCustomElement(root);
       }
    }

parseCustomElement()方法暂时忽略,到自定义标签时,再进行分析。

此刻,我们需要关注的是parseDefaultElement()。

3.3具体分别处理import,alias,bean,beans子元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
    private  void  parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
       if  (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
          importBeanDefinitionResource(ele);
       }
       elseif (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
          processAliasRegistration(ele);
       }
       elseif (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
          processBeanDefinition(ele, delegate);
       }
       elseif (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
          // recurse
          doRegisterBeanDefinitions(ele);
       }
    }

3.4处理import标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
    void  org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.importBeanDefinitionResource(Element ele)
protected  void  importBeanDefinitionResource(Element ele) {
       String location = ele.getAttribute(RESOURCE_ATTRIBUTE);
//略:处理路径,获取资源
          try  {
             int  importCount = getReaderContext().getReader().loadBeanDefinitions(location, actualResources);
             if  (logger.isDebugEnabled()) {
                 logger.debug( "Imported "  + importCount +  " bean definitions from URL location ["  + location +  "]" );
             }
          }
          catch  (BeanDefinitionStoreException ex) {
             getReaderContext().error(
                    "Failed to import bean definitions from URL location ["  + location +  "]" , ele, ex);
          }
//其他
}

3.5处理alias标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
   protected  void  processAliasRegistration(Element ele) {
       String name = ele.getAttribute(NAME_ATTRIBUTE);
       String alias = ele.getAttribute(ALIAS_ATTRIBUTE);
       boolean  valid =  true ;
     //验证信息有效性
       if  (valid) {
          try  {
             getReaderContext().getRegistry().registerAlias(name, alias);
          }
          catch  (Exception ex) {
             getReaderContext().error( "Failed to register alias '"  + alias +
                    "' for bean with name '"  + name +  "'" , ele, ex);
          }
          getReaderContext().fireAliasRegistered(name, alias, extractSource(ele));
       }
    }
void  org.springframework.context.support.GenericApplicationContext.registerAlias(String beanName, String alias)
    publicvoid registerAlias(String beanName, String alias) {
       this .beanFactory.registerAlias(beanName, alias);
    }
 
   void  org.springframework.core.SimpleAliasRegistry.registerAlias(String name, String alias) 
 
    public  void  registerAlias(String name, String alias) {
       Assert.hasText(name,  "'name' must not be empty" );
       Assert.hasText(alias,  "'alias' must not be empty" );
       if  (alias.equals(name)) {
          this .aliasMap.remove(alias);
       }
       else  {
          if  (!allowAliasOverriding()) {
             String registeredName =  this .aliasMap.get(alias);
             if  (registeredName !=  null  && !registeredName.equals(name)) {
                 thrownew IllegalStateException( "Cannot register alias '"  + alias +  "' for name '"  +
                       name +  "': It is already registered for name '"  + registeredName +  "'." );
             }
          }
          checkForAliasCircle(name, alias);
          this .aliasMap.put(alias, name);
       }
    }

3.6解析bean,重中之重,每一步都进行分析。

void org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   protected  void  processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
       if  (bdHolder !=  null ) {
          bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
          try  {
             // Register the final decorated instance.
             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
          }
          catch  (BeanDefinitionStoreException ex) {
             getReaderContext().error( "Failed to register bean definition with name '"  +
                    bdHolder.getBeanName() +  "'" , ele, ex);
          }
          // Send registration event.
          getReaderContext().fireComponentRegistered( new  BeanComponentDefinition(bdHolder));
       }
    }

3.7

BeanDefinitionHolder org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
   public  BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
       String id = ele.getAttribute(ID_ATTRIBUTE);
       String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
       //处理别名
       List<String> aliases =  new  ArrayList<String>();
       if  (StringUtils.hasLength(nameAttr)) {
          String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
          aliases.addAll(Arrays.asList(nameArr));
       }
       //设置名称
       String beanName = id;
       if  (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
          beanName = aliases.remove( 0 );
          if  (logger.isDebugEnabled()) {
             logger.debug( "No XML 'id' specified - using '"  + beanName +
                    "' as bean name and "  + aliases +  " as aliases" );
          }
       }
       //判断名称,唯一性
       if  (containingBean ==  null ) {
          checkNameUniqueness(beanName, aliases, ele);
       }
  
       AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
       if  (beanDefinition !=  null ) {
          //建立关系BD 与名称映射关系
          if  (!StringUtils.hasText(beanName)) {
             try  {
                 if  (containingBean !=  null ) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(
                          beanDefinition,  this .readerContext.getRegistry(),  true );
                 }
                 else  {
                    beanName =  this .readerContext.generateBeanName(beanDefinition);
                    // Register an alias for the plain bean class name, if still possible,
                    // if the generator returned the class name plus a suffix.
                    // This is expected for Spring 1.2/2.0 backwards compatibility.
                    String beanClassName = beanDefinition.getBeanClassName();
                    if  (beanClassName !=  null  &&
                          beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                          ! this .readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                       aliases.add(beanClassName);
                    }
                 }
                 if  (logger.isDebugEnabled()) {
                    logger.debug( "Neither XML 'id' nor 'name' specified - "  +
                          "using generated bean name ["  + beanName +  "]" );
                 }
             }
             catch  (Exception ex) {
                 error(ex.getMessage(), ele);
                 return  null ;
             }
          }
          String[] aliasesArray = StringUtils.toStringArray(aliases);
          returnnew BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
       }
  
       return  null ;
    }

3.8

 AbstractBeanDefinition org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
   public  AbstractBeanDefinition parseBeanDefinitionElement(
          Element ele, String beanName, BeanDefinition containingBean) {
  
       this .parseState.push( new  BeanEntry(beanName));
  
       String className =  null ;
       if  (ele.hasAttribute(CLASS_ATTRIBUTE)) {
          className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
       }
  
       try  {
          String parent =  null ;
          if  (ele.hasAttribute(PARENT_ATTRIBUTE)) {
             parent = ele.getAttribute(PARENT_ATTRIBUTE);
          }
          //初始化bd
          AbstractBeanDefinition bd = createBeanDefinition(className, parent);
          //赋bd属性(scope,lazy-init,abstract...)
          parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
          bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
  
          parseMetaElements(ele, bd);
          //lookup-method
          parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
          //replaced-method
          parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
  
          parseConstructorArgElements(ele, bd);
          parsePropertyElements(ele, bd);
          //qualifier
          parseQualifierElements(ele, bd);
  
          bd.setResource( this .readerContext.getResource());
          bd.setSource(extractSource(ele));
  
          return  bd;
       }
       catch  (ClassNotFoundException ex) {
          error( "Bean class ["  + className +  "] not found" , ele, ex);
       }
       catch  (NoClassDefFoundError err) {
          error( "Class that bean class ["  + className +  "] depends on not found" , ele, err);
       }
       catch  (Throwable ex) {
          error( "Unexpected failure during bean definition parsing" , ele, ex);
       }
       finally  {
          this .parseState.pop();
       }
  
       returnnull;
    }

到这里,就不继续探究了。下面就是逐次调用类似ele.getAttribute()从xml取值,bd.setXXX()赋值的过程。


比较重要的对象。

BeanDefinition为XML <bean>标签数据载体对象。通过分析,可以找到xml对应节点或属性,大部分都可以在AbstractBeanDefinition中找到。

BeanDefinition是支持层级的,在这儿就不重点分析了。

wKioL1Z78AXTrcsXAABWt8gaR4Q147.png

wKioL1Z78CLAfuLDAABqcwIQ1vM371.png

XML解析相关类图

wKioL1Z78EjiT_nUAABqcwIQ1vM549.png



本文出自 “简单” 博客,请务必保留此出处http://dba10g.blog.51cto.com/764602/1728020

目录
相关文章
|
10天前
|
XML JavaScript 前端开发
xml文件使用及解析
xml文件使用及解析
|
20天前
|
安全 Java 数据安全/隐私保护
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
【深入浅出Spring原理及实战】「EL表达式开发系列」深入解析SpringEL表达式理论详解与实际应用
43 1
|
6天前
|
XML Java 数据格式
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
14 0
Bean工厂探秘:解析Spring底层工厂体系BeanFactory的神奇之道
|
6天前
|
XML Java 数据格式
从入门到精通:Spring基础注解的全面解析
从入门到精通:Spring基础注解的全面解析
22 2
从入门到精通:Spring基础注解的全面解析
|
6天前
|
Java 数据库 Spring
切面编程的艺术:Spring动态代理解析与实战
切面编程的艺术:Spring动态代理解析与实战
19 0
切面编程的艺术:Spring动态代理解析与实战
|
6天前
|
Java 关系型数据库 MySQL
高级对象装配:解析Spring创建复杂对象的秘诀
高级对象装配:解析Spring创建复杂对象的秘诀
20 0
高级对象装配:解析Spring创建复杂对象的秘诀

推荐镜像

更多