第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 的配置系统采用分层设计:
scss
配置系统
├── 主配置文件 (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 类的核心属性:
java
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 配置项管理方法
java
// 添加 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 配置验证方法
java
// 验证配置完整性
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:
java
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 配置解析
java
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 配置解析
java
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 文件:
java
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 命名空间绑定
java
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 语句解析
java
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 源码分析
java
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 接口上的注解:
java
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:
java
@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 支持通过插件系统扩展配置:
java
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class CustomConfigInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 自定义配置处理逻辑
return invocation.proceed();
}
}
6.2 插件系统的配置集成
插件系统与配置系统的集成:
java
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 跟踪配置解析的完整流程
让我们通过一个完整的例子来跟踪配置解析流程:
java
public class ConfigurationParseExample {
public static void main(String[] args) throws IOException {
// 1. 创建 XMLConfigBuilder
String 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. 获取 Mapper
UserMapper 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 自定义配置解析器
实现自定义配置解析器:
java
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 的配置系统在整个架构中的作用。