Mybatis总体执行流程

Mybatis总体执行流程

0. 总体概述

mybatis的总体执行流程如下:

  1. 读取mybatis配置文件,构建configuration对象,将mapper.xml配置文件与Mapper接口绑定,并将文件中select|insert|update|delete 配置,封装成一个个MappedStatement对象,用于执行接口方法时读取配置信息。

  2. 创建数据库会话,用于执行数据库的增删改查

  3. 创建Mapper接口的代理对象MapperProxy,这里使用的是JDK动态代理

  4. 对Mapper接口参数进行解析,这里分为两个阶段:形参解析和实参解析

  5. 解析动态SQL,主要是根据条件,进行SQL拼接

  6. SQL解析后含有占位符,这里会设置SQL参数

  7. SQL的执行,调用的是Statement的execute方法(和JDBC一样)

  8. 结果解析:根据返回值类型和参数映射,进行解析返回List

1. 读取配置初始化

构建Configuration

根据mybatis配置文件解析(inputStream就是读取配置文件的输入流):

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

重点就是parser.parse(),也就是调用XMLConfigBuilder#parse

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

重点:parseConfiguration(parser.evalNode("/configuration"))

这里的parser属性是XPathParser对象,在构造XMLConfigBuilder对象时创建的:

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

可以看到这里解析分成了两步:

  1. 解析根节点configurationparser.evalNode("/configuration")

  2. 再根据根节点,解析整个配置文件:parseConfiguration(XNode root)

解析根节点,主要是利用XPathParse 进行解析,这里不再深入。主要还是看配置各个节点的解析:

  private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      // 解析properties节点,存储在Configuration.variables属性中
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      // 解析别名,存储在Configuration.typeAliasRegistry.typeAliases属性中(含内置别名)
      typeAliasesElement(root.evalNode("typeAliases"));
      // 解析插件,存储在Configuration.interceptorChain.interceptors属性中
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      // read it after objectFactory and objectWrapperFactory issue #631
      // 解析环境配置,主要是数据库连接属性以及事务管理器,存储在Configuration.environment属性中
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      // 解析类型处理器,负责处理jdbc类型和Java类型之间的转换,存储在Configuration.typeHandlerRegistry.allTypeHandlersMap属性中(含内置类型处理器)
      typeHandlerElement(root.evalNode("typeHandlers"));
      // 解析mapper配置:
      // (1)注册mapper接口及其代理对象工厂,存储在Configuration.mapperRegistry.knownMappers属性中
      // (2)解析mapper.xml文件,将select|insert|update|delete标签及其内容,解析成MappedStatement对象,存储在Configuration.mappedStatements属性中
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }
别名解析
// 解析别名,存储在Configuration.typeAliasRegistry.typeAliases属性中(含内置别名)
typeAliasesElement(root.evalNode("typeAliases"));

别名主要是为了书写的简便,不用写类型的全限定类名,在开发中,经常会给实体类增加别名,直接写类名即可,比如没有注册类型别名,需要这样写:

    <select id="getUser" resultType="com.qxf.pojo.User">
        select u.id,u.username,u.password,u.is_valid
        from t_user u
        where u.username = #{username} and u.password = #{password}
    </select>

可以看到resultType 需要写全限定类名com.qxf.pojo.User

但是如果设置了类型别名:

    <!-- 自定义别名 -->
    <typeAliases>
        <package name="com.qxf.pojo"></package>
    </typeAliases>

就可以直接使用类名:

    <select id="getUser" resultType="User">
        select u.id,u.username,u.password,u.is_valid
        from t_user u
        where u.username = #{username} and u.password = #{password}
    </select>

别名大小写不敏感,因为注册的时候,都会转换为小写TypeAliasRegistry#registerAlias:

  public void registerAlias(String alias, Class<?> value) {
    if (alias == null) {
      throw new TypeException("The parameter alias cannot be null");
    }
    // 别名转换为小写
    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);
  }

除了我们注册的类型别名,mybatis帮我们内置了如下别名:

  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);

    registerAlias("ResultSet", ResultSet.class);
  }

除了这些内置别名外,在创建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);
  }
插件解析
 // 解析插件,存储在Configuration.interceptorChain.interceptors属性中
 pluginElement(root.evalNode("plugins"));
插件的注册与管理

插件的注册和管理是通过InterceptorChain进行的,它管理了所有的插件。

public class InterceptorChain {

  private final List<Interceptor> interceptors = new ArrayList<>();

  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      target = interceptor.plugin(target);
    }
    return target;
  }

  public void addInterceptor(Interceptor interceptor) {
    interceptors.add(interceptor);
  }

  public List<Interceptor> getInterceptors() {
    return Collections.unmodifiableList(interceptors);
  }

}
插件类型

