当前位置: 首页 > news >正文

【源码解读之 Mybatis】【基础篇】-- 第2篇:配置系统深度解析

第2篇:配置系统深度解析

1. 配置系统概述

1.0 第1篇思考题解答

在深入学习配置系统之前,让我们先回顾并解答第1篇中提出的思考题,这将帮助我们更好地理解配置系统在整个架构中的作用。

思考题1:为什么 MyBatis 要采用三层架构设计?

答案要点

  • 职责分离:接口层提供API,核心处理层处理业务逻辑,基础支持层提供基础服务
  • 降低耦合:每层只依赖下一层,通过接口通信
  • 提高可扩展性:每层可独立扩展,支持不同实现策略
  • 便于维护:架构清晰,问题定位容易

配置系统的作用:Configuration 作为基础支持层的核心,为上层提供统一的配置管理服务。

思考题2:各个核心组件的职责分工有什么优势?

答案要点

  • 单一职责:每个组件专注特定功能,降低复杂度
  • 高内聚低耦合:组件内部高度相关,组件间依赖最小化
  • 协作机制:通过接口抽象、依赖注入、配置驱动实现协作

配置系统的协作:Configuration 通过依赖注入为其他组件提供配置信息,实现松耦合的协作。

思考题3:如何理解 MyBatis 的"半自动化"特性?

答案要点

  • 自动化部分:JDBC连接管理、参数绑定、结果映射、事务管理、缓存管理
  • 手动控制部分:SQL编写、映射配置、事务边界、性能优化
  • 优势:性能控制精确、灵活性高、学习成本适中

配置系统的作用:通过配置实现自动化和手动控制的平衡,提供灵活的配置机制。

思考题4:应该从哪个组件开始深入源码分析?

推荐顺序:Configuration → SqlSession → Executor → StatementHandler

从 Configuration 开始的原因

  • Configuration 是配置系统的核心,其他组件都依赖它
  • 理解配置系统有助于理解整个系统的构建过程
  • 为后续学习其他组件奠定基础

1.1 配置系统的作用和重要性

MyBatis 的配置系统是整个框架的核心基础,它负责:

  1. 统一配置管理:集中管理所有 MyBatis 相关的配置项
  2. 配置解析:解析 XML 和注解配置,构建内部数据结构
  3. 配置验证:验证配置的正确性和完整性
  4. 配置扩展:支持自定义配置项和扩展功能
  5. 性能优化:提供配置缓存和懒加载机制

重要提示:理解配置系统是深入 MyBatis 源码的关键,后续的会话管理、执行器、缓存等模块都依赖于配置系统。

1.2 配置文件的层次结构

MyBatis 的配置系统采用分层设计:

配置系统
├── 主配置文件 (mybatis-config.xml)
│   ├── 环境配置 (environments)
│   ├── 数据源配置 (dataSource)
│   ├── 事务管理配置 (transactionManager)
│   ├── 类型别名配置 (typeAliases)
│   ├── 类型处理器配置 (typeHandlers)
│   ├── 插件配置 (plugins)
│   ├── 缓存配置 (cache)
│   └── Mapper 配置 (mappers)
├── Mapper XML 配置文件
│   ├── SQL 语句定义
│   ├── 结果映射定义
│   ├── 参数映射定义
│   └── 缓存配置
└── Mapper 接口注解配置├── @Select、@Insert、@Update、@Delete├── @Results、@Result└── @Param、@Options

1.3 配置系统的核心组件

组件 职责 关键类
配置中心 统一管理所有配置项 Configuration
XML 解析器 解析主配置文件 XMLConfigBuilder
Mapper 解析器 解析 Mapper XML XMLMapperBuilder
注解解析器 解析 Mapper 注解 MapperAnnotationBuilder
配置验证器 验证配置正确性 内置验证逻辑

2. Configuration 类深度解析

2.1 Configuration 类的结构和职责

Configuration 类是 MyBatis 配置系统的核心,它承担着以下职责:

  1. 配置存储:存储所有 MyBatis 配置项
  2. 配置管理:提供配置项的增删改查功能
  3. 配置验证:验证配置的正确性和完整性
  4. 配置扩展:支持插件和自定义配置
  5. 性能优化:提供配置缓存和懒加载

