Mybatis源码(一)— 配置文件解析

上篇博客已经简单介绍了Mybatis的整体架构,源码部分也会按照架构图来梳理,本节就先从Mybatis的配置文件开始。

mybatis-config.xml

平时工作时,大家用Mybatis注解多一些还是xml多一些?对于Mybatis框架而言,xml可能更加灵活、方便一些,常用且主要的xml一共有2个,一个是mybatis-config.xml,另一个就是Mapper对应的xml。所以,先从xml开始讲解。
mybatis-config.xml作为我们常用并且很主要的配置文件,主要配置mybatis全局配置,db连接等信息。

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

    <properties resource="db.properties" ></properties>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.test.bean"/>
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
    <databaseIdProvider type="DB_VENDOR">
        <property name="MySQL" value="mysql"/>
        <property name="SQL Server" value="sqlserver"/>
        <property name="Oracle" value="oracle"/>
    </databaseIdProvider>
    <mappers>
        <package name="com.test.dao"/>
    </mappers>
</configuration>

测试类

通过读取配置文件以流的形式构建SqlSessionFactory来看底层的一个实现。

 public void test02 () {
            // 根据全局配置文件创建出SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = null;
            try {
                inputStream = Resources.getResourceAsStream(resource);
            } catch (IOException e) {
                e.printStackTrace();
            }
            // SqlSessionFactory:负责创建SqlSession对象的工厂
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            // SqlSession:表示跟数据库建议的一次会话
            // 获取数据库的会话,创建出数据库连接的会话对象(事务工厂,事务对象,执行器,如果有插件的话会进行插件的解析)
            SqlSession sqlSession = sqlSessionFactory.openSession();
            Emp empByEmpno = null;
            try {
                // 获取要调用的接口类,创建出对应的mapper的动态代理对象(mapperRegistry.knownMapper)
                EmpDao mapper = sqlSession.getMapper(EmpDao.class);
                // 调用方法开始执行
                empByEmpno = mapper.findEmpByEmpnoAndEname(7369, "SMITH");
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                sqlSession.close();
            }
            System.out.println(empByEmpno);
        }

SqlSessionFactoryBuilder().build()

build方法中会根据解析到的xml文件,转化成XMLConfigBuilder对象,

//properties:引入额外配置
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

XMLConfigBuilder

XPathParser中,会将xml转换成document文件,并用this方法对XMLConfigBuilder和Configuration进行初始化赋值

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
	
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    //将mybatis.xml转换成docment
    this.document = createDocument(new InputSource(inputStream));
  }
 //调用this构造器方法,对XMLConfigBuilder进行初始化赋值
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
	
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

XMLMapperEntityResolver

根据类中dtd文件对mybatis-config.xml中各种标签进行规范

public class XMLMapperEntityResolver implements EntityResolver {

  //根据dtd文件校验mybatis-config.xml规范,看xml中标签是否在dtd中存在等
  private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd";
  private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd";
  private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd";

  private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd";
  private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd";
  //省略部分代码。。。
  }

Configuration

//初始化Configuration,注册别名
  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

XPathParser


/**
 * XPath解析器,用的都是JDK的类包,封装了一下,使得使用起来更方便
 */
public class XPathParser {

  // 文档对象
  private final Document document;
  // 是否开启验证
  private boolean validation;
  // 用于加载本地DTD文件
  private EntityResolver entityResolver;
  // mybatis-config.xml中properties标签定义的键值对集合
  private Properties variables;
  // Xpath对象
  private XPath xpath;

	//省略部分代码···
}		

创建SqlSessionFactory

前两步已经将mybatis-config.xml加载到了内存中,并且初始化了Configuration对象,接下来会执行parse方法,方法中会解析xml中的属性节点,为Configuration对象赋值。

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      // 前戏:准备工作:将配置文件加载到内存中并生成一个document对象 ,同时初始化Configuration对象
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }
  
  // 解析配置
  public Configuration parse() {
    // 根据parsed变量的值判断是否已经完成了对mybatis-config.xml配置文件的解析
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    // 在mybatis-config.xml配置文件中查找<configuration>节点,并开始解析
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

parseConfiguration

解析xml下各个节点。

private void parseConfiguration(XNode root) {
    try {
      //解析configuration下的properties节点
      propertiesElement(root.evalNode("properties"));
      //解析setting节点
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      // 设置vfsImpl字段
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      //解析类型别名,注册到typeAlias集合
      typeAliasesElement(root.evalNode("typeAliases"));
      //解析插件
      pluginElement(root.evalNode("plugins"));
      //对象工厂
      objectFactoryElement(root.evalNode("objectFactory"));
      //对象包装工厂
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //反射工厂
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      //设置具体属性到configuration
      settingsElement(settings);
      //解析environments,
      environmentsElement(root.evalNode("environments"));
      // databaseIdProvider
      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);
    }
  }