MyBatis的插件类型主要分为四种,分别是:

  1. 在Configuration创建Executor对象时,会执行插件功能:
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    // 执行插件功能,增强Executor
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
  1. 在Configuration创建StatementHandler对象时,会执行插件功能:
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    // 执行插件功能,增强StatementHandler
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
  1. 在Configuration创建ParameterHandler对象时,会执行插件功能:
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // 执行插件功能,增强ParameterHandlerer
    parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
  1. 在Configuration创建ResultSetHandler对象时,会执行插件功能:
   public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    // 执行插件功能,增强ResultSetHandler
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

这些插件可以对MyBatis的四大核心对象进行拦截,以增强核心对象的功能。

常见插件

MyBatis的常见插件包括:

  1. 分页插件:基于Mybatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于写基本List查询。
  2. 性能分析插件:可输出Sql语句以及其执行时间,建议开发测试时启用该功能,能有效解决慢查询。
  3. 全局拦截插件:提供全表delete、update操作智能分析阻断,预防误操作。
  4. 代码生成插件Mybatis-generator:可以针对数据库表自动生成MyBatis执行所需要的代码(如Mapper.java、Mapper.xml、POJO)。
自定义插件

todo

环境解析
// 解析环境配置,主要是数据库连接属性以及事务管理器,存储在Configuration.environment属性中
environmentsElement(root.evalNode("environments"));

主要是解析配置文件的environments节点:

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!--  & 代表特殊符号 &  -->
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_test?serverTimezone=UTC&useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="19930919"/>
            </dataSource>
        </environment>
    </environments>

可以看到,这里可以配置多个环境,每个环境可以配置自己的事务管理器和数据源配置

  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        // 读取默认环境id
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        // 激活默认环境:default的值等于environment节点id属性值
        if (isSpecifiedEnvironment(id)) {
          // 根据配置的事务管理器类型:这里配置的type="JDBC",根据Configuration对象创建的注册的别名:
          // typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class),可以拿到JdbcTransactionFactory
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          // 同理,根据数据源类型,这里配置的type="POOLED",根据Configuration对象创建的注册的别名:
          // typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class),可以拿到PooledDataSourceFactory
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

这里用到的别名,可以参考Configuration对象的构造函数注册的别名。

类型解析
 // 解析类型处理器,负责处理jdbc类型和Java类型之间的转换,存储在Configuration.typeHandlerRegistry.allTypeHandlersMap属性中(含内置类型处理器)
 typeHandlerElement(root.evalNode("typeHandlers"));

这里主要解析配置的自定义类型处理器,目前没有自定义。

注册逻辑:

  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
    if (javaType != null) {
      Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
      if (map == null || map == NULL_TYPE_HANDLER_MAP) {
        map = new HashMap<>();
        typeHandlerMap.put(javaType, map);
      }
      map.put(jdbcType, handler);
    }
    allTypeHandlersMap.put(handler.getClass(), handler);
  }

