MyBatis的配置信息存储在XML配置文件中,使用Configuration对象作为一个所偶有配置信息的容器,Configuration对象的组织结构和XML配置文件的组织结构几乎完全一样,因此MyBatis初始化就是加载XML配置文件,创建Configuration对象的过程;
个人主页:tuzhenyu’s page
原文地址:深入理解MyBatis(二)—MyBatis初始化
String resource = "mybatis/config.xml";
InputStream is = Main.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder e = new XMLConfigBuilder(reader, environment, properties);
var5 = this.build(e.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException var13) {
;
}
}
return var5;
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
this.commonConstructor(validation, variables, entityResolver);
this.document = this.createDocument(new InputSource(inputStream));
}
createDocument()方法根据XML输入流创建Document实例的方法和Spring中将XML文件解析成Doucment方法相同,都是通过SAX的方法解析XML文档;
首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource()返回Document对象;在解析过程中调用”org.xml.sax”包下额SAX解析方法,创建DOM树;
解完的Document会存储在XPathParser实例对象中,XPathParser对象会存储在XMLConfigBuilder对象中,调用XMLConfigBuilder的parse()方法进行进一步解析;
private Document createDocument(InputSource inputSource) {
try {
DocumentBuilderFactory e = DocumentBuilderFactory.newInstance();
e.setValidating(this.validation);
e.setNamespaceAware(false);
e.setIgnoringComments(true);
e.setIgnoringElementContentWhitespace(false);
e.setCoalescing(false);
e.setExpandEntityReferences(true);
DocumentBuilder builder = e.newDocumentBuilder();
builder.setEntityResolver(this.entityResolver);
builder.setErrorHandler(new ErrorHandler() {
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception var4) {
throw new BuilderException("Error creating document instance. Cause: " + var4, var4);
}
}
XPathParser实例的parse()方法通过xpath的方法对XML进行解析,首先定位configuration标签作为根标签;
与Spring解析XML不同的是Spring仅仅通过SAX方法将XML文件解析成Document对象,然后对根节点下的标签进行遍历解析生成对应的BeanDefinitiion,而MyBatis在解析玩完Document后利用XPath对标签进行定位获取;
public Configuration parse() {
if(this.parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
} else {
this.parsed = true;
this.parseConfiguration(this.parser.evalNode("/configuration"));
return this.configuration;
}
}
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties e = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(e);
this.typeAliasesElement(root.evalNode("typeAliases"));
this.pluginElement(root.evalNode("plugins"));
this.objectFactoryElement(root.evalNode("objectFactory"));
this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
this.settingsElement(e);
this.environmentsElement(root.evalNode("environments"));
this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
this.typeHandlerElement(root.evalNode("typeHandlers"));
this.mapperElement(root.evalNode("mappers"));
} catch (Exception var3) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
private void mapperElement(XNode parent) throws Exception {
if(parent != null) {
Iterator var2 = parent.getChildren().iterator();
while(true) {
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
String resource;
if("package".equals(child.getName())) {
resource = child.getStringAttribute("name");
this.configuration.addMappers(resource);
} else {
resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
XMLMapperBuilder mapperParser;
InputStream mapperInterface1;
if(resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
mapperInterface1 = Resources.getResourceAsStream(resource);
mapperParser = new XMLMapperBuilder(mapperInterface1, this.configuration, resource, this.configuration.getSqlFragments());
mapperParser.parse();
} else if(resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
mapperInterface1 = Resources.getUrlAsStream(url);
mapperParser = new XMLMapperBuilder(mapperInterface1, this.configuration, url, this.configuration.getSqlFragments());
mapperParser.parse();
} else {
if(resource != null || url != null || mapperClass == null) {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
Class mapperInterface = Resources.classForName(mapperClass);
this.configuration.addMapper(mapperInterface);
}
}
}
return;
}
}
}
每一个mapper映射文件都会创建一个XMLMapperBuilder对象,调用其parse()方法进行单个映射文件mapper的解析;
主要是对mapper映射文件下的ResultMap,cache-ref和具体的sql操作语句(select,insert,update,delete)进行解析;
public void parse() {
if(!this.configuration.isResourceLoaded(this.resource)) {
this.configurationElement(this.parser.evalNode("/mapper"));
this.configuration.addLoadedResource(this.resource);
this.bindMapperForNamespace();
}
this.parsePendingResultMaps();
this.parsePendingCacheRefs();
this.parsePendingStatements();
}
private void configurationElement(XNode context) {
try {
String e = context.getStringAttribute("namespace");
if(e != null && !e.equals("")) {
this.builderAssistant.setCurrentNamespace(e);
this.cacheRefElement(context.evalNode("cache-ref"));
this.cacheElement(context.evalNode("cache"));
this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
this.resultMapElements(context.evalNodes("/mapper/resultMap"));
this.sqlElement(context.evalNodes("/mapper/sql"));
this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} else {
throw new BuilderException("Mapper\'s namespace cannot be empty");
}
} catch (Exception var3) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
}
}
private void buildStatementFromContext(List list, String requiredDatabaseId) {
Iterator var3 = list.iterator();
while(var3.hasNext()) {
XNode context = (XNode)var3.next();
XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);
try {
statementParser.parseStatementNode();
} catch (IncompleteElementException var7) {
this.configuration.addIncompleteStatement(statementParser);
}
}
}
public void parseStatementNode() {
String id = this.context.getStringAttribute("id");
String databaseId = this.context.getStringAttribute("databaseId");
if(this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
Integer fetchSize = this.context.getIntAttribute("fetchSize");
Integer timeout = this.context.getIntAttribute("timeout");
String parameterMap = this.context.getStringAttribute("parameterMap");
String parameterType = this.context.getStringAttribute("parameterType");
Class parameterTypeClass = this.resolveClass(parameterType);
String resultMap = this.context.getStringAttribute("resultMap");
String resultType = this.context.getStringAttribute("resultType");
String lang = this.context.getStringAttribute("lang");
LanguageDriver langDriver = this.getLanguageDriver(lang);
Class resultTypeClass = this.resolveClass(resultType);
String resultSetType = this.context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(this.context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = this.resolveResultSetType(resultSetType);
String nodeName = this.context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = this.context.getBooleanAttribute("flushCache", Boolean.valueOf(!isSelect)).booleanValue();
boolean useCache = this.context.getBooleanAttribute("useCache", Boolean.valueOf(isSelect)).booleanValue();
boolean resultOrdered = this.context.getBooleanAttribute("resultOrdered", Boolean.valueOf(false)).booleanValue();
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(this.configuration, this.builderAssistant);
includeParser.applyIncludes(this.context.getNode());
this.processSelectKeyNodes(id, parameterTypeClass, langDriver);
SqlSource sqlSource = langDriver.createSqlSource(this.configuration, this.context, parameterTypeClass);
String resultSets = this.context.getStringAttribute("resultSets");
String keyProperty = this.context.getStringAttribute("keyProperty");
String keyColumn = this.context.getStringAttribute("keyColumn");
String keyStatementId = id + "!selectKey";
keyStatementId = this.builderAssistant.applyCurrentNamespace(keyStatementId, true);
Object keyGenerator;
if(this.configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = this.configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = this.context.getBooleanAttribute("useGeneratedKeys", Boolean.valueOf(this.configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))).booleanValue()?Jdbc3KeyGenerator.INSTANCE:NoKeyGenerator.INSTANCE;
}
this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
}
public class BoundSql {
private String sql;
private List parameterMappings;
private Object parameterObject;
private Map additionalParameters;
private MetaObject metaParameters;
public BoundSql(Configuration configuration, String sql, List parameterMappings, Object parameterObject) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.parameterObject = parameterObject;
this.additionalParameters = new HashMap();
this.metaParameters = configuration.newMetaObject(this.additionalParameters);
}
public String getSql() {
return this.sql;
}
public List getParameterMappings() {
return this.parameterMappings;
}
public Object getParameterObject() {
return this.parameterObject;
}
public boolean hasAdditionalParameter(String name) {
String paramName = (new PropertyTokenizer(name)).getName();
return this.additionalParameters.containsKey(paramName);
}
public void setAdditionalParameter(String name, Object value) {
this.metaParameters.setValue(name, value);
}
public Object getAdditionalParameter(String name) {
return this.metaParameters.getValue(name);
}
}
MyBatis的初始化主要是加载解析XML配置文件,根据解析结果创建Configuration对象;将创建好的Configuration对象装载生成SqlSessionFactory实例对象;
加载mappers标签过程中,会对每个mapper映射文件创建一个XMLMapperBuilder对象进行解析,在单个mapper映射文件解析过程中,会对每个sql操作标签创建一个XMLStatementBuilder对象用于解析,解析每一个sql操作生成对应的MapperStatement对象用于后续的操作;