properties标签解析
根据properties中resource和url属性(resource=“db.properties”),进行资源加载,并放入hashTable中,并更新XPathParser和configuration中Variables对象

 private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      //解析Properties子标签property中name和value属性,记录到Properties
      Properties defaults = context.getChildrenAsProperties();
      //获取resource属性
      String resource = context.getStringAttribute("resource");
      //获取url属性
      String url = context.getStringAttribute("url");
      //resource和url有一个
      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) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
      Properties vars = configuration.getVariables();
      //与Configuration对象中Properties对象(继承了HashTable)合并
      if (vars != null) {
        defaults.putAll(vars);
      }
      //更新XPathParser和configuration中Variables对象
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

settings标签解析

private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    // 解析settings子节点的name和value属性,并返回properties对象
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    // 创建configuration对应的MetaClass对象
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    // 检测Configuration中是否定义了key指定属性的setter方法
    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;
  }

typeAliases别名标签解析
获取别名,并注册到typeAliases集合(Map<String, Class<?>>)中。

 private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      // 处理全部子节点
      for (XNode child : parent.getChildren()) {
        // 处理package节点
        if ("package".equals(child.getName())) {
          // 获取指定的包名
          String typeAliasPackage = child.getStringAttribute("name");
          // 通过TypeAliasRegistry扫描指定包中所有的类,并解析@Alias注解,完成别名注册
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
          // 处理typeAlias节点
        } else {
          // 获取指定的别名
          String alias = child.getStringAttribute("alias");
          // 获取别名对应的类型
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            // 根据Class名字来注册类型别名
            // 调用TypeAliasRegistry.registerAlias
            if (alias == null) {
              // 扫描@Alias注解,完成注册
              typeAliasRegistry.registerAlias(clazz);
            } else {
              // 注册别名
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

registerAliases

 //扫描并注册包下所有继承于superType的类型别名
  public void registerAliases(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 查找指定包下的superType类型类
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
    for (Class<?> type : typeSet) {
      // 过滤掉内部类、接口以及抽象类
      if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
        registerAlias(type);
      }
    }
  }

//注册类型别名
  public void registerAlias(Class<?> type) {
    // 类的简单名称(不包含包名)
    String alias = type.getSimpleName();
    // 读取@Alias注解
    Alias aliasAnnotation = type.getAnnotation(Alias.class);
    if (aliasAnnotation != null) {
      alias = aliasAnnotation.value();
    }
    // 检测此别名不存在后,会将其记录到typeAlias集合中
    registerAlias(alias, type);
  }

 //注册类型别名
  public void registerAlias(String alias, Class<?> value) {
    // 检测alias为null,则直接抛出异常
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // issue #748
    // 将别名转换为小写
    String key = alias.toLowerCase(Locale.ENGLISH);
    // 检测别名是否已经存在
    if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
      throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
    }
    // 注册别名
    typeAliases.put(key, value);
  }

TypeAliasRegistry

/**
 * 类型别名注册机
 */
public class TypeAliasRegistry {

  private final Map<String, Class<?>> typeAliases = new HashMap<>();

  public TypeAliasRegistry() {
    //构造函数里注册系统内置的类型别名
    registerAlias("string", String.class);
    //基本包装类型
    registerAlias("byte", Byte.class);
    registerAlias("long", Long.class);
    registerAlias("short", Short.class);
    registerAlias("int", Integer.class);
    registerAlias("integer", Integer.class);
    registerAlias("double", Double.class);
    registerAlias("float", Float.class);
    registerAlias("boolean", Boolean.class);

    //基本数组包装类型
    registerAlias("byte[]", Byte[].class);
    registerAlias("long[]", Long[].class);
    registerAlias("short[]", Short[].class);
    registerAlias("int[]", Integer[].class);
    registerAlias("integer[]", Integer[].class);
    registerAlias("double[]", Double[].class);
    registerAlias("float[]", Float[].class);
    registerAlias("boolean[]", Boolean[].class);

    //加个下划线,就变成了基本类型
    registerAlias("_byte", byte.class);
    registerAlias("_long", long.class);
    registerAlias("_short", short.class);
    registerAlias("_int", int.class);
    registerAlias("_integer", int.class);
    registerAlias("_double", double.class);
    registerAlias("_float", float.class);
    registerAlias("_boolean", boolean.class);

    //加个下划线,就变成了基本数组类型
    registerAlias("_byte[]", byte[].class);
    registerAlias("_long[]", long[].class);
    registerAlias("_short[]", short[].class);
    registerAlias("_int[]", int[].class);
    registerAlias("_integer[]", int[].class);
    registerAlias("_double[]", double[].class);
    registerAlias("_float[]", float[].class);
    registerAlias("_boolean[]", boolean[].class);

    //日期数字型
    registerAlias("date", Date.class);
    registerAlias("decimal", BigDecimal.class);
    registerAlias("bigdecimal", BigDecimal.class);
    registerAlias("biginteger", BigInteger.class);
    registerAlias("object", Object.class);

    registerAlias("date[]", Date[].class);
    registerAlias("decimal[]", BigDecimal[].class);
    registerAlias("bigdecimal[]", BigDecimal[].class);
    registerAlias("biginteger[]", BigInteger[].class);
    registerAlias("object[]", Object[].class);

    //集合型
    registerAlias("map", Map.class);
    registerAlias("hashmap", HashMap.class);
    registerAlias("list", List.class);
    registerAlias("arraylist", ArrayList.class);
    registerAlias("collection", Collection.class);
    registerAlias("iterator", Iterator.class);

    //还有个ResultSet型
    registerAlias("ResultSet", ResultSet.class);
  }
  //省略部分代码。。。
}

settingsElement
为configuration设置初始值。

private void settingsElement(Properties props) {
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
    configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
    configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
    configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
  }

environmentsElement
为environment赋值,并且根据参数创建事务工厂和DB工厂。初始化Environment对象

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      //未指定XMLConfigBuilder.environment字段,则使用default
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      //遍历子节点,
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        //与XmlConfigBuilder.environment字段匹配
        if (isSpecifiedEnvironment(id)) {
          //创建事务工厂
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //创建DB工厂
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          //创建Environment对象
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          //设置configuration中environment变量
          configuration.setEnvironment(environmentBuilder.build());
          break;
        }
      }
    }
  }