先看内置的类型处理器:

  public TypeHandlerRegistry() {
    register(Boolean.class, new BooleanTypeHandler());
    register(boolean.class, new BooleanTypeHandler());
    register(JdbcType.BOOLEAN, new BooleanTypeHandler());
    register(JdbcType.BIT, new BooleanTypeHandler());

    register(Byte.class, new ByteTypeHandler());
    register(byte.class, new ByteTypeHandler());
    register(JdbcType.TINYINT, new ByteTypeHandler());

    register(Short.class, new ShortTypeHandler());
    register(short.class, new ShortTypeHandler());
    register(JdbcType.SMALLINT, new ShortTypeHandler());

    register(Integer.class, new IntegerTypeHandler());
    register(int.class, new IntegerTypeHandler());
    register(JdbcType.INTEGER, new IntegerTypeHandler());

    register(Long.class, new LongTypeHandler());
    register(long.class, new LongTypeHandler());

    register(Float.class, new FloatTypeHandler());
    register(float.class, new FloatTypeHandler());
    register(JdbcType.FLOAT, new FloatTypeHandler());

    register(Double.class, new DoubleTypeHandler());
    register(double.class, new DoubleTypeHandler());
    register(JdbcType.DOUBLE, new DoubleTypeHandler());

    register(Reader.class, new ClobReaderTypeHandler());
    register(String.class, new StringTypeHandler());
    register(String.class, JdbcType.CHAR, new StringTypeHandler());
    register(String.class, JdbcType.CLOB, new ClobTypeHandler());
    register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
    register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
    register(JdbcType.CHAR, new StringTypeHandler());
    register(JdbcType.VARCHAR, new StringTypeHandler());
    register(JdbcType.CLOB, new ClobTypeHandler());
    register(JdbcType.LONGVARCHAR, new StringTypeHandler());
    register(JdbcType.NVARCHAR, new NStringTypeHandler());
    register(JdbcType.NCHAR, new NStringTypeHandler());
    register(JdbcType.NCLOB, new NClobTypeHandler());

    register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
    register(JdbcType.ARRAY, new ArrayTypeHandler());

    register(BigInteger.class, new BigIntegerTypeHandler());
    register(JdbcType.BIGINT, new LongTypeHandler());

    register(BigDecimal.class, new BigDecimalTypeHandler());
    register(JdbcType.REAL, new BigDecimalTypeHandler());
    register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
    register(JdbcType.NUMERIC, new BigDecimalTypeHandler());

    register(InputStream.class, new BlobInputStreamTypeHandler());
    register(Byte[].class, new ByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
    register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
    register(byte[].class, new ByteArrayTypeHandler());
    register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
    register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
    register(JdbcType.BLOB, new BlobTypeHandler());

    register(Object.class, unknownTypeHandler);
    register(Object.class, JdbcType.OTHER, unknownTypeHandler);
    register(JdbcType.OTHER, unknownTypeHandler);

    register(Date.class, new DateTypeHandler());
    register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
    register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
    register(JdbcType.TIMESTAMP, new DateTypeHandler());
    register(JdbcType.DATE, new DateOnlyTypeHandler());
    register(JdbcType.TIME, new TimeOnlyTypeHandler());

    register(java.sql.Date.class, new SqlDateTypeHandler());
    register(java.sql.Time.class, new SqlTimeTypeHandler());
    register(java.sql.Timestamp.class, new SqlTimestampTypeHandler());

    register(String.class, JdbcType.SQLXML, new SqlxmlTypeHandler());

    register(Instant.class, new InstantTypeHandler());
    register(LocalDateTime.class, new LocalDateTimeTypeHandler());
    register(LocalDate.class, new LocalDateTypeHandler());
    register(LocalTime.class, new LocalTimeTypeHandler());
    register(OffsetDateTime.class, new OffsetDateTimeTypeHandler());
    register(OffsetTime.class, new OffsetTimeTypeHandler());
    register(ZonedDateTime.class, new ZonedDateTimeTypeHandler());
    register(Month.class, new MonthTypeHandler());
    register(Year.class, new YearTypeHandler());
    register(YearMonth.class, new YearMonthTypeHandler());
    register(JapaneseDate.class, new JapaneseDateTypeHandler());

    // issue #273
    register(Character.class, new CharacterTypeHandler());
    register(char.class, new CharacterTypeHandler());
  }

类型处理器,都实现了TypeHandler接口:

public interface TypeHandler<T> {

  /**
   * @description 设置参数
   * @author qiuxinfa 
   * @time 2023/11/29 23:50       
   * @param ps 
   * @param i SQL中占位符的下标
   * @param parameter Java参数
   * @param jdbcType jdbc参数
   * @throws SQLException
   */
  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  /**
   * @description 从查询结果集中,根据列名获取值
   * @author qiuxinfa 
   * @time 2023/11/29 23:52       
   * @param rs 结果集
   * @param columnName 列名
   * @return T 返回值
   * @throws SQLException
   */
  T getResult(ResultSet rs, String columnName) throws SQLException;

  /**
   * @description 从查询结果集中,根据下标获取值
   * @author qiuxinfa
   * @time 2023/11/29 23:52       
   * @param rs 结果集
   * @param columnIndex 下标
   * @return T 返回值
   * @throws SQLException
   */
  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}
Mapper解析
mapperElement(root.evalNode("mappers"));
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      // mapper的4种配置方式
      for (XNode child : parent.getChildren()) {
        // 1. 配置包名
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          // 根据包名,批量注册mapper接口及其代理对象工厂
          configuration.addMappers(mapperPackage);
        } else {
          // 2. 配置资源的位置
          String resource = child.getStringAttribute("resource");
          // 3. 配置资源的url
          String url = child.getStringAttribute("url");
          // 4. 配置接口类名:和package类似,只不过package是批量配置
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            // 解析mapper.xml文件
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            // 解析mapper.xml文件
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            // 注册mapper接口及其代理对象工厂
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }
配置包名
注册代理工厂

配置包名,调用的是Configuration对象的addMappers方法:

          // 根据包名,批量注册mapper接口及其代理对象工厂
          configuration.addMappers(mapperPackage);

最终会委托给MapperRegistry#addMappers:

  public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
  }