2.2 核心属性分析

让我们深入分析 Configuration 类的核心属性:

public class Configuration {// 环境配置protected Environment environment;// 数据库相关配置protected boolean safeRowBoundsEnabled;protected boolean safeResultHandlerEnabled;protected boolean mapUnderscoreToCamelCase;protected boolean aggressiveLazyLoading;protected boolean multipleResultSetsEnabled;protected boolean useGeneratedKeys;protected boolean useColumnLabel;protected boolean callSettersOnNulls;protected boolean useActualParamName;protected boolean returnInstanceForEmptyRow;// 日志配置protected String logPrefix;protected Class<? extends Log> logImpl;// 缓存配置protected boolean cacheEnabled;protected LocalCacheScope localCacheScope;// 类型处理配置protected JdbcType jdbcTypeForNull;protected Set<String> lazyLoadTriggerMethods;// 超时配置protected Integer defaultStatementTimeout;protected Integer defaultFetchSize;protected ResultSetType defaultResultSetType;// 执行器配置protected ExecutorType defaultExecutorType;// 映射配置protected AutoMappingBehavior autoMappingBehavior;protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;// 核心注册表protected ReflectorFactory reflectorFactory;protected ObjectFactory objectFactory;protected ObjectWrapperFactory objectWrapperFactory;protected MapperRegistry mapperRegistry;protected InterceptorChain interceptorChain;protected TypeHandlerRegistry typeHandlerRegistry;protected TypeAliasRegistry typeAliasRegistry;protected LanguageDriverRegistry languageRegistry;// 映射存储protected Map<String, MappedStatement> mappedStatements;protected Map<String, Cache> caches;protected Map<String, ResultMap> resultMaps;protected Map<String, ParameterMap> parameterMaps;protected Map<String, KeyGenerator> keyGenerators;// 其他配置protected Properties variables;protected Set<String> loadedResources;protected String databaseId;protected Class<?> configurationFactory;protected Map<String, String> cacheRefMap;
}

2.3 核心方法分析

2.3.1 配置项管理方法

// 添加 MappedStatement
public void addMappedStatement(MappedStatement ms) {mappedStatements.put(ms.getId(), ms);
}// 获取 MappedStatement
public MappedStatement getMappedStatement(String id) {return mappedStatements.get(id);
}// 添加 Mapper
public <T> void addMapper(Class<T> type) {mapperRegistry.addMapper(type);
}// 获取 Mapper
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);
}

2.3.2 配置验证方法

// 验证配置完整性
public void validate() {// 验证必要的配置项if (environment == null) {throw new IllegalStateException("Environment was not set");}// 验证 Mapper 配置for (MappedStatement ms : mappedStatements.values()) {if (ms.getCache() != null && ms.getCache().getClass().equals(PerpetualCache.class)) {// 验证缓存配置}}
}

3. XML 配置解析流程

3.1 XMLConfigBuilder 源码分析

XMLConfigBuilder 是 MyBatis 主配置文件的解析器,它继承自 BaseBuilder:

public class XMLConfigBuilder extends BaseBuilder {private boolean parsed;private final XPathParser parser;private String environment;private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();public XMLConfigBuilder(Reader reader) {this(reader, null, null);}public XMLConfigBuilder(Reader reader, String environment) {this(reader, environment, null);}public XMLConfigBuilder(Reader reader, String environment, Properties props) {super(new Configuration());this.environment = environment;this.parser = new XPathParser(reader, true, props, new XMLMapperEntityResolver());}public Configuration parse() {if (parsed) {throw new BuilderException("Each XMLConfigBuilder can only be used once.");}parsed = true;parseConfiguration(parser.evalNode("/configuration"));return configuration;}private void parseConfiguration(XNode root) {try {// 解析 properties 配置propertiesElement(root.evalNode("properties"));// 解析 settings 配置Properties settings = settingsAsProperties(root.evalNode("settings"));loadCustomVfs(settings);loadCustomLogImpl(settings);loadCustomInterceptors(settings);loadCustomTypeHandlers(settings);loadCustomObjectFactory(settings);loadCustomObjectWrapperFactory(settings);loadCustomReflectorFactory(settings);settingsElement(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"));// 解析 settings 配置settingsElement(settings);// 解析 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);}}
}

3.2 主配置文件解析过程

3.2.1 Properties 配置解析

private void propertiesElement(XNode context) throws Exception {if (context != null) {Properties defaults = context.getChildrenAsProperties();String resource = context.getStringAttribute("resource");String url = context.getStringAttribute("url");if (resource != null && url != null) {throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");}if (resource != null) {defaults.putAll(Resources.getResourceAsProperties(resource));} else if (url != null) {defaults.putAll(Resources.getUrlAsProperties(url));}Properties vars = configuration.getVariables();if (vars != null) {defaults.putAll(vars);}parser.setVariables(defaults);configuration.setVariables(defaults);}
}

3.2.2 Settings 配置解析

private void settingsElement(Properties props) throws Exception {configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));configuration.setLogPrefix(props.getProperty("logPrefix"));configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}

3.3 配置项验证和默认值处理

MyBatis 在解析配置时会进行以下验证:

  1. 必需配置验证:检查必需的配置项是否存在
  2. 配置值验证:验证配置值的有效性和范围
  3. 依赖关系验证:检查配置项之间的依赖关系
  4. 默认值设置:为未配置的项设置合理的默认值

4. Mapper 配置解析

4.1 XMLMapperBuilder 源码分析

XMLMapperBuilder 负责解析 Mapper XML 文件:

public class XMLMapperBuilder extends BaseBuilder {private final XPathParser parser;private final MapperBuilderAssistant builderAssistant;private final Map<String, XNode> sqlFragments;private final String resource;public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments);}public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {this(new XPathParser(reader, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments);}private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {super(configuration);this.builderAssistant = new MapperBuilderAssistant(configuration, resource);this.parser = parser;this.sqlFragments = sqlFragments;this.resource = resource;}public void parse() {if (!configuration.isResourceLoaded(resource)) {configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.isEmpty()) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);cacheRefElement(context.evalNode("cache-ref"));cacheElement(context.evalNode("cache"));parameterMapElement(context.evalNodes("/mapper/parameterMap"));resultMapElements(context.evalNodes("/mapper/resultMap"));sqlElement(context.evalNodes("/mapper/sql"));buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}}
}

4.2 Mapper 接口和 XML 的绑定

4.2.1 命名空间绑定

private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {boundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {// ignore, bound type is not required}if (boundType != null) {if (!configuration.hasMapper(boundType)) {configuration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}
}

4.2.2 SQL 语句解析

private void buildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) {buildStatementFromContext(list, configuration.getDatabaseId());}buildStatementFromContext(list, null);
}private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try {statementParser.parseStatementNode();} catch (IncompleteElementException e) {configuration.addIncompleteStatement(statementParser);}}
}

4.3 SQL 语句的解析和存储

4.3.1 XMLStatementBuilder 源码分析

public class XMLStatementBuilder extends BaseBuilder {private final MapperBuilderAssistant builderAssistant;private final XNode context;private final String requiredDatabaseId;public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {super(configuration);this.builderAssistant = builderAssistant;this.context = context;this.requiredDatabaseId = databaseId;}public void parseStatementNode() {String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}String nodeName = context.getNode().getNodeName();SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);boolean useCache = context.getBooleanAttribute("useCache", isSelect);boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);// 解析 SQL 语句XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());processSelectKeyNodes(id, parameterTypeClass, langDriver);SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");String parameterMap = context.getStringAttribute("parameterMap");String resultType = context.getStringAttribute("resultType");Class<?> resultTypeClass = resolveClass(resultType);String resultMap = context.getStringAttribute("resultMap");String resultSetType = context.getStringAttribute("resultSetType");ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);String keyProperty = context.getStringAttribute("keyProperty");String keyColumn = context.getStringAttribute("keyColumn");String resultSets = context.getStringAttribute("resultSets");builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}
}

5. 注解配置解析

5.1 MapperAnnotationBuilder 源码分析

MapperAnnotationBuilder 负责解析 Mapper 接口上的注解:

