Mybaits 소스 코드 분석 (2 개) 구성 분석
개요 :
이전 기사에서 우리는 mybaits가 SqlsessionFacotry를 빌드하고 실제로 다양한 xml 구성의 구문 분석을 완료하기 위해 일련의 BaseBuilder 하위 클래스 (구성 속성 포함)를 호출하고 있음을 알고 있지만 실제로 구문 분석 작업은 XPathParser 및 패키징 dom 노드 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 소스 코드 분석 (1) 핵심 실행 프로세스 : https://blog.csdn.net/shuixiou1/article/details/113531162
1. 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는 궁극적으로 평가 함수를 호출합니다. 표현식, 쿼리 상위 노드, 노드 유형의 세 가지 매개 변수가 있습니다.
public List<MyXNode> evalNodes(String expression) {
return evalNodes(document, expression);
}
public List<MyXNode> evalNodes(Object root, String expression) {
List<MyXNode> xnodes = new ArrayList<MyXNode>();
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의 본문과 속성을 미리 구문 분석하므로 나중에 필요할 때 여러 작업을 수행 할 필요가 없습니다.
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 변수의 속성에 대해 이야기 해 보겠습니다 .Xpathparser에서는 Configure 초기 구성시 전달되며 Xpathparser로 설정할 수 있습니다 .XpathParser가 Xnode 노드를 생성 할 때 변수 속성을 전달해야합니다. 이 속성은 매개 변수를 처리하기위한 것입니다. 예를 들어 자리 표시 자 목적으로 $ {username}을 사용하고이 사용자 이름이 변수에 있으면 변수의 사용자 이름 값으로 구문 분석됩니다.
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;
}
파싱 로직도 매우 간단합니다. 즉, 접두사를 지속적으로 조회하고 접두사가 발견되면 접미사를 찾고 처리를 위해 내용을 처리자에게 던집니다. 접두사 만있는 경우 접미사가 없으면이 속성 자리 표시자는 유효하지 않습니다.
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 인터페이스는 입력 문자열에서 반환 된 값 문자열을 처리하는 것이며 변수에서 쿼리해야합니다. 즉, 변수가 생성자 매개 변수로 사용되며, 핸들러 메서드는 변수가 발견되면 반환하고 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、解析环境配置
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${db.driver}" />
<property name="url" value="${db.url}" />
<property name="username" value="${db.username}" />
<property name="password" value="${db.password}" />
</dataSource>
</environment>
</environments>
*/
@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<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
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<String> 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<String> list = Arrays.asList(arr);
builderObj.items = list;
}
}
public BuilderObj build() {
return builderObj;
}
}
}
}
끝!