🥂(❁´◡`❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞
💖📕🎉🔥 支持我:点赞👍+收藏⭐️+留言📝欢迎留言讨论
🔥🔥🔥(源码 + 调试运行 + 问题答疑)
🔥🔥🔥 有兴趣可以联系我。
我们常常在当下感到时间慢,觉得未来遥远,但一旦回头看,时间已经悄然流逝。对于未来,尽管如此,也应该保持一种从容的态度,相信未来仍有许多可能性等待着我们。
-
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
<?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
java
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream) {
try {
// 1. 创建配置解析器
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream);
// 2. 解析配置,返回完整的Configuration对象
Configuration config = parser.parse();
// 3. 使用配置创建SqlSessionFactory
return 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(简化版)
java
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解析API
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(inputStream);
Element root = document.getDocumentElement();
// 解析environments
parseEnvironments(root);
// 解析mappers
parseMappers(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);
// 配置Environment
Environment 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")) {
// 解析包下的所有Mapper
String 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之后,为开发者提供便捷的使用体验。
💖学习知识需费心,
📕整理归纳更费神。
🎉源码免费人人喜,
🔥码农福利等你领!💖常来我家多看看,
📕我是程序员扣棣,
🎉感谢支持常陪伴,
🔥点赞关注别忘记!💖山高路远坑又深,
📕大军纵横任驰奔,
🎉谁敢横刀立马行?
🔥唯有点赞+关注成!