博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybaits源码分析(二) 配置解析
阅读量:4075 次
发布时间:2019-05-25

本文共 10403 字,大约阅读时间需要 34 分钟。

mybaits源码分析(二) 配置解析

   概述:

   从上篇我们知道,mybaits在构建SqlsessionFacotry,实际是调用一系列的BaseBuilder的子类(内有configuration属性),来完成各种xml配置的解析,而实际上解析工作,是由XPathParser和包装dom节点node的Xnode完成解析工作。下面是XMLConfigBuilder的部分代码。

private XPathParser parser;  // xpath解析器  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);  }  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {    super(new Configuration());    ErrorContext.instance().resource("SQL Mapper Configuration");    this.configuration.setVariables(props);    this.parsed = false;    this.environment = environment;    this.parser = parser;  }

配置解析源码过程大致过程不在复述,详见前篇

mybaits源码分析(一) 核心执行流程  :

一、XPathParser和Xnode的详解

1、XPathParser的详解

1) XPathParser的成员变量和构造函数

private Document document; // 顶级document节点	private boolean validation;	private EntityResolver entityResolver;	private Properties variables; // 额外的props	private XPath xpath; // 原生Xpath解析	private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {		this.validation = validation;		this.entityResolver = entityResolver;		this.variables = variables;		XPathFactory factory = XPathFactory.newInstance();		this.xpath = factory.newXPath();	}	public MyXPathParser(String xml) {         // 注入属性		commonConstructor(false, null, null);        // dom解析出document		this.document = createDocument(new InputSource(new StringReader(xml)));	}

2) XPathParser解析节点

 不管是解析节点元素、还是列表元素,最终XPathParser调用的是一个evaluate的函数。  它有三个参数分别为:表达式、查询的父节点、节点的类型

public List
evalNodes(String expression) { return evalNodes(document, expression); } public List
evalNodes(Object root, String expression) { List
xnodes = new ArrayList
(); NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { xnodes.add(new MyXNode(this, nodes.item(i), variables)); } return xnodes; } public MyXNode evalNode(String expression) { return evalNode(document, expression); } public MyXNode evalNode(Object root, String expression) { Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } return new MyXNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { try { return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } }

2、XNode详解

1) 成员变量和构造函数

public class MyXNode {  private Node node; // 原生dom节点  private String name; // node名称  private String body; // 从node预解析出来的dom的text  private Properties attributes; // 从node预解析出来属性  private Properties variables; // 扩展props  private MyXPathParser xpathParser; // xpathParser引用 (一个Builder实例共享一个XpathParser,这样Xnode节点一直可根据顶级节点扩展解析额外的信息)  public MyXNode(MyXPathParser 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);  }

2) Xnode预解析

在构造函数中,我们对Xnode的body和属性预先进行了解析,这样后面需要的时候,就不需要多次进行操作。