public class MapperAnnotationBuilder {private final Configuration configuration;private final MapperBuilderAssistant builderAssistant;private final Class<?> type;public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {String resource = type.getName().replace('.', '/') + ".java (best guess)";this.configuration = configuration;this.builderAssistant = new MapperBuilderAssistant(configuration, resource);this.type = type;}public void parse() {String resource = type.toString();if (!configuration.isResourceLoaded(resource)) {loadXmlResource();configuration.addLoadedResource(resource);assistant.setCurrentNamespace(type.getName());parseCache();parseCacheRef();Method[] methods = type.getMethods();for (Method method : methods) {try {if (!method.isBridge()) {parseStatement(method);}} catch (IncompleteElementException e) {configuration.addIncompleteMethod(new MethodResolver(this, method));}}}parsePendingMethods();}private void parseStatement(Method method) {Class<?> parameterTypeClass = getParameterType(method);LanguageDriver languageDriver = getLanguageDriver(method);SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);if (sqlSource != null) {Options options = method.getAnnotation(Options.class);final String mappedStatementId = type.getName() + "." + method.getName();final SqlCommandType sqlCommandType = getSqlCommandType(method);final boolean isSelect = sqlCommandType == SqlCommandType.SELECT;final boolean flushCache = !isSelect;final boolean useCache = isSelect;KeyGenerator keyGenerator;String keyProperty = null;String keyColumn = null;if (options != null) {if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;keyProperty = options.keyProperty();keyColumn = options.keyColumn();} else {keyGenerator = NoKeyGenerator.INSTANCE;}} else {keyGenerator = NoKeyGenerator.INSTANCE;}Integer fetchSize = null;Integer timeout = null;StatementType statementType = StatementType.PREPARED;ResultSetType resultSetType = configuration.getDefaultResultSetType();boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = !isSelect;boolean useCache = isSelect;if (options != null) {if (options.useCache() != null) {useCache = options.useCache();}if (options.flushCache() != null) {flushCache = options.flushCache();}if (options.fetchSize() > -1) {fetchSize = options.fetchSize();}if (options.timeout() > -1) {timeout = options.timeout();}if (options.statementType() != StatementType.DEFAULT) {statementType = options.statementType();}if (options.resultSetType() != ResultSetType.DEFAULT) {resultSetType = options.resultSetType();}}String resultMapId = null;if (method.getAnnotation(Results.class) != null) {resultMapId = parseResults(method);} else if (isSelect) {resultMapId = parseResultMap(method);}assistant.addMappedStatement(mappedStatementId, sqlSource, statementType, sqlCommandType, fetchSize, timeout, null, parameterTypeClass, resultMapId, getReturnType(method), resultSetType, flushCache, useCache, false, keyGenerator, keyProperty, keyColumn, null, languageDriver, null);}}
}

5.2 注解与 XML 的优先级处理

MyBatis 处理注解和 XML 配置的优先级规则:

  1. XML 优先:如果同时存在 XML 和注解配置,XML 配置优先
  2. 注解补充:注解配置作为 XML 配置的补充
  3. 冲突处理:相同配置项冲突时,XML 配置覆盖注解配置

5.3 动态 SQL 注解解析

MyBatis 支持通过注解实现动态 SQL:

@Select("<script>" +"SELECT * FROM users WHERE 1=1" +"<if test='name != null'> AND name = #{name}</if>" +"<if test='email != null'> AND email = #{email}</if>" +"</script>")
List<User> findUsers(@Param("name") String name, @Param("email") String email);

6. 配置系统扩展

6.1 自定义配置项处理

MyBatis 支持通过插件系统扩展配置:

@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class CustomConfigInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {// 自定义配置处理逻辑return invocation.proceed();}
}

6.2 插件系统的配置集成

插件系统与配置系统的集成:

private void pluginElement(XNode parent) throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {String interceptor = child.getStringAttribute("interceptor");Properties properties = child.getChildrenAsProperties();Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();interceptorInstance.setProperties(properties);configuration.addInterceptor(interceptorInstance);}}
}

6.3 配置系统的性能优化

MyBatis 配置系统的性能优化策略:

  1. 懒加载:延迟加载非必需的配置项
  2. 缓存机制:缓存解析后的配置对象
  3. 批量处理:批量解析相关配置项
  4. 内存优化:优化配置对象的内存使用

7. 实践案例

7.1 跟踪配置解析的完整流程

让我们通过一个完整的例子来跟踪配置解析流程:

