Mybatis 原始碼學習(二)
Mybatis原始碼分析(二)
話不多說我們們上程式碼!!!!!!
核心配置檔案
Mybatis的核心配置檔案,一般命名為mybatis-config.xml,這個檔案包含了使用mybatis的時候的所有配置,只有正確載入了該配置檔案,mybatis才能正常工作;
<?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>
<!--開啟日誌輸出-->
<settings>
<!-- 設定日誌輸出為LOG4J -->
<setting name="logImpl" value="LOG4J" />
</settings>
<!--配置類別名,配置後在Mapper配置檔案(通常我們將編寫SQL語句的配置檔案成為Mapper配置檔案)中需要使用pojo包中的類時,使用簡單類名即可-->
<typeAliases>
<package name="com.li.domain" />
</typeAliases>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/practice?characterEncoding=utf-8"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com.li.mapper"/>
<!-- <mapper class="com.li.domain.Person"/>-->
</mappers>
</configuration>
這裡只研究mybatis的對映檔案方式,如下為mapper的對映檔案
Mapper對映檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.li.mapper.PersonMapper" >
<resultMap id="BaseResultMap" type="com.li.domain.Person" >
<result column="id" property="id" jdbcType="VARCHAR" />
<result column="email" property="email" jdbcType="VARCHAR" />
</resultMap>
<select id="selectById" parameterType="java.lang.String" resultType="com.li.domain.Person">
select * from person where id = #{id}
</select>
</mapper>
上面的mapper對映檔案中,只有一個select標籤,對應的對映檔案的介面為PersonMapper.java
MapperPerson介面檔案
package com.li.mapper;
import com.li.domain.Person;
public interface PersonMapper {
Person selectById(String id);
}
介面中的方法名稱和對映檔案中的select標籤中的id進行對應,必須保持一一對應;
domain檔案
package com.li.domain;
public class Person {
private String id;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id == null ? null : id.trim();
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email == null ? null : email.trim();
}
}
Mybatis的測試類
package com.li.test;
import com.li.domain.Person;
import com.li.mapper.PersonMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
/**
* When I wrote this, only God and I understood what I was doing
* Now, God only knows
*
* @ClassName: MybatisTest
* @Description: mybatis原始碼測試
* @Author: Li
* @Date: Created in 2020/11/10 21:37
*/
public class MybatisTest {
public static void main(String[] args) throws Exception{
//載入mybatis的核心配置檔案
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//使用建造者模式建立一個SqlSession的工廠SqlSessionFactory物件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//通過工廠生產一個sqlsession物件
SqlSession sqlSession = sqlSessionFactory.openSession();
//通過SqlSession獲取一個PersionMapper介面的代理物件
PersonMapper mapper = sqlSession.getMapper(PersonMapper.class);
//執行selectById方法傳入入參 "1"
Person person = mapper.selectById("1");
//輸出查詢結果
System.out.println(person);
//關閉Sqlsession
sqlSession.close();
}
}
mybatis的執行流程
-
讀取配置檔案
//載入mybatis的核心配置檔案 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
讀取mybatis的核心配置檔案轉化為InputStream流物件,這裡的Resources類是mybatis用來載入類路徑下的檔案;
-
SqlSessionFactoryBuilder 使用建造者模式建立SqlSessionFactory工廠;下面是他所持有的方法。
可以看到SqlSessionFactoryBuilder 中所有的方法都是build方法,這就是標準的建造者模式,方法的返回值都是SqlSessionFactory;
// 1.這裡呼叫的是build(inputStream)方法
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
// 2.下面又呼叫
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
// XMLConfigBuilder:用來解析XML配置檔案
// 使用構建者模式
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// parser.parse():使用XPATH解析XML配置檔案,將配置檔案封裝為Configuration物件
// 返回DefaultSqlSessionFactory物件,該物件擁有Configuration物件(封裝配置檔案資訊)
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.
}
}
}
-
在上面的build(InputStream inputStream, String environment, Properties properties)方法中,又使用建造者模式建立了一個XMLConfigBuilder的類,用來解析讀取到的mybatis核心配置檔案,生成configuration物件並封裝到SqlSessionFactory中;
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); } // 3.通過建構函式建立XMLConfigBuilder類 private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { // 建立Configuration物件,並通過TypeAliasRegistry註冊一些Mybatis內部相關類的別名 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
-
建立XMLConfigBuilder類後,通過呼叫上面<2>中build(parser.parse())方法來進行mybatis核心配置檔案的第一步解析
/** * 解析XML配置檔案 * @return */ public Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // parser.evalNode("/configuration"):通過XPATH解析器,解析configuration根節點 // 從configuration根節點開始解析,最終將解析出的內容封裝到Configuration物件中 parseConfiguration(parser.evalNode("/configuration")); return configuration; } // 接著呼叫parseConfiguration(parser.evalNode("/configuration")),對mybatis的核心配置檔案進行解析 private void parseConfiguration(XNode root) { try { //issue #117 read properties first // 解析</properties>標籤 propertiesElement(root.evalNode("properties")); // 解析</settings>標籤 Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); // 解析</typeAliases>標籤 typeAliasesElement(root.evalNode("typeAliases")); // 解析</plugins>標籤 pluginElement(root.evalNode("plugins")); // 解析</objectFactory>標籤 objectFactoryElement(root.evalNode("objectFactory")); // 解析</objectWrapperFactory>標籤 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // 解析</reflectorFactory>標籤 reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 解析</environments>標籤 environmentsElement(root.evalNode("environments")); //做主要分析 // 解析</databaseIdProvider>標籤 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // 解析</typeHandlers>標籤 typeHandlerElement(root.evalNode("typeHandlers")); // 解析</mappers>標籤 mapperElement(root.evalNode("mappers")); //做主要分析 } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
呼叫parseConfiguration(XNode root)方法進行mybatis核心配置檔案的解析,我們在這裡主要分析(environments|mappers)這兩個標籤,其他配置就不詳細分析了;
-
呼叫environmentsElement(root.evalNode(“environments”)),解析核心配置檔案中的“environments”標籤;
// 解析‘environments’標籤 private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { //獲取到environments 的default 屬性 environment = context.getStringAttribute("default"); } //迴圈解析environments子節點的‘environment’標籤 for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); //解析environment子節點的‘dataSource’標籤,使用建造者模式建立DataSourceFactory,生成DataSource物件 DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); //將配置檔案中的environment解析完成後封裝成一個Environment物件,封裝在configuration物件中 Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } }
-
呼叫mapperElement(root.evalNode(“mappers”)),解析核心配置檔案中的“mappers”標籤
/** * 解析<mappers>標籤 * @param parent mappers標籤對應的XNode物件 * @throws Exception */ private void mapperElement(XNode parent) throws Exception { if (parent != null) { // 獲取<mappers>標籤的子標籤 for (XNode child : parent.getChildren()) { // <package>子標籤 if ("package".equals(child.getName())) { // 獲取mapper介面和mapper對映檔案對應的package包名 String mapperPackage = child.getStringAttribute("name"); // 將包下所有的mapper介面以及它的代理物件儲存到一個Map集合中,key為mapper介面型別,value為代理物件工廠 configuration.addMappers(mapperPackage); } else {// <mapper>子標籤 // 獲取<mapper>子標籤的resource屬性 String resource = child.getStringAttribute("resource"); // 獲取<mapper>子標籤的url屬性 String url = child.getStringAttribute("url"); // 獲取<mapper>子標籤的class屬性 String mapperClass = child.getStringAttribute("class"); // 它們是互斥的 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); // 專門用來解析mapper對映檔案 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); // 通過XMLMapperBuilder解析mapper對映檔案 mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); // 通過XMLMapperBuilder解析mapper對映檔案 mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); // 將指定mapper介面以及它的代理物件儲存到一個Map集合中,key為mapper介面型別,value為代理物件工廠 configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
到此為止解析Mybatis的核心配置檔案的過程就到此為止了,在上面程式碼中根據配置的不同,建立XMLMapperBuilder對mapper對映檔案進行解析;
解析mapper對映檔案的過程,請聽下回分解;
相關文章
- MyBatis原始碼分析(二)MyBatis原始碼
- MyBatis學習(二)MyBatis
- 學習RadonDB原始碼(二)原始碼
- mybatis原始碼學習:一級快取和二級快取分析MyBatis原始碼快取
- mybatis通用mapper原始碼解析(二)MyBatisAPP原始碼
- mybatis原始碼學習------resultMap和sql片段的解析MyBatis原始碼SQL
- MyBatis原始碼學習筆記(一) 初遇篇MyBatis原始碼筆記
- mybatis原始碼學習------cache-ref和cache的解析MyBatis原始碼
- mybatis原始碼學習:從SqlSessionFactory到代理物件的生成MyBatis原始碼SQLSession物件
- Vuex原始碼學習(二)脈絡梳理Vue原始碼
- Spring原始碼學習之路---IOC初探(二)Spring原始碼
- Mybatis底層原理學習(二):從原始碼角度分析一次查詢操作過程MyBatis原始碼
- Dubbo SPI 原始碼學習 & admin安裝(二)原始碼
- Vue原始碼學習(二)——從巨集觀看VueVue原始碼
- Vue原始碼學習二 ———— Vue原型物件包裝Vue原始碼原型物件
- 原始碼學習原始碼
- 精盡MyBatis原始碼分析 - MyBatis-Spring 原始碼分析MyBatis原始碼Spring
- mybatis原始碼學習:外掛定義+執行流程責任鏈MyBatis原始碼
- 【分享】從Mybatis原始碼中,學習到的10種設計模式MyBatis原始碼設計模式
- 雲端計算學習路線原始碼框架筆記:Mysql原始碼二原始碼框架筆記MySql
- MyBatis原始碼解析MyBatis原始碼
- Mybatis原始碼分析MyBatis原始碼
- Android FrameWork學習(二)Android系統原始碼除錯AndroidFramework原始碼除錯
- Spring原始碼深度解析(郝佳)-學習-原始碼解析-基於註解注入(二)Spring原始碼
- 07.ElementUI 2.X 原始碼學習:原始碼剖析之工程化(二)UI原始碼
- fishhook原始碼學習Hook原始碼
- MMKV原始碼學習原始碼
- vue原始碼學習Vue原始碼
- 【原始碼學習】ThreadLocal原始碼thread
- EventBus原始碼學習原始碼
- ObjectMapper原始碼學習ObjectAPP原始碼
- express原始碼學習Express原始碼
- go原始碼學習Go原始碼
- 學習HashMap原始碼HashMap原始碼
- Mybatis原始碼分析(二)XML的解析和Annotation的支援MyBatis原始碼XML
- myBatis原始碼解析-二級快取的實現方式MyBatis原始碼快取
- mybatis學習MyBatis
- OKHttp原始碼學習同步請求和非同步請求(二)HTTP原始碼非同步