🥂(?′?`?)您的點贊👍?評論📝?收藏?是作者創作的最大動力🤞
💖📕🎉🔥 支持我:點贊👍+收藏??+留言📝歡迎留言討論
🔥🔥🔥(源碼 + 調試運行 + 問題答疑)
🔥🔥🔥 ?有興趣可以聯系我。
我們常常在當下感到時間慢,覺得未來遙遠,但一旦回頭看,時間已經悄然流逝。對于未來,盡管如此,也應該保持一種從容的態度,相信未來仍有許多可能性等待著我們。?
MyBatis架構基石:SqlSessionFactoryBuilder如何構建整個MyBatis宇宙
配置文件解析的藝術:深入剖析SqlSessionFactoryBuilder的構建過程
手寫MyBatis構建者:從XML配置到完整SqlSessionFactory的魔法轉換
設計模式實戰:Builder模式在MyBatis框架中的精妙應用
MyBatis啟動流程解密:SqlSessionFactoryBuilder如何協調全局配置解析
正文
在MyBatis的架構體系中,有一個看似短暫卻至關重要的組件——SqlSessionFactoryBuilder
。它如同一個高效的建筑工程師,負責讀取設計藍圖(配置文件),準備所有建筑材料(配置信息),最終建造出宏偉的SqlSessionFactory
大廈。今天,我們將深入探討這個"用完即丟"卻不可或缺的構建者。
一、SqlSessionFactoryBuilder的職責與定位
SqlSessionFactoryBuilder
在MyBatis框架中扮演著構建協調者的角色,它的核心使命是將各種分散的配置信息整合成一個完整的、可用的SqlSessionFactory
實例。這種設計體現了經典的Builder模式,將復雜對象的構建過程與其表示分離。
為什么需要專門的構建者?
構建過程復雜:MyBatis的配置涉及多個層面,包括數據源、事務管理器、映射器、類型處理器等,需要專門的組件來協調這些配置的解析和組裝。
支持多種配置源:需要支持從XML文件、Properties文件、編程式配置等多種方式構建配置。
構建過程與使用過程分離:避免將復雜的構建邏輯污染到
SqlSessionFactory
本身,保持類的單一職責。
二、全局配置文件:MyBatis的藍圖
在深入了解構建過程之前,我們先看看MyBatis全局配置文件通常包含哪些內容:
<?xml version="1.0" encoding="UTF-8"?><configuration><!-- 環境配置,支持多環境 --><environments default="development"><environment id="development"><!-- 事務管理器 --><transactionManager type="JDBC"/><!-- 數據源 --><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis"/><property name="username" value="root"/><property name="password" value="password"/></dataSource></environment></environments><!-- 映射器注冊 --><mappers><mapper resource="mapper/UserMapper.xml"/><mapper class="com.example.mapper.ProductMapper"/><package name="com.example.mapper"/></mappers></configuration>
這些配置項共同構成了MyBatis運行的完整藍圖,SqlSessionFactoryBuilder
就是解讀這個藍圖的專家。
三、構建過程詳解:從配置文件到工廠實例
讓我們通過一個序列圖來詳細展示SqlSessionFactoryBuilder
的完整構建過程:
現在,讓我們用代碼實現這個構建過程:
第一步:創建SqlSessionFactoryBuilder
public class SqlSessionFactoryBuilder {?public SqlSessionFactory build(InputStream inputStream) {try {// 1. 創建配置解析器XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);// 2. 解析配置,返回完整的Configuration對象Configuration config = parser.parse();// 3. 使用配置創建SqlSessionFactoryreturn new DefaultSqlSessionFactory(config);} catch (Exception e) {throw new RuntimeException("Error building SqlSessionFactory. Cause: " + e);} finally {try {if (inputStream != null) {inputStream.close();}} catch (IOException e) {// 忽略關閉異常}}}// 支持其他構建方式public SqlSessionFactory build(Reader reader) {// 類似實現...}public SqlSessionFactory build(Configuration config) {return new DefaultSqlSessionFactory(config);}}
第二步:實現XMLConfigBuilder(簡化版)
public class XMLConfigBuilder {private final InputStream inputStream;private final Configuration configuration;public XMLConfigBuilder(InputStream inputStream) {this.inputStream = inputStream;this.configuration = new Configuration();}public Configuration parse() {try {// 使用JDK內置的XML解析APIDocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();DocumentBuilder builder = factory.newDocumentBuilder();Document document = builder.parse(inputStream);Element root = document.getDocumentElement();// 解析environmentsparseEnvironments(root);// 解析mappersparseMappers(root);return configuration;} catch (Exception e) {throw new RuntimeException("Error parsing XML config. Cause: " + e);}}private void parseEnvironments(Element root) {NodeList environmentList = root.getElementsByTagName("environment");if (environmentList.getLength() > 0) {Element envElement = (Element) environmentList.item(0);String id = envElement.getAttribute("id");// 解析數據源Element dataSourceElement = (Element) envElement.getElementsByTagName("dataSource").item(0);DataSource dataSource = createDataSource(dataSourceElement);// 解析事務管理器Element txElement = (Element) envElement.getElementsByTagName("transactionManager").item(0);TransactionFactory txFactory = createTransactionFactory(txElement);// 配置EnvironmentEnvironment environment = new Environment(id, txFactory, dataSource);configuration.setEnvironment(environment);}}private void parseMappers(Element root) {NodeList mapperList = root.getElementsByTagName("mapper");for (int i = 0; i < mapperList.getLength(); i++) {Element mapperElement = (Element) mapperList.item(i);if (mapperElement.hasAttribute("resource")) {// 解析XML映射文件String resource = mapperElement.getAttribute("resource");parseMapperXml(resource);} else if (mapperElement.hasAttribute("class")) {// 解析注解Mapper接口String className = mapperElement.getAttribute("class");parseMapperClass(className);} else if (mapperElement.hasAttribute("package")) {// 解析包下的所有MapperString packageName = mapperElement.getAttribute("package");parseMapperPackage(packageName);}}}private void parseMapperXml(String resource) {try (InputStream mapperStream = Resources.getResourceAsStream(resource)) {XMLMapperParser mapperParser = new XMLMapperParser(mapperStream, configuration);mapperParser.parse();} catch (IOException e) {throw new RuntimeException("Error loading mapper resource: " + resource, e);}}private void parseMapperClass(String className) {try {Class<?> mapperClass = Class.forName(className);configuration.addMapper(mapperClass);} catch (ClassNotFoundException e) {throw new RuntimeException("Error loading mapper class: " + className, e);}}// 其他輔助方法...}
四、SqlSessionFactoryBuilder的生命周期:為何"用完即丟"?
SqlSessionFactoryBuilder
的設計遵循了"一次性使用"原則,這基于以下幾個考慮:
無狀態性:
SqlSessionFactoryBuilder
本身不持有任何狀態信息,它的唯一作用就是構建SqlSessionFactory
。一旦構建完成,它的使命就結束了。資源釋放:構建過程中可能會占用資源(如文件流),使用后立即銷毀有助于及時釋放這些資源。
線程安全:由于無狀態,它可以被多線程安全地使用,每個線程都可以創建自己的構建者實例。
內存優化:避免長時間持有對大型配置對象的引用,減少內存占用。
這種設計模式在很多框架中都有應用,比如Java中的StringBuilder
(雖然名稱相似但用途不同)、Spring的BeanDefinitionBuilder
等。
五、構建過程中的關鍵技術點
配置解析的容錯性:良好的錯誤處理和詳細的錯誤信息輸出
資源管理:確保所有打開的流都能正確關閉
配置驗證:對必要的配置項進行驗證,避免運行時錯誤
擴展性考慮:為后續支持更多配置項預留擴展點
六、總結與最佳實踐
通過實現SqlSessionFactoryBuilder
,我們完成了MyBatis啟動流程的關鍵一環。這個看似簡單的構建者實際上承擔著重要的協調工作:
配置整合:將分散的配置信息整合成統一的
Configuration
對象資源協調:協調多個解析器的工作,確保配置的正確加載
異常處理:提供統一的錯誤處理機制,確保構建過程的穩定性
在實際使用中,我們應該遵循以下最佳實踐:
將
SqlSessionFactoryBuilder
作為局部變量使用,避免長期持有確保配置文件的正確性和完整性
在應用啟動時完成
SqlSessionFactory
的構建,避免重復構建的開銷
SqlSessionFactoryBuilder
雖然生命周期短暫,但它的工作為整個MyBatis框架的正常運行奠定了堅實的基礎。正是這種精妙的職責劃分和設計,使得MyBatis成為一個既靈活又穩定的ORM框架。
下次當你看到SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
這行代碼時,你會知道在這背后發生了一個復雜而精妙的構建過程。這就是框架設計的魅力所在——將復雜性封裝在簡潔的API之后,為開發者提供便捷的使用體驗。
💖學習知識需費心,
📕整理歸納更費神。
🎉源碼免費人人喜,
🔥碼農福利等你領!💖常來我家多看看,
📕我是程序員扣棣,
🎉感謝支持常陪伴,
🔥點贊關注別忘記!💖山高路遠坑又深,
📕大軍縱橫任馳奔,
🎉誰敢橫刀立馬行?
🔥唯有點贊+關注成!