调用重载方法:

   public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 将package包及其子包的类,存放在Set<Class<? extends T>> matches属性种
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    // 拿到package包及其子包下的类
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 遍历所有的类,执行注册逻辑
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
  }

注册逻辑:

  public <T> void addMapper(Class<T> type) {
    // 只有接口才能注册
    if (type.isInterface()) {
      // 不能重复注册
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        // 注册mapper接口及创建mapper接口代理对象的工厂
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        // 解析接口对应的xml映射文件
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

到此,创建mapper接口代理对象的工厂,已经注册到了MapperRegistry.knownMappers

现在看下解析mapper.xml文件的过程:

        // 解析接口对应的xml映射文件
        parser.parse();

调用的是MapperAnnotationBuilder#parse:

  public void parse() {
    String resource = type.toString();
    // 判断mapper.xml资源是否加载过了
    if (!configuration.isResourceLoaded(resource)) {
      // 加载接口对应的xml资源
      loadXmlResource();
      // 放入到已加载的集合中
      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.incompleteMethods
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    // 对解析出现异常的方法,重新解析一次
    parsePendingMethods();
  }
解析xml
      // 加载接口对应的xml资源
      loadXmlResource();
  private void loadXmlResource() {
    // Spring may not know the real resource name so we check a flag
    // to prevent loading again a resource twice
    // this flag is set at XMLMapperBuilder#bindMapperForNamespace
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      // 根据接口,找到xml资源路径
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      // #1347
      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());
        // 解析xml
        xmlParser.parse();
      }
    }
  }

重点来到XMLMapperBuilder#parse

  public void parse() {
    // 避免重复解析
    if (!configuration.isResourceLoaded(resource)) {
      // 解析mapper.xml文件内容,转换为对象存储
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      // 通过mapper.xml文件的namespace拿到接口的全限定类名,注册mapper接口及其代理对象工厂,存储在Configuration.mapperRegistry.knownMappers属性中
      // 对于配置接口或者配置包名的方式,在此之前,已经注册了,这里主要是针对先解析mapper.xml的方式,需要执行注册
      bindMapperForNamespace();
    }
    // 对解析异常的进行重试
    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

重点看下解析逻辑:

      // 解析mapper.xml文件内容,转换为对象存储
      configurationElement(parser.evalNode("/mapper"));

首先解析mapper.xml文件的mapper标签节点,再以此为根节点,解析整个mapper.xml:

    private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      // 解析parameterMap,存在configuration的parameterMaps属性中
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      // 解析resultMap,存在configuration的resultMaps属性中
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      // 解析sql片段,存在XMLMapperBuilder的sqlFragments属性中
      sqlElement(context.evalNodes("/mapper/sql"));
      // 解析select|insert|update|delete为XMLStatementBuilder对象,存在configuration的mappedStatements属性中
      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);
    }
  }

解析resultMap

      // 解析resultMap,存在configuration的resultMaps属性中
      resultMapElements(context.evalNodes("/mapper/resultMap"));

这里主要看:

  • resultMap的解析
  private void resultMapElements(List<XNode> list) throws Exception {
    // 遍历解析每个ResultMap节点
    for (XNode resultMapNode : list) {
      try {
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
  }

解析过程:

  private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
    return resultMapElement(resultMapNode, Collections.emptyList(), null);
  }

调用的是重载的方法:

  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
    Class<?> typeClass = resolveClass(type);
    if (typeClass == null) {
      typeClass = inheritEnclosingType(resultMapNode, enclosingType);
    }
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<>();
    resultMappings.addAll(additionalResultMappings);
    // 拿到resultMap的所有子节点,遍历解析实体属性和数据库字段的映射关系ResultMapping
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        List<ResultFlag> flags = new ArrayList<>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        // 将解析的ResultMapping添加到resultMappings
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    String id = resultMapNode.getStringAttribute("id",
            resultMapNode.getValueBasedIdentifier());
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    // 根据resultMappings等信息,构建ResultMap
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

这里的重点是解析ResultMapping,其实是解析我们配置的映射关系

    <resultMap id="userMap" type="User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="password" column="password"></result>
        <result property="isValid" column="is_valid"></result>
        <collection property="blogs" javaType="list" ofType="Blog">
            <id property="id" column="blog_id"></id>
            <result property="title" column="title"></result>
            <result property="userId" column="user_id"></result>
        </collection>
    </resultMap>
  private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
    String property;
    if (flags.contains(ResultFlag.CONSTRUCTOR)) {
      property = context.getStringAttribute("name");
    } else {
      property = context.getStringAttribute("property");
    }
    String column = context.getStringAttribute("column");
    String javaType = context.getStringAttribute("javaType");
    String jdbcType = context.getStringAttribute("jdbcType");
    String nestedSelect = context.getStringAttribute("select");
    String nestedResultMap = context.getStringAttribute("resultMap",
        processNestedResultMappings(context, Collections.emptyList(), resultType));
    String notNullColumn = context.getStringAttribute("notNullColumn");
    String columnPrefix = context.getStringAttribute("columnPrefix");
    String typeHandler = context.getStringAttribute("typeHandler");
    String resultSet = context.getStringAttribute("resultSet");
    String foreignColumn = context.getStringAttribute("foreignColumn");
    boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
    Class<?> javaTypeClass = resolveClass(javaType);
    Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
  }

