目录
3.解析配置文件、将配置文件中的信息装载到Configuration中
4.根据Configuration创建SqlSessionFactory并返回
1.初始化阶段整体流程
前面几篇分析了MyBatis的日志、数据源和缓存模块的源码,本篇将分析MyBatis核心流程三大阶段的第一阶段:初始化阶段。MyBatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中,Configuration为单例,生命周期为应用级。MyBatis初始化流程大致有三步:
1)加载配置文件
2)解析配置文件、将配置文件中的信息装载到Configuration中。
3)根据Configuration创建SqlSessionFactory并返回。
2.加载配置文件
下面我们来看一段经典查询操作:
String resouce = "config/mybatis/mybatis-config.xml";
InputStream is = Resources.getResourceAsStream(resouce);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = sqlSessionFactory.openSession();
user = session.selectOne("com.luoxn28.dao.UserDao.getById", 1);
以上代码经过了MyBatis初始化、创建sqlSession、执行sql语句3个过程。首先由mybatis-config.xml配置文件创建SqlSessionFactory,然后由session工厂创建SqlSession对象,执行SQL语句。当然初始化的第一阶段:扫描配置文件所在包路径并加载。
3.解析配置文件、将配置文件中的信息装载到Configuration中
让我们来看一下梦开始的地方:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);
跟进build()方法,我们可以看到new了一个XMLConfigBuilder对象并调用了parse()方法:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//创建XMLConfigBuilder对象解析XML配置
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
//将XML配置解析成Configuration对象,通过Configuration对象创建SqlSessionFactory
return build(parser.parse());
}
....
}
跟进parse()方法,我们可以看到parser.evalNode("/configuration"),evalNode为xml结点解析器,可以解析指定参数结点的信息,再看这个"/configuration"有点眼熟丫,这不就是mybatis.xml的根节点嘛:
public Configuration parse() {
...
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
看到这,我们就不得不提初始化的三大金刚了,分别是XMLConfigBuilder、XMLMapperBuilder、XMLStatementBuilder。
XMLConfigBuilder:主要负责解析mybatis-config.xml
XMLMapperBuilder:主要负责解析映射配置文件
XMLStatementBuilder:主要负责解析映射配置文件中的sql节点
三大金刚图解:
MyBatis中的xml文件是由三大金刚读取到Configuration类中,那么我们来看下Configuration类的数据结构:
Configuration类的源码实在太多,童鞋们先对这个类有个大致印象,了解下该类中有哪些成员变量对应存储着些什么数据。下面主要列举几个比较重要的成员变量:
MapperRegistry:mapper接口动态代理工厂类的注册中心。通过mapperProxy实现InvocationHandler接口,其中的MapperProxyFactory用于生成动态代理的实例对象;
ResultMap:用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素;
MappedStatement:用于存储mapper.xml文件中select、insert、update和delete节点,同时还包含了这些节点的重要属性;
SqlSource:mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;
接上面XMLConfigBuilder开始解析"/configuration"节点:
parseConfiguration(parser.evalNode("/configuration"));
private void parseConfiguration(XNode root) {
try {
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"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
点进去一看,就是对照着MyBatis官网主配置文件中的元素一个一个的进行解析
在解析"mappers"节点的时候,就引入了XMLMapperBuilder开始对映射配置文件进行解析
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
...
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. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
对应官网提供的节点信息进行解析
下面大家猜也猜到了,在解析具体select、insert、update、delete的时候,引入了XMLStatementBuilder对节点数据进行解析:
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);
}
}
}
继三大金刚分别解析自己负责的xml文件之后,Configuration对象的数据被填充完毕,初始化的第二阶段:解析配置文件,将数据装载进Configuration对象完成。
4.根据Configuration创建SqlSessionFactory并返回
第三阶段就是根据SqlSessionFactoryBuilder的内部方法直接返回一个DefaultSqlSessionFactory:
public class SqlSessionFactoryBuilder {
...
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
此工厂内封装了Configuration对象:
public class DefaultSqlSessionFactory implements SqlSessionFactory {
private final Configuration configuration;
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
...
}
初始化阶段图解:
至此,MyBatis初始化阶段完成。