private Properties parseAttributes(Node n) {    Properties attributes = new Properties();    NamedNodeMap attributeNodes = n.getAttributes(); // 属性map    if (attributeNodes != null) {      for (int i = 0; i < attributeNodes.getLength(); i++) {        Node attribute = attributeNodes.item(i);        // 对属性的值占位符做了处理,比如:${username}        String value = PropertyParser.parse(attribute.getNodeValue(), variables);        attributes.put(attribute.getNodeName(), value); // 解析后添加到props      }    }    return attributes;  }  private String parseBody(Node node) {    String data = getBodyData(node);    if (data == null) {      NodeList children = node.getChildNodes(); // 子节点      for (int i = 0; i < children.getLength(); i++) {        Node child = children.item(i);        data = getBodyData(child); // text节点处理,并且也做了占位符处理        if (data != null) break;      }    }    return data;  }

上面重点看:  String value = PropertyParser.parse(attribute.getNodeValue(), variables) 这个代码。这个是处理${}属性占位符用的。

另外说下Properties variables这个属性,我们在Xpathparser中是由Configure中初始构建时传入,并且可以设置到Xpathparser中,而XpathParser创建Xnode节点时,一定会传入variables属性,这个Properties的作用就是处理参数占位用途的,比如我们用了${username},而这个username如果在我们的variables中有,那么就会被解析成username在variables中的值。

3、PropertyParser详解

PropertyParser.parse方法,实际上是调用一个TokenParser的类,这个类可以传入占位前缀、占位后缀,以及用来处理内容的Hanlder接口。

class PropertyParsers {	public static String parse(String string, Properties variables) {		MTokenHandler handler = new MVariableTokenHandler(variables);		MTokenParser parser = new MTokenParser("${", "}", handler);		return parser.parse(string);	}}class MTokenParser {	private String beforeToken; // token前缀	private String endToken; // token后缀	private MTokenHandler mTokenHandler; // handler	public MTokenParser(String beforeToken, String endToken, MTokenHandler mTokenHandler) {		super();		this.beforeToken = beforeToken;		this.endToken = endToken;		this.mTokenHandler = mTokenHandler;	}

解析逻辑也很简单,就是不停查询前缀,找到前缀就找后缀,内容丢给hanlder处理,如果只有前缀没有后缀此属性占位符就是无效的。

public String parse(String content) {		StringBuilder builder = new StringBuilder();		if (content != null && content.length() > 0) {			char[] src = content.toCharArray();			int offset = 0;			int start = content.indexOf(beforeToken, offset);			while (start > -1) {				if (start > 0 && src[start - 1] == '\\') {					builder.append(src, offset, start - offset - 1);					offset = start + beforeToken.length();				} else {					int end = content.indexOf(endToken, start);					if (end == -1) { // 没有直接添加全部						builder.append(src, offset, src.length - offset);						offset = src.length;					} else { // 常规遍历						builder.append(src, offset, start - offset);						offset = start + beforeToken.length();						String str = new String(src, offset, end - offset);						builder.append(mTokenHandler.handler(str));						offset = end + endToken.length();					}				}				start = content.indexOf(beforeToken, offset);			}			if (offset < src.length) {				builder.append(src, offset, src.length - offset);			}		}		return builder.toString();	}

MTokenHandler接口,就是对输入的string处理返回值string,而我们需要从variables中查询,就是variables作为构造函数参数,handler方法到variables找到就返回,没找到返回null。

class MVariableTokenHandler implements MTokenHandler {	private Properties variables;	public MVariableTokenHandler(Properties properties) {		super();		this.variables = properties;	}	@Override	public String handler(String content) {		if (variables != null && variables.containsKey(content)) {			return variables.getProperty(content);		}		return null;	}}

二、解析测试

第一部分对xpathparser和xnode的功能做了分析,下面我们模拟XMLConfigBuilder的解析过程,截取部分解析工作,用XpathParse进行模拟测试解析流程。

public class XPathTest {		private static MyXPathParser myXPathParser;	static {		try {			InputStream in = Resources.getResourceAsStream("custom/sqlMapConfig2.xml");			myXPathParser = new MyXPathParser(in);		} catch (IOException e) {			e.printStackTrace();		}	}		/**	 * 1、解析properties的node	 */	@Test	public void test1() throws IOException {		MyXNode root = myXPathParser.evalNode("/configuration");		MyXNode propNode = root.evalNode("properties");		Properties defaults = propNode.getChildrenAsProperties();	    String resource = propNode.getStringAttribute("resource");	    defaults.putAll(Resources.getResourceAsProperties(resource));		System.out.println(defaults);	}		/**	 * 2、解析环境配置	  
*/ @Test public void test2() throws IOException, InstantiationException, IllegalAccessException { MyXNode root = myXPathParser.evalNode("/configuration"); MyXNode propNode = root.evalNode("properties"); Properties defaults = propNode.getChildrenAsProperties(); String resource = propNode.getStringAttribute("resource"); defaults.putAll(Resources.getResourceAsProperties(resource)); // 将解析的属性对设置到XpathParser中,这样此节点内部的解析会把${}参数替换成props的对应值。 myXPathParser.setVariables(defaults); // 别名隐射- 下面要用 Map
> TYPE_ALIASES = new HashMap
>(); TYPE_ALIASES.put("POOLED", PooledDataSourceFactory.class); MyXNode environments = root.evalNode("environments"); String systemEnviroment = ""; // 假设系统环境,如果没有设置,就是取environments的default属性 systemEnviroment = environments.getStringAttribute("default"); for (MyXNode child : environments.getChildren()) { String id = child.getStringAttribute("id"); if (systemEnviroment.equals(id)) { // 系统配置的等于此子环境配置 //DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); // 开始解析datasource节点 MyXNode datasourceNode = child.evalNode("dataSource"); String type = datasourceNode.getStringAttribute("type"); DataSourceFactory factory = (DataSourceFactory) TYPE_ALIASES.get(type).newInstance(); Properties properties = datasourceNode.getChildrenAsProperties(); factory.setProperties(properties); // System.out.println(properties); DataSource dataSource = factory.getDataSource(); } } } }

另附:

mybaits在解析配置成ResultMapping等对象的时候,以及其他很多复杂对象的构建,都用到了构建模式(内部类的构建模式),下面一个范例进行演示。

/** * mybaits框架ResultMapping等类的构建模式的演示 */public class BuilderDemo {	public static void main(String[] args) {		BuilderObj.Builder builder = new BuilderObj.Builder("id", "name");		builder.setVar1("var1");		builder.setVar2("var2");		BuilderObj obj = builder.build();		System.out.println(obj);	}	public static class BuilderObj {		private String name;		private String id;		private String var1;		private String var2;		private List
items; @Override public String toString() { return "BuilderObj [name=" + name + ", id=" + id + ", var1=" + var1 + ", var2=" + var2 + ", items=" + items + "]"; } public static class Builder { private BuilderObj builderObj = new BuilderObj(); public Builder(String id) { builderObj.id = id; } public Builder(String id, String name) { this(id); builderObj.name = name; } public void setVar1(String var) { builderObj.var1 = var; } public void setVar2(String var) { builderObj.var2 = var; } public void setItems(String[] arr) { if (arr != null) { List
list = Arrays.asList(arr); builderObj.items = list; } } public BuilderObj build() { return builderObj; } } }}

 

end !

 

 

 

 

转载地址:http://druni.baihongyu.com/

你可能感兴趣的文章
Linux +Win LAMPP Tools XAMPP 1.7.3 / 5.6.3
查看>>
my read_university
查看>>
network manager
查看>>
OS + Linux Disk disk lvm / disk partition / disk mount / disk io
查看>>
RedHat + OS CPU、MEM、DISK
查看>>
net TCP/IP / TIME_WAIT / tcpip / iperf / cain
查看>>
webServer kzserver/1.0.0
查看>>
OS + Unix IBM Aix basic / topas / nmon / filemon / vmstat / iostat / sysstat/sar
查看>>
my ReadMap subway / metro / map / ditie / gaotie / traffic / jiaotong
查看>>
OS + Linux DNS Server Bind
查看>>
linux下安装django
查看>>
Android 解决TextView设置文本和富文本SpannableString自动换行留空白问题
查看>>
Android开发中Button按钮绑定监听器的方式完全解析
查看>>
Android自定义View实现商品评价星星评分控件
查看>>
postgresql监控工具pgstatspack的安装及使用
查看>>
postgresql查看表的和索引的情况,判断是否膨胀
查看>>
postgresql中根据oid和filenode去找表的物理文件的位置
查看>>
postgresql减少wal日志生成量的方法
查看>>
swift中单例的创建及销毁
查看>>
获取App Store中App的ipa包
查看>>