1、Mybatis是如何获取数据源的?
在https://mybatis.org/mybatis-3/zh/configuration.html#environments mybatis官方 文档里面,根据xml配置可以查看到
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<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>
数据源配置主要包含在environments,dataSource即为数据源,通过代码查看谁解析了dataSource,那么数据源就被谁拥有;
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream)
XMLConfigBuilder.parse
private boolean parsed;为XMLConfigBuilder成员变量
上面方法使用的parser在XMLConfigBuilder构造函数的时候进行初始化
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties)
//
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;//
}
XMLConfigBuilder类属性
private boolean parsed;//实现为XPathParser
private final XPathParser parser;
private String environment;
parser.evalNode("/configuration");
String value = parser.evalNode("/configuration");
<configuration>
<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="com/wolf/dao/SysParamMapper.xml"/>
</mappers>
</configuration>
通过此其实就看到把整个mybatis-config.xml给解析出来了。
由于我们这里只研究dataSource到底被谁解析和持有,通过上述xml配置可以发现dataSource在environments里面。所以看看具体解析代码parseConfiguration方法
environmentsElement方法解析
- 看看context内容是什么?
<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>
其实就是我们environmentsElement需要解析的environments配置信息
- child内容
<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下面的子标签进行遍历循环处理
- isSpecifiedEnvironment(id)这个方法在干什么
这个方法就是判断environments的default配置和要解析的environment的id是否相等,如果相等了,就需要去解析默认的环境(environment ) private boolean isSpecifiedEnvironment(String id) { if (environment == null) { throw new BuilderException("No environment specified."); } else if (id == null) { throw new BuilderException("Environment requires an id attribute."); } else if (environment.equals(id)) { return true; } return false; }
- TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));解析transactionManager然后生成txFactory
private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type");//去拿到 <transactionManager type="JDBC"/>标签的type属性 Properties props = context.getChildrenAsProperties();//这里把xml标签转化为Properties //通过配置,我只设置了JDBC,通过这块代码 //resolveClass(type)这个应该会返回一个Class,然后getDeclaredConstructor调用无参构造函数newInstance()然后实例化他 //那么JDBC对应的Class是什么呢。【JDBC-org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory】下面源码 TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance(); factory.setProperties(props);//将transactionManager 的子标签信息设置到TransactionFactory中,后面需要时,在使用 return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }
- resolveClass(type)这个方法搞了些什么呢?
protected <T> Class<? extends T> resolveClass(String alias) { if (alias == null) { return null; } try { //resolveAlias去拿Class return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } }
protected <T> Class<? extends T> resolveAlias(String alias) { //由于这个方法在XmlConfigBuilder中,那么typeAliasRegistry为一个属性看看这个属性结构及初始化内容 return typeAliasRegistry.resolveAlias(alias); }
- XmlConfigBuilder.typeAliasRegistry属性
BaseBuilder为抽象类为XmlConfigBuilder的父类 BaseBuilder.typeAliasRegistry
通过初始化方法可以看见哦,typeAliasRegistry=this.configuration.getTypeAliasRegistry()中获取的,那么想,在XmlConfigBuilder类实例化的时候,typeAliasRegistry的内容其实已经就完成了初始化
1、configuration及typeAliasRegistry及typeHandlerRegistry都完成了初始化
2、configuration
在其子类通过new Configuration进行实例化并调用了父类的构造函数;【打住,学习源码的时候不要走远了,上面我们要分析typeAliasRegistry是怎么来的】
OK!!!回归正题,我们看到typeAliasRegistry=this.configuration.getTypeAliasRegistry()中获取的
那么我们这里看到了configuration中typeAliasRegistry的初始化,于是我们就找到了JDBC的class,里面还有很多其他参数,这里仅针对JDBC解析(org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory)
通过调试即可以看到TransactionFactory的最终实例就是JdbcTransactionFactory。【有时候通过调试其实最终知道他是什么,但是他如何来,这个就必须通过上述源码分析得到】
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);
}
==========【思路在回到:environmentsElement方法解析】
上面中间插了很多步骤,其实就是为了分析上述代码中TransactionFactory是如何来的。
- 然后在看看dataSource是如何解析的?
分析dataSource跟分析TransactionFactory一样的道理;通过调试最终得到了PooledDataSourceFactory实例;(因为xml中dataSource配置的是<dataSource type="POOLED">)
实例就是这么来的?
-
【在思路在回到:environmentsElement方法解析】
上述中找到了TransactionFactory和DataSourceFactory如何来的。DataSource是通过DataSourceFactory中获取的来的 - 抽丝剥茧,我要看看dataSource是如何通过DataSourceFactory拿来的。
通过调试发现DataSourceFactory的实例为PooledDataSourceFactory
先看看PooledDataSourceFactory类的构成
继承UnpooledDataSourceFactory,
PooledDataSourceFactory类属性
UnpooledDataSourceFactory类属性
- 【在思路在回到:environmentsElement方法解析--environmentBuilder如何构造】
学习到此我们在看看下面代码
Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource);
1、可以看到这个是一个建造者模式,那他建造了什么。
看看Evironment源码呢?
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
public static class Builder {
private final String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
}
1、Builder为Environment的内部类
2、Builder与Environment拥有相同的实例
3、Builder每次追加以后都会返回自己的实例,有点像StringBuilder或者StringBuffer
4、在最终Builder.build的时候,才会去构造Environment
这里通过内部类Builder建造了Envrionment对象;
最后将构造的Environment设置到configuration对象的属性中
configuration.setEnvironment(environmentBuilder.build());
到此为止DataSource解析完毕;
最终
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(InputStream inputStream)
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parse()
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(parser.evalNode("/configuration"))
》org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement(root.evalNode("environments"))
》org.apache.ibatis.builder.xml.XMLConfigBuilder.dataSourceElement(child.evalNode("dataSource"))
》org.apache.ibatis.builder.xml.XMLConfigBuilder.configuration.setEnvironment(environmentBuilder.build())
####new XMLConfigBuilder().configuration.setEnvironment(Environment)####
看到了设置的执行步骤!
总结:
1、class XMLConfigBuilder extends BaseBuilder
1、configuration对象被BaseBuilder持有
2、Configuration持有Environment
3、Environment类属性
(1)持有mybatis-config.xml中environment的标签配置信息
(2)持有TransactionFactory实例(<transactionManager type="JDBC"/>-->PooledDataSourceFactory)
(3)持有dataSource实例(<dataSource type="POOLED">--JdbcTransactionFactory)
到此为止!!!整个解析过去就是对mybatis-config.xml配置的标签进行解析,解析后通过一系列对象进行封装和处理;