1 读取 Mybatis-config 配置文件
InputStream inputStream = new FileInputStream
( "D:\\Users\\Administrator\\IdeaProjects\\mybatis_demo\\src\\main\\resources\\Mybatis-config.xml" ) ;
2 构建 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder ( ) . build ( inputStream) ;
3 进入 build() 方法内部
类 : SqlSessionFactoryBuilder 作用 : 构建SqlSessionFactory 方法: build()
public SqlSessionFactory build ( InputStream inputStream) {
return build ( inputStream, null, null) ;
}
public SqlSessionFactory build ( InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder ( inputStream, environment, properties) ;
return build ( parser. parse ( ) ) ;
} catch ( Exception e) {
throw ExceptionFactory. wrapException ( "Error building SqlSession." , e) ;
} finally {
ErrorContext. instance ( ) . reset ( ) ;
try {
inputStream. close ( ) ;
} catch ( IOException e) {
}
}
}
3.1 进入 XMLConfigBuilder 构造器内部
类 : XMLConfigBuilder 作用 : 负责解析mybatis-config.xml 构造器
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private XPathParser parser;
private String environment;
private ReflectorFactory localReflectorFactory = new DefaultReflectorFactory ( ) ;
public XMLConfigBuilder ( InputStream inputStream, String environment, Properties props) {
this ( new XPathParser ( inputStream, true , props, new XMLMapperEntityResolver ( ) ) , environment, props) ;
}
}
3.1.1 进入 XPathParser 构造器内部
类 : XPathParser 作用 : 读取 xml 文件解析为文档对象 构造器
public class XPathParser {
private Document document;
private boolean validation;
private EntityResolver entityResolver;
private Properties variables;
private XPath xpath;
public XPathParser ( InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor ( validation, variables, entityResolver) ;
this . document = createDocument ( new InputSource ( inputStream) ) ;
}
}
3.1.2 进入 createDocument() 方法
类 : XPathParser 方法 : createDocument() 作用 : 解析 xml 文件为 Document 对象后赋值到 document 属性中
private Document createDocument ( InputSource inputSource) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory. newInstance ( ) ;
factory. setValidating ( validation) ;
factory. setNamespaceAware ( false ) ;
factory. setIgnoringComments ( true ) ;
factory. setIgnoringElementContentWhitespace ( false ) ;
factory. setCoalescing ( false ) ;
factory. setExpandEntityReferences ( true ) ;
DocumentBuilder builder = factory. newDocumentBuilder ( ) ;
builder. setEntityResolver ( entityResolver) ;
builder. setErrorHandler ( new ErrorHandler ( ) {
@Override
public void error ( SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError ( SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning ( SAXParseException exception) throws SAXException {
}
} ) ;
return builder. parse ( inputSource) ;
} catch ( Exception e) {
throw new BuilderException ( "Error creating document instance. Cause: " + e, e) ;
}
}
3.1 总结 : 此时 XMLConfigBuilder 对象已经产生
3.1 过程完成了解析mybatis-config.xml —>document对象 XmlConfigBuilder 属性对象中 XPathParser 属性对象中 document 属性中已经封装了 mybatis-config.xml 配置文件 接下来将在 3 中 XMLConfigBuilder 对象调用 parse() 方法将 document 配置到Configuration 对象中
3.2 回到 parse() 方法
类 : XMLConfigBuilder 方法 : parse() 作用 : 解析 document 对象 (mybatis-config.xml) 封装到 Configuration 对象中
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private XPathParser parser;
. . .
public Configuration parse ( ) {
if ( parsed) {
throw new BuilderException ( "Each XMLConfigBuilder can only be used once." ) ;
}
parsed = true ;
parseConfiguration ( parser. evalNode ( "/configuration" ) ) ;
return configuration;
}
3.2.1 parseConfiguration() 解析 document 对象到 Configuration 对象过程
类 : XMLConfigBuilder 方法 : parseConfiguration() 作用 : 每个方法都是解析各个节点封装到 Configuration 对象中的过程
private void parseConfiguration ( XNode root) {
try {
Properties settings = settingsAsPropertiess ( root. evalNode ( "settings" ) ) ;
propertiesElement ( root. evalNode ( "properties" ) ) ;
loadCustomVfs ( settings) ;
typeAliasesElement ( root. evalNode ( "typeAliases" ) ) ;
pluginElement ( root. evalNode ( "plugins" ) ) ;
objectFactoryElement ( root. evalNode ( "objectFactory" ) ) ;
objectWrapperFactoryElement ( root. evalNode ( "objectWrapperFactory" ) ) ;
reflectorFactoryElement ( root. evalNode ( "reflectorFactory" ) ) ;
settingsElement ( settings) ;
environmentsElement ( root. evalNode ( "environments" ) ) ;
databaseIdProviderElement ( root. evalNode ( "databaseIdProvider" ) ) ;
typeHandlerElement ( root. evalNode ( "typeHandlers" ) ) ;
mapperElement ( root. evalNode ( "mappers" ) ) ;
} catch ( Exception e) {
throw new BuilderException ( "Error parsing SQL Mapper Configuration. Cause: " + e, e) ;
}
}
3.2.2 mapperElement() 重点看一下解析 Mapper.xml 文件过程
类 : XMLConfigBuilder 方法 : mapperElement() 作用 : 查找 mybatis-config 中配置的< mappers/ >标签,即 Mapper.xml
private void mapperElement ( XNode parent) throws Exception {
if ( parent != null) {
for ( XNode child : parent. getChildren ( ) ) {
if ( "package" . equals ( child. getName ( ) ) ) {
String mapperPackage = child. getStringAttribute ( "name" ) ;
configuration. addMappers ( mapperPackage) ;
} else {
String resource = child. getStringAttribute ( "resource" ) ;
String url = child. getStringAttribute ( "url" ) ;
String mapperClass = child. getStringAttribute ( "class" ) ;
if ( resource != null && url == null && mapperClass == null) {
ErrorContext. instance ( ) . resource ( resource) ;
InputStream inputStream = Resources. getResourceAsStream ( resource) ;
XMLMapperBuilder mapperParser = new XMLMapperBuilder ( inputStream, configuration, resource, configuration. getSqlFragments ( ) ) ;
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 ( ) ) ;
mapperParser. parse ( ) ;
} else if ( resource == null && url == null && mapperClass != null) {
Class< ? > mapperInterface = Resources. classForName ( mapperClass) ;
configuration. addMapper ( mapperInterface) ;
} else {
throw new BuilderException ( "A mapper element may only specify a url, resource or class, but not more than one." ) ;
}
}
}
}
}
3.2 总结 :
3.2 将解析 mybatis-config.xml 产生的document 对象配置到 Configuration 对象中 接下来开始扫描 Mapper.xml 文件并解析封装到 Configuration 对象中
4.1 addMappers() 使用包扫描时注册接口方法
类 : Configuration 方法 : addMappers() 作用 : 注册和获取Mapper对象的代理
public void addMappers ( String packageName) {
mapperRegistry. addMappers ( packageName) ;
}
4.1.1 addMapper() 具体注册接口方法
类 : MapperRegistry 方法 : addMapper() 作用 : 注册接口,为该接口生成代理对象 注 : 此处省略扫描包内所有接口的过程 , 直接看注册接口具体实现
public class MapperRegistry {
private final Configuration config;
private final Map< Class< ? > , MapperProxyFactory< ? >> knownMappers = new HashMap < Class< ? > , MapperProxyFactory< ? >> ( ) ;
public < T> void addMapper ( Class< T> type) {
if ( type. isInterface ( ) ) {
if ( hasMapper ( type) ) {
throw new BindingException ( "Type " + type + " is already known to the MapperRegistry." ) ;
}
boolean loadCompleted = false ;
try {
knownMappers. put ( type, new MapperProxyFactory < T> ( type) ) ;
MapperAnnotationBuilder parser = new MapperAnnotationBuilder ( config, type) ;
parser. parse ( ) ;
loadCompleted = true ;
} finally {
if ( ! loadCompleted) {
knownMappers. remove ( type) ;
}
}
}
}
}
4.1.1.1 parse() 解析 Mapper.xml 方法
类 : MapperAnnotationBuilder 方法 : parse() 作用 : 解析映射文件或者注解方法
public class MapperAnnotationBuilder {
private final Set< Class< ? extends Annotation >> sqlAnnotationTypes = new HashSet < Class< ? extends Annotation >> ( ) ;
private final Set< Class< ? extends Annotation >> sqlProviderAnnotationTypes = new HashSet < Class< ? extends Annotation >> ( ) ;
private Configuration configuration;
private MapperBuilderAssistant assistant;
private Class< ? > type;
public void parse ( ) {
String resource = type. toString ( ) ;
if ( ! configuration. isResourceLoaded ( resource) ) {
loadXmlResource ( ) ;
configuration. addLoadedResource ( resource) ;
assistant. setCurrentNamespace ( type. getName ( ) ) ;
parseCache ( ) ;
parseCacheRef ( ) ;
Method[ ] methods = type. getMethods ( ) ;
for ( Method method : methods) {
try {
if ( ! method. isBridge ( ) ) {
parseStatement ( method) ;
}
} catch ( IncompleteElementException e) {
configuration. addIncompleteMethod ( new MethodResolver ( this , method) ) ;
}
}
}
parsePendingMethods ( ) ;
}
}
4.1.1.2 loadXmlResource() 加载Mapper.xml资源方法
private void loadXmlResource ( ) {
if ( ! configuration. isResourceLoaded ( "namespace:" + type. getName ( ) ) ) {
String xmlResource = type. getName ( ) . replace ( '.' , '/' ) + ".xml" ;
InputStream inputStream = null;
try {
inputStream = Resources. getResourceAsStream ( type. getClassLoader ( ) , xmlResource) ;
} catch ( IOException e) {
}
if ( inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder ( inputStream, assistant. getConfiguration ( ) , xmlResource, configuration. getSqlFragments ( ) , type. getName ( ) ) ;
xmlParser. parse ( ) ;
}
}
}
4.1.1.2.1 XMLMapperBuilder 构造器
类 : XMLMapperBuilder 方法 : 构造器 对比 :
XMLConfigBuilder : 读取 mybatis-config.xml 到 XPathParser 对象的 document 中 XMLMapperBuilder : 读取 Mapper.xml 到 XPathParser 对象的 document 中
相同点 : 都通过 prase() 方法解析 document 对象到 configuration 对象中
public class XMLMapperBuilder extends BaseBuilder {
private XPathParser parser;
private MapperBuilderAssistant builderAssistant;
private Map< String, XNode> sqlFragments;
private String resource;
public XMLMapperBuilder ( InputStream inputStream, Configuration configuration, String resource, Map< String, XNode> sqlFragments) {
this ( new XPathParser ( inputStream, true , configuration. getVariables ( ) , new XMLMapperEntityResolver ( ) ) ,
configuration, resource, sqlFragments) ;
}
4.1.1.2.1.1 进入 XPathParser 构造器内部
类 : XPathParser 作用 : 读取 xml 文件解析为文档对象 构造器
public XPathParser ( InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor ( validation, variables, entityResolver) ;
this . document = createDocument ( new InputSource ( inputStream) ) ;
}
4.1 总结:
Mapper.xml 已经解析为 Document 对象 接下来准备解析 Document 对象配置到 Configuration对象中
4.2 回到4.1.1.2 parse() 方法
类 : XMLMapperBuilder 方法 : parse() 作用 : 将 Document 对象解析配置到 Configuration 对象中
public void parse ( ) {
if ( ! configuration. isResourceLoaded ( resource) ) {
configurationElement ( parser. evalNode ( "/mapper" ) ) ;
configuration. addLoadedResource ( resource) ;
bindMapperForNamespace ( ) ;
}
parsePendingResultMaps ( ) ;
parsePendingChacheRefs ( ) ;
parsePendingStatements ( ) ;
}
4.2.1 configurationElement() 配置Mapper中元素
类 : XMLMapperBuilder 方法 : configurationElement() 作用 : 各个方法都是解析Mapper.xml 的各个节点, 并配置到 configuration 中
private void configurationElement ( XNode context) {
try {
String namespace = context. getStringAttribute ( "namespace" ) ;
if ( namespace == null || namespace. equals ( "" ) ) {
throw new BuilderException ( "Mapper's namespace cannot be empty" ) ;
}
builderAssistant. setCurrentNamespace ( namespace) ;
cacheRefElement ( context. evalNode ( "cache-ref" ) ) ;
cacheElement ( context. evalNode ( "cache" ) ) ;
parameterMapElement ( context. evalNodes ( "/mapper/parameterMap" ) ) ;
resultMapElements ( context. evalNodes ( "/mapper/resultMap" ) ) ;
sqlElement ( context. evalNodes ( "/mapper/sql" ) ) ;
buildStatementFromContext ( context. evalNodes ( "select|insert|update|delete" ) ) ;
} catch ( Exception e) {
throw new BuilderException ( "Error parsing Mapper XML. Cause: " + e, e) ;
}
}
4.2.1.1 cacheRefElement ("cache-ref ")
元素存放容器: 解析完的 cache-ref 放在 cacheRefMap ( HashMap ) 中 具体存放类 : Configuration对象中
Key : mapper 文件的 namespace Value : < cache-ref > 中配置的namespace
4.2.1.2 cacheElement (“cache”)
根据< cache >配置的属性创建一个缓存对象,使用该对象作为 mybatis 缓存
4.2.1.3 parameterMapElement (“parameterMap”)
元素存放容器: 解析完的ParameterMap放在 parameterMaps (即StrictMap )中 具体存放类 : Configuration对象中
Key : mapper 的 namespace +"."+< parameterMap>标签中的 id 属性 Value : ParameterMap对象
4.2.1.4 resultMapElements(“resultMap”)
元素存放容器: 解析完的ResultMap放在 resultMaps (即StrictMap )中 具体存放类 : Configuration对象中
Key : mapper 的 namespace +"."+< resultMap>标签中的 id 属性 Value : ResultMap对象
4.2.1.5 sqlElement(“sql”)
元素存放容器: 解析完的 ResultMap 放在 resultMaps (即StrictMap )中 具体存放类 : XMLMapperBuilder对象中
Key : mapper 的 namespace +"."+< sql>标签中的 id 属性 Value : ResultMap对象
4.2.1.6 buildStatementFromContext(“select|insert|update|delete”)
元素存放容器: 解析完的 MappedStatement 对象 放在 mappedStatements(即StrictMap )中 具体存放类 : Configuration对象中
Key : mapper的namespace+"."+< select>|< insert>|< update>|< delete>标签中的id属性 Value : MappedStatement 对象
4.2 总结
已经将 Mapper.xml 生成的 Document 对象内容配置到 Configuration 对象中 接下来,详细看下解析sql语句生成MappedStatement 对象过程
4.3 buildStatementFromContext() 构建 MappedStatement 对象
类 : XMLMapperBuilder 方法 : buildStatementFromContext("select | insert | update | delete ") 作用 : 从 CURD节点中构建 statement对象
private void buildStatementFromContext ( List< XNode> list, String requiredDatabaseId) {
for ( XNode context : list) {
final XMLStatementBuilder statementParser = new XMLStatementBuilder ( configuration, builderAssistant, context, requiredDatabaseId) ;
try {
statementParser. parseStatementNode ( ) ;
} catch ( IncompleteElementException e) {
configuration. addIncompleteStatement ( statementParser) ;
}
}
}
4.3.1 parseStatementNode() 解析CURD节点内属性
类 : XMLStatementBuilder 方法 : buildStatementFromContext("select | insert | update | delete ") 作用 : 从CURD各个节点中解析出各自的属性
public class XMLStatementBuilder extends BaseBuilder {
private MapperBuilderAssistant builderAssistant;
private XNode context;
private String requiredDatabaseId;
public void parseStatementNode ( ) {
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" ) ;
Class< ? > parameterTypeClass = resolveClass ( parameterType) ;
String resultMap = context. getStringAttribute ( "resultMap" ) ;
String resultType = context. getStringAttribute ( "resultType" ) ;
String lang = context. getStringAttribute ( "lang" ) ;
LanguageDriver langDriver = getLanguageDriver ( lang) ;
Class< ? > resultTypeClass = resolveClass ( resultType) ;
String resultSetType = context. getStringAttribute ( "resultSetType" ) ;
StatementType statementType = StatementType. valueOf ( context. getStringAttribute ( "statementType" , StatementType. PREPARED. toString ( ) ) ) ;
ResultSetType resultSetTypeEnum = resolveResultSetType ( resultSetType) ;
String nodeName = context. getNode ( ) . getNodeName ( ) ;
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 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) )
? new Jdbc3KeyGenerator ( ) : new NoKeyGenerator ( ) ;
}
builderAssistant. addMappedStatement ( id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets) ;
}
4.3.2 addMappedStatement() 创建 MappedStatement对象
类 : MapperBuilderAssistant 方法 : addMappedStatement() 作用 : 传递参数,创建对应的MappedStatement 对象
public MappedStatement addMappedStatement (
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class< ? > parameterType,
String resultMap,
Class< ? > resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if ( unresolvedCacheRef) {
throw new IncompleteElementException ( "Cache-ref not yet resolved" ) ;
}
id = applyCurrentNamespace ( id, false ) ;
boolean isSelect = sqlCommandType == SqlCommandType. SELECT;
MappedStatement. Builder statementBuilder = new MappedStatement. Builder ( configuration, id, sqlSource, sqlCommandType)
. resource ( resource)
. fetchSize ( fetchSize)
. timeout ( timeout)
. statementType ( statementType)
. keyGenerator ( keyGenerator)
. keyProperty ( keyProperty)
. keyColumn ( keyColumn)
. databaseId ( databaseId)
. lang ( lang)
. resultOrdered ( resultOrdered)
. resultSets ( resultSets)
. resultMaps ( getStatementResultMaps ( resultMap, resultType, id) )
. resultSetType ( resultSetType)
. flushCacheRequired ( valueOrDefault ( flushCache, ! isSelect) )
. useCache ( valueOrDefault ( useCache, isSelect) )
. cache ( currentCache) ;
ParameterMap statementParameterMap = getStatementParameterMap ( parameterMap, parameterType, id) ;
if ( statementParameterMap != null) {
statementBuilder. parameterMap ( statementParameterMap) ;
}
MappedStatement statement = statementBuilder. build ( ) ;
configuration. addMappedStatement ( statement) ;
return statement;
}
4.3 总结
4.3 过程完成了 MappedStatement 对象创建并存放在 Configuration 对象中
总结
读取mybatis-config.xml 配置文件时,会将该配置文件配置属性封装到configuration对象中, 并且将Mapper.xml 中sql 解析为一个个MappedStatement 对象,放入configuration对象中 ,实际就是将mybatis 的所有配置文件解析封装到 configuration 对象内部