第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 的配置系统是整个框架的核心基础,它负责:
- 统一配置管理:集中管理所有 MyBatis 相关的配置项
- 配置解析:解析 XML 和注解配置,构建内部数据结构
- 配置验证:验证配置的正确性和完整性
- 配置扩展:支持自定义配置项和扩展功能
- 性能优化:提供配置缓存和懒加载机制
重要提示:理解配置系统是深入 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 配置系统的核心,它承担着以下职责:
- 配置存储:存储所有 MyBatis 配置项
- 配置管理:提供配置项的增删改查功能
- 配置验证:验证配置的正确性和完整性
- 配置扩展:支持插件和自定义配置
- 性能优化:提供配置缓存和懒加载
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 在解析配置时会进行以下验证:
- 必需配置验证:检查必需的配置项是否存在
- 配置值验证:验证配置值的有效性和范围
- 依赖关系验证:检查配置项之间的依赖关系
- 默认值设置:为未配置的项设置合理的默认值
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 配置的优先级规则:
- XML 优先:如果同时存在 XML 和注解配置,XML 配置优先
- 注解补充:注解配置作为 XML 配置的补充
- 冲突处理:相同配置项冲突时,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 配置系统的性能优化策略:
- 懒加载:延迟加载非必需的配置项
- 缓存机制:缓存解析后的配置对象
- 批量处理:批量解析相关配置项
- 内存优化:优化配置对象的内存使用
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();}
}
执行流程分析:
- XMLConfigBuilder 创建:创建配置解析器
- 配置文件解析:解析 mybatis-config.xml
- Configuration 构建:构建 Configuration 对象
- 配置验证:验证配置的正确性
- SqlSessionFactory 创建:基于配置创建工厂
- SqlSession 创建:创建数据库会话
- Mapper 获取:获取 Mapper 接口
- SQL 执行:执行数据库操作
7.2 分析配置项的生命周期
配置项的生命周期管理:
- 解析阶段:从 XML 或注解解析配置项
- 存储阶段:将配置项存储到 Configuration 对象
- 验证阶段:验证配置项的正确性
- 使用阶段:在运行时使用配置项
- 销毁阶段:在应用关闭时清理配置项
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;}
}
思考题:
- 为什么 MyBatis 要设计如此复杂的配置系统?
- 配置系统的扩展性体现在哪些方面?
- 如何优化配置解析的性能?
- 基于配置系统的理解,你认为应该从哪个组件开始深入源码分析?
下篇预告:在下一篇文章中,我们将深入分析 SqlSession 会话管理机制,并详细解答以上思考题,帮助大家更好地理解 MyBatis 的配置系统在整个架构中的作用。