Mybatis系列六:MapperAnnotationBuilder解析Mapper Class

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
       }
     }
   }
 }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值