Mybatis Mapper 接口工作原理 1 - knownMappers 初始化
knownMappers
是 MyBatis 中 MapperRegistry
的一个核心属性,用于存储所有注册的 Mapper 接口和对应的 MapperProxyFactory
。它的初始化过程涉及 MyBatis 的配置解析和 Mapper 接口的注册。下面我们从源码级别详细分析 knownMappers
的初始化过程。
1. knownMappers
的定义
knownMappers
是 MapperRegistry
中的一个 Map
,定义如下:
public class MapperRegistry {
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
}
knownMappers
:存储 Mapper 接口和对应的MapperProxyFactory
。MapperProxyFactory
:用于创建 Mapper 接口的代理对象。
2. knownMappers
的初始化过程
knownMappers
的初始化过程分为以下几个步骤:
2.1 MyBatis 配置文件的解析
MyBatis 在启动时会解析配置文件(如 mybatis-config.xml
),并将配置信息加载到 Configuration
对象中。Configuration
是 MyBatis 的核心配置类,负责管理所有的配置信息。
2.1.1 解析 <mappers>
标签
在 MyBatis 配置文件中,<mappers>
标签用于注册 Mapper 接口或 XML 文件。例如:
<mappers>
<mapper class="com.example.mapper.UserMapper"/>
</mappers>
MyBatis 通过 XMLConfigBuilder
解析配置文件,并调用 mapperElement
方法处理 <mappers>
标签:
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 加载 XML 文件
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// 加载 URL 资源
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// 加载 Mapper 接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
configuration.addMapper()
:将 Mapper 接口注册到Configuration
中。
2.2 Configuration.addMapper()
的源码分析
Configuration.addMapper()
方法用于将 Mapper 接口注册到 Configuration
中,并最终注册到 MapperRegistry
的 knownMappers
中:
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
knownMappers.put(type, new MapperProxyFactory<>(type))
:将 Mapper 接口和对应的MapperProxyFactory
注册到knownMappers
中。MapperAnnotationBuilder
:用于解析 Mapper 接口上的注解(如@Select
、@Insert
等)。
2.3 MapperProxyFactory
的创建
MapperProxyFactory
是用于创建 Mapper 接口代理对象的工厂类。它的构造函数如下:
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
}
mapperInterface
:Mapper 接口的类型。
2.4 MapperAnnotationBuilder.parse()
的源码分析
MapperAnnotationBuilder
用于解析 Mapper 接口上的注解,并将其转换为 MyBatis 的 MappedStatement
:
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
for (Method method : type.getMethods()) {
if (!canHaveStatement(method)) {
continue;
}
if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
|| method.getAnnotation(Insert.class) != null
|| method.getAnnotation(Update.class) != null
|| method.getAnnotation(Delete.class) != null) {
parseStatement(method);
}
}
parsePendingMethods();
}
}
parseStatement()
:解析方法上的注解,并创建MappedStatement
。MappedStatement
:封装了 SQL 语句的配置信息。
3. 总结
knownMappers
的初始化过程可以概括为以下步骤:
-
解析配置文件:
- MyBatis 解析配置文件中的
<mappers>
标签,加载 Mapper 接口或 XML 文件。
- MyBatis 解析配置文件中的
-
注册 Mapper 接口:
- 调用
Configuration.addMapper()
方法,将 Mapper 接口注册到MapperRegistry
的knownMappers
中。
- 调用
-
创建
MapperProxyFactory
:- 为每个 Mapper 接口创建
MapperProxyFactory
,并将其存储到knownMappers
中。
- 为每个 Mapper 接口创建
-
解析注解:
- 使用
MapperAnnotationBuilder
解析 Mapper 接口上的注解,并创建MappedStatement
。
- 使用
通过以上流程,knownMappers
完成了初始化,并存储了所有注册的 Mapper 接口和对应的 MapperProxyFactory
。这些信息在后续的 Mapper 接口代理对象创建和 SQL 查询中起到了关键作用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具