配置文件中的配置信息以及测试类:
首先在mybatis配置文件中添加properties和enviroments配置,并引入外部配置db.properties
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="db.properties">
<property name="userName" value="root"/>
<property name="password" value="123456"/>
</properties>
<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="cn/zsm/mybatis/man/ManMapper.xml"/>
<mapper resource="cn/zsm/mybatis/student/StudentMapper.xml"/>
</mappers>
</configuration>
db.properties中配置的属性
userName=root
password=123456
url=jdbc:mysql://localhost:3306/zsmtest
driver=com.mysql.jdbc.Driver
然后我们启动测试类,进入XMLConfigBuilder 的parseConfiguration(XNode root)方法, 逐步观察mybatis是如何解析这两个标签的:
@Test
public void test3() throws IOException {
StudentMapper mapper = getSqlSession().getMapper(StudentMapper.class);
List<Student> students = mapper.selectStudents();
for (Student student : students){
System.out.println(student.toString());
}
}
private SqlSession getSqlSession() throws IOException {
String resource = "configuration.xml";
InputStream stream = Resources.getResourceAsStream(resource);
SqlSessionFactory build = new SqlSessionFactoryBuilder().build(stream);
SqlSession sqlSession = build.openSession();
return sqlSession;
}
XMLConfigBuilder :parseConfiguration(XNode root)方法
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;
}
}
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);
}
}
- this.propertiesElement(root.evalNode("properties"));解析properties标签
- this.environmentsElement(root.evalNode("environments"));解析environments标签
propertiesElement:
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
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) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
Properties vars = this.configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
this.parser.setVariables(defaults);
this.configuration.setVariables(defaults);
}
}
1、获取properties标签下的信息和内部定义property信息:
2、获取resource属性和url属性信息,且如果两个属性最多只能有一个属性为空
3、读书resource或url指向的配置文件的配置信息。主要这里读取配置信息并存储时,将信息存储在default中:
而这时defaults中已经存储了解析property标签时的信息,如果引入的配置文件中含有与property中相同的key值,则会将原来的值覆盖。这里也可以看出配置文件中属性读取的顺序和属性取值的优先级。
这时我们再看deaults中的值:
4、到这里properties中的属性信息已经读取完毕,读取的信息复制给variables属性。
environmentsElement:
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (this.environment == null) {
this.environment = context.getStringAttribute("default");
}
Iterator var2 = context.getChildren().iterator();
while(var2.hasNext()) {
XNode child = (XNode)var2.next();
//解析id属性值
String id = child.getStringAttribute("id");
//如果当前enviroment的id等于默认的id,则进行解析
if (this.isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = this.transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = this.dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Builder environmentBuilder = (new Builder(id)).transactionFactory(txFactory).dataSource(dataSource);
this.configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
1、开始解析enviroments标签,如果当前enciroment属性为空,则获取default属性指定的enviromentID
2、获取enviroments标签下所有子标签enviroment,并遍历。由于我们这里只配置了一个enviroment,所以context.getChildren()也只有一个元素。
public List<XNode> getChildren() {
List<XNode> children = new ArrayList();
NodeList nodeList = this.node.getChildNodes();
if (nodeList != null) {
int i = 0;
for(int n = nodeList.getLength(); i < n; ++i) {
Node node = nodeList.item(i);
if (node.getNodeType() == 1) {
children.add(new XNode(this.xpathParser, node, this.variables));
}
}
}
return children;
}
3、解析enviroment标签下,其它标签的信息:id、transactionManager、datasource,并获取TransactionFactory事物工厂和DataSourceFactory数据库链接工厂。
TransactionFactory:
public interface TransactionFactory {
void setProperties(Properties var1);
Transaction newTransaction(Connection var1);
Transaction newTransaction(DataSource var1, TransactionIsolationLevel var2, boolean var3);
}
DataSourceFactory:
public interface DataSourceFactory {
void setProperties(Properties var1);
DataSource getDataSource();
}
两个工厂的信息如下图:
我们可以看到,datasource中有一些连接池的配置信息,但是我们并没有配置连接池。这是mybatis中默认配置的连接池信息。
会议一下我们使用JDBC链接数据库的时候,需要用到Connection类,而这里也是链接数据库,其中DataSource就是java.sql包下的类:
package javax.sql;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Wrapper;
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}
到这里,数据库链接的配置读取和设置完毕。也就是我们已经可以与数据库建立连接了。