10.MyBatis源码分析笔记–通过Debug分析源码01
宏观认识 MyBatis
MyBatis 本质上是一个 ORM 框架(Object Relational Mapping),用于实现面向对象编程语言里不同类型系统的数据之间的转换。
整体运行流程
MyBatis 处理流程图
微观分析 MyBatis 源码
Mybatis 如何获取数据库源
为 org.apache.ibatis.demo.MybatisMain 中的以下代码设置断点:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
进入 Debug 模式。
Debug 流程
-
进入断点
-
Step Into(F7)
-
Step Into(F7)
-
Step Into(F7)
-
Step Into(F7)
-
Step Over(F8)
-
Step Into(F7): parse
-
Step Over(F8)
-
Step Over(F8)
-
Step Into(F7): evalNode
-
XNode 对象属性
-
private final Node node; private final String name; private final String body; private final Properties attributes; private final Properties variables; private final XPathParser xpathParser;
-
-
Step Out(Shift + F8)
-
Step Into(F7): parseConfiguration
-
查看 parseConfiguration(XNode root) 中传入的 root 的值
-
Evaluate Expression
-
Evaluate
-
Copy Value
-
实际上就是 mybatis-config.xml 配置文件中 configuration 标签下的内容
-
<configuration> <properties resource="config.properties"> </properties> <typeAliases> <typeAlias alias="user" type="org.apache.ibatis.demo.pojo.User"/> </typeAliases> <plugins> <plugin interceptor="org.apache.ibatis.demo.plugin.SqlPrintInterceptor"/> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
-
-
-
解析 configuration 标签下的内容
-
//org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration //--- parseConfiguration(parser.evalNode("/configuration")); //--- //org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration //解析 configuration 标签下的内容 //--- propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); mapperElement(root.evalNode("mappers")); //---
-
configuration 标签下的标签(参考:https://mybatis.org/mybatis-3/configuration.html)
- properties
- settings
- typeAliases
- typeHandlers
- objectFactory
- plugins
- environments
- databaseIdProvider
- mappers
-
-
Step Into(F7): propertiesElement
-
查看 propertiesElement(XNode context) 中传入的 context 的值
-
实际上就是 mybatis-config.xml 配置文件中 properties 标签下的内容
-
<properties resource="config.properties"> </properties>
-
通过 propertiesElement 方法将 config.properties 中的内容以键值对的形式存入到 parser(org.apache.ibatis.parsing.XPathParser) 和 configuration(org.apache.ibatis.session.Configuration) 中
-
if (context != null) { //java.util.Properties //public class Properties extends Hashtable<Object,Object> Properties defaults = context.getChildrenAsProperties(); String resource = context.getStringAttribute("resource"); String url = context.getStringAttribute("url"); if (resource != null && url != null) { throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other."); } if (resource != null) { //java.util.Properties#putAll //public synchronized void putAll(Map<?, ?> t) defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); } //org.apache.ibatis.session.Configuration#getVariables //public Properties getVariables() Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } //org.apache.ibatis.parsing.XPathParser //private final XPathParser parser parser.setVariables(defaults); //org.apache.ibatis.session.Configuration //protected final Configuration configuration configuration.setVariables(defaults); }
-
-
-
-
Step Into(F7): environmentsElement
-
查看 environmentsElement(XNode context) 中传入的 context 的值
-
实际上就是 mybatis-config.xml 配置文件中 environments 标签下的内容
-
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_learn?characterEncoding=utf8&serverTimezone=UTC&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="root123456"/> </dataSource> </environment> </environments>
-
-
-
Resume Programe(F9)
-
Step Into: dataSourceElement
-
查看 dataSourceElement(XNode context) 中传入的 context 的值
-
<dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis_learn?characterEncoding=utf8&serverTimezone=UTC&useSSL=false"/> <property name="username" value="root"/> <property name="password" value="root123456"/> </dataSource>
-
-
通过 dataSourceElement 方法解析 dataSource 标签下的内容
-
if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory.");
-
props 的内容
-
-
Step Into: resolveClass
-
Step Over
-
Step Into
-
Step Into
-
Step Over
-
Step Over
-
Step Over
-
Step Over
-
Evaluate Expression: typeAliases.get(key)
-
class org.apache.ibatis.datasource.pooled.PooledDataSourceFactory
-
-
Step Over
-
Step Over
-
Step Over
-
Step Over
- 通过 getDeclaredConstructor 方法获取 PooledDataSourceFactory 类的制定参数类型的所有构造器,包括 public 的和非 public 的,当然也包括 private 的。
- 通过 newInstance 方法将 PooledDataSourceFactory 类变成一个对象。
-
Step Over
-
Step Over
-
Step Over
-
Step Into
- 返回当前的数据库源
-
Step Over
-
Step Over
-
Evaluate Expression: dataSource
-
通过
for (XNode child : context.getChildren())
循环,说明 dataSource 可以配置多个。 -
Step Over
-
Step Into: setEnvironment
-
org.apache.ibatis.mapping.Environment 的属性
-
private final String id; private final TransactionFactory transactionFactory; private final DataSource dataSource;
-
与 mybatis-config.xml 的属性一一对应
-
<environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment>
-
-
-
Step Over
-
Step Over
-
Step Over
MyBatis 处理流程总结
├─org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
│ ├─org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
│ │ ├─org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration
│ │ │ ├─org.apache.ibatis.builder.xml.XMLConfigBuilder.propertiesElement
│ │ │ │ ├─org.apache.ibatis.session.Configuration.setVariables
│ │ │ ├─org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement
│ │ │ │ ├─org.apache.ibatis.session.Configuration.setEnvironment
│ │ │ │ │ ├─org.apache.ibatis.builder.xml.XMLConfigBuilder.dataSourceElement
│ │ │ │ │ │ ├─org.apache.ibatis.builder.BaseBuilder.resolveClass
│ │ │ │ │ │ │ ├─org.apache.ibatis.builder.BaseBuilder.resolveAlias
│ │ │ │ │ │ │ │ ├─org.apache.ibatis.type.TypeAliasRegistry.resolveAlias
│ │ │ │ │ │ ├─java.lang.Class.getDeclaredConstructor
│ │ │ │ │ │ ├─java.lang.reflect.Constructor.newInstance
│ │ │ │ │ │ ├─org.apache.ibatis.datasource.DataSourceFactory.setProperties