目录
- mybatis中mapper代理的生成过程
- 与Spring集成时mapper代理的生成过程
- 与SpringBoot集成时mapper代理的生成过程
mybatis中mapper代理的生成过程
构建代理类工厂
从入口点开始一步一步看,首先SqlSessionFactoryBuilder
类中build()
方法加载配置文件
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
// ...省略
}
}
将配置文件读取为XMLConfigBuilder
对象,并调用parse()
方法来解析文件,进到parse()
中
public Configuration parse() {
// ...省略
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
可以看到具体的解析过程是在parseConfiguration
方法中进行的。
private void parseConfiguration(XNode root) {
try {
// ...省略
//解析mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
这里重点看一下最后解析mapper的方法mapperElement(root.evalNode("mappers"))
,进到方法里,
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// package 形式加载 ,加载package下的所有class文件
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) {
// 通过Mapper.xml 加载
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) {
// 通过Mapper.xml 加载
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文件加载
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.");
}
}
}
}
}
整个mapperElement()
方法就是加载mapper的过程了,可以看到加载mapper
有两种形式:通过class文件和通过xml文件。
构建mapper代理的过程也就是从这开始的,那就一步一步分析。
看一下通过XML文件加载的过程,mybatis将mapper相关的配置读取为一个XMLMapperBuilder
对象,并通过parse()
方法进行解析,进到这个方法中
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 加载xml文件
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 加载mapper class文件
bindMapperForNamespace();
}
// ...省略
}
parse()
方法做了主要做了两件事,加载xml文件和加载class文件。
看一下加载xml的过程
private void configurationElement(XNode context) {
try {
// 获取xml文件的namespace
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 保存获取xml文件的namespace
builderAssistant.setCurrentNamespace(namespace);
// ...省略
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
本文是分析mapper代理的生成过程,所以加载xml的具体细节就不详细分析了,这里注意的是读取xml文件中namespace
标签的值,并将值设置到builderAssistant
对象中
现在回过头来看一下加载class文件的过程。进到bindMapperForNamespace()
方法中去
private void bindMapperForNamespace() {