Mybatis 如何创建出SqlSessionFactory
本篇文章是通过看视频学习总结的内容, 如有错误的地方请谅解,并联系博主及时修改,谢谢您的阅读.
源码地址: mybatis 中文注释版
从上一篇延申到本篇博客 《Mybatis(一) - Mybatis 最原始是使用方式》
- 1 从Junit 测试类进入Mybatis 源码中
@Before
public void prepare() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 进入 build 方法
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
- 2 进入 build 方法, 这里不难看出 XMLConfigBuilder使用来解析mybatis-config.xml文件的
// 进入到 build 方法
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// 用于解析 mybatis-config.xml,同时创建了 Configuration 对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// 解析XML,最终返回一个 DefaultSqlSessionFactory
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
- 3 进入到 XMLConfigBuilder 类中,手动创建了Configuration对象,这个对象好像很熟悉,不就是 mybatis-config.xml 的root节点吗
// 进入到 XMLConfigBuilder 类中,
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 手动new出了 Configuration 对象,
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
- 4 那么我们继续进入到
org.apache.ibatis.session.Configuration
对象中,一探究竟,在这里我只放了一部成员变量的代码,在这里面会发现,定义了很多变量,例如cacheEnabled=true
打开二级缓存的标志,原来Mybatis 在这里设置了默认值。因为在XMLConfigBuilder
类中,调用了 Configuration 对象的无参构造器,那么无参构造器中究竟干了什么事情。
public class Configuration implements Serializable {
protected Environment environment;
protected boolean safeRowBoundsEnabled;
protected boolean safeResultHandlerEnabled = true;
protected boolean mapUnderscoreToCamelCase;
protected boolean aggressiveLazyLoading;
protected boolean multipleResultSetsEnabled = true;
protected boolean useGeneratedKeys;
protected boolean useColumnLabel = true;
protected boolean cacheEnabled = true;
protected boolean callSettersOnNulls;
protected boolean useActualParamName = true;
protected boolean returnInstanceForEmptyRow;
....
}
- 5 Configuration 无参构造器,全局的设置了类的别名,例如
LruCache.class
全局上下问中就可以使用 LRU来表示淘汰机制的缓存实现
public Configuration() {
// 为类型注册别名
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
- 6 紧接着回到 SqlSessionFactory 类的 build方法中, 找到
build(parser.parse());
进入parser.parse()
中,可以很明确的发现,这里开始正式解析 xml文件到 Configuration 对象中,里面包含了对mapper.xml
文件、自定义类的别名、插件、数据源…等等解析
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 拿到 root(configuration) 节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
// 解析root节点下所有节点
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 对于全局配置文件各种标签的解析
propertiesElement(root.evalNode("properties"));
// 解析 settings 标签
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"));
// settings 子标签赋值,默认值就是在这里提供的 >>
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// 创建了数据源 >>
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析引用的Mapper映射器
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
- 7 上一步是 找到
build(parser.parse());
进入parser.parse()
中,而这一步则是进入build()
方法中,看看Mybatis是怎么创建 SqlSessionFactory 出来的,我靠,居然是直接new 出一个默认的SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
由此可见,Mybatis 在初始化的时候,完成sql与statementId映射关系,也包含POJO的与Class的映射关系,总而言之,在Configuration初始化完毕后所有的初始化动作将完毕。主要初始化动作是在org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration()
方法中。