Ibatis源码学习(三):配置文件的解析
ibatis文件的文件的解析从主文件sql-map-config.xml开始。见类:com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser
public SqlMapConfigParser() {
parser.setValidation(true);
parser.setEntityResolver(new SqlMapClasspathEntityResolver());
addSqlMapConfigNodelets(); //"/sqlMapConfig/end()"
addGlobalPropNodelets(); //"/sqlMapConfig/properties"
addSettingsNodelets(); //"/sqlMapConfig/settings"
addTypeAliasNodelets(); //"/sqlMapConfig/typeAlias"
addTypeHandlerNodelets(); //"/sqlMapConfig/typeHandler"
addTransactionManagerNodelets(); //"/sqlMapConfig/transactionManager/property" ...
addSqlMapNodelets(); //"/sqlMapConfig/sqlMap"
addResultObjectFactoryNodelets(); //"/sqlMapConfig/resultObjectFactory" ...
}先将上诉的节点默认添加进来,然后根据具体的设置在利用回调来初始化各个节点的具体属性值,以addSettingsNodelets()为例:
private void addSettingsNodelets() {
parser.addNodelet("/sqlMapConfig/settings", new Nodelet() {
public void process(Node node) throws Exception { //该方法做会调用
Properties attributes = NodeletUtils.parseAttributes(node, state.getGlobalProps());
SqlMapConfiguration config = state.getConfig();
String classInfoCacheEnabledAttr = attributes.getProperty("classInfoCacheEnabled");
boolean classInfoCacheEnabled = (classInfoCacheEnabledAttr == null || "true".equals(classInfoCacheEnabledAttr));
config.setClassInfoCacheEnabled(classInfoCacheEnabled);
String lazyLoadingEnabledAttr = attributes.getProperty("lazyLoadingEnabled");
boolean lazyLoadingEnabled = (lazyLoadingEnabledAttr == null || "true".equals(lazyLoadingEnabledAttr));
config.setLazyLoadingEnabled(lazyLoadingEnabled);
String statementCachingEnabledAttr = attributes.getProperty("statementCachingEnabled");
boolean statementCachingEnabled = (statementCachingEnabledAttr == null || "true".equals(statementCachingEnabledAttr));
config.setStatementCachingEnabled(statementCachingEnabled);
String cacheModelsEnabledAttr = attributes.getProperty("cacheModelsEnabled");
boolean cacheModelsEnabled = (cacheModelsEnabledAttr == null || "true".equals(cacheModelsEnabledAttr));
config.setCacheModelsEnabled(cacheModelsEnabled);
String enhancementEnabledAttr = attributes.getProperty("enhancementEnabled");
boolean enhancementEnabled = (enhancementEnabledAttr == null || "true".equals(enhancementEnabledAttr));
config.setEnhancementEnabled(enhancementEnabled);
String useColumnLabelAttr = attributes.getProperty("useColumnLabel");
boolean useColumnLabel = (useColumnLabelAttr == null || "true".equals(useColumnLabelAttr));
config.setUseColumnLabel(useColumnLabel);
String forceMultipleResultSetSupportAttr = attributes.getProperty("forceMultipleResultSetSupport");
boolean forceMultipleResultSetSupport = "true".equals(forceMultipleResultSetSupportAttr);
config.setForceMultipleResultSetSupport(forceMultipleResultSetSupport);
String defaultTimeoutAttr = attributes.getProperty("defaultStatementTimeout");
Integer defaultTimeout = defaultTimeoutAttr == null ? null : Integer.valueOf(defaultTimeoutAttr);
config.setDefaultStatementTimeout(defaultTimeout);
String useStatementNamespacesAttr = attributes.getProperty("useStatementNamespaces");
boolean useStatementNamespaces = "true".equals(useStatementNamespacesAttr);
state.setUseStatementNamespaces(useStatementNamespaces);
}
});
}parser.addNodelet(xpath,nodelet)添加一个节点,见类:com.ibatis.common.xml.NodeletParser
public void addNodelet(String xpath, Nodelet nodelet) {
letMap.put(xpath, nodelet);
}在这个类中,解析节点的时候,
public void parse(Reader reader) throws NodeletException {
try {
Document doc = createDocument(reader);
parse(doc.getLastChild());
} catch (Exception e) {
throw new NodeletException("Error parsing XML. Cause: " + e, e);
}
}
public void parse(InputStream inputStream) throws NodeletException {
try {
Document doc = createDocument(inputStream);
parse(doc.getLastChild());
} catch (Exception e) {
throw new NodeletException("Error parsing XML. Cause: " + e, e);
}
}
/**
* Begins parsing from the provided Node.
*/
public void parse(Node node) {
Path path = new Path();
processNodelet(node, "/");
process(node, path);
}
/**
* A recursive method that walkes the DOM tree, registers XPaths and
* calls Nodelets registered under those XPaths.
*/
private void process(Node node, Path path) {
if (node instanceof Element) {
// Element
String elementName = node.getNodeName();
path.add(elementName);
processNodelet(node, path.toString());
processNodelet(node, new StringBuffer("//").append(elementName).toString());
// Attribute
NamedNodeMap attributes = node.getAttributes();
int n = attributes.getLength();
for (int i = 0; i < n; i++) {
Node att = attributes.item(i);
String attrName = att.getNodeName();
path.add("@" + attrName);
processNodelet(att, path.toString());
processNodelet(node, new StringBuffer("//@").append(attrName).toString());
path.remove();
}
// Children
NodeList children = node.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
process(children.item(i), path);
}
path.add("end()");
processNodelet(node, path.toString());
path.remove();
path.remove();
} else if (node instanceof Text) {
// Text
path.add("text()");
processNodelet(node, path.toString());
processNodelet(node, "//text()");
path.remove();
}
}
private void processNodelet(Node node, String pathString) {
Nodelet nodelet = (Nodelet) letMap.get(pathString);
if (nodelet != null) {
try {
nodelet.process(node); //这个方法进行回调,设置初始值
} catch (Exception e) {
throw new RuntimeException("Error parsing XPath '" + pathString + "'. Cause: " + e, e);
}
}
} 在com.ibatis.sqlmap.engine.builder.xml.SqlMapParser里面解析到statement时:
protected void addStatementNodelets() {
parser.addNodelet("/sqlMap/statement", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new MappedStatement());
}
});
parser.addNodelet("/sqlMap/insert", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new InsertStatement());
}
});
parser.addNodelet("/sqlMap/update", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new UpdateStatement());
}
});
parser.addNodelet("/sqlMap/delete", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new DeleteStatement());
}
});
parser.addNodelet("/sqlMap/select", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new SelectStatement());
}
});
parser.addNodelet("/sqlMap/procedure", new Nodelet() {
public void process(Node node) throws Exception {
statementParser.parseGeneralStatement(node, new ProcedureStatement());
}
});
} 实际上调用了com.ibatis.sqlmap.engine.builder.xml.SqlStatementParser的parseGeneralStatement方法:public void parseGeneralStatement(Node node, MappedStatement statement) {
// ...
MappedStatementConfig statementConf = state.getConfig().newMappedStatementConfig(id, statement,
new XMLSqlSource(state, node), parameterMapName, parameterClass, resultMapName, additionalResultMapNames,
resultClass, additionalResultClasses, resultSetType, fetchSizeInt, allowRemappingBool, timeoutInt, cacheModelName,
xmlResultName);
findAndParseSelectKey(node, statementConf);
} 此时又是调用了com.ibatis.sqlmap.engine.config.SqlMapConfiguration的newMappedStatementConfig方法:public MappedStatementConfig newMappedStatementConfig(String id, MappedStatement statement, SqlSource processor,
String parameterMapName, Class parameterClass,
String resultMapName, String[] additionalResultMapNames,
Class resultClass, Class[] additionalResultClasses,
String resultSetType, Integer fetchSize,
boolean allowRemapping, Integer timeout, String cacheModelName,
String xmlResultName) {
return new MappedStatementConfig(this, id, statement, processor, parameterMapName, parameterClass, resultMapName,
additionalResultMapNames, resultClass, additionalResultClasses, cacheModelName, resultSetType, fetchSize,
allowRemapping, timeout, defaultStatementTimeout, xmlResultName);
} 返回一个com.ibatis.sqlmap.engine.config.MappedStatementConfig的实例MappedStatementConfig(SqlMapConfiguration config, String id, MappedStatement statement, SqlSource processor,
String parameterMapName, Class parameterClass, String resultMapName,
String[] additionalResultMapNames, Class resultClass, Class[] additionalResultClasses,
String cacheModelName, String resultSetType, Integer fetchSize, boolean allowRemapping,
Integer timeout, Integer defaultStatementTimeout, String xmlResultName) {
this.errorContext = config.getErrorContext();
this.client = config.getClient();
SqlMapExecutorDelegate delegate = client.getDelegate();
this.typeHandlerFactory = config.getTypeHandlerFactory();
errorContext.setActivity("parsing a mapped statement");
errorContext.setObjectId(id + " statement");
errorContext.setMoreInfo("Check the result map name.");
if (resultMapName != null) {
statement.setResultMap(client.getDelegate().getResultMap(resultMapName));
if (additionalResultMapNames != null) {
for (int i = 0; i < additionalResultMapNames.length; i++) {
statement.addResultMap(client.getDelegate().getResultMap(additionalResultMapNames[i]));
}
}
}
errorContext.setMoreInfo("Check the parameter map name.");
if (parameterMapName != null) {
statement.setParameterMap(client.getDelegate().getParameterMap(parameterMapName));
}
statement.setId(id);
statement.setResource(errorContext.getResource());
if (resultSetType != null) {
if ("FORWARD_ONLY".equals(resultSetType)) {
statement.setResultSetType(new Integer(ResultSet.TYPE_FORWARD_ONLY));
} else if ("SCROLL_INSENSITIVE".equals(resultSetType)) {
statement.setResultSetType(new Integer(ResultSet.TYPE_SCROLL_INSENSITIVE));
} else if ("SCROLL_SENSITIVE".equals(resultSetType)) {
statement.setResultSetType(new Integer(ResultSet.TYPE_SCROLL_SENSITIVE));
}
}
if (fetchSize != null) {
statement.setFetchSize(fetchSize);
}
// set parameter class either from attribute or from map (make sure to match)
ParameterMap parameterMap = statement.getParameterMap();
if (parameterMap == null) {
statement.setParameterClass(parameterClass);
} else {
statement.setParameterClass(parameterMap.getParameterClass());
}
// process SQL statement, including inline parameter maps
errorContext.setMoreInfo("Check the SQL statement.");
Sql sql = processor.getSql();
setSqlForStatement(statement, sql);
// set up either null result map or automatic result mapping
ResultMap resultMap = (ResultMap) statement.getResultMap();
if (resultMap == null && resultClass == null) {
statement.setResultMap(null);
} else if (resultMap == null) {
resultMap = buildAutoResultMap(allowRemapping, statement, resultClass, xmlResultName);
statement.setResultMap(resultMap);
if (additionalResultClasses != null) {
for (int i = 0; i < additionalResultClasses.length; i++) {
statement.addResultMap(buildAutoResultMap(allowRemapping, statement, additionalResultClasses[i], xmlResultName));
}
}
}
statement.setTimeout(defaultStatementTimeout);
if (timeout != null) {
try {
statement.setTimeout(timeout);
} catch (NumberFormatException e) {
throw new SqlMapException("Specified timeout value for statement " + statement.getId() + " is not a valid integer");
}
}
errorContext.setMoreInfo(null);
errorContext.setObjectId(null);
statement.setSqlMapClient(client);
if (cacheModelName != null && cacheModelName.length() > 0 && client.getDelegate().isCacheModelsEnabled()) {//这个判断很关键
CacheModel cacheModel = client.getDelegate().getCacheModel(cacheModelName);
mappedStatement = new CachingStatement(statement, cacheModel);
} else {
mappedStatement = statement;
}
rootStatement = statement;
delegate.addMappedStatement(mappedStatement);
}上诉的if (cacheModelName != null && cacheModelName.length() > 0 && client.getDelegate().isCacheModelsEnabled())判断,直接决定了是否启用缓存,假设前面没有设置setting这个属性,该值是为空的,不存在默认值,故此时是不会启用缓存的。
总结:
当解析实际的配置文件sql-map-config.xml时,
如果设置了<setting>这个属性,则回调addSettingsNodelets里面的addNodelet里面的Nodelet的process方法。
设置里面的属性值,假如此时没有设置<setting>属性(注意,是没有设置,不是设置为空,因为设置为空,同样的调用回调,设置了默认值),则不再调用此回调函数,则此时将没有默认值,如果此时想启动缓存等,都是无效的。
故,此时应注意,如果想利用默认值,setting是必须设置的,哪怕设置为空。但,如果设置了setting后,文件的先后顺序是有要求的,如果A文件利用到b文件的命名空间的某个引用id,则此时必须使得B文件在A文件之前装载,否则将出现文件A引用文件B中的id时出现不存在的异常。