Mybaits 소스 코드 분석 (2 개) 구성 분석

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;
			}
		}
	}
}

 

끝!

 

 

 

 

추천

출처blog.csdn.net/shuixiou1/article/details/113573036