您的位置:首页 > 其它

Mybatis 源码解析 -- 基于配置的源码解析(二)

2017-11-14 15:13 681 查看
mapper解析

接着上篇的配置,本篇主要讲解mappers标签

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="dev">
<environment id="dev">
<transactionManager type="JDBC" />
<dataSource type="com.xiaoye.clearworld.datasource.CustomDruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
<environment id="uat">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.xiaoye.clearworld.persistence.WebsiteZlgeduCompanyMapper"/>
<!-- 更多………… -->
</mappers>
</configuration>

该标签的主要功能:标记Mybatis数据库接口或Mybatis映射文件,配置方式有三:

<package name=""/> 数据库接口所在父包路径
<mapper class=""/> 数据库接口类路径
<mapper resource=""/> 数据库映射文件路径
<mapper url=""/> 数据库映射文件资源路径

源码解析:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)

private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));

// mappers 标签解析
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

进入到方法

private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) { // 解析 mappers 的子节点
if ("package".equals(child.getName())) { // 配置接口扫描包 package 节点(扫描包)
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage); // 如果配置了package节点,mybatis默认会将该包下所有的接口类加载到mybatis中
} else { // 单独配置
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) { // resource 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) {
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.");
}
}
}
}
}

得到mappers节点后,解析其子节点。

如果子节点中存在 package 节点,则解析方式如下:

获取包路径 name属性值

解析:configuration.addMappers(mapperPackage);
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}

/**
* @since 3.2.2 也就是mybatis3.2.2版本才增加了这一功能
*/
public void addMappers(String packageName) {
// 添加mappers,并指定要添加的类或子类的类型
addMappers(packageName, Object.class);
}

/**
* @since 3.2.2
*/
public void addMappers(String packageName, Class<?> superType) {
// 工具类:用来判断是否是指定类或其子类,并存储结果
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
// 查找类
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}

public ResolverUtil<T> find(Test test, String packageName) {
// 获取包路径
String path = getPackagePath(packageName);

try {
// 获取包路径下的所有文件路径(格式:com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.class)
List<String> children = VFS.getInstance().list(path);
// 遍历包路径下的所有文件路径
for (String child : children) {
// 过滤 class 文件
if (child.endsWith(".class")) {
// 判断是否是指定类或指定类的子类
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}

return this;
}

// 所以包路径的格式类似:com.xxx.xxx.xxx
protected String getPackagePath(String packageName) {
return packageName == null ? null : packageName.replace('.', '/');
}

// 判断是否是指定类或指定类的子类
protected void addIfMatching(Test test, String fqn) {
try {
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
// 得到类的Class类型
Class<?> type = loader.loadClass(externalName);
// 判断,符合存储到set集合中
if (test.matches(type)) {
matches.add((Class<T>) type);
}
} catch (Throwable t) {
log.warn("Could not examine class '" + fqn + "'" + " due to a " +
t.getClass().getName() + " with message: " + t.getMessage());
}
}

// 添加
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<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
分析上面的代码:a、根据包名称转换得到包路径;b、根据包路径获取该包下所有的文件;c、遍历文件,取出class结尾的文件;d、判断是否是指定类或指定类的子类;e:添加并解析

具体的解析(非package节点的解析直接进入这):
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<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
分析:1、首先是接口;2、之前未添加过haddMapper;3、存储:knownMappers.put(type, new MapperProxyFactory<T>(type));4、映射文件解析

映射文件解析
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;

sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);

sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) { // 判断是否解析过
loadXmlResource(); // 解析映射文件
configuration.addLoadedResource(resource); // 添加已解析
assistant.setCurrentNamespace(type.getName()); // 校验映射文件命名空间
parseCache(); // 缓存解析
parseCacheRef(); // TODO
Method[] methods = type.getMethods(); // 获取接口的所有方法
for (Method method : methods) { // 遍历
try {
// issue #237
if (!method.isBridge()) { //
parseStatement(method); //
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods(); //
}
映射文件解析:loadXmlResource();

private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// 映射文件默认和数据库接口文件在同一包下
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e) {
// ignore, resource is not required
}
if (inputStream != null) {
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  mybatis