这里就是读取配置的属性信息,最后构建ResultMapping。

MapperBuilderAssistant#addResultMap构建ResultMapp:

  public ResultMap addResultMap(
      String id,
      Class<?> type,
      String extend,
      Discriminator discriminator,
      List<ResultMapping> resultMappings,
      Boolean autoMapping) {
    id = applyCurrentNamespace(id, false);
    extend = applyCurrentNamespace(extend, true);

    if (extend != null) {
      if (!configuration.hasResultMap(extend)) {
        throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
      }
      ResultMap resultMap = configuration.getResultMap(extend);
      List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
      extendedResultMappings.removeAll(resultMappings);
      // Remove parent constructor if this resultMap declares a constructor.
      boolean declaresConstructor = false;
      for (ResultMapping resultMapping : resultMappings) {
        if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
          declaresConstructor = true;
          break;
        }
      }
      if (declaresConstructor) {
        extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
      }
      resultMappings.addAll(extendedResultMappings);
    }
    // 根据resultMappings信息,解析mappedColumns、mappedProperties、idResultMappings、constructorResultMappings、propertyResultMappings属性值
    ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
        .discriminator(discriminator)
        .build();
    // 将ResultMap存放到Configuration对象中
    configuration.addResultMap(resultMap);
    return resultMap;
  }

解析sql片段

      // 解析sql片段,存在XMLMapperBuilder的sqlFragments属性中
      sqlElement(context.evalNodes("/mapper/sql"));

遍历所有的sql节点:

  private void sqlElement(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }

调用重载的方法:

  private void sqlElement(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
      // id = currentNamespace + "." + id;
      id = builderAssistant.applyCurrentNamespace(id, false);
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        // 存放在sqlFragments属性中
        sqlFragments.put(id, context);
      }
    }
  }
构建MappedStatement

MappedStatement的构建,主要看这段逻辑:

      // 解析select|insert|update|delete为XMLStatementBuilder对象,存在configuration的mappedStatements属性中
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

拿到所有的elect|insert|update|delete节点进行解析:

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

调用重载方法:

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        // 遍历所有的节点,调用XMLStatementBuilder#parseStatementNode解析
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        // 解析异常的,放入的未完成的集合中,进行重试
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

看XMLStatementBuilder#parseStatementNode是如何解析的:

  public void parseStatementNode() {
    // 拿到节点id,跟接口方法名一一对应
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    // 拿到节点名称,据此判断是select|insert|update|delete中的哪种操作
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    // 解析包含的sql片段
    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);

    // Parse selectKey after includes and remove them.
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    // 最终keyStatementId = currentNamespace + "." + id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
    // 拿到sql主体
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    // 尝试获取statementType属性,默认是PREPARED,即PreparedStatement
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");
    // 根据解析的属性,添加MappedStatement到Configuration中
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

核心当然是最后一行代码MapperBuilderAssistant#addMappedStatement:

  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 = currentNamespace + "." + id;
    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    // 根据解析的参数,构建MappedStatement
    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();
    // 添加到Configuration对象中
    configuration.addMappedStatement(statement);
    return statement;
  }

至此,配置的解析基本完成。

配置resource

与配置包名类似,只不过是先解析mapper.xml文件,再解析mapper接口,刚好与配置包名的解析步骤相反。

配置url

没用过这种方式。

配置接口

与配置包名的逻辑类似。

创建SqlSessionFactory

Configuration对象作为构造参数,直接new了一个DefaultSqlSessionFactory

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

2. 创建数据库会话

        // SqlSession工厂创建SqlSession,executor默认为SimpleExecutor,被包装成CachingExecutor
        SqlSession sqlSession = factory.openSession();

