写在前面
本文主要是分析使用注解方式配置的mapper接口的解析过程,作为这篇文章的补充。想要搭建调试环境的话,可以参考这篇文章。
想要系统学习的,可以参考这篇文章,重要!!!。
1:入口
在重新回顾下,可能如下的测试代码:
String resource = "mybatis/mybatis-config-annotation.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// <202108101757>
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
<202108101757>
处源码如下:
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)
public SqlSessionFactory build(InputStream inputStream) {
// <202108101758>
return build(inputStream, null, null);
}
<202108101758>
处源码如下:
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
// <202108101759>
return build(inputStream, null, properties);
}
<202108101759>
处源码如下:
org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
// <202108101800>
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
...snip...
}
}
<202108101800>
处parser.parse()
源码如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#parse
public Configuration parse() {
...snip...
// 解析全局配置文件中的configuration节点
// <202108101801>
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
<202108101801>
处是解析全局配置文件中的configuration节点,源码如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration
private void parseConfiguration(XNode root) {
try {
// 解析全局配置文件中的properties节点
propertiesElement(root.evalNode("properties"));
// 解析全局配置文件中的typeAliases节点
typeAliasesElement(root.evalNode("typeAliases"));
// 解析全局配置文件的plugins节点
pluginElement(root.evalNode("plugins"));
// 解析全局配置文件中的objectFactory节点
objectFactoryElement(root.evalNode("objectFactory"));
// 解析全局配置文件中的objectWrapperFactory节点
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析全局配置文件中的settings节点
settingsElement(root.evalNode("settings"));
// 解析全局配置文件中的environments节点
environmentsElement(root.evalNode("environments"));
// 解析全局配置文件中的databseIdProvider节点
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析全局配置文件中的typeHandlers节点
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析全局配置文件中的mappers节点,重要!!!
// <202108101805>
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
<202108101805>
处是重点中的重点,解析mapper映射信息可能是xml方式也可能是注解方式
,源码如下:
org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElement
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// package方式直接配置要扫描的包路径
if ("package".equals(child.getName())) {
// 获取通过name属性配置的包路径
String mapperPackage = child.getStringAttribute("name");
// 执行解析
// <202108101808>
configuration.addMappers(mapperPackage);
} else {
...snip...
}
}
}
}
<202108101808>
处是对要扫描的包路径执行具体的解析,也就是我们要分析的入口,具体可以参考2:注解配置解析
。
2:注解配置解析
在1:入口
我们已经分析了调用的过程,这里就一起来具体看下,源码如下:
org.apache.ibatis.session.Configuration#addMappers(java.lang.String)
public void addMappers(String packageName) {
mapperRegistry.addMappers(packageName);
}
一直执行到如下方法:
org.apache.ibatis.binding.MapperRegistry#addMappers(java.lang.String, java.lang.Class<?>)
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
// new ResolverUtil.IsA(superType):创建ResolverUtil的IsA内部内部类,并将superType作为参数,这里的值一般都是java.lang.Object
// packageName:要查找的包路径
// 总体就是从指定的包路径下查找执行类型的.class文件
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
// 获取查找到的class集合
// <202108111702>
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// 循环依次处理每个类的注解信息(如果需要的话)
for (Class<?> mapperClass : mapperSet) {
// 处理注解,并添加映射mapper
// <202108111740>
addMapper(mapperClass);
}
}
<202108111702>
如下图是我本地的包含了测试包路径下的类
:
<202108111740>
处源码如下:
org.apache.ibatis.binding.MapperRegistry#addMapper
public <T> void addMapper(Class<T> type) {
// 必须是接口
if (type.isInterface()) {
// 如果是已经存在当前类型的mapper信息,则异常
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
// 加载完成标记,默认为false,代表没有加载完成
boolean loadCompleted = false;
try {
// 添加当前类型的映射信息,防止重复加载
knownMappers.put(type, new MapperProxyFactory<T>(type));
// 创建用于解析注解类的MapperAnnotationBuilder
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 执行解析
// <202108111747>
parser.parse();
// 修改是否加载完成标记
loadCompleted = true;
} finally {
// 最终没有加载完成,则移除类型映射信息
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
<202108111747>
处是执行解析,具体参考2.1:解析注解类
。
2.1:解析注解类
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parse
public void parse() {
// 获取接口对应的字符串
// 如:interface yudaosourcecode.mybatis.annotation.TestMybatisAnnotationMapper
String resource = type.toString();
// 没有加载过才继续,其中加载过程的资源使用
// protected final Set<String> loadedResources = new HashSet<String>();存储
if (!configuration.isResourceLoaded(resource)) {
// 加载对应的xml文件,因为可能存在同时使用注解和xml配置的方式
// <202108111811>
loadXmlResource();
// 添加到已加载资源集合中,防止重复加载
configuration.addLoadedResource(resource);
// 设置namespace属性
assistant.setCurrentNamespace(type.getName());
// 解析@org.apache.ibatis.annotation.CacheNamespace注解
// 该注解用于启用二级缓存,注意需要在全局配置文件的setting中设置“cacheEnabled=true”
// <202108121039>
parseCache();
// 解析@org.apache.ibatis.anotion.CacheNamespaceRef注解
// <202108121047>
parseCacheRef();
// 获取所有的方法,依次解析每个方法的注解信息
Method[] methods = type.getMethods();
// 依次解析每个方法的注解信息
for (Method method : methods) {
try {
// 非桥接方法才处理,桥接方法是为了兼容jdk1.5之后的泛型而生成的方法
if (!method.isBridge()) {
// 根据方法解析生成statement
// <202108121135>
parseStatement(method);
}
} catch (IncompleteElementException e) {
// 发生异常,则添加到不完备的解析方法集合中
// protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
// 重复解析未完成的方法解析工作
parsePendingMethods();
}
<202108111811>
处是解析xml的配置文件,详细可以参考这篇文章。<202108121039>
处是解析@org.apache.ibatis.annotatition.CacheNamespace
注解,具体参考2.2:解析CacheNamespace注解
。<202108121047>
处是解析@org.apache.ibatis.annotation.CacheNamespaceRef
注解,具体参考2.3:解析@CacheNamespaceRef注解
。<202108121135>
处是通过方法和其使用的注解信息生成statement,具体参考3:解析方法生成statement
。
2.2:解析@CacheNamespace注解
可能的配置如下:
@CacheNamespace(implementation = PerpetualCache.class, eviction = LruCache.class)
public interface TestMybatisAnnotationMapper { ...snip... }
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCache
private void parseCache() {
// 通过class类型对象获取CacheNamespace注解
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
// 如果是标记了CahceNamespace注解
if (cacheDomain != null) {
// 添加缓存配置信息
// <202108121044>
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), cacheDomain.flushInterval(), cacheDomain.size(), cacheDomain.readWrite(), null);
}
}
<202108121044>
处是添加缓存配置信息,具体可以参考这篇文章中的2.1.2:解析cache标签
。
2.3:解析@CacheNamespaceRef注解
该注解是直接相当于是mapper xml配置文件中的<cache-ref/>
标签,源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseCacheRef
private void parseCacheRef() {
// 通过class类型获取@org.apache.ibatis.annotation.CacheNamespaceRef注解
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
if (cacheDomainRef != null) {
// 应用@org.apache.ibatis.annotation.CacheNamespaceRef注解
// <202108121129>
assistant.useCacheRef(cacheDomainRef.value().getName());
}
}
<202108121129>
处是应用引用的cache信息,具体可以参考这篇文章中的2.1.1:解析cache-ref
。
3:解析方法生成statement
可能的代码如下:
@Insert(value = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})")
void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);
@Select(value = "SELECT * FROM test_mybatis_annotation t WHERE t.`id`=#{id} ")
TestMybatisAnnotation findOne(int id);
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseStatement
void parseStatement(Method method) {
// 获取参数类型,如方法:void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);
// 结果就是“class yudaosourcecode.mybatis.annotation.TestMybatisAnnotationDto”
// <202108121322>
Class<?> parameterTypeClass = getParameterType(method);
// 获取方法上配置的语言驱动类LanguageDriver
// <202108121336>
LanguageDriver languageDriver = getLanguageDriver(method);
// 获取SqlSource对象,这是一个封装sql信息的接口
// <202108121554>
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
// 获取@org.apache.ibatis.annotation.Options注解
// <2021-08-13 10:43:31>
Options options = method.getAnnotation(Options.class);
// 使用类全限定名称和方法名称的组合作为statement的ID
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
// statementType默认使用org.apache.ibatis.mapping.StatementType.PREPARED
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
// 获取sql语句类型
SqlCommandType sqlCommandType = getSqlCommandType(method);
// 是否为查询
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 不是select则默认刷新缓存
boolean flushCache = !isSelect;
// 是select则默认使用缓存
boolean useCache = isSelect;
// 主键生成对象
KeyGenerator keyGenerator;
// 主键属性默认为id
String keyProperty = "id";
String keyColumn = null;
// 是插入或者是更新
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// 获取@org.apache.ibatis.annotation.SelectKey注解信息,用于生成主键使用
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
// 根据@org.apache.ibatis.annotation.SelectKey注解信息生成keyGenerator对象
// <2021-08-13 11:07:18>
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
// 使用@org.apache.ibatis.annotation.SelectKey注解配置的主键属性值
keyProperty = selectKey.keyProperty();
// 如果是没有配置@org.apache.ibatis.annotation.SelectKey注解
} else {
// 如果是没有配置@org.apache.ibatis.annotation.Options注解
if (options == null) {
// 如果是全局参数配置了使用生成的主键,则使用Jdbc3KeyGenerator,否则使用NoKeyGenerator
keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
// 如果是配置了@org.apache.ibatis.annotation.Options注解
} else {
// 如果是@org.apache.ibatis.annotation.Options注解配置了使用生成主键则使用
// Jdbc3KeyGenerator,否则使用NoKeyGenerator
keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
// 获取主键属性
keyProperty = options.keyProperty();
// 获取数据库列
keyColumn = options.keyColumn();
}
}
// 如果既不是插入操作,也不是更新操作,则使用NoKeyGenerator,即不需要生成主键
} else {
keyGenerator = new NoKeyGenerator();
}
// 如果是配置了@org.apache.ibatis.annotation.Options注解,则获取其配置的相关信息
if (options != null) {
flushCache = options.flushCache();
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null;
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
String resultMapId = null;
// 从方法上获取@org.apache.ibatis.annotation.ResultMap注解信息,一般配置
// @org.apache.ibatis.annotation.Results注解使用
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
// 如果是配置了@org.apache.ibatis.annotation.ResultMap注解
if (resultMapAnnotation != null) {
// 获取配置的value值
String[] resultMaps = resultMapAnnotation.value();
// 循环生成resultMap的ID值
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) sb.append(",");
sb.append(resultMap);
}
resultMapId = sb.toString();
// 如果是数据查询,即使用的注解是@org.apache.ibatis.annotation.Select
} else if (isSelect) {
// 解析其他注解,如构造函数相关注解@org.apache.ibatis.annotation.ConstructorArgs
// <2021-08-13 13:44:29>
resultMapId = parseResultMap(method);
}
// 创建MappedStatement,并添加到org.apache.ibatis.session.Configuration对象中
// <2021-08-13 18:10:41>
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
null,
parameterTypeClass,
resultMapId, // ResultMapID
getReturnType(method),
resultSetType,
flushCache,
useCache,
false,
keyGenerator,
keyProperty,
keyColumn,
null,
languageDriver,
null);
}
}
<202108121322>
处是获取方法的参数类型,具体参考3.1:获取参数类型
。<202108121336>
处是获取语言驱动类LanguageDriver,具体参考3.2:获取语言驱动类
。<202108121554>
处是获取SqlSource对象,具体参考3.3:获取SqlSource对象
。<2021-08-13 10:43:31>
处是获取@org.apache.ibatis.annotaion.Options
注解,该注解用于对statement进行一些配置,如果缓存,statement类型等,源码如下:
org.apache.ibatis.annotations.Options
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Options {
// 是否使用缓存
boolean useCache() default true;
// 是否刷新缓存
boolean flushCache() default false;
// 返回结果集类型
ResultSetType resultSetType() default ResultSetType.FORWARD_ONLY;
// statement类型
StatementType statementType() default StatementType.PREPARED;
// 每次取回数据量
int fetchSize() default -1;
// 超时时长
int timeout() default -1;
// 是否使用生成的主键,true则会返回生成的主键,false则不返回
boolean useGeneratedKeys() default false;
// 主键对应的属性
String keyProperty() default "id";
// 主键对应的列
String keyColumn() default "";
}
<2021-08-13 11:07:18>
处是根据XXXProvider方法上配置的@org.apache.ibatis.annotation.SelectKey注解生成org.apache.ibatis.executor.keygen.KeyGenerator对象,具体参考3.4:生成KeyGenerator对象
。<2021-08-13 13:44:29>
处是解析返回结果相关的其他注解,如构造函数相关注解@org.apache.ibatis.annotation.ConstructorArgs,具体参考3.5:解析返回结果相关注解
。<2021-08-13 18:10:41>
处事创建statement信息对应的org.apache.ibatis.mapping.MappedStatement对象,详细可以参考这里。
3.1:获取参数类型
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getParameterType
private Class<?> getParameterType(Method method) {
// 结果类型
Class<?> parameterType = null;
// 获取参数类型数组
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
// 不是org.apache.ibatis.session.RowBounds类型并且不是org.apache.ibatis.session.ResultHandler类型
if (!RowBounds.class.isAssignableFrom(parameterTypes[i]) && !ResultHandler.class.isAssignableFrom(parameterTypes[i])) {
// 如果是当前结果参数类型还为null,则使用当前遍历到的参数类型作为结果
if (parameterType == null) {
parameterType = parameterTypes[i];
} else {
// 否则使用org.apache.ibatis.binding.MapperMethod.ParamMap.class作为结果
// 这是mybatis扩展HashMap的一个子类,签名如下:
// public static class ParamMap<V> extends HashMap<String, V>
parameterType = ParamMap.class; // issue #135
}
}
}
// 返回结果
return parameterType;
}
3.2:获取语言驱动类
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getLanguageDriver
private LanguageDriver getLanguageDriver(Method method) {
// 获取@org.apache.ibatis.annotation.Lang注解
Lang lang = method.getAnnotation(Lang.class);
Class<?> langClass = null;
// 如果配置了lang则获取配置的value值
if (lang != null) {
langClass = lang.value();
}
// 创建LanguageDriver类实例
// <202108121422>
return assistant.getLanguageDriver(langClass);
}
<202108121422>
处是获取LanguageDriver对象,具体参考这里。
3.3:获取SqlSource对象
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlSourceFromAnnotations
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
// 获取statement的类型
// <202108121636>
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
// 获取@org.apache.ibatis.annotation.XXXProvider,如@org.apache.ibatis.annotation.InsertProvider,此类注解是获取sql语句的另外一种方式
// <202108121759>
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
// 优先处理使用了sql注解的情况
if (sqlAnnotationType != null) {
// 执行到此处说明提供了sql注解,判断如果是也提供了sqlProvider注解,则直接异常
// 我觉得这里完全可以忽略即可,不知道为什么只允许一个
if (sqlProviderAnnotationType != null) {
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
// 获取sql注解
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
// 通过反射调用注解的value方法,获取配置的sql语句数组值,这里虽然是数组并非多个独立
// 的sql语句,而是将一个sql语句拆分成多个部分,比如可以select一个部分,from一个部分
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
// 通过配置的sql语句数组构建SqlSource对象
// <2021-08-13 09:31:47>
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
// 处理使用了sqlProvider的情况
} else if (sqlProviderAnnotationType != null) {
// 获取XXXProvideer的注解
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
// 通过全局配置和注解信息创建ProviderSqlSource对象
// <2021-08-13 10:02:52>
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation);
}
// 既没有sql注解,也没有sqlProvider注解的情况,直接返回null
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}
<202108121636>
处是获取statement的类型,具体参考3.4.1:获取statement类型
。<202108121759>
是通过XXXProvider获取sql语句,如下使用@org.apache.ibatis.annotation.Insert
注解的方式:
@Insert(value = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})")
void insertOne(TestMybatisAnnotationDto testMybatisAnnotationDto);
如果是使用XXXProvider的方式则可能如下:
public class MySelectSqlProvider {
public String provideInsertSql() {
String insertSql = "INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})";
// INSERT INTO `test_mybatis_annotation` (`myname`, `myage`) VALUES (#{myname}, #{myage})
return insertSql;
}
}
@InsertProvider(type = MySelectSqlProvider.class, method = "provideInsertSql")
void insertWithSelectSqlProvider(TestMybatisAnnotationDto testMybatisAnnotationDto);
效果是完全一样的,具体参考3.4.2:获取XXXProvider
。<2021-08-13 09:31:47>
处是通过解析在注解中配置的sql语句数组来构建SqlSource对象,具体参考3.4.3:基于sql数组构建SqlSource
。<2021-08-13 10:02:52>
处是创建XXXProvider注解方式的SqlSource,具体参考3.4.4:XXXProvider创建SqlSource
。
3.3.1:获取statement类型
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlAnnotationType
private Class<? extends Annotation> getSqlAnnotationType(Method method) {
// <202108121639>
return chooseAnnotationType(method, sqlAnnotationTypes);
}
<202108121639>
处sqlAnnotationTypes定义如下:
private final Set<Class<? extends Annotation>> sqlAnnotationTypes = new HashSet<Class<? extends Annotation>>();
其中数据的初始化是在构造函数中完成的,源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#MapperAnnotationBuilder
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
...snip...
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
...snip...
}
<202108121639>
处源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#chooseAnnotationType
private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
// 循环遍历所有的类型
for (Class<? extends Annotation> type : types) {
// 尝试从方法上获取当前循环到的注解,如果是获取到了,则认为就是当前的类型,并返回
Annotation annotation = method.getAnnotation(type);
if (annotation != null) {
return type;
}
}
// 没有匹配的,则返回null,代表注解中不包含如下注解中的任何一个
// @org.apache.ibatis.annotation.Select
// @org.apache.ibatis.annotation.Insert
// @org.apache.ibatis.annotation.Update
// @org.apache.ibatis.annoation.Delete
return null;
}
3.3.2:获取XXXProvider
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getSqlProviderAnnotationType
private Class<? extends Annotation> getSqlProviderAnnotationType(Method method) {
// <2021-08-12 18:08:41>
return chooseAnnotationType(method, sqlProviderAnnotationTypes);
}
<2021-08-12 18:08:41>
处sqlProviderAnnotationTypes
定义如下:
private final Set<Class<? extends Annotation>> sqlProviderAnnotationTypes = new HashSet<Class<? extends Annotation>>();
其中数据的初始化是在构造函数中完成的,源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#MapperAnnotationBuilder
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
...snip...
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
<2021-08-12 18:08:41>
处源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#chooseAnnotationType
private Class<? extends Annotation> chooseAnnotationType(Method method, Set<Class<? extends Annotation>> types) {
// 遍历获取匹配的类型作为结果,并返回
for (Class<? extends Annotation> type : types) {
Annotation annotation = method.getAnnotation(type);
if (annotation != null) {
return type;
}
}
return null;
}
3.3.3:基于sql数组构建SqlSource
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#buildSqlSourceFromStrings
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
// 将sql语句片段数组组装成最终的sql语句
final StringBuilder sql = new StringBuilder();
for (String fragment : strings) {
sql.append(fragment);
sql.append(" ");
}
// 创建SQLSource对象
// <2021-08-13 09:43:23>
return languageDriver.createSqlSource(configuration, sql.toString(), parameterTypeClass);
}
<2021-08-13 09:43:23>
处是通过方法中的参数,sql语句,配置信息创建SqlSource对象,源码如下:
org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, java.lang.String, java.lang.Class<?>)
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) {
if (script.startsWith("<script>")) { // issue #3
XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());
return createSqlSource(configuration, parser.evalNode("/script"), parameterType);
} else {
script = PropertyParser.parse(script, configuration.getVariables()); // issue #127
TextSqlNode textSqlNode = new TextSqlNode(script);
if (textSqlNode.isDynamic()) {
return new DynamicSqlSource(configuration, textSqlNode);
} else {
return new RawSqlSource(configuration, script, parameterType);
}
}
}
详细分析这里先留个TODO
。
3.3.4:XXXProvider创建SqlSource
源码如下:
org.apache.ibatis.builder.annotation.ProviderSqlSource#ProviderSqlSource
public ProviderSqlSource(Configuration config, Object provider) {
// XXXProvider的方法名称,即用于获取sql语句的方法名称
String providerMethodName = null;
try {
this.sqlSourceParser = new SqlSourceBuilder(config);
// 反射获取XXXProvider类的class类型
this.providerType = (Class<?>) provider.getClass().getMethod("type").invoke(provider);
// 反射获取XXXProvider的method的值
providerMethodName = (String) provider.getClass().getMethod("method").invoke(provider);
// 遍历XXXProvider类的所有的方法
for (Method m : this.providerType.getMethods()) {
// 如果和method配置的方法名称一样,则可能是目标方法
if (providerMethodName.equals(m.getName())) {
// 参数个数小于2个,并且返回类型是String的,才可能是目标方法
if (m.getParameterTypes().length < 2
&& m.getReturnType() == String.class) {
// 当前方法作为暂时的目标方法,暂时的原因是,循环并没有停止,因此可能的才会作为最终的目标方法
// 不知道为什么要这么实现
this.providerMethod = m;
// 如果是参数的个数是1个则说明是需要参数对象
this.providerTakesParameterObject = m.getParameterTypes().length == 1;
}
}
}
} catch (Exception e) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Cause: " + e, e);
}
// 如果是最终没有获取到目标方法,则说明配置的方法不存在,直接异常
if (this.providerMethod == null) {
throw new BuilderException("Error creating SqlSource for SqlProvider. Method '"
+ providerMethodName + "' not found in SqlProvider '" + this.providerType.getName() + "'.");
}
}
3.4:生成KeyGenerator对象
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#handleSelectKeyAnnotation
private KeyGenerator handleSelectKeyAnnotation(SelectKey selectKeyAnnotation, String baseStatementId, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
// 生成id,如xxx.yyy.Obj!selectKey
String id= baseStatementId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
// 获取resultType的class类型
Class<?> resultTypeClass = selectKeyAnnotation.resultType();
// 获取org.apache.ibatis.mapping.StatementType的枚举值
StatementType statementType = selectKeyAnnotation.statementType();
// 获取主键属性名称
String keyProperty = selectKeyAnnotation.keyProperty();
// 获取主键列名称
String keyColumn = selectKeyAnnotation.keyColumn();
// 是否在执行sql语句前赋值主键值
boolean executeBefore = selectKeyAnnotation.before();
// 各种默认值
boolean useCache = false;
KeyGenerator keyGenerator = new NoKeyGenerator();
Integer fetchSize = null;
Integer timeout = null;
boolean flushCache = false;
String parameterMap = null;
String resultMap = null;
ResultSetType resultSetTypeEnum = null;
// 根据selectKey的sql语句生成SqlSource
// <2021-08-13 11:32:04>
SqlSource sqlSource = buildSqlSourceFromStrings(selectKeyAnnotation.statement(), parameterTypeClass, languageDriver);
// sql语句的类型
SqlCommandType sqlCommandType = SqlCommandType.SELECT;
// 创建MappedStament并添加org.apache.ibatis.session.Configuration对象中
// <2021-08-13 11:36:25>
assistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum,
flushCache, useCache, false,
keyGenerator, keyProperty, keyColumn, null, languageDriver, null);
// 应用命名空间前缀
id = assistant.applyCurrentNamespace(id, false);
// 获取已经创建完的org.apache.ibatis.mapping.MappedStatement
MappedStatement keyStatement = configuration.getMappedStatement(id, false);
// 创建SelectKeyGenerator对象
SelectKeyGenerator answer = new SelectKeyGenerator(keyStatement, executeBefore);
// 添加当前statement使用的keygenerator
configuration.addKeyGenerator(id, answer);
// 返回keygenerator
return answer;
}
<2021-08-13 11:32:04>
处是根据sql语句和参数等信息生成SqlSource对象,具体参考3.4.3:基于sql数组构建SqlSource
。<2021-08-13 11:36:25>
处是创建org.apache.ibatis.mapping.MappedStatement对象,详细可以参考这里。
3.5:解析返回结果相关注解
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#parseResultMap
private String parseResultMap(Method method) {
// 获取查询结果的返回类型,是普通的对象?集合?还是Map?
// <2021-08-13 14:02:51>
Class<?> returnType = getReturnType(method);
// 如果配置了@org.apache.ibatis.annotation.ConstructorArgs注解,则查询结果
// 通过构造函数封装为对象
ConstructorArgs args = method.getAnnotation(ConstructorArgs.class);
// 如果是配置了@org.apache.ibatis.annotation.Results注解,则是先创建对象
// 然后通过属性的读写方法设置属性值
Results results = method.getAnnotation(Results.class);
// 如果是配置了@org.apache.ibatis.annotation.TypeDiscirminator注解则会根据某属性的
// 查询结果值动态的设置另外一个属性的值
TypeDiscriminator typeDiscriminator = method.getAnnotation(TypeDiscriminator.class);
// 生成resultMap的ID
// <2021-08-13 17:25:38>
String resultMapId = generateResultMapName(method);
// 生成ResultMap对象
// <2021-08-13 17:45:20>
applyResultMap(resultMapId, returnType, argsIf(args), resultsIf(results), typeDiscriminator);
return resultMapId;
}
<2021-08-13 14:02:51>
处是获取查询结果的返回类型,具体参考3.5.1:获取查询结果类型
。<2021-08-13 17:25:38>
处是生成resultMap的ID,具体参考3.5.2:生成ResultMap的ID值
。<2021-08-13 17:45:20>
处argsIf(args)用于获取构造函数的参数信息值,源码如下:
private Arg[] argsIf(ConstructorArgs args) {
returnargs == null ? new Arg[0] : args.value();
}
resultsIf(results)是获取配置的resule信息,源码如下:
private Result[] resultsIf(Results results) {
return results == null ? new Result[0] : results.value();
}
。<2021-08-13 17:45:20>
处是生成ResultMap对象,具体参考3.5.3:生成ResultMap对象
。
3.5.1:获取查询结果类型
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#getReturnType
private Class<?> getReturnType(Method method) {
// 获取方法签名中返回类型
Class<?> returnType = method.getReturnType();
// 如果是方法签名的返回类型的void,则尝试使用@org.apache.ibatis.annotation.ResultType注解配置的值
// 这里为什么方法的返回值是void,还要获取呢,因为获取还是要获取的,虽然不使用
if (void.class.equals(returnType)) {
// 获取@org.apache.ibatis.annotation.ResultType注解
ResultType rt = method.getAnnotation(ResultType.class);
// 如果是配置了@org.apache.ibatis.annotation.ResultType注解,则使用其value作为结果
if (rt != null) {
returnType = rt.value();
}
// 如果是返回类型是java.lang.Collection集合类型的子类
} else if (Collection.class.isAssignableFrom(returnType)) {
// 获取返回的泛型的类型
Type returnTypeParameter = method.getGenericReturnType();
// 如果是java.lang.relect.ParameterizedType类型的,当时带有泛型的集合会用这种类型
// 的接口来进行分装
if (returnTypeParameter instanceof ParameterizedType) {
// 获取泛型的类型信息,如List<String>,这这里的结果就是[java.lang.String.class]
Type[] actualTypeArguments = ((ParameterizedType) returnTypeParameter).getActualTypeArguments();
// 集合类型只处理有一个泛型参数信息的情况
if (actualTypeArguments != null && actualTypeArguments.length == 1) {
// 获取实际的泛型参数
returnTypeParameter = actualTypeArguments[0];
// 如果泛型的类型是java.lang.Class
if (returnTypeParameter instanceof Class) {
// 直接强转java.lang.Class作为结果
returnType = (Class<?>) returnTypeParameter;
// 这种情况是集合里也使用了带有泛型的集合,如List<Set<String>>
} else if (returnTypeParameter instanceof ParameterizedType) {
// 直接返回原始类型,如List<Set<String>>,结果就是java.util.Set.class
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
// 如果是泛型数组
} else if (returnTypeParameter instanceof GenericArrayType) {
// 如List<List<Integer>[]>,这里获取的结果就是java.util.List<java.lang.Integer>
Class<?> componentType = (Class<?>) ((GenericArrayType) returnTypeParameter).getGenericComponentType();
// 创建长度为0的数组实例并获取class,作为结果
// (issue #525) support List<byte[]>
returnType = Array.newInstance(componentType, 0).getClass();
}
}
}
// 如果是方法上使用了@org.apache.ibatis.annotation.MapKey注解,并且返回类型是java.util.Map的子类
} else if (method.isAnnotationPresent(MapKey.class) && Map.class.isAssignableFrom(returnType)) {
// 获取带有反省信息的返回类型如,Map<Integer, TestMybatisAnnotation>
// 结果就是:java.util.Map<java.lang.Integer, yudaosourcecode.mybatis.annotation.TestMybatisAnnotation>
Type returnTypeParameter = method.getGenericReturnType();
// 如果是带有泛型的类型
if (returnTypeParameter instanceof ParameterizedType) {
// 获取实际的参数类型数组,如Map<Integer, TestMybatisAnnotation>
// 这里的结果就是
// 0 = {Class@271} "class java.lang.Integer"
// 1 = {Class@1512} "class yudaosourcecode.mybatis.annotation.TestMybatisAnnotation"
Type[] actualTypeArguments = ((ParameterizedType) returnTypeParameter).getActualTypeArguments();
// 有实际泛型参数,并且参数长度为2,长度为2是因为Map有key,value
if (actualTypeArguments != null && actualTypeArguments.length == 2) {
// 第二个作为返回的参数
returnTypeParameter = actualTypeArguments[1];
// 如果是Class
if (returnTypeParameter instanceof Class) {
returnType = (Class<?>) returnTypeParameter;
// 如果又是带有反省的参数,如Map<Integer, List<TestMybatisAnnotation>>
// 这里的结果就是List<TestMybatisAnnotation>
} else if (returnTypeParameter instanceof ParameterizedType) {
// 直接返回原始类型,作为结果类型,如Map<Integer, List<TestMybatisAnnotation>>
// 这里的结果就是java.util.List.Class
returnType = (Class<?>) ((ParameterizedType) returnTypeParameter).getRawType();
}
}
}
}
// 返回类型
return returnType;
}
3.5.2:生成ResultMap的ID值
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#generateResultMapName
private String generateResultMapName(Method method) {
StringBuilder suffix = new StringBuilder();
// 根据参数类型生成后缀
for (Class<?> c : method.getParameterTypes()) {
suffix.append("-");
suffix.append(c.getSimpleName());
}
// 如果是没有参数则拼上"-void"后缀
if (suffix.length() < 1) {
suffix.append("-void");
}
// "类名.方法名.后缀"作为最终结果
return type.getName() + "." + method.getName() + suffix;
}
3.5.3:生成ResultMap对象
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyResultMap
private void applyResultMap(String resultMapId, Class<?> returnType, Arg[] args, Result[] results, TypeDiscriminator discriminator) {
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
// 应用构造函数参数信息
// <2021-08-13 17:51:14>
applyConstructorArgs(args, returnType, resultMappings);
// 应用Results信息
// <2021-08-13 18:00:14>
applyResults(results, returnType, resultMappings);
// 应用Discriminator
// <2021-08-13 18:06:49>
Discriminator disc = applyDiscriminator(resultMapId, returnType, discriminator);
assistant.addResultMap(resultMapId, returnType, null, disc, resultMappings, null); // TODO add AutoMappingBehaviour
createDiscriminatorResultMaps(resultMapId, returnType, discriminator);
}
<2021-08-13 17:51:14>
处是应用构造函数参数信息,这种方式是使用构造函数来对属性赋值,具体参考3.5.3.1:应用构造函数参数
。<2021-08-13 18:00:14>
处是应用Results注解的信息,这种方式是使用对象的属性的读写方法来进行赋值,具体参考3.5.3.2:应用results信息
。<2021-08-13 18:06:49>
处是应用discriminator信息,具体参考3.5.3.3:应用discriminator信息
。
3.5.3.1:应用构造函数参数
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyConstructorArgs
private void applyConstructorArgs(Arg[] args, Class<?> resultType, List<ResultMapping> resultMappings) {
// 依次遍历每一个参数
for (Arg arg : args) {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
flags.add(ResultFlag.CONSTRUCTOR);
if (arg.id()) flags.add(ResultFlag.ID);
// 构造org.apache.ibatis.mapping.ResultMaping
// 一个ResultMapping代表一个参数配置
// <2021-08-13 17:59:47>
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
null,
nullOrEmpty(arg.column()),
arg.javaType() == void.class ? null : arg.javaType(),
arg.jdbcType() == JdbcType.UNDEFINED ? null : arg.jdbcType(),
nullOrEmpty(arg.select()),
nullOrEmpty(arg.resultMap()),
null,
null,
arg.typeHandler() == UnknownTypeHandler.class ? null : arg.typeHandler(),
flags,
null,
null,
false);
// 添加ResultMapping
resultMappings.add(resultMapping);
}
}
<2021-08-13 17:59:47>
详细可以参考这里。
3.5.3.2:应用results信息
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyResults
private void applyResults(Result[] results, Class<?> resultType, List<ResultMapping> resultMappings) {
// 循环处理每一个属性的配置
for (Result result : results) {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
if (result.id()) flags.add(ResultFlag.ID);
// <2021-08-13 18:04:04>
ResultMapping resultMapping = assistant.buildResultMapping(
resultType,
nullOrEmpty(result.property()),
nullOrEmpty(result.column()),
result.javaType() == void.class ? null : result.javaType(),
result.jdbcType() == JdbcType.UNDEFINED ? null : result.jdbcType(),
hasNestedSelect(result) ? nestedSelectId(result) : null,
null,
null,
null,
result.typeHandler() == UnknownTypeHandler.class ? null : result.typeHandler(),
flags,
null,
null,
isLazy(result));
resultMappings.add(resultMapping);
}
}
<2021-08-13 18:04:04>
处可以参考这里。
3.5.3.3:应用discriminator信息
源码如下:
org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#applyDiscriminator
private Discriminator applyDiscriminator(String resultMapId, Class<?> resultType, TypeDiscriminator discriminator) {
if (discriminator != null) {
String column = discriminator.column();
Class<?> javaType = discriminator.javaType() == void.class ? String.class : discriminator.javaType();
JdbcType jdbcType = discriminator.jdbcType() == JdbcType.UNDEFINED ? null : discriminator.jdbcType();
Class<? extends TypeHandler<?>> typeHandler = discriminator.typeHandler() == UnknownTypeHandler.class ? null : discriminator.typeHandler();
Case[] cases = discriminator.cases();
Map<String, String> discriminatorMap = new HashMap<String, String>();
for (Case c : cases) {
String value = c.value();
String caseResultMapId = resultMapId + "-" + value;
discriminatorMap.put(value, caseResultMapId);
}
// 各种解析之后创建对象并返回
return assistant.buildDiscriminator(resultType, column, javaType, jdbcType, typeHandler, discriminatorMap);
}
return null;
}