Mybatis源码学习(4)-解析器模块之标签应用

导读:本篇文章讲解 Mybatis源码学习(4)-解析器模块之标签应用,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

<properties>标签应用

  该部分主要分析<properties>标签的应用过程,其中主要讲解PropertyParser在这个过程中扮演的角色和作用。

1、场景设定

  该场景主要是使用<properties>标签定义数据库连接参数,然后在数据源配置中使用<properties>标签中定义的参数,其中并开启了占位符的默认配置。

 第一部分:
 <properties>
	    <property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> 
		<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/> 
		<property name="driver" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/test?useUnicode=true"/>
		<property name="username" value="root"/>
	    <!---<property name="password" value="123456"/>   -->
   </properties>
第二部分:
<environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC" />
            <!-- 配置数据库连接信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="${driver}" />
                <property name="url" value="${url}" />
                <property name="username" value="${username}" />
                <property name="password" value="${password?:123456}" />
            </dataSource>
        </environment>
    </environments>
2、<properties>标签的加载过程

  第一步,从XMLConfigBuilder类的parseConfiguration()方法开始进行分析。代码如下(其他代码省略):

 private void parseConfiguration(XNode root) {
    try {
    	……
    	……
    	//解析配置文件中的properties元素
        propertiesElement(root.evalNode("properties"));
        ……
        ……
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

  第二步:propertiesElement()方法是解析<properties>标签的入口,在调用该方法时,传入的参数是<properties>标签对应的XNode实例对象。

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {//resource 和 url 只能够配置其中一个,否则抛出异常。
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
      //properties元素体内指定的属性会被配置文件中同名属性覆盖
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      //方法参数传递的属性,覆盖已读取的同名属性
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      //解析器,属性值配置
      parser.setVariables(defaults);
      //configuration对象的属性值赋值
      configuration.setVariables(defaults);
    }
  }
public Properties getChildrenAsProperties() {
    Properties properties = new Properties();
    for (XNode child : getChildren()) {
      String name = child.getStringAttribute("name");
      String value = child.getStringAttribute("value");
      if (name != null && value != null) {
        properties.setProperty(name, value);
      }
    }
    return properties;
  }
 public List<XNode> getChildren() {
    List<XNode> children = new ArrayList<XNode>();
    NodeList nodeList = node.getChildNodes();
    if (nodeList != null) {
      for (int i = 0, n = nodeList.getLength(); i < n; i++) {
        Node node = nodeList.item(i);
        if (node.getNodeType() == Node.ELEMENT_NODE) {
          children.add(new XNode(xpathParser, node, variables));
        }
      }
    }
    return children;
  }
  public XNode(XPathParser xpathParser, Node node, Properties variables) {
    this.xpathParser = xpathParser;
    this.node = node;
    this.name = node.getNodeName();
    this.variables = variables;
    this.attributes = parseAttributes(node);
    this.body = parseBody(node);
  }

  上述,第1部分代码,第3行代码中XNode.getChildrenAsProperties()方法,是用于解析<properties>标签子元素<property>的,并把解析结果转换成Properties形式参数,最终的值被存储到了configuration实例对象的variables字段中,具体实现如第2部分代码所示,其中第3行代码中getChildren()方法,用来获取<properties>标签对应的XNode对象的所有子元素,即所有<property>标签对应的XNode对象;getChildren()方法的具体实现,如第3部分代码所示,其中第8行代码用来构建<property>标签对应的XNode对象,具体代码实现如第4部分代码所示。在第4部分代码中,第6行代码是用来解析<property>标签中的属性,并存到对象的attributes 属性字段中,具体实现如下:

 private Properties parseAttributes(Node n) {
    Properties attributes = new Properties();
    NamedNodeMap attributeNodes = n.getAttributes();
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }

  在上述代码片段中,我们就看见了梦寐以求的主角——PropertyParser类了。从现在开始就进入了PropertyParser的世界中来了,首先以下面代码为例:

<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> 

这个时候在第一次调用PropertyParser.parse()方法时,第一个参数就是”org.apache.ibatis.parsing.PropertyParser.enable-default-value”了。在调用parse()方法时,因为参数中没有含有开始标签,所以根据前面讲到的GenericTokenParser.parse()方法,就直接原样返回了。
  通过前面的分析,我们可以了解到,<properties>标签中的子元素,通过一系列的解析处理,最终是key-value的Properties形式,存储到了configuration实例对象的variables变量中,等待后续的使用。

  第三步:在占位符中如何使用上述定义的参数。首先,还是从XMLConfigBuilder类的parseConfiguration()开始,然后进入environmentsElement()方法中,再进入dataSourceElement()方法中,该方法的参数即为<dataSource>标签对应XNode实例对象。dataSourceElement()方法代码如下:

private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

  在上述代码中,第4行,即XNode.getChildrenAsProperties()方法,是用于解析当前标签下子标签<property>的,并把解析结果转换成Properties形式参数。后续过程和上面分析的<properties>标签一样,不再重复,其中,主要区别在于下面代码中的value的值(<properties>标签中是常量,本处是占位符):

   <property name="driver" value="${driver}" />
   <property name="url" value="${url}" />
   <property name="username" value="${username}" />
   <property name="password" value="${password?:123456}" />
private Properties parseAttributes(Node n) {
    Properties attributes = new Properties();
    NamedNodeMap attributeNodes = n.getAttributes();
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }

因为在解析<dataSource>标签下的子标签<property>时,其中的value值对应的是占位符,所以在上述第7行代码中,PropertyParser.parse(attribute.getNodeValue(), variables)方法传递的第一个参数是”

x

x

x

&quot;

v

a

r

i

a

b

l

e

s

c

o

n

f

i

g

u

r

a

t

i

o

n

v

a

r

i

a

b

l

e

s

P

r

o

p

e

r

t

y

P

a

r

s

e

r

.

p

a

r

s

e

(

)

V

a

r

i

a

b

l

e

T

o

k

e

n

H

a

n

d

l

e

r

G

e

n

e

r

i

c

T

o

k

e

n

P

a

r

s

e

r

G

e

n

e

r

i

c

T

o

k

e

n

P

a

r

s

e

r

p

a

r

s

e

(

)

G

e

n

e

r

i

c

T

o

k

e

n

P

a

r

s

e

r

p

a

r

s

e

(

)

&quot;

{xxx}&quot;格式的,且variables的值即为configuration实例对象的variables字段值。在PropertyParser.parse()中,首先定义了VariableTokenHandler实例对象,然后又定义了GenericTokenParser实例对象,最后执行GenericTokenParser实例对象的parse()方法。具体过程如下:在GenericTokenParser实例对象的parse()方法处理&quot;

xxxvariablesconfigurationvariablesPropertyParser.parse()VariableTokenHandlerGenericTokenParserGenericTokenParserparse()GenericTokenParserparse(){xxx}“格式的参数。根据前面分析该方法的作用,最终是把”${xxx}“格式的参数解析成了”xxx”,并赋值给了expression变量。然后在其中调用了VariableTokenHandler实例对象的handleToken()方法,并把expression变量的值作为参数进行传递。最后,在handleToken()方法中,根据expression变量的值,即变量key,再从variables变量中取出key对应的参数值。然后进行返回使用。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68918.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!