mybatis源码解析-标签解析

62 阅读5分钟

1.properties标签解析

private void propertiesElement(XNode context) throws Exception {
  //context :<properties resource="org/apache/ibatis/databases/blog/blog-derby.properties" />
  if (context == null) {
    return;
  }
  Properties defaults = context.getChildrenAsProperties();
  //获取resource属性值
  String resource = context.getStringAttribute("resource");
  //获取url属性值
  String url = context.getStringAttribute("url");
  //url和resource对应的资源文件不能同时指定
  if (resource != null && url != null) {
    throw new BuilderException(
        "The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
  }
  if (resource != null) {
    //加载resource资源文件信息到defaults中
    defaults.putAll(Resources.getResourceAsProperties(resource));
  } else if (url != null) {
    //加载url资源文件信息到defaults中
    defaults.putAll(Resources.getUrlAsProperties(url));
  }
  Properties vars = configuration.getVariables();
  if (vars != null) {
    defaults.putAll(vars);
  }
  parser.setVariables(defaults);
  //最终设置到Configuration 对象 protected Properties variables = new Properties()中
  //解析  <properties resource="org/apache/ibatis/databases/blog/blog-derby.properties"/>标签完成
  configuration.setVariables(defaults);
}
<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

从dtd文件可以看出,properties有url(绝对路径)和resource(相对路径)两个属性。 最终设置到Configuration 对象 protected Properties variables = new Properties()中

2.settings标签解析

private Properties settingsAsProperties(XNode context) {
  /**
   * context :
   * <settings>
   *   <setting name="cacheEnabled" value="true" />
   *   <setting name="lazyLoadingEnabled" value="false" />
   *   <setting name="multipleResultSetsEnabled" value="true" />
   *   <setting name="useColumnLabel" value="true" />
   *   <setting name="useGeneratedKeys" value="false" />
   *   <setting name="defaultExecutorType" value="SIMPLE" />
   *   <setting name="defaultStatementTimeout" value="25" />
   * </settings>
   */
  if (context == null) {
    return new Properties();
  }
  //把标签里面的name和value值放到Properties中
  Properties props = context.getChildrenAsProperties();
  // Check that all settings are known to the configuration class
  //校验setting里面配置name属性值要在Configuration中定义。也就是自己随意定义的name值是不行的
  MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
  for (Object key : props.keySet()) {
    if (!metaConfig.hasSetter(String.valueOf(key))) {
      throw new BuilderException(
          "The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
    }
  }
  //解析完成返回
  return props;
}

解析settings标签成Properties对象

3.typeAliases 别名配置解析

private void typeAliasesElement(XNode context) {
  /**
   * context:
   * <typeAliases>
   *   <typeAlias alias="Author" type="org.apache.ibatis.domain.blog.Author" />
   *   <typeAlias alias="Blog" type="org.apache.ibatis.domain.blog.Blog" />
   *   <typeAlias alias="Comment" type="org.apache.ibatis.domain.blog.Comment" />
   *   <typeAlias alias="Post" type="org.apache.ibatis.domain.blog.Post" />
   *   <typeAlias alias="Section" type="org.apache.ibatis.domain.blog.Section" />
   *   <typeAlias alias="Tag" type="org.apache.ibatis.domain.blog.Tag" />
   * </typeAliases>
   */
  if (context == null) {
    return;
  }
  for (XNode child : context.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeAliasPackage = child.getStringAttribute("name");
      configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
    } else {
      //获取alias属性值
      String alias = child.getStringAttribute("alias");
      //获取type属性值
      String type = child.getStringAttribute("type");
      try {
        /**
         * 注册到
         *TypeAliasRegistry
         *   private final Map<String, Class<?>> typeAliases = new HashMap<>();
         */
        Class<?> clazz = Resources.classForName(type);
        if (alias == null) {
       
          typeAliasRegistry.registerAlias(clazz);
        } else {
          typeAliasRegistry.registerAlias(alias, clazz);
        }
      } catch (ClassNotFoundException e) {
        throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
      }
    }
  }
}

4.plugins插件标签解析

private void pluginsElement(XNode context) throws Exception {
  /**
   * context:
   * <plugins>
   *   <plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">
   *     <property name="pluginProperty" value="100" />
   *   </plugin>
   * </plugins>
   */
  if (context != null) {
    for (XNode child : context.getChildren()) {
      //获取<plugin interceptor="org.apache.ibatis.builder.ExamplePlugin">interceptor属性值
      String interceptor = child.getStringAttribute("interceptor");
      //获取  <property name="pluginProperty" value="100" /> name,value属性值
      Properties properties = child.getChildrenAsProperties();
      //根据interceptor值反射获取Interceptor实例
      //如果TypeAliasRegistry   private final Map<String, Class<?>> typeAliases = new HashMap<>();中存在,则从中获取
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor()
        .newInstance();
      //设置到properties中
      interceptorInstance.setProperties(properties);
      //添加到Configuration   protected final InterceptorChain interceptorChain = new InterceptorChain();拦截器chain中
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

解析完成添加到Configuration对象 protected final InterceptorChain interceptorChain = new InterceptorChain()中

5.objectFactory标签解析

private void objectFactoryElement(XNode context) throws Exception {
  /**
   * context:
   * <objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory">
   *   <property name="objectFactoryProperty" value="100" />
   * </objectFactory>
   */
  if (context != null) {
    // 获取<objectFactory type="org.apache.ibatis.builder.ExampleObjectFactory"> type属性值
    String type = context.getStringAttribute("type");
    //获取<property name="objectFactoryProperty" value="100" /> name,value属性值
    Properties properties = context.getChildrenAsProperties();
    //由type值反射获取ObjectFactory实例。
    //若TypeAliasRegistry private final Map<String, Class<?>> typeAliases = new HashMap<>();
    //中存在,则从中获取
    ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    //设置properties中
    factory.setProperties(properties);
    //设置Configuration protected ObjectFactory objectFactory = new DefaultObjectFactory();中
    configuration.setObjectFactory(factory);
  }

解析完成,添加到Configuration protected ObjectFactory objectFactory = new DefaultObjectFactory()中

6.objectWrapperFactory标签解析

private void objectWrapperFactoryElement(XNode context) throws Exception {
  if (context != null) {
    //获取属性值type
    String type = context.getStringAttribute("type");
    //由type反射获取ObjectWrapperFactory
    ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    //设置到Configuration   protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();中
    configuration.setObjectWrapperFactory(factory);
  }
}

解析成,添加到Configuration对象 protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory()中

7.reflectorFactory标签解析

private void reflectorFactoryElement(XNode context) throws Exception {
  if (context != null) {
    //获取属性值type
    String type = context.getStringAttribute("type");
    //由type反射获取ReflectorFactory
    ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance();
    //设置到Configuration protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();中
    configuration.setReflectorFactory(factory);
  }
}

8.environments标签解析

private void environmentsElement(XNode context) throws Exception {
  /**
   * context:
   *  <environments default="development">
   *     <environment id="development">
   *       <transactionManager type="JDBC">
   *         <property name="" value=""/>
   *       </transactionManager>
   *       <dataSource type="UNPOOLED">
   *         <property name="driver" value="${driver}"/>
   *         <property name="url" value="${url}"/>
   *         <property name="username" value="${username}"/>
   *         <property name="password" value="${password}"/>
   *       </dataSource>
   *     </environment>
   *   </environments>
   */
  if (context == null) {
    return;
  }
  if (environment == null) {
    //  <environments default="development">
    environment = context.getStringAttribute("default");
  }
  for (XNode child : context.getChildren()) {
    // <environment id="development">
    String id = child.getStringAttribute("id");
    //判断environment值和id是否相同
    //因为 <environments default="development">可以有多个子标签,取
    //子标签<environment id="development">的id属性值等于default属性值
    if (isSpecifiedEnvironment(id)) {
      //获取TransactionFactory
      //<transactionManager type="JDBC">
      //  <property name="" value=""/>
      //</transactionManager>
      TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
     //获取DataSourceFactory
      //<dataSource type="UNPOOLED">
      //  <property name="driver" value="${driver}"/>
      //  <property name="url" value="${url}"/>
      //  <property name="username" value="${username}"/>
      //  <property name="password" value="${password}"/>
      //</dataSource>
      DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
      DataSource dataSource = dsFactory.getDataSource();
      //构建Environment对象
      Environment.Builder environmentBuilder = new Environment.Builder(id).transactionFactory(txFactory)
        .dataSource(dataSource);
      //设置到Configuration  protected Environment environment; 中
      configuration.setEnvironment(environmentBuilder.build());
      break;
    }
  }
}

9.databaseIdProvider标签解析

private void databaseIdProviderElement(XNode context) throws Exception {
  if (context == null) {
    return;
  }
  String type = context.getStringAttribute("type");
  // awful patch to keep backward compatibility
  if ("VENDOR".equals(type)) {
    type = "DB_VENDOR";
  }
  //获取name,value属性值
  Properties properties = context.getChildrenAsProperties();
  //根据type属性值构建DatabaseIdProvider实例
  DatabaseIdProvider databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor()
    .newInstance();
  databaseIdProvider.setProperties(properties);
  Environment environment = configuration.getEnvironment();
  if (environment != null) {
    //根据Environment  DataSource 获取databaseId
    String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
    //设置databaseId
    configuration.setDatabaseId(databaseId);
  }
}

10.typeHandlers标签解析

private void typeHandlersElement(XNode context) {
  /**
   * context:
   *   <typeHandlers>
   *     <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
   *   </typeHandlers>
   */
  if (context == null) {
    return;
  }
  for (XNode child : context.getChildren()) {
    if ("package".equals(child.getName())) {
      String typeHandlerPackage = child.getStringAttribute("name");
      typeHandlerRegistry.register(typeHandlerPackage);
    } else {

      //    <typeHandler javaType="String" jdbcType="VARCHAR" handler="org.apache.ibatis.builder.CustomStringTypeHandler"/>
      //获取javaType属性值
      String javaTypeName = child.getStringAttribute("javaType");
      //获取jdbcType属性值
      String jdbcTypeName = child.getStringAttribute("jdbcType");
      //获取handler属性值
      String handlerTypeName = child.getStringAttribute("handler");
      Class<?> javaTypeClass = resolveClass(javaTypeName);
      JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
      //根据handler属性值,反射构建实例
      //org.apache.ibatis.builder.CustomStringTypeHandler
      Class<?> typeHandlerClass = resolveClass(handlerTypeName);
      //注册TypeHandlerRegistry,注册到对应集合中,方便后续调用
      // private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class);
      //  private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>();
      //  private final TypeHandler<Object> unknownTypeHandler;
      //  private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>();
      //
      //  private static final Map<JdbcType, TypeHandler<?>> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();
      //
      //  private Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
      if (javaTypeClass != null) {
        if (jdbcType == null) {
          typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
        } else {
          typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
        }
      } else {
        typeHandlerRegistry.register(typeHandlerClass);
      }
    }
  }
}

11.mappers标签解析

<mappers >
<!--  <package name="xxx.xxx"/>
  <mapper class="xxx"/>
  <mapper url="xxx"/>-->
  <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>
  <mapper resource="org/apache/ibatis/builder/CachedAuthorMapper.xml"/>
  <mapper resource="org/apache/ibatis/builder/PostMapper.xml"/>
  <mapper resource="org/apache/ibatis/builder/NestedBlogMapper.xml"/>
</mappers>

根据配置的不同标签属性,获取xml资源文件

rosourde:相对路径

url:绝对路径

class:单个接口

package:包

虽然获取资源的方式不同,但结果都是把xml配文件里面的信息加载到Configuration对象的 属性字段中。

private void mappersElement(XNode context) throws Exception {
  /**
   * context:
   *  <mappers>
   *     <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
   *     <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>
   *     <mapper resource="org/apache/ibatis/builder/CachedAuthorMapper.xml"/>
   *     <mapper resource="org/apache/ibatis/builder/PostMapper.xml"/>
   *     <mapper resource="org/apache/ibatis/builder/NestedBlogMapper.xml"/>
   *   </mappers>
   */
  if (context == null) {
    return;
  }
  for (XNode child : context.getChildren()) {
    //package
    if ("package".equals(child.getName())) {
      String mapperPackage = child.getStringAttribute("name");
      configuration.addMappers(mapperPackage);
    } else {
      //<mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>
      String resource = child.getStringAttribute("resource");
      String url = child.getStringAttribute("url");
      String mapperClass = child.getStringAttribute("class");
      //resource加载方式
      if (resource != null && url == null && mapperClass == null) {
        ErrorContext.instance().resource(resource);
        try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource,
            configuration.getSqlFragments());
          mapperParser.parse();
        }
      }
      //url加载方式
      else if (resource == null && url != null && mapperClass == null) {
        ErrorContext.instance().resource(url);
        try (InputStream inputStream = Resources.getUrlAsStream(url)) {
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url,
            configuration.getSqlFragments());
          mapperParser.parse();
        }
      }
      //mapperClass加载方式
      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.");
      }
    }
  }
}

parse()方法

public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    //<mapper />
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    //绑定namespace接口类型和MapperProxyFactory
    //例如 AuthorMapper 和 AuthorMapper.xml
    bindMapperForNamespace();
  }
  configuration.parsePendingResultMaps(false);
  configuration.parsePendingCacheRefs(false);
  configuration.parsePendingStatements(false);
}

