Loading... # MyBatis源码解析:Mapper文件加载机制 MyBatis作为一款优秀的持久层框架,其**Mapper文件加载机制**是理解MyBatis源码的关键。本文将深入解析MyBatis的Mapper文件是如何加载的,帮助大家更好地理解其内部原理。😊 ## 一、MyBatis整体架构概览 在深入Mapper文件加载机制之前,我们先简要了解MyBatis的整体架构。 ```mermaid graph LR A[SqlSessionFactoryBuilder] --> B[SqlSessionFactory] B --> C[SqlSession] C --> D[Executor] D --> E[StatementHandler] E --> F[ParameterHandler & ResultSetHandler] ``` **解释**: - **SqlSessionFactoryBuilder**:构建SqlSessionFactory的构建器。 - **SqlSessionFactory**:SqlSession的工厂,负责创建SqlSession。 - **SqlSession**:执行SQL、获取Mapper等操作的接口。 - **Executor**:执行器,负责SQL语句的执行。 - **StatementHandler**:处理JDBC Statement的接口。 - **ParameterHandler**:处理SQL参数。 - **ResultSetHandler**:处理结果集。 ## 二、Mapper文件加载的流程 MyBatis加载Mapper文件的流程主要分为以下几个步骤: 1. **解析全局配置文件(mybatis-config.xml)**。 2. **注册Mapper接口和Mapper.xml文件**。 3. **解析Mapper.xml文件**。 4. **生成MappedStatement对象**。 ### 1. 解析全局配置文件 在构建SqlSessionFactory时,MyBatis首先会解析全局配置文件 `mybatis-config.xml`。 ```java Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); ``` **解释**: - `Resources.getResourceAsReader`:从classpath中读取配置文件。 - `SqlSessionFactoryBuilder().build(reader)`:根据配置文件构建SqlSessionFactory。 ### 2. 注册Mapper接口和Mapper.xml文件 在全局配置文件中,我们可以通过以下方式注册Mapper: ```xml <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> <package name="com.example.mapper"/> </mappers> ``` **解释**: - `<mapper resource="...">`:指定Mapper.xml文件的位置。 - `<package name="...">`:指定Mapper接口所在的包,MyBatis会自动扫描。 ### 3. 解析Mapper.xml文件 MyBatis在解析全局配置文件时,会调用 `XMLMapperBuilder`解析Mapper.xml文件。 ```java public void parse() { if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } } ``` **解释**: - `configurationElement`:解析 `<mapper>`节点。 - `bindMapperForNamespace`:为当前Namespace绑定对应的Mapper接口。 ### 4. 生成MappedStatement对象 在解析 `<select>`, `<insert>`, `<update>`, `<delete>`等SQL语句节点时,MyBatis会为每个节点生成一个 `MappedStatement`对象。 ```java private void parseStatementNode(XNode context) { String id = context.getStringAttribute("id"); // 省略部分代码 MappedStatement mappedStatement = builderAssistant.addMappedStatement( id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); } ``` **解释**: - `parseStatementNode`:解析SQL语句节点。 - `builderAssistant.addMappedStatement`:创建并添加 `MappedStatement`对象。 ## 三、Mapper文件加载机制原理分析 ### 1. Mapper接口的注册 MyBatis在启动时,会扫描所有的Mapper接口,并将其注册到 `MapperRegistry`中。 ```java public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<>(type)); // 解析对应的Mapper.xml文件 MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } } ``` **解释**: - `addMapper`:将Mapper接口添加到 `knownMappers`中。 - `MapperProxyFactory`:为Mapper接口创建代理工厂。 - `MapperAnnotationBuilder`:解析Mapper接口上的注解。 ### 2. Mapper.xml文件的解析 在解析Mapper.xml文件时,MyBatis会根据XML配置创建相应的 `MappedStatement`对象,并存储在 `Configuration`中。 ```java public void parse(XNode context) { String namespace = context.getStringAttribute("namespace"); // 解析<resultMap>、<sql>、<select>等节点 resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } ``` **解释**: - `resultMapElements`:解析 `<resultMap>`节点。 - `sqlElement`:解析 `<sql>`片段。 - `buildStatementFromContext`:解析SQL语句节点。 ### 3. MappedStatement的存储 所有的 `MappedStatement`对象都会存储在 `Configuration`的 `mappedStatements`映射中,键是 `namespace.id`格式。 ```java configuration.addMappedStatement(mappedStatement); ``` **解释**: - `addMappedStatement`:将 `MappedStatement`添加到 `mappedStatements`中。 ### 4. SqlSession获取Mapper 当我们在代码中获取Mapper接口的实例时,MyBatis会通过 `MapperProxy`创建一个代理对象。 ```java UserMapper userMapper = sqlSession.getMapper(UserMapper.class); ``` **解释**: - `getMapper`:从 `MapperRegistry`中获取Mapper接口的代理对象。 ## 四、Mapper文件加载机制流程图 ```mermaid flowchart TD A[开始] --> B[解析mybatis-config.xml] B --> C[注册Mapper接口和Mapper.xml] C --> D[解析Mapper.xml文件] D --> E[生成MappedStatement对象] E --> F[存储到Configuration] F --> G[结束] ``` **解释**: - **A到B**:MyBatis启动,解析全局配置文件。 - **B到C**:注册Mapper接口和Mapper.xml文件。 - **C到D**:解析Mapper.xml文件内容。 - **D到E**:为每个SQL语句生成 `MappedStatement`。 - **E到F**:将 `MappedStatement`存储到 `Configuration`。 - **F到G**:Mapper文件加载完成。 ## 五、总结 MyBatis的Mapper文件加载机制是通过解析配置文件、注册Mapper接口、解析Mapper.xml文件、生成 `MappedStatement`对象等步骤完成的。理解这个过程有助于我们深入掌握MyBatis的工作原理,提高开发效率。👍 --- **表1:MyBatis Mapper文件加载流程** | 步骤 | 描述 | | ------------------------------ | -------------------------------------------------------- | | **解析全局配置文件** | 加载 `mybatis-config.xml`,读取基本配置信息。 | | **注册Mapper接口和文件** | 注册Mapper接口和Mapper.xml文件,准备解析。 | | **解析Mapper.xml文件** | 解析Mapper.xml中的SQL语句、ResultMap等配置。 | | **生成MappedStatement** | 为每个SQL语句生成对应的 `MappedStatement`对象。 | | **存储到Configuration** | 将 `MappedStatement`存储到 `Configuration`的映射中。 | --- **公式1:MappedStatement的存储键** $$ \text{Key} = \text{Namespace} + "." + \text{Statement\_ID} $$ - **Namespace**:Mapper的命名空间,一般为Mapper接口的全限定名。 - **Statement_ID**:SQL语句在Mapper.xml文件中的 `id`属性。 --- **重点提示**: - **Mapper接口与Mapper.xml文件的Namespace必须一致**,否则无法正确匹配。 - **MappedStatement是MyBatis执行SQL的核心对象**,包含了SQL语句、输入输出参数等信息。 - **正确理解Mapper文件的加载机制,有助于排查MyBatis使用中的各种问题**。🎯 --- 希望通过本文的解析,您对MyBatis的Mapper文件加载机制有了更深入的理解。😊 最后修改:2024 年 10 月 05 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