《别只会写XML了!从源码看懂MyBatis配置系统如何驱动整个框架》
第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 配置系统的核心组件
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2. Configuration 类深度解析
2.1 Configuration 类的结构和职责
Configuration 类是 MyBatis 配置系统的核心,它承担着以下职责:
-
1. 配置存储:存储所有 MyBatis 配置项 -
2. 配置管理:提供配置项的增删改查功能 -
3. 配置验证:验证配置的正确性和完整性 -
4. 配置扩展:支持插件和自定义配置 -
5. 性能优化:提供配置缓存和懒加载
2.2 核心属性分析
让我们深入分析 Configuration 类的核心属性:
publicclassConfiguration {// 环境配置protected Environment environment;// 数据库相关配置protectedboolean safeRowBoundsEnabled;protectedboolean safeResultHandlerEnabled;protectedboolean mapUnderscoreToCamelCase;protectedboolean aggressiveLazyLoading;protectedboolean multipleResultSetsEnabled;protectedboolean useGeneratedKeys;protectedboolean useColumnLabel;protectedboolean callSettersOnNulls;protectedboolean useActualParamName;protectedboolean returnInstanceForEmptyRow;// 日志配置protected String logPrefix;protected Class<? extendsLog> logImpl;// 缓存配置protectedboolean 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 配置项管理方法
// 添加 MappedStatementpublicvoidaddMappedStatement(MappedStatement ms) { mappedStatements.put(ms.getId(), ms);}// 获取 MappedStatementpublic MappedStatement getMappedStatement(String id) {return mappedStatements.get(id);}// 添加 Mapperpublic <T> voidaddMapper(Class<T> type) { mapperRegistry.addMapper(type);}// 获取 Mapperpublic <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}
2.3.2 配置验证方法
// 验证配置完整性publicvoidvalidate() {// 验证必要的配置项if (environment == null) {thrownewIllegalStateException("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:
publicclassXMLConfigBuilderextendsBaseBuilder {privateboolean parsed;privatefinal XPathParser parser;private String environment;privatefinalReflectorFactorylocalReflectorFactory=newDefaultReflectorFactory();publicXMLConfigBuilder(Reader reader) {this(reader, null, null); }publicXMLConfigBuilder(Reader reader, String environment) {this(reader, environment, null); }publicXMLConfigBuilder(Reader reader, String environment, Properties props) {super(newConfiguration());this.environment = environment;this.parser = newXPathParser(reader, true, props, newXMLMapperEntityResolver()); }public Configuration parse() {if (parsed) {thrownewBuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; parseConfiguration(parser.evalNode("/configuration"));return configuration; }privatevoidparseConfiguration(XNode root) {try {// 解析 properties 配置 propertiesElement(root.evalNode("properties"));// 解析 settings 配置Propertiessettings= 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) {thrownewBuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }}
3.2 主配置文件解析过程
3.2.1 Properties 配置解析
privatevoidpropertiesElement(XNode context)throws Exception {if (context != null) {Propertiesdefaults= context.getChildrenAsProperties();Stringresource= context.getStringAttribute("resource");Stringurl= context.getStringAttribute("url");if (resource != null && url != null) {thrownewBuilderException("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)); } elseif (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); }Propertiesvars= configuration.getVariables();if (vars != null) { defaults.putAll(vars); } parser.setVariables(defaults); configuration.setVariables(defaults); }}
3.2.2 Settings 配置解析
privatevoidsettingsElement(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 文件:
publicclassXMLMapperBuilderextendsBaseBuilder {privatefinal XPathParser parser;privatefinal MapperBuilderAssistant builderAssistant;privatefinal Map<String, XNode> sqlFragments;privatefinal String resource;publicXMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {this(newXPathParser(inputStream, true, configuration.getVariables(), newXMLMapperEntityResolver()), configuration, resource, sqlFragments); }publicXMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {this(newXPathParser(reader, true, configuration.getVariables(), newXMLMapperEntityResolver()), configuration, resource, sqlFragments); }privateXMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {super(configuration);this.builderAssistant = newMapperBuilderAssistant(configuration, resource);this.parser = parser;this.sqlFragments = sqlFragments;this.resource = resource; }publicvoidparse() {if (!configuration.isResourceLoaded(resource)) { configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }privatevoidconfigurationElement(XNode context) {try {Stringnamespace= context.getStringAttribute("namespace");if (namespace == null || namespace.isEmpty()) {thrownewBuilderException("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) {thrownewBuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); } }}
4.2 Mapper 接口和 XML 的绑定
4.2.1 命名空间绑定
privatevoidbindMapperForNamespace() {Stringnamespace= 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 语句解析
privatevoidbuildStatementFromContext(List<XNode> list) {if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } buildStatementFromContext(list, null);}privatevoidbuildStatementFromContext(List<XNode> list, String requiredDatabaseId) {for (XNode context : list) {finalXMLStatementBuilderstatementParser=newXMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } }}
4.3 SQL 语句的解析和存储
4.3.1 XMLStatementBuilder 源码分析
publicclassXMLStatementBuilderextendsBaseBuilder {privatefinal MapperBuilderAssistant builderAssistant;privatefinal XNode context;privatefinal String requiredDatabaseId;publicXMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {super(configuration);this.builderAssistant = builderAssistant;this.context = context;this.requiredDatabaseId = databaseId; }publicvoidparseStatementNode() {Stringid= context.getStringAttribute("id");StringdatabaseId= context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return; }StringnodeName= context.getNode().getNodeName();SqlCommandTypesqlCommandType= SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));booleanisSelect= sqlCommandType == SqlCommandType.SELECT;booleanflushCache= context.getBooleanAttribute("flushCache", !isSelect);booleanuseCache= context.getBooleanAttribute("useCache", isSelect);booleanresultOrdered= context.getBooleanAttribute("resultOrdered", false);// 解析 SQL 语句XMLIncludeTransformerincludeParser=newXMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); processSelectKeyNodes(id, parameterTypeClass, langDriver);SqlSourcesqlSource= langDriver.createSqlSource(configuration, context, parameterTypeClass);StatementTypestatementType= StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));IntegerfetchSize= context.getIntAttribute("fetchSize");Integertimeout= context.getIntAttribute("timeout");StringparameterMap= context.getStringAttribute("parameterMap");StringresultType= context.getStringAttribute("resultType"); Class<?> resultTypeClass = resolveClass(resultType);StringresultMap= context.getStringAttribute("resultMap");StringresultSetType= context.getStringAttribute("resultSetType");ResultSetTyperesultSetTypeEnum= resolveResultSetType(resultSetType);StringkeyProperty= context.getStringAttribute("keyProperty");StringkeyColumn= context.getStringAttribute("keyColumn");StringresultSets= 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 接口上的注解:
publicclassMapperAnnotationBuilder {privatefinal Configuration configuration;privatefinal MapperBuilderAssistant builderAssistant;privatefinal Class<?> type;publicMapperAnnotationBuilder(Configuration configuration, Class<?> type) {Stringresource= type.getName().replace('.', '/') + ".java (best guess)";this.configuration = configuration;this.builderAssistant = newMapperBuilderAssistant(configuration, resource);this.type = type; }publicvoidparse() {Stringresource= 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(newMethodResolver(this, method)); } } } parsePendingMethods(); }privatevoidparseStatement(Method method) { Class<?> parameterTypeClass = getParameterType(method);LanguageDriverlanguageDriver= getLanguageDriver(method);SqlSourcesqlSource= getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);if (sqlSource != null) {Optionsoptions= method.getAnnotation(Options.class);finalStringmappedStatementId= type.getName() + "." + method.getName();finalSqlCommandTypesqlCommandType= getSqlCommandType(method);finalbooleanisSelect= sqlCommandType == SqlCommandType.SELECT;finalbooleanflushCache= !isSelect;finalbooleanuseCache= isSelect; KeyGenerator keyGenerator;StringkeyProperty=null;StringkeyColumn=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; }IntegerfetchSize=null;Integertimeout=null;StatementTypestatementType= StatementType.PREPARED;ResultSetTyperesultSetType= configuration.getDefaultResultSetType();booleanisSelect= sqlCommandType == SqlCommandType.SELECT;booleanflushCache= !isSelect;booleanuseCache= 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(); } }StringresultMapId=null;if (method.getAnnotation(Results.class) != null) { resultMapId = parseResults(method); } elseif (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})})publicclassCustomConfigInterceptorimplementsInterceptor {@Overridepublic Object intercept(Invocation invocation)throws Throwable {// 自定义配置处理逻辑return invocation.proceed(); }}
6.2 插件系统的配置集成
插件系统与配置系统的集成:
privatevoidpluginElement(XNode parent)throws Exception {if (parent != null) {for (XNode child : parent.getChildren()) {Stringinterceptor= child.getStringAttribute("interceptor");Propertiesproperties= child.getChildrenAsProperties();InterceptorinterceptorInstance= (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); interceptorInstance.setProperties(properties); configuration.addInterceptor(interceptorInstance); } }}
6.3 配置系统的性能优化
MyBatis 配置系统的性能优化策略:
-
1. 懒加载:延迟加载非必需的配置项 -
2. 缓存机制:缓存解析后的配置对象 -
3. 批量处理:批量解析相关配置项 -
4. 内存优化:优化配置对象的内存使用
7. 实践案例
7.1 跟踪配置解析的完整流程
让我们通过一个完整的例子来跟踪配置解析流程:
publicclassConfigurationParseExample {publicstaticvoidmain(String[] args)throws IOException {// 1. 创建 XMLConfigBuilderStringresource="mybatis-config.xml";InputStreaminputStream= Resources.getResourceAsStream(resource);XMLConfigBuilderparser=newXMLConfigBuilder(inputStream, "development", null);// 2. 解析配置文件Configurationconfiguration= parser.parse();// 3. 验证配置 configuration.validate();// 4. 使用配置SqlSessionFactorysqlSessionFactory=newSqlSessionFactoryBuilder().build(configuration);SqlSessionsession= sqlSessionFactory.openSession();// 5. 获取 MapperUserMappermapper= session.getMapper(UserMapper.class);// 6. 执行查询Useruser= 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 自定义配置解析器
实现自定义配置解析器:
publicclassCustomConfigParser {publicvoidparseCustomConfig(Configuration configuration, String configFile) {// 解析自定义配置文件Propertiesprops= loadConfigFile(configFile);// 处理自定义配置项StringcustomProperty= props.getProperty("custom.property");if (customProperty != null) {// 设置自定义配置 configuration.setVariables(props); } }private Properties loadConfigFile(String configFile) {Propertiesprops=newProperties();try (InputStreamis= Resources.getResourceAsStream(configFile)) { props.load(is); } catch (IOException e) {thrownewRuntimeException("Failed to load config file: " + configFile, e); }return props; }}
思考题:
-
1. 为什么 MyBatis 要设计如此复杂的配置系统? -
2. 配置系统的扩展性体现在哪些方面? -
3. 如何优化配置解析的性能? -
4. 基于配置系统的理解,你认为应该从哪个组件开始深入源码分析?
下篇预告:在下一篇文章中,我们将深入分析 SqlSession 会话管理机制,并详细解答以上思考题,帮助大家更好地理解 MyBatis 的配置系统在整个架构中的作用。
夜雨聆风
