1.使用JAVA API 方式构建
JAVA API初始化的方式虽然不常用,但是相较于XML的方式可以更清楚的看到Configuration的构成,其示例如下:
PooledDataSource dataSource = new PooledDataSource();
dataSource.setDriver("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=GMT");
dataSource.setUsername("root");
dataSource.setPassword("root");
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(UmsMemberMapper.class);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
2.使用XML方式构建
2.1.获取InputStream
【代码2-1】
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream("/mybatis-config.xml");
} catch (IOException e) {
e.printStackTrace();
}
在代码2-1中将配置文件的路径传递给了Resource中的getResourceAsStream方法。我们下面来看看getResourceAsStream方法的详情
【代码2-2】org.apache.ibatis.io.Resources
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream((ClassLoader)null, resource);
}
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
} else {
return in;
}
}
代码【2-3】org.apache.ibatis.io.ClassLoaderWrapper
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
return this.getResourceAsStream(resource, this.getClassLoaders(classLoader));
}
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
ClassLoader[] var3 = classLoader;
int var4 = classLoader.length;
for(int var5 = 0; var5 < var4; ++var5) {
ClassLoader cl = var3[var5];
if (null != cl) {
InputStream returnValue = cl.getResourceAsStream(resource);
if (null == returnValue) {
returnValue = cl.getResourceAsStream("/" + resource);
}
if (null != returnValue) {
return returnValue;
}
}
}
return null;
}
代码2-2的 getResourceAsStream方法都在org.apache.ibatis.io中的Resource类中,代码2-1中的代码调用了Resource类中的getResourceAsStream方法,Resource类中的getResourceAsStream方法最终调用了ClassLoaderWrapper的getResourceAsStream(String resource, ClassLoader[] classLoader),根据 配置文件的路径获取到配置文件的输入流。代码2-3给出了该方法的源码。
代码2-3的getResourceAsStream方法会依次调用传入的每一个类加载器的getResourceAsStream方法来尝试获取配置文件的输入流。在尝试过程中如果失败的话,会在传入的地址前加入"/" 再试一次,只要尝试成功,即表明成功加载了指定的资源,会将所获得的输入流返回。
整个过程中涉及的Resource类和ClassLoadWrapper均在Mybatis的io包中,这也印证了Resource类和ClassLoaderWrapper类是负责读写外部文件的。
补充说明:类加载器具有读取外部资源的能力,所以这里要借助的是类加载器的这种能力
2.2.配置信息读取
读取inputStream之后,然后进行的代码2-4的操作
【代码2-4】
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
代码2-4首先构造了一个SqlSessionFactoryBuilder对象,然后调用SqlSessionFactoryBuilder对象的build。然后返回SqlSessionFactory对象,build方法是有多个的,其中的核心代码如代码2-5所示
【代码2-5】
public SqlSessionFactory build(InputStream inputStream) {
return this.build((InputStream)inputStream, (String)null, (Properties)null);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
SqlSessionFactory var5;
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
} catch (Exception var14) {
throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException var13) {
}
}
return var5;
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
build(InputStream inputStream, String environment, Properties properties)方法最核心的部分如代码2-6所示
【代码2-6】
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
var5 = this.build(parser.parse());
这两行代码进行了三步操作
1.生成了一个XMLConfigBuilder对象
2.调用XMLConfigBuilder对象的parse方法,得到一个Configuration对象(因为parse方法的返回值是Configuration对象)
3.调用了SqlSessionFactoryBuilder的build方法,传入参数为Configuration对象
我们对上述三步进行追踪,首先找到XMLConfigBuilder类的parse方法,如代码2-7所示
【代码2-7】
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;
}
}
代码2-7核心部分为 this.parseConfiguration(this.parser.evalNode("/configuration"));,因为configuration节点是mybatis配置文件的根节点,因此这里是解析整个配置文件的入口。而parseConfiguration是解析配置文件的入口,parseConfiguration方法的详情如代码【2-8】所示
【代码2-8】
private void parseConfiguration(XNode root) {
try {
this.propertiesElement(root.evalNode("properties"));
Properties settings = this.settingsAsProperties(root.evalNode("settings"));
this.loadCustomVfs(settings);
this.loadCustomLogImpl(settings);
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(settings);
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);
}
}
在代码 2-8中,parseConfiguration方法依次解析了配置文件的configuration下的一级节点,各个子方法,每个子方法对应的是每一个一级节点,解析出来的信息都放到Configuration实例中。Configuration对象保存配置文件的所有设置信息,也保存映射文件的信息。
2.3 构造SqlSessionFactory
生成Configuration象之对后,之后进行的是代码2-9的操作
【代码2-9】
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
代码2-9返回的DefaultSqlSessionFactory对象,SqlSessionFactory是一个接口, DefaultSqlSessionFactory是实现SqlSessionFactory接口的实现类。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 这一句解析就结束了,
总结
通过上面的分析,Mybatis的初始化阶段已经分析完毕,在初始化阶段,Mybatis主要进行了以下几项工作:
1.根据配置文件的位置,获取它的输入流InputStream。
2.从配置文件的根节点开始,逐层解析配置文件,解析过程中不断把解析结果放进Configuration对象
3.以配置好的Configuration对象为参数,获取一个SqlsessionFactory对象