调用的是DefaultSqlSessionFactory#openSession

  public SqlSession openSession() {
    // 默认ExecutorType.SIMPLE
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

接着调用重载方法,开启数据库会话:

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      // 获取配置的环境信息,拿到事务工厂和数据源配置
      final Environment environment = configuration.getEnvironment();
      // 根据配置的<transactionManager type="JDBC"/>,拿到JdbcTransactionFactory
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      // 根据数据源、事务隔离级别、是否自动提交,创建事务JdbcTransaction
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 根据事务和执行器类型创建执行器,默认的Executor是SimpleExecutor,但是会被CachingExecutor包装,并且调用插件方法增强
      final Executor executor = configuration.newExecutor(tx, execType);
      // 根据configuration和Executor创建DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

3. 创建Mapper接口代理

获取mapper接口的代理对象:

UserMapper mapper = sqlSession.getMapper(UserMapper.class);

调用的是上一环节创建的DefaultSqlSession#getMapper:

  public <T> T getMapper(Class<T> type) {
    return configuration.getMapper(type, this);
  }

委托给了Configuration#getMapper:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

又委托给了MapperRegistry#getMapper:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从knownMappers里面获取(解析mapper接口时存入的)
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      // 通过代理对象工厂创建代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

创建代理对象的过程MapperProxyFactory#newInstance:

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

可以看到,这里利用的是JDK动态代理,而MapperProxy 实现了InvocationHandler接口,所有代理对象都会调用其invoke方法。

4. 执行接口方法

执行mapper接口方法:

User user = mapper.getUserAndBlogByUserId("123");

根据第3部分(创建Mapper接口的代理对象)可以知道,当我们调用mapper接口的方法时,实际调用的是MapperProxy#invoke:

   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        if (privateLookupInMethod == null) {
          return invokeDefaultMethodJava8(proxy, method, args);
        } else {
          return invokeDefaultMethodJava9(proxy, method, args);
        }
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 1. 根据接口方法,创建MapperMethod
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 2. 执行MapperMethod#execute,并返回结果
    return mapperMethod.execute(sqlSession, args);
  }

创建MapperMethod

  private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method,
        k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
  }
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    // 构建SqlCommand:包含statementId和sql命令类型(select|insert|update|delete)
    this.command = new SqlCommand(config, mapperInterface, method);
    // 构建MethodSignature:方法返回值类型和参数解析器
    this.method = new MethodSignature(config, mapperInterface, method);
  }

重点看下MethodSignature的构造方法:

    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      // 解析返回值类型
      Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
      if (resolvedReturnType instanceof Class<?>) {
        this.returnType = (Class<?>) resolvedReturnType;
      } else if (resolvedReturnType instanceof ParameterizedType) {
        this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
      } else {
        this.returnType = method.getReturnType();
      }
      this.returnsVoid = void.class.equals(this.returnType);
      this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
      this.returnsCursor = Cursor.class.equals(this.returnType);
      this.returnsOptional = Optional.class.equals(this.returnType);
      this.mapKey = getMapKey(method);
      this.returnsMap = this.mapKey != null;
      this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
      this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
      // 参数名称解析器
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

1. 参数解析

在创建MapperMethod时,需要创建MethodSignature用来解析返回值类型和参数解析,而参数解析利用的是ParamNameResolver,所以在执行:

    // 2. 执行MapperMethod#execute,并返回结果
    return mapperMethod.execute(sqlSession, args);

在执行MapperMethod#execute时,要先解析参数:

 Object param = method.convertArgsToSqlCommandParam(args);
    public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }

而解析则是委托给了ParamNameResolver#getNamedParams:

  public Object getNamedParams(Object[] args) {
    // names属性存储:key为参数的下标(0开始),value是参数的名称
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      // 1. 如果没有标注@Param注解,并且只有一个参数,就直接返回实参的第一个参数
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      // 2. 解析实参param:key为参数名称,value为参数值
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // 同时也会放入通用的参数:param1->value1,param2->value2
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

这里解析的实参,也就是为实际参数赋值,那么形式参数是在哪里解析的呢?

实际上,在创建ParamNameResolver对象时,就会解析形参。而创建ParamNameResolver对象的时机是:

  1. 调用mapper接口方法

  2. 调用MapperProxy#invoke

  3. 创建MapperMethod对象(接口方法的实际执行对象)

  4. 创建MethodSignature(解析方法返回值类型和创建参数解析器)

  5. 创建ParamNameResolver

  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    // 因为方法可以有多个参数,每个参数可以有多个注解,paramAnnotations二维数组代表的含义:annotations[i][j],第i个参数,第j个注解
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<>();
    // 参数的数量
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      // 如果是RowBounds和ResultHandler类型的参数,跳过
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      // 遍历每个参数的所有注解,拿到@Param注解信息
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      // 参数下标->参数名称的映射
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

可以看到最终解析成:参数下标->参数名称的映射,并存储在names属性中。

至此参数解析已完成:

  1. 解析形参:参数下标->参数名称的映射

  2. 解析实体:根据参数下标,拿到实际参数的值,并赋值给对应的参数,得到:key为参数名称,value为参数值的最终执行参数

2. 设置SQL参数

以执行查询为例SimpleExecutor#doQuery

  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      // 创建StatementHandler(RoutingStatementHandler,其delegate默认为PreparedStatementHandler)
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      // 创建PreparedStatement对象(含SQL参数设置)
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    // 创建PrepareStatement对象
    stmt = handler.prepare(connection, transaction.getTimeout());
    // 设置SQL占位符参数
    handler.parameterize(stmt);
    return stmt;
  }