mapperElement!!!
解析mapper标签,并添加到mapperRegistry中,

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");
          configuration.addMappers(mapperPackage);
        } else {
          //获取mapper标签中resource、url、class属性,三个属性互斥
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //如果使用resource
          if (resource != null && url == null && mapperClass == null) {
            // 创建XMLMapperBuilder对象,解析映射配置文件
            ErrorContext.instance().resource(resource);
            try(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);
            try(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属性,则注册到mapperRegistry中。
            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.");
          }
        }
      }
    }
  }

addMappers
将接口添加到knownMappers(Map<Class<?>, MapperProxyFactory<?>>)中,并进行解析。

 public <T> void addMapper(Class<T> type) {
    //如果是接口类型
    if (type.isInterface()) {
      //判断是否在knowsMapper中
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        //将mapper接口对应的Class对象和MapperProxyFactory添加到knownMappers中
        knownMappers.put(type, new MapperProxyFactory<>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        //如果过程中出现了异常,还需要从knownMappers中移除。
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

MapperAnnotationBuilder
将注解接口封装成一个对象,可通过注解进行识别。

public class MapperAnnotationBuilder {

  private static final Set<Class<? extends Annotation>> statementAnnotationTypes = Stream
      .of(Select.class, Update.class, Insert.class, Delete.class, SelectProvider.class, UpdateProvider.class,
          InsertProvider.class, DeleteProvider.class)
      .collect(Collectors.toSet());
}      

parse
解析mapper.xml

public void parse() {
   String resource = type.toString();
   //查看是否加载过该接口
   if (!configuration.isResourceLoaded(resource)) {
     //检查是否加载过对应的mapper.xml,如果没加载过,则创建XMLMapperBuilder对象解析对应的映射文件
     loadXmlResource();
     //放到loadedResources集合中
     configuration.addLoadedResource(resource);
     assistant.setCurrentNamespace(type.getName());
     // 解析@CacheNamespace注解
     parseCache();
     // 解析@CacheNamespaceRef注解
     parseCacheRef();
     for (Method method : type.getMethods()) {
       if (!canHaveStatement(method)) {
         continue;
       }
       if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
           && method.getAnnotation(ResultMap.class) == null) {
         parseResultMap(method);
       }
       try {
         //解析@SelectKey,@ResultMap等注解,并创建MappedStatement对象
         parseStatement(method);
       } catch (IncompleteElementException e) {
         //解析出错,放入incompleteMethods中保存
         configuration.addIncompleteMethod(new MethodResolver(this, method));
       }
     }
   }
   // 遍历incompleteMethods集合中记录的未解析的方法,并重新进行解析
   parsePendingMethods();
 }

loadXmlResource
加载mapper.xml

//获取到对应的xml文件并进行加载解析
private void loadXmlResource() {

    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      //获取对应的mapper.xml
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      // 将xml转换成stream流
      InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
      if (inputStream == null) {
        // Search XML mapper that is not in the module but in the classpath.
        try {
          inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
        } catch (IOException e2) {
          // ignore, resource is not required
        }
      }
      if (inputStream != null) {
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }

xmlParser.parse
解析mapper标签

public void parse() {
    //判断是否加载过该文件
    if (!configuration.isResourceLoaded(resource)) {
      //解析mapper.xml文件中的mapper标签
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

configurationElement

private void configurationElement(XNode context) {
    try {
      //获取到namespace
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      //设置当前命名空间
      builderAssistant.setCurrentNamespace(namespace);
      //解析cache-ref
      cacheRefElement(context.evalNode("cache-ref"));
      //解析cache
      cacheElement(context.evalNode("cache"));
      //解析parameterMap节点
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //返回resultMap节点
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //解析sql节点
      sqlElement(context.evalNodes("/mapper/sql"));
      //解析select、insert、update等标签属性节点
      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);
    }
  }

buildStatementFromContext
解析SQL

 private void buildStatementFromContext(List<XNode> list) {
    //看DatabaseID是否为null,都是调用buildStatementFromContext方法
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      // 构建所有语句,一个mapper下可以有很多select
      // 语句比较复杂,核心都在这里面,所以调用XMLStatementBuilder
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        //核心,解析SQL语句
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

parseStatementNode
解析mapper.xml中各个标签、参数类型、返回类型等。并构建MappedStatement

public void parseStatementNode() {
    //Id -》 方法名 以及databaseId属性。
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");
    //如果databaseId值不匹配,退出。
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    // 根据SQL节点的名称决定其SqlCommandType
    String nodeName = context.getNode().getNodeName();
    //SqlCommandType 枚举类,有SELECT、UPDATE、INSERT、DELETE和FLUSH等属性
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    //看是否是select标签
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    //是否需要缓存select结果
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    //仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。
    //这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    // 在解析SQL语句之前,先处理其中的include节点
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());
    //参数类型
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    //脚本语言
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    //解析之前,先解析selectKey节点
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    KeyGenerator keyGenerator;
    //获取selectKey节点对应的SelectKeyGenerator的id
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    //SQL节点下存在SelectKey节点
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
    //解析sqlSource 一般是DynamicSqlSource
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    //获取语句类型 共STATEMENT, PREPARED(预处理,防止SQL注入), CALLABLE(存储过程)
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    //获取每次批量返回行数
    Integer fetchSize = context.getIntAttribute("fetchSize");
    //超时时间
    Integer timeout = context.getIntAttribute("timeout");
    //引入外部parameterMap
    String parameterMap = context.getStringAttribute("parameterMap");
    //结果类型
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    //返回类型
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    //结果集类型 FORWARD_ONLY、SCROLL_INSENSITIVE、SCROLL_SENSITIVE中的一种
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    //(仅对insert有用)标记一个属性,Mybatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值
    String keyProperty = context.getStringAttribute("keyProperty");
    //(仅对insert有用)标记一个属性,Mybatis会通过getGeneratedKeys或者通过insert语句的selectKey子元素设置它的值
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    //通过builderAssistant创建MappedStatement对象
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

MappedStatement
使用构造者模式创建MappedStatement,类中属性包含整个SQL名、接口Dao名,参数值,返回类型等。为后面动态代理创建Dao对象,执行对应方法做准备。

public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }
    //id为对应的Dao加方法名
    id = applyCurrentNamespace(id, false);
    //是否是select类型
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //构造者模式
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
    //构造者模式创建MappedStatement
    MappedStatement statement = statementBuilder.build();
    //将statement放入mappedStatements( Map<String, MappedStatement>)中,key为Dao的全限定名+方法名组成
    //因为Dao是接口,需要动态代理进行创建,具体要执行的Dao中方法是哪一个SQL语句,放入mappedStatements后,可在其中获取。
    configuration.addMappedStatement(statement);
    return statement;
  }

流程图

加载mybatis-config.xml,并转化成Stream形式,通过SqlSessionFactoryBuilder解析Mybatis-config.xml,解析各个标签,并构建Configuration全局配置类,解析mapper标签时,会找到对应的mapper.xml和对应的Dao接口,今儿解析mapper.xml标签,并将Dao类型放入MapperRegister中,为动态代理做准备,并解析mapper.xml中标签,将属性通过构造者模式,创建MappedStatement类。
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值