public class ConfigurationParseExample {public static void main(String[] args) throws IOException {// 1. 创建 XMLConfigBuilderString resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, "development", null);// 2. 解析配置文件Configuration configuration = parser.parse();// 3. 验证配置configuration.validate();// 4. 使用配置SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);SqlSession session = sqlSessionFactory.openSession();// 5. 获取 MapperUserMapper mapper = session.getMapper(UserMapper.class);// 6. 执行查询User user = mapper.selectById(1);System.out.println("查询结果: " + user);session.close();}
}

执行流程分析

  1. XMLConfigBuilder 创建:创建配置解析器
  2. 配置文件解析:解析 mybatis-config.xml
  3. Configuration 构建:构建 Configuration 对象
  4. 配置验证:验证配置的正确性
  5. SqlSessionFactory 创建:基于配置创建工厂
  6. SqlSession 创建:创建数据库会话
  7. Mapper 获取:获取 Mapper 接口
  8. SQL 执行:执行数据库操作

7.2 分析配置项的生命周期

配置项的生命周期管理:

  1. 解析阶段:从 XML 或注解解析配置项
  2. 存储阶段:将配置项存储到 Configuration 对象
  3. 验证阶段:验证配置项的正确性
  4. 使用阶段:在运行时使用配置项
  5. 销毁阶段:在应用关闭时清理配置项

7.3 自定义配置解析器

实现自定义配置解析器:

public class CustomConfigParser {public void parseCustomConfig(Configuration configuration, String configFile) {// 解析自定义配置文件Properties props = loadConfigFile(configFile);// 处理自定义配置项String customProperty = props.getProperty("custom.property");if (customProperty != null) {// 设置自定义配置configuration.setVariables(props);}}private Properties loadConfigFile(String configFile) {Properties props = new Properties();try (InputStream is = Resources.getResourceAsStream(configFile)) {props.load(is);} catch (IOException e) {throw new RuntimeException("Failed to load config file: " + configFile, e);}return props;}
}

思考题

  1. 为什么 MyBatis 要设计如此复杂的配置系统?
  2. 配置系统的扩展性体现在哪些方面?
  3. 如何优化配置解析的性能?
  4. 基于配置系统的理解,你认为应该从哪个组件开始深入源码分析?

下篇预告:在下一篇文章中,我们将深入分析 SqlSession 会话管理机制,并详细解答以上思考题,帮助大家更好地理解 MyBatis 的配置系统在整个架构中的作用。

http://www.wxhsa.cn/company.asp?id=4498

相关文章:

  • eSIM笔记
  • 拯救者Y7000过热降频解决方法
  • 普通大语言模型(LLM)和向量化模型(Embedding Model)的区别以及其各自的作用
  • ios电脑如何改成windows系统
  • PythonFlask 运用 DBUtils 创建通用连接池
  • 帧同步、快照同步与状态同步
  • 内存一致性模型
  • MahMetro 框架学习
  • 基于MATLAB的标准化降水蒸散指数(SPEI)实现
  • Prometheus Probe 监控配置文档
  • 客户案例|邦普循环x甄知科技,筑牢高效智能的IT运维底座
  • VMware Exporter 指标转换方案
  • 可5V使用引脚兼容STM32F103C8T6的国产32位MCU
  • git clone操作报错diffie-hellman-group1-sha1的解决方案
  • Celery inspect 常用命令手册
  • 都可以!燕千云ITSM一站式接入全球主流AI大模型
  • 删边最短路
  • 问题解决模板
  • 一站式接入全球股票数据:日本、美国、印度、马来西亚等多国API对接实战
  • 基于MATLAB的图像处理程序
  • 跨网文件安全交换系统推荐厂商详解
  • 走迷宫
  • MVC 架构解析
  • 鸿蒙应用开发从入门到实战(五):ArkUI概述
  • 好用的跨网文件安全交换系统:守护企业数据流转的核心屏障!
  • SIM笔记
  • 2025第五届“长城杯”网络安全大赛暨京津冀蒙网络安全技能竞赛 WP Web全
  • FTP替代工具哪个产品好,高效安全之选
  • c++之内存对齐模板类aligned_storage
  • ABC 423先慢慢改吧题解