着重看下设置SQL参数的过程:

  public void parameterize(Statement statement) throws SQLException {
    delegate.parameterize(statement);
  }

调用的是PreparedStatementHandler#parameterize:

  public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

接着调用DefaultParameterHandler#setParameters:

  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 拿到参数映射关系
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 拿到参数名称
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            // 从参数对象里,得到参数值(见参数解析:参数名->参数值的映射)
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 设置SQL占位符参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

这里的核心就是利用了前面参数解析得到的:参数名->参数值的映射关系,再通过PreparedStatement对象的setXx方法设置占位符的值(mybatis使用了策略模式)

3. 执行SQL

参数解析并设置SQL占位符后,就可以执行SQL语句了:

return handler.query(stmt, resultHandler);

调用PreparedStatementHandler#query:

  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    // 执行SQL
    ps.execute();
    // 解析结果集,并返回
    return resultSetHandler.handleResultSets(ps);
  }

4. 结果解析

    // 解析结果集,并返回
    return resultSetHandler.handleResultSets(ps);

调用DefaultResultSetHandler#handleResultSets:

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 拿到第一个结果集,并包装成ResultSetWrapper
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 获取MappedStatement所有ResultMap(因为可能存在嵌套)
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    // 有结果集,必须要有ResultMap,否则抛出异常
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 处理结果集
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 获取下一个结果集
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
    // 返回结果
    return collapseSingleResultList(multipleResults);
  }

重点看下处理结果集的过程:

  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
          // 创建默认的结果集处理器
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          // 处理这个结果集的所有行数据,并存放到DefaultResultHandler的list属性中
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          // 将处理结果添加到要返回的结果集合中
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }

来到handleRowValues:

  public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    // 存在嵌套映射:一对一、一对多
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      // 处理嵌套映射
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      // 处理简单映射
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

简单映射

      // 处理简单映射
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      // Discriminator:鉴别器,可以看作是字典转换,很少使用,跳过
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      // 获取每行的值对象
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 将结果集存放到DefaultResultSetHandler的list属性中
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

看是如何将每一行转换为对象的:

  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
    final ResultLoaderMap lazyLoader = new ResultLoaderMap();
    // 通过反射创建目标对象:objectFactory.create(resultType),此时对象所有属性为空
    Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
    if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
      // 将目标对象,包装为MetaObject
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      boolean foundValues = this.useConstructorMappings;
      // 判断是否需要应用自动映射:AutoMappingBehavior.NONE != configuration.getAutoMappingBehavior()
      if (shouldApplyAutomaticMappings(resultMap, false)) {
        // 应用自动映射
        foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
      }
      // 应用属性映射(ResultMap)
      foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
      foundValues = lazyLoader.size() > 0 || foundValues;
      rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
    }
    return rowValue;
  }

可以看到,这里主要是分为几个步骤:

  1. 根据返回值类型,通过反射创建对象

  2. 根据列名,找到属性名,构建映射关系

  3. 从结果集中拿到列名对应的值,根据映射关系,给对象的属性赋值

嵌套映射

      // 处理嵌套映射
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    final DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    Object rowValue = previousRowValue;
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
      final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      final CacheKey rowKey = createRowKey(discriminatedResultMap, rsw, null);
      Object partialObject = nestedResultObjects.get(rowKey);
      // issue #577 && #542
      if (mappedStatement.isResultOrdered()) {
        if (partialObject == null && rowValue != null) {
          nestedResultObjects.clear();
          storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
      } else {
        // 获取每一个行对象
        rowValue = getRowValue(rsw, discriminatedResultMap, rowKey, null, partialObject);
        if (partialObject == null) {
          storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
      }
    }
    if (rowValue != null && mappedStatement.isResultOrdered() && shouldProcessMoreRows(resultContext, rowBounds)) {
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
      previousRowValue = null;
    } else if (rowValue != null) {
      previousRowValue = rowValue;
    }
  }

看下如何处理行对象的:

  private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, CacheKey combinedKey, String columnPrefix, Object partialObject) throws SQLException {
    final String resultMapId = resultMap.getId();
    Object rowValue = partialObject;
    if (rowValue != null) {
      final MetaObject metaObject = configuration.newMetaObject(rowValue);
      putAncestor(rowValue, resultMapId);
      applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);
      ancestorObjects.remove(resultMapId);
    } else {
      final ResultLoaderMap lazyLoader = new ResultLoaderMap();
      rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
      if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
        final MetaObject metaObject = configuration.newMetaObject(rowValue);
        boolean foundValues = this.useConstructorMappings;
        // 判断是否使用自动映射
        if (shouldApplyAutomaticMappings(resultMap, true)) {
          // 应用自动映射
          foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
        }
        // 映射属性映射
        foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
        // 将祖先对象及其resultMapId存储在ancestorObjects属性中
        putAncestor(rowValue, resultMapId);
        // 跟简单映射不一样的地方:应用嵌套映射(association、collection都属性嵌套映射)
        foundValues = applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;
        ancestorObjects.remove(resultMapId);
        foundValues = lazyLoader.size() > 0 || foundValues;
        rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
      }
      if (combinedKey != CacheKey.NULL_CACHE_KEY) {
        nestedResultObjects.put(combinedKey, rowValue);
      }
    }
    return rowValue;
  }

重点看如何处理嵌套映射:

  private boolean applyNestedResultMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String parentPrefix, CacheKey parentRowKey, boolean newObject) {
    boolean foundValues = false;
    for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
      final String nestedResultMapId = resultMapping.getNestedResultMapId();
      // association和collection才有nestedResultMapId
      if (nestedResultMapId != null && resultMapping.getResultSet() == null) {
        try {
          final String columnPrefix = getColumnPrefix(parentPrefix, resultMapping);
          // 拿到association和collection里面的映射关系
          final ResultMap nestedResultMap = getNestedResultMap(rsw.getResultSet(), nestedResultMapId, columnPrefix);
          if (resultMapping.getColumnPrefix() == null) {
            // try to fill circular reference only when columnPrefix
            // is not specified for the nested result map (issue #215)
            Object ancestorObject = ancestorObjects.get(nestedResultMapId);
            if (ancestorObject != null) {
              if (newObject) {
                linkObjects(metaObject, resultMapping, ancestorObject); // issue #385
              }
              continue;
            }
          }
          final CacheKey rowKey = createRowKey(nestedResultMap, rsw, columnPrefix);
          final CacheKey combinedKey = combineKeys(rowKey, parentRowKey);
          Object rowValue = nestedResultObjects.get(combinedKey);
          boolean knownValue = rowValue != null;
          // 根据映射关系,实例化集合属性,并设置到目标对象的属性中,并返回集合对象:
          // propertyValue = objectFactory.create(type);
          // metaObject.setValue(propertyName, propertyValue);
          instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject); // mandatory
          if (anyNotNullColumnHasValue(resultMapping, columnPrefix, rsw)) {
            // 通过递归的方式:实例化关联对象
            rowValue = getRowValue(rsw, nestedResultMap, combinedKey, columnPrefix, rowValue);
            if (rowValue != null && !knownValue) {
              // 设置到关联的属性中
              linkObjects(metaObject, resultMapping, rowValue);
              foundValues = true;
            }
          }
        } catch (SQLException e) {
          throw new ExecutorException("Error getting nested result map values for '" + resultMapping.getProperty() + "'.  Cause: " + e, e);
        }
      }
    }
    return foundValues;
  }

看下属性设置的方法:

  private void linkObjects(MetaObject metaObject, ResultMapping resultMapping, Object rowValue) {
    final Object collectionProperty = instantiateCollectionPropertyIfAppropriate(resultMapping, metaObject);
    if (collectionProperty != null) {
      // 处理集合属性,因为在此之前已经将该集合设置到了对象属性中,所以只需要将该对象放入到集合中即可
      final MetaObject targetMetaObject = configuration.newMetaObject(collectionProperty);
      targetMetaObject.add(rowValue);
    } else {
      // 设置单个对象属性
      metaObject.setValue(resultMapping.getProperty(), rowValue);
    }
  }
  • 13
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值