问题:mybatis中mapper接口和xml文件是如何对应起来的。
在mybatis-config.xml中配置一个mapper
<mappers>
<mapper resource="com/tiantao/learn/mappers/UserMapper.xml" />
</mappers>是最简单最直接的方式了。这样我们就可以通过SqlSession来获取这个mapper了。
表明上我们获取mapper是通过sqlSession的getMapper函数,稍微看一下源码,发现实际sqlSession调用的Configuration的getMapper函数。继续跟下去,Configuration又调用MapperRegistry类的getMapper函数。
看来MapperRegistry才是最终管理mapper注册和获取的类。
那么mybatis是怎么通过一个xml文件,来注册一个对应的mapper的呢。
研究最简单的加载方式:
public class App
{
public static void main( String[] args )
{
String resource = "mybatis-config.xml";
try {
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}来看一下调用栈:

当我们看到 下面的这个函数实现,心里就豁然开朗了。xml的各种配置方式都在这。
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) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(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) {
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.");
}
}
}
}
}
再往下看 当resource不为空的时候,最终会去执行mapperParser.parse();这个函数中关键的一个地方,bindMapperForNamespace();
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)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}"namespace" 是关键,就是通过这个命名空间来找到对应的接口的,所以要注册一个mapper最终还有要找到这个接口。
原来怀疑是可以通过xml文件的内容直接反射来注册一个mapper的想法是错误的!
最后看一下完整的调用栈:

本文详细解析了MyBatis中Mapper接口与XML文件的对应原理。通过具体代码示例,介绍了如何通过mybatis-config.xml配置文件加载Mapper,并深入探讨了Configuration与MapperRegistry类在这一过程中的作用。
545

被折叠的 条评论
为什么被折叠?