configurationElement()方法

解析xml文件中的所有标签,并加载到Configuration对象的属性字段中

private void configurationElement(XNode context) {
  try {
    //<mapper namespace="org.apache.ibatis.domain.blog.mappers.AuthorMapper">
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.isEmpty()) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    //添加缓存对象
    //Configuration  protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
    cacheRefElement(context.evalNode("cache-ref"));
    //解析cache属性值
    //Configuration  protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
    cacheElement(context.evalNode("cache"));
    //解析 <parameterMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
    //        <parameter property="id" />
    //    </parameterMap>
    //标签
    //添加到Configuration  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //解析  <resultMap id="selectAuthor" type="org.apache.ibatis.domain.blog.Author">
    //        <id column="id" property="id" />
    //        <result property="username" column="username" />
    //        <result property="password" column="password" />
    //        <result property="email" column="email" />
    //        <result property="bio" column="bio" />
    //        <result property="favouriteSection" column="favourite_section" />
    //    </resultMap>
    //标签
    //添加到添加到Configuration   protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    //解析sql片段
    sqlElement(context.evalNodes("/mapper/sql"));
    //解析select|insert|update|delete标签
    //添加到添加到添加到Configuration  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>(
    //      "Mapped Statements collection")
    //          .conflictMessageProducer((savedValue, targetValue) -> ". please check " + savedValue.getResource() + " and "
    //              + targetValue.getResource());
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

bindMapperForNamespace()

把namespace和MapperProxyFactory进行绑定

private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      // ignore, bound type is not required
    }
    if (boundType != null && !configuration.hasMapper(boundType)) {
      // Spring may not know the real resource name so we set a flag
      // to prevent loading again this resource from the mapper interface
      // look at MapperAnnotationBuilder#loadXmlResource
      configuration.addLoadedResource("namespace:" + namespace);
      //添加到Configuration   protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
      configuration.addMapper(boundType);
    }
  }
}

总结

  1. 主要完成了config配置文件,mapper文件,mapper接口的解析和注册

  2. 获取一个Confinguratin对象,里面包含各种配置和一些容器对象