Loading... ### MyBatis源码分析:Mapper文件加载与解析过程 MyBatis 是一个流行的持久层框架,提供了简洁易用的 SQL 映射功能。在 MyBatis 中,`Mapper`文件负责将 SQL 语句与 Java 对象进行映射。了解 MyBatis 是如何加载和解析 `Mapper` 文件,对掌握 MyBatis 的运行原理至关重要。 下面我们从**Mapper文件加载与解析的过程**入手,逐步分析 MyBatis 在内部是如何实现这些功能的。 ### 一、Mapper 文件的加载过程 #### 1.1 Mapper 加载的时机 在 MyBatis 中,Mapper 文件的加载通常发生在应用启动时,伴随 MyBatis `SqlSessionFactory` 的初始化过程。**`SqlSessionFactory`** 是 MyBatis 中用于创建 `SqlSession` 实例的工厂,所有对数据库的操作都依赖于 `SqlSession`。 Mapper 文件的加载与解析发生在 `SqlSessionFactory` 的初始化过程中,具体步骤如下: 1. 配置文件加载(包括全局配置和数据源配置)。 2. 注册 Mapper 文件或接口。 3. 解析 Mapper XML 文件并构建 Mapper 对象。 #### 1.2 核心加载类:**Configuration** `Configuration` 是 MyBatis 的核心配置类,存储了全局的配置、Mapper 信息、SQL 语句等。Mapper 文件的加载主要通过 `Configuration` 类中的方法实现。 #### 1.3 Mapper 加载的入口:**XMLMapperBuilder** 在 MyBatis 中,**`XMLMapperBuilder`** 类负责解析 Mapper XML 文件。Mapper 文件中的每一个 SQL 语句都会被解析,并映射到 `Configuration` 中对应的 MappedStatement 中。 ##### 加载 Mapper 文件的过程: 1. MyBatis 在启动时,会通过 `XMLConfigBuilder` 解析全局配置文件(通常是 `mybatis-config.xml`)。 2. 当 `XMLConfigBuilder` 发现需要加载的 Mapper 文件时,会调用 `MapperRegistry` 注册 Mapper。 3. `MapperRegistry` 会根据 Mapper 的配置,使用 `XMLMapperBuilder` 来解析并加载 Mapper 文件。 下面是核心代码的简化流程: ```java // 在 Configuration 类中注册 Mapper public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); } // MapperRegistry 中的 addMapper 方法 public <T> void addMapper(Class<T> type) { // 检查 Mapper 是否已经注册 if (hasMapper(type)) { return; } // 解析 XML 文件 XMLMapperBuilder parser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); parser.parse(); } ``` - **addMapper()**:将指定的 `Mapper` 类注册到 `MapperRegistry` 中。 - **XMLMapperBuilder**:负责解析 Mapper 文件,并将其内容存储到 `Configuration` 中。 ### 二、Mapper 文件的解析过程 #### 2.1 XMLMapperBuilder 类 `XMLMapperBuilder` 是 MyBatis 用于解析 Mapper 文件的核心类。该类继承自 `BaseBuilder`,解析的过程主要包括以下几个方面: 1. 读取并解析 `mapper.xml` 文件。 2. 提取 SQL 语句(如 `select`、`insert`、`update` 和 `delete`)。 3. 将解析后的 SQL 语句映射到 `Configuration` 中。 ```java // XMLMapperBuilder 的 parse 方法 public void parse() { if (!configuration.isResourceLoaded(resource)) { // 解析 <mapper> 节点 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } } ``` - **configurationElement()**:负责解析 `<mapper>` 节点,并处理其中的 SQL 语句。 - **bindMapperForNamespace()**:将 Mapper 的命名空间与对应的 Mapper 接口进行绑定。 #### 2.2 Mapper 文件结构解析 Mapper XML 文件一般包含以下几个主要部分: 1. **`<mapper>`**:根节点,包含命名空间(namespace)。 2. **`<select>`、`<insert>`、`<update>`、`<delete>`**:定义 SQL 语句。 3. **`<resultMap>`**:用于定义 SQL 结果集与 Java 对象的映射关系。 4. **`<sql>`**:用于定义可复用的 SQL 片段。 以下是典型的 `mapper.xml` 文件结构: ```xml <mapper namespace="com.example.UserMapper"> <select id="selectUser" resultType="com.example.User"> SELECT * FROM users WHERE id = #{id} </select> <insert id="insertUser" parameterType="com.example.User"> INSERT INTO users (name, email) VALUES (#{name}, #{email}) </insert> </mapper> ``` - **namespace**:用于绑定 Mapper 接口和 XML 文件。 - **id**:SQL 语句的唯一标识符,用于在调用 SQL 时通过 `id` 进行调用。 #### 2.3 SQL 语句的解析 `XMLMapperBuilder` 解析 SQL 语句的具体过程主要依赖于 `XMLStatementBuilder`。该类负责解析 SQL 语句(如 `<select>`、`<insert>` 等),并将这些语句封装到 `MappedStatement` 中。 ```java // XMLStatementBuilder 中解析 SQL 语句的方法 public void parseStatementNode() { String id = context.getStringAttribute("id"); String sqlCommandType = context.getName(); // 创建 MappedStatement MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType); // 设置 SQL 参数、结果类型等 statementBuilder.parameterMap(parameterMap); statementBuilder.resultMaps(resultMaps); // 将解析后的 MappedStatement 注册到 Configuration 中 configuration.addMappedStatement(statementBuilder.build()); } ``` - **MappedStatement**:MyBatis 用于存储每个 SQL 语句的映射关系,包括 SQL 语句本身、参数类型、结果类型等。 - **sqlCommandType**:SQL 语句的类型(`SELECT`、`INSERT` 等)。 ### 三、Mapper 与 Mapper 接口的绑定 在 MyBatis 中,Mapper 接口与 Mapper 文件通过命名空间进行绑定。命名空间是 XML 文件中的 `namespace` 属性,它的值必须与 Mapper 接口的全限定类名一致。 ```xml <mapper namespace="com.example.UserMapper"> <!-- SQL 语句定义 --> </mapper> ``` 当 MyBatis 解析完 `mapper.xml` 文件后,会通过 `MapperRegistry` 将解析的 SQL 语句与 `UserMapper` 接口中的方法进行绑定。调用接口方法时,MyBatis 会根据方法名称和参数自动找到对应的 SQL 语句并执行。 ### 四、重要的类和方法总结 | **类名** | **作用** | | --------------------------------- | ------------------------------------------------------------------------------------------------- | | **`Configuration`** | MyBatis 核心配置类,存储全局配置、已解析的 Mapper 文件和 SQL 映射信息。 | | **`MapperRegistry`** | 用于管理和注册 Mapper 接口与其对应的 Mapper 文件。 | | **`XMLMapperBuilder`** | 用于解析 `mapper.xml` 文件,并将解析结果存储到 `Configuration` 中。 | | **`MappedStatement`** | 表示 SQL 语句的映射,包含 SQL 语句的定义、参数类型、返回类型等信息。 | | **`XMLStatementBuilder`** | 负责解析具体的 SQL 语句(如 `<select>`、`<insert>`),并创建对应的 `MappedStatement` 对象。 | ### 五、总结 MyBatis 的 Mapper 文件加载与解析过程通过一系列核心类和方法实现: 1. `SqlSessionFactory` 初始化时,通过 `Configuration` 类来管理并加载 Mapper 文件。 2. `XMLMapperBuilder` 类负责解析 `mapper.xml` 文件中的 SQL 语句、`resultMap` 等。 3. 解析后的 SQL 语句和映射关系被存储到 `Configuration` 中,通过 `MappedStatement` 进行管理。 4. MyBatis 通过 `MapperRegistry` 将 Mapper 接口与 Mapper XML 文件中的 SQL 语句绑定,保证应用程序中的 SQL 调用能够正确执行。 通过深入了解 MyBatis Mapper 文件的加载与解析机制,开发者可以更好地掌握 MyBatis 的运行原理,从而在实际项目中灵活运用,提升系统的可维护性与扩展性。 最后修改:2024 年 09 月 27 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请随意赞赏