前言
针对源码总结分为二部分来看,一部分解析环境变量部分和mapper部分,另一部分为执行sql部分。先来看第一部分,xml解析的源码。
例子
一个简单的configuration配置文件和mapper文件,结合这个来看源码
config.xml
UserMapper.xml
入口
XMLConfigBuilder 看这个类的名字我们可以看出来用来解析config配置文件,那我们来看源码部分
/**
* 解析XML配置文件
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// parser.evalNode("/configuration"):通过XPATH解析器,解析configuration根节点,
//也就是 这个估计大家没有看到过,因为spring结合mybatis封装好了。
// 从configuration根节点开始解析,最终将解析出的内容封装到Configuration对象中
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
通过parseConfiguration(parser.evalNode("/configuration"))方法来看下一方法
private void parseConfiguration(XNode root) {
try {
。。。。
//这个里面有好多的方法,着重看这两个
// 根据方法名顾名思义解析 数据源环境配置例如datasour解析就在这部分
environmentsElement(root.evalNode("environments"));
// 这个方法用来解析我们mappers标签,每个mappers里面有对应的子mapper的映射
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这块来分层来看一下
1.数据源解析入口
environmentsElement(root.evalNode(“environments”))
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//结合我们例子xml,如果为空取默认的属性,解析这块可以看个人Dom4j解析xml内容
environment = context.getStringAttribute("default");
}
//获取子标签的属性
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
//事务部分
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//数据源部分,解析出来的数据源放入configuration对象中。这块使用了构建者模式
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//最终configuration封装了数据源信息
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
数据源文件解析
dataSourceElement(child.evalNode(“dataSource”))
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
String type = context.getStringAttribute("type");
//这块相当于获取property标签并解析
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
//把Properties 封装到DataSourceFactory ,最终datasource会封装到configuration
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
property标签解析
getChildrenAsProperties()
public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
//获取property标签name属性:driver,url信息等
String name = child.getStringAttribute("name");
//获取property标签value属性对应的值
String value = child.getStringAttribute("value");
if (name != null && value != null) {
//获取到以后封装到properties对象中
properties.setProperty(name, value);
}
}
return properties;
}
到此块数据源信息就解析完成了。
2.Mapper映射文件入口
mappers文件解析
mapperElement(root.evalNode(“mappers”))
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 获取标签的子标签,也就是mapper.xml
for (XNode child : parent.getChildren()) {
// 子标签
if ("package".equals(child.getName())) {
// 获取mapper接口和mapper映射文件对应的package包名
String mapperPackage = child.getStringAttribute("name");
// 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
configuration.addMappers(mapperPackage);
} else {// 子标签
// 获取resource属性
String resource = child.getStringAttribute("resource");
// 获取url属性
String url = child.getStringAttribute("url");
// 获取class属性
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 专门用来解析mapper映射文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 通过XMLMapperBuilder解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
// 通过XMLMapperBuilder解析mapper映射文件
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
Class> mapperInterface = Resources.classForName(mapperClass);
// 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
mapper文件解析
mapperParser.parse()
public void parse() {
// mapper映射文件是否已经加载过
if (!configuration.isResourceLoaded(resource)) {
// 从映射文件中的根标签开始解析,直到完整的解析完毕
configurationElement(parser.evalNode("/mapper"));
// 标记已经解析
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
configurationElement(parser.evalNode("/mapper"))
mapper每个标签的属性
private void configurationElement(XNode context) {
try {
// 获取namespace值,也就是命名空间
String namespace = context.getStringAttribute("namespace");
// 命名空间不能为空
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 设置当前的命名空间为namespace的值
builderAssistant.setCurrentNamespace(namespace);
// 解析子标签
cacheRefElement(context.evalNode("cache-ref"));
// 解析子标签
cacheElement(context.evalNode("cache"));
// 解析子标签
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析子标签
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析子标签,也就是SQL片段
sqlElement(context.evalNodes("/mapper/sql"));
// 解析
buildStatementFromContext(context.evalNodes(“select|insert|update|delete”))
private void buildStatementFromContext(List list) {
if (configuration.getDatabaseId() != null) {
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 构建MappedStatement
buildStatementFromContext(list, null);
}
buildStatementFromContext(list, null);
private void buildStatementFromContext(List list, String requiredDatabaseId) {
for (XNode context : list) {
// MappedStatement解析器
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析select等4个标签,创建MappedStatement对象
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
configuration.addIncompleteStatement(statementParser);
}
}
}
parseStatementNode()
public void parseStatementNode() {
// 获取statement的id属性
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
// 获取入参类型
String parameterType = context.getStringAttribute("parameterType");
// 别名处理,获取入参对应的Java类型
Class> parameterTypeClass = resolveClass(parameterType);
// 获取ResultMap
String resultMap = context.getStringAttribute("resultMap");
// 获取结果映射类型
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// 别名处理,获取返回值对应的Java类型
Class> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
// 设置默认StatementType为Prepared,该参数指定了后面的JDBC处理时,采用哪种Statement
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
// 解析SQL命令类型是什么?确定操作是CRUD中的哪一种
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
//是否查询语句
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// 标签解析
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// 解析标签
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// 创建SqlSource,解析SQL,封装SQL语句(未参数绑定)和入参信息
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
// 通过构建者助手,创建MappedStatement对象
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
用来解析sql文本,并将#{}和${}替换
createSqlSource是个接口
实现类 XMLLanguageDriver (解析SQL)
public SqlSource createSqlSource(Configuration configuration, XNode script, Class> parameterType) {
// 初始化了动态SQL标签处理器
XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
// 解析动态SQL
return builder.parseScriptNode();
}
parseScriptNode()
public SqlSource parseScriptNode() {
// 解析select\insert\ update\delete标签中的SQL语句,最终将解析到的SqlNode封装到MixedSqlNode中的List集合中
// ${} SQL文本封装到TextSqlNode
// #{} SQL文本封装到StaticTextSqlNode
// 动态SQL标签中的SQL文本分别封装到不同的SqlNode中
MixedSqlNode rootSqlNode = parseDynamicTags(context);
SqlSource sqlSource = null;
// 如果SQL中包含${}和动态SQL语句,则将SqlNode封装到DynamicSqlSource
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
// 如果SQL中包含#{},则将SqlNode封装到RawSqlSource中,并指定parameterType
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
parseDynamicTags()
protected MixedSqlNode parseDynamicTags(XNode node) {
List contents = new ArrayList<>();
//获取
isDynamic()
public boolean isDynamic() {
DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser();
// 创建分词解析器,主要用来解析SQL字符串的#{}和${}
GenericTokenParser parser = createParser(checker);
// 进行解析,如果是${},此处直接解析成完整的SQL语句
parser.parse(text);
return checker.isDynamic();
}
解析${}和#{}
parse()
public String parse(String text) {
if (text == null || text.isEmpty()) {
return "";
}
// search open token
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
if (start > 0 && src[start - 1] == '\\') {
// this open token is escaped. remove the backslash and continue.
builder.append(src, offset, start - offset - 1).append(openToken);
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();
end = text.indexOf(closeToken, offset);
} else {
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
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);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
至此sql语句就解析完了,请求参数和返回参数没有去看,那块根据类型去调用反射生成具体的文件。下一篇看sql执行部分。