以前有个同事(虎哥)和我说过,每一天都要有输出,输出才能让自己得到成长。
在一点英语上面背单词时,看上面的英语视频有那么段话是这样的。如果你今天做的事和昨天的事是一样的,那么你得到的东西和昨天也是一样的。
这上面的观点我都是特别的认可。分享给大家,希望对你也是有益的
废话不多说了~ 进入主题
一: 目录总概
类不是很多。这次我可以一个一个类慢慢说,反射包中的类多,代码也多,所以只能把源码打包上传到资源中让大家下载(下载不需要积分)。
看这个图中,我说这一个模块是对xpath进行封装,是不是想问xpath是什么东西? 点击链接学习下Xpath
二: XPathParser ,PropertyParser,GenericTokenParser 这三个解析类分析。
GenericTokenParser: 看下面的注释和代码。这个还是比较简单的。
/**
* 有的东西看起来特别多的时候,又看不懂的时候,去调试就对了。
* 找单测。去调试
*/
package org.apache.ibatis.parsing;
/**
* 通用的 Token 解析器
*
* @author Clinton Begin
*/
public class GenericTokenParser {
/**
* 开始的 token 字符串
*/
private final String openToken;
/**
* 结束的 token 字符串
*/
private final String closeToken;
/**
* token 处理器
*/
private final TokenHandler handler;
/**
* 构造方法
* @param openToken 开始的token 字符串
* @param closeToken 结束的token字符串
* @param handler token的处理器
*/
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
public String parse(String text) {
if (text == null || text.isEmpty()) {
return "";
}
// search open token
// 寻找开始的 openToken 的位置
int start = text.indexOf(openToken);
if (start == -1) {
// 找不到,直接返回
return text;
}
char[] src = text.toCharArray();
// 起始查找位置
int offset = 0;
// 接收结果的字符串构造器
final StringBuilder builder = new StringBuilder();
// 匹配到 openToken 和 closeToken 之间的表达式
StringBuilder expression = null;
do {
// 转义字符
if (start > 0 && src[start - 1] == '\\') {
// this open token is escaped. remove the backslash and continue.
// 因为 openToken 前面一个位置是 \ 转义字符,所以忽略 \
// 添加 [offset, start - offset - 1] 和 openToken 的内容,添加到 builder 中
builder.append(src, offset, start - offset - 1).append(openToken);
// 修改 offset
offset = start + openToken.length();
} else {
// found open token. let's search close token.
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {
if (end > offset && src[end - 1] == '\\') {
// this close token is escaped. remove the backslash and continue.
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
// ABCA.indexOf("A",2),从第二个下标开始,查找A第一次出现的位置
// 返回指定字符第一次出现的字符串内的索引,以指定的索引开始搜索。
end = text.indexOf(closeToken, offset);
} else {
// append(str,offset,len) str是一个数组。如果是 lilerong. 这个式子是append("lilerong",2,3), 那么表示从lilerong这个字符中的第2个下标开始,往后截取3个长度。-》ler
expression.append(src, offset, end - offset);
break;
}
}
if (end == -1) {
// close token was not found.
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
} while (start > -1);
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
public static void main(String[] args) {
final StringBuilder builder = new StringBuilder();
String text="James T Kirk reporting.";
char[] src = text.toCharArray();
builder.append(src, 2, 3);
System.out.println(builder.toString());
// 输出的是:mes
}
}
package org.apache.ibatis.parsing;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
class GenericTokenParserTest {
public static class VariableTokenHandler implements TokenHandler {
private Map variables = new HashMap<>();
VariableTokenHandler(Map variables) {
this.variables = variables;
}
@Override
public String handleToken(String content) {
return variables.get(content);
}
}
@Test
void shouldDemonstrateGenericTokenReplacement() {
GenericTokenParser parser = new GenericTokenParser("${", "}", new VariableTokenHandler(new HashMap() {
{
put("first_name", "James");
put("initial", "T");
put("last_name", "Kirk");
put("var{with}brace", "Hiya");
put("", "");
}
}));
assertEquals("James T Kirk reporting.", parser.parse("${first_name} ${initial} ${last_name} reporting."));
}
}
PropertyParser: 这个类,还是使用了通用的token处理器,代码还是比较简单,里面的单测类看下就能明白的。
/**
* Copyright 2009-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.parsing;
import java.util.Properties;
/**
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class PropertyParser {
private static final String KEY_PREFIX = "org.apache.ibatis.parsing.PropertyParser.";
/**
* The special property key that indicate whether enable a default value on placeholder.
*
* The default value is {@code false} (indicate disable a default value on placeholder)
* If you specify the {@code true}, you can specify key and default value on placeholder (e.g. {@code ${db.username:postgres}}).
*
* @since 3.4.2
*/
public static final String KEY_ENABLE_DEFAULT_VALUE = KEY_PREFIX + "enable-default-value";
/**
* The special property key that specify a separator for key and default value on placeholder.
*
* The default separator is {@code ":"}.
*
* @since 3.4.2
*/
public static final String KEY_DEFAULT_VALUE_SEPARATOR = KEY_PREFIX + "default-value-separator";
/**
* 默认分隔符是否可用
*/
private static final String ENABLE_DEFAULT_VALUE = "false";
/**
* 默认分隔符
*/
private static final String DEFAULT_VALUE_SEPARATOR = ":";
private PropertyParser() {
// Prevent Instantiation
}
public static String parse(String string, Properties variables) {
VariableTokenHandler handler = new VariableTokenHandler(variables);
// 调用通用的token 解析器: 开始的 token 字符串${,结束的 token 字符串:},使用的处理器为 VariableTokenHandler
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}
/**
* 可变 token 处理器
*/
private static class VariableTokenHandler implements TokenHandler {
private final Properties variables;
// 是否使用默认分隔符: 默认false。
private final boolean enableDefaultValue;
// 默认分隔符
private final String defaultValueSeparator;
private VariableTokenHandler(Properties variables) {
this.variables = variables;
this.enableDefaultValue = Boolean.parseBoolean(getPropertyValue(KEY_ENABLE_DEFAULT_VALUE, ENABLE_DEFAULT_VALUE));
this.defaultValueSeparator = getPropertyValue(KEY_DEFAULT_VALUE_SEPARATOR, DEFAULT_VALUE_SEPARATOR);
}
private String getPropertyValue(String key, String defaultValue) {
return (variables == null) ? defaultValue : variables.getProperty(key, defaultValue);
}
@Override
public String handleToken(String content) {
if (variables != null) {
String key = content;
if (enableDefaultValue) {
final int separatorIndex = content.indexOf(defaultValueSeparator);
String defaultValue = null;
if (separatorIndex >= 0) {
key = content.substring(0, separatorIndex);
defaultValue = content.substring(separatorIndex + defaultValueSeparator.length());
}
if (defaultValue != null) {
return variables.getProperty(key, defaultValue);
}
}
if (variables.containsKey(key)) {
return variables.getProperty(key);
}
}
return "${" + content + "}";
}
}
}
/**
* Copyright 2009-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.parsing;
import java.util.Properties;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
class PropertyParserTest {
@Test
void replaceToVariableValue() {
Properties props = new Properties();
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
props.setProperty("key", "value");
props.setProperty("tableName", "members");
props.setProperty("orderColumn", "member_id");
props.setProperty("a:b", "c");
Assertions.assertThat(PropertyParser.parse("${key}", props)).isEqualTo("value");
Assertions.assertThat(PropertyParser.parse("${key:aaaa}", props)).isEqualTo("value");
Assertions.assertThat(PropertyParser.parse("SELECT * FROM ${tableName:users} ORDER BY ${orderColumn:id}", props)).isEqualTo("SELECT * FROM members ORDER BY member_id");
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "false");
Assertions.assertThat(PropertyParser.parse("${a:b}", props)).isEqualTo("c");
props.remove(PropertyParser.KEY_ENABLE_DEFAULT_VALUE);
Assertions.assertThat(PropertyParser.parse("${a:b}", props)).isEqualTo("c");
}
@Test
void notReplace() {
Properties props = new Properties();
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
Assertions.assertThat(PropertyParser.parse("${key}", props)).isEqualTo("${key}");
Assertions.assertThat(PropertyParser.parse("${key}", null)).isEqualTo("${key}");
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "false");
Assertions.assertThat(PropertyParser.parse("${a:b}", props)).isEqualTo("${a:b}");
props.remove(PropertyParser.KEY_ENABLE_DEFAULT_VALUE);
Assertions.assertThat(PropertyParser.parse("${a:b}", props)).isEqualTo("${a:b}");
}
@Test
void applyDefaultValue() {
Properties props = new Properties();
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
Assertions.assertThat(PropertyParser.parse("${key:default}", props)).isEqualTo("default");
Assertions.assertThat(PropertyParser.parse("SELECT * FROM ${tableName:users} ORDER BY ${orderColumn:id}", props)).isEqualTo("SELECT * FROM users ORDER BY id");
Assertions.assertThat(PropertyParser.parse("${key:}", props)).isEmpty();
Assertions.assertThat(PropertyParser.parse("${key: }", props)).isEqualTo(" ");
Assertions.assertThat(PropertyParser.parse("${key::}", props)).isEqualTo(":");
}
@Test
void applyCustomSeparator() {
Properties props = new Properties();
props.setProperty(PropertyParser.KEY_ENABLE_DEFAULT_VALUE, "true");
props.setProperty(PropertyParser.KEY_DEFAULT_VALUE_SEPARATOR, "?:");
Assertions.assertThat(PropertyParser.parse("${key?:default}", props)).isEqualTo("default");
Assertions.assertThat(PropertyParser.parse("SELECT * FROM ${schema?:prod}.${tableName == null ? 'users' : tableName} ORDER BY ${orderColumn}", props)).isEqualTo("SELECT * FROM prod.${tableName == null ? 'users' : tableName} ORDER BY ${orderColumn}");
Assertions.assertThat(PropertyParser.parse("${key?:}", props)).isEmpty();
Assertions.assertThat(PropertyParser.parse("${key?: }", props)).isEqualTo(" ");
Assertions.assertThat(PropertyParser.parse("${key?::}", props)).isEqualTo(":");
}
}
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.parsing;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.apache.ibatis.builder.BuilderException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
/**
* 用来解析mybatis-config.xml 和**Mapper.xml等xml配置文件
*
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public class XPathParser {
/**
* xml document 对象
*/
private final Document document;
/**
* 是否校验
*/
private boolean validation;
/**
* xml 实体解析器
*/
private EntityResolver entityResolver;
/**
* 变量properties 对象
*/
private Properties variables;
/**
* java xpath 对象
*/
private XPath xpath;
public XPathParser(String xml) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream) {
commonConstructor(false, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document) {
commonConstructor(false, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation) {
commonConstructor(validation, null, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation) {
commonConstructor(validation, null, null);
this.document = document;
}
public XPathParser(String xml, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables) {
commonConstructor(validation, variables, null);
this.document = document;
}
/**
* 构造 XPathParser 对象
* @param xml xml 文件地址
* @param validation 是否校验xml
* @param variables 变量properties对象
* @param entityResolver xml 实体解析器
*/
public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(new StringReader(xml)));
}
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = document;
}
public void setVariables(Properties variables) {
this.variables = variables;
}
public String evalString(String expression) {
return evalString(document, expression);
}
public String evalString(Object root, String expression) {
String result = (String) evaluate(expression, root, XPathConstants.STRING);
result = PropertyParser.parse(result, variables);
return result;
}
public Boolean evalBoolean(String expression) {
return evalBoolean(document, expression);
}
public Boolean evalBoolean(Object root, String expression) {
return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN);
}
public Short evalShort(String expression) {
return evalShort(document, expression);
}
public Short evalShort(Object root, String expression) {
return Short.valueOf(evalString(root, expression));
}
public Integer evalInteger(String expression) {
return evalInteger(document, expression);
}
public Integer evalInteger(Object root, String expression) {
return Integer.valueOf(evalString(root, expression));
}
public Long evalLong(String expression) {
return evalLong(document, expression);
}
public Long evalLong(Object root, String expression) {
return Long.valueOf(evalString(root, expression));
}
public Float evalFloat(String expression) {
return evalFloat(document, expression);
}
public Float evalFloat(Object root, String expression) {
return Float.valueOf(evalString(root, expression));
}
public Double evalDouble(String expression) {
return evalDouble(document, expression);
}
public Double evalDouble(Object root, String expression) {
return (Double) evaluate(expression, root, XPathConstants.NUMBER);
}
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 XNode(this, nodes.item(i), variables));
}
return xnodes;
}
public XNode evalNode(String expression) {
return evalNode(document, expression);
}
public XNode evalNode(Object root, String expression) {
Node node = (Node) evaluate(expression, root, XPathConstants.NODE);
if (node == null) {
return null;
}
return new XNode(this, node, variables);
}
/**
* 获取指定元素或者节点的值
* @param expression 表达式
* @param root 指定节点
* @param returnType 返回类型
* @return 值
*/
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);
}
}
/**
* 创建document 对象
* @param inputSource inputSource xml的inputSource 对象
* @return document 对象
*/
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
// 1: 创建 DocumentBuilderFactory 对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
// 2: 创建 DocumentBuilder对象
DocumentBuilder builder = factory.newDocumentBuilder();
// 设置实体解析器
builder.setEntityResolver(entityResolver);
// 实现都是空的
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
// NOP
}
});
// 3: 解析xml文件
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
/**
* 公用的构造方法逻辑。代码如下
* @param validation
* @param variables
* @param entityResolver
*/
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();
}
}
我自己看下来,这个就是可以根据地址获取和解析里面的值,相当于对xpath进行增强。其实增强体现在哪里。我还没发现。就像上篇那个发射器类。我就可能把代码读明白了几层。但是我就搞不明白为啥不停的包装又包装。
看着就觉得麻烦。也许我现在的这些观点只是我没什么远见。就像井底之蛙。自己的眼界还是小了。所以体会不了。
继续加油~
总结: 解析器模板的代码就在这了。我写这个只是记录自己看的一个过程。如果刚好你也在做这个事情,希望对你有帮助。如果有不对还需要改正的,希望大家都提点。谢谢