Mybatis系列六:MapperAnnotationBuilder解析Mapper Class
概述
在**《Mybatis系列五:MapperRegistry注册并解析Mapper Class》**一文【2.3 添加Mapper Class】章节用到了MapperAnnotationBuilder解析Mapper类,创建MappedStatement实例,保存到
Configuration#mappedStatements中。
一、MapperAnnotationBuilder
1.1 实例化时注册默认注解
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);
}
1.2 解析Mapper类
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
// XMLMapperBuilder记录XML配置文件:记录已经加载的XML文件,XML配置文件路径作为标识。记录加载的Mapper类,namespace: + Mapper接口完全限定名作为标识。
// MapperAnnotationBuilder记录Mapper类:Mapper接口完全限定名作为标识,会调用XMLMapperBuilder加载同路径下的Mapper配置文件。
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
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();
}
1.3 加载接口同目录下XML文件
传送门《Mybatis系列四:XMLMapperBuilder解析XL配置文件》
private void loadXmlResource() {
// Spring可能不知道真实的资源名称,因此我们检查一个标志以防止再次加载资源两次此标志在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();
}
}
}
1.4 根据@CacheNamespace注解创建缓存Cache实例
private void parseCache() {
CacheNamespace cacheDomain = type.getAnnotation(CacheNamespace.class);
if (cacheDomain != null) {
Integer size = cacheDomain.size() == 0 ? null : cacheDomain.size();
Long flushInterval = cacheDomain.flushInterval() == 0 ? null : cacheDomain.flushInterval();
Properties props = convertToProperties(cacheDomain.properties());
assistant.useNewCache(cacheDomain.implementation(), cacheDomain.eviction(), flushInterval, size, cacheDomain.readWrite(), cacheDomain.blocking(), props);
}
}
private Properties convertToProperties(Property[] properties) {
if (properties.length == 0) {
return null;
}
Properties props = new Properties();
for (Property property : properties) {
props.setProperty(property.name(),
PropertyParser.parse(property.value(), configuration.getVariables()));
}
return props;
}
1.5 根据@CacheNamespaceRef注解获取缓存Cache实例
private void parseCacheRef() {
CacheNamespaceRef cacheDomainRef = type.getAnnotation(CacheNamespaceRef.class);
if (cacheDomainRef != null) {
// 默认值void.class
Class<?> refType = cacheDomainRef.value();
String refName = cacheDomainRef.name();
if (refType == void.class && refName.isEmpty()) {
throw new BuilderException(“Should be specified either value() or name() attribute in the @CacheNamespaceRef”);
}
if (refType != void.class && !refName.isEmpty()) {
throw new BuilderException(“Cannot use both value() and name() attribute in the @CacheNamespaceRef”);
}
String namespace = (refType != void.class) ? refType.getName() : refName;
assistant.useCacheRef(namespace);
}
}
1.6 基于注解创建MappedStatement实例 parseStatement
创建MappedStatement实例,保存到Configuration#mappedStatements中。
void parseStatement(Method method) {
Class<?> parameterTypeClass = getParameterType(method);
// 默认是XMLLanguageDriver,可以在方法上通过@Lang指定LanguageDriver
LanguageDriver languageDriver = getLanguageDriver(method);
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
Options options = method.getAnnotation(Options.class);
…
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
} else {
keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = NoKeyGenerator.INSTANCE;
}
if (options != null) {
…
}
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
…
// 创建MappedStatement实例,保存到Configuration#mappedStatements中
assistant.addMappedStatement(
…);
}
}
1.7 根据Mapper接口方法上的注解创建SqlSource实例
private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
try {
Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
// @Select和@SelectProvider不能同时存在
if (sqlAnnotationType != null) {
if (sqlProviderAnnotationType != null) {
throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
}
Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
final String[] strings = (String[]) sqlAnnotation.getClass().getMethod(“value”).invoke(sqlAnnotation);
return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
} else if (sqlProviderAnnotationType != null) {
Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
}
return null;
} catch (Exception e) {
throw new BuilderException("Could not find value method on SQL annotation. Cause: " + e, e);
}
}
1.8 创建SqlSource实例 buildSqlSourceFromStrings
private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass, LanguageDriver languageDriver) {
final StringBuilder sql = new StringBuilder();
for (String fragment : strings) {
sql.append(fragment);
sql.append(" ");
}
return languageDriver.createSqlSource(configuration, sql.toString().trim(), parameterTypeClass);
}
1.9 解析待处理方法 parsePendingMethods
// 因依赖资源不存在而解析失败的,在解析完之后重新尝试再次解析。6.6 parseStatement 因依赖资源可能解析失败。
private void parsePendingMethods() {
Collection incompleteMethods = configuration.getIncompleteMethods();
synchronized (incompleteMethods) {
Iterator iter = incompleteMethods.iterator();
while (iter.hasNext()) {
try {
iter.next().resolve();
iter.remove();
} catch (IncompleteElementException e) {
// This method is still missing a resource
}
}
}
}