Mybatis 源码学习二: mybatis执行流程

一、mybatis总体执行流程

如图所示:
在这里插入图片描述
以上为mybatis大概的执行流程,细节部分在下面详细阐述。

二、Mybatis-config 配置文件解析

1. 配置文件说明

配置文件为:mybatis-config.xml,配置文件中主要包含了对11个元素节点,具体如下:

节点说明
properties属性及属性文件配置可以通过properties配置文件来动态获取数据
settings一些布尔参数值的设置
typeAliases通过对po对象位置所在建立别名,避免mapper.xml中繁琐的去指定完全限定名
plugins配置自定义插件,使自定义插件生效
objectFactory指定自定义对象工厂来覆盖mybatis默认对象工厂来创建结果集的对象
objectWrapperFactory
reflectorFactory
environments配置jdbc,数据源等环境,一个环境对应一个SqlSessionFactory实例
databaseIdProvider数据库厂商标识
typeHandlersjava类型与jdbc类型的对应关系,提供了默认的对应关系,一般无须指定
mappers配置具体的mapper.xml文件所在位置,有四种配置方式, package优先

2.配置文件解析流程

配置文件的解析主要由XmlConfigBuilder 的parseConfiguration方法进行解析如下:

  private void parseConfiguration(XNode root) {
    try {
      // 按11个配置节点依次解析
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
      loadCustomLogImpl(settings);
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
      
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

在实例化XmlConfigBuilder对象的时候会将通过Resuore获取的配置文件的流做为参数传入,并实例一个XpathParser对象来对xml进行解析。
paserConfiguration方法中每一个方法的解析过程的结果都会去更新Configuration对象的状态。
最终通过parse方法返回this.configuration。

2.1 propertiesElement方法

private void propertiesElement(XNode context) throws Exception {
    if (context != null) {
      // 获取所有子节点属性,<property></property>, Properties 继承了hashTable 本质是一个特定的集合
      Properties defaults = context.getChildrenAsProperties();
      // 获取资源属性的值
      String resource = context.getStringAttribute("resource");
      // 获取url属性的值
      String url = context.getStringAttribute("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));
      }
      // 获取configuration实例中properties的值
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      // 将解析的属性key-value重新设置到parser和configuration中
      parser.setVariables(defaults);
      configuration.setVariables(defaults);
    }
  }

2.2 settingsAsProperties 方法

// settingsAsProperties 方法主要为解析settings标签并检查解析的key-value是否与configuration中规定的吻合
  private Properties settingsAsProperties(XNode context) {
    if (context == null) {
      return new Properties();
    }
    Properties props = context.getChildrenAsProperties();
    // 检测解析的属性是否为Configuration对象中规定的,不存在抛异常
    MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
    for (Object key : props.keySet()) {
      if (!metaConfig.hasSetter(String.valueOf(key))) {
        throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
      }
    }
    return props;
  }

2.3 typeAliasesElement 方法

private void typeAliasesElement(XNode parent) {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        // <package name=""/> 标签
        if ("package".equals(child.getName())) {
          String typeAliasPackage = child.getStringAttribute("name");
          configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
        } else {
          // <typeAlias alias="role" type="com.mamba.po.Role" /> 标签
          String alias = child.getStringAttribute("alias");
          String type = child.getStringAttribute("type");
          try {
            Class<?> clazz = Resources.classForName(type);
            // typeAliasRegistry是在Configuration中new出来的, typeAliasRegistry中包含一个typeAlias的集合
            if (alias == null) { 
              // 如果alias没有配置,会采用Class.getSimpleName() 获得一个,例如,Role -> role
              typeAliasRegistry.registerAlias(clazz);
            } else {
              // 如果typeAlias包含当前key,并且对应的值不为空且当前值与集合中的值不一致,说明key重复不唯一,会抛出异常
              typeAliasRegistry.registerAlias(alias, clazz);
            }
          } catch (ClassNotFoundException e) {
            throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
          }
        }
      }
    }
  }

2.4 pluginElement 方法

配置文件中标签格式如下:

<plugins>
    <plugin interceptor="">
        <property name="" value=""/>
    </plugin>
</plugins>
private void pluginElement(XNode parent) throws Exception {
    if (parent != null) {
      // 遍历 plugin 
      for (XNode child : parent.getChildren()) {
        // 获取interceptor属性值
        String interceptor = child.getStringAttribute("interceptor");
        // 获取所有子节点属性值
        Properties properties = child.getChildrenAsProperties();
        // 实例化Interceptor
        Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
        // 将对象属性赋值
        interceptorInstance.setProperties(properties);
        // 加入到Configuration对象的拦截器链中
        configuration.addInterceptor(interceptorInstance);
      }
    }
  }

2.5 environmentsElement方法

environment主要是对事物管理器与数据源的配置管理。

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        // 获取指定的默认数据源 <environments default="xxx" />
        environment = context.getStringAttribute("default");
      }
      // 可能存在多数据源配置
      for (XNode child : context.getChildren()) {
        // environment指定的的id属性
        String id = child.getStringAttribute("id");
        // 判断当前id指示的environment是不是默认的
        if (isSpecifiedEnvironment(id)) {
          // 事物管理器工厂对象 TransactionFactory 只是接口,具体实现类由<transactionManager type="">中type指定的决定
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          // 数据源工厂对象 同事物管理器一样,实现类由 datasource中 type 决定
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          // 通过Environment中静态内部类Builder来构建Environment
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          // build()方法内部也是new Environment(id, tx, datasource)
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

2.6 mapperElement 方法

该方法是解析mpper.xml文件的核心方法,后面详细分析,每种配置对应的具体解析方式

private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      // 可能从在多个<mapper> 所以采用循环
      for (XNode child : parent.getChildren()) {
        // 如果是<package> 方式
        if ("package".equals(child.getName())) {
          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");
          if (resource != null && url == null && mapperClass == null) {
            // resource方式 resource="com.mamba.mapper.XxxMapper"
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            // 解析 mapper.xml文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            // url 方式 url="file:///var/mappers/AuthorMapper.xml"
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            // 解析 mapper.xml 文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            // class 方式 通过指定接口找mapper.xml文件进行解析
            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.");
          }
        }
      }
    }
  }

三、Mapper.xml 文件解析

1. 元素节点说明

节点说明
cachenamespace下的二级缓存
cache-ref在另一个namespace下缓存也有效
resultMap结果集封装
sqlsql片段
insert增加语句
update修改语句
delete删除语句
select查询语句

2. 解析方式

2.1 Package配置方式

在XmlConfigBuilder的mapperElement方法中首先获取到package标签的值,具体的解析过程采用MapperRegistry的addMappers方法。

// MapperRegistry 类下的方法:
// superType在解析时被指定为Object.class; packageName = "com.mamba.mapper"
public void addMappers(String packageName, Class<?> superType) {
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    // 该方法会找到mapper包下所有的mapper接口的类对象方式一个matches的set集合中
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 对mapper包下获取的mapper接口遍历处理
    for (Class<?> mapperClass : mapperSet) {
      addMapper(mapperClass);
    }
}

// ResoulverUtil 下的find方法:
// test = "is assignable to Object" test是ResoulverUtils中的IsA对象,parent=Object.class
public ResolverUtil<T> find(Test test, String packageName) {
    // 获取packgeName路径,将.替换成/ ; path = "com/mamba/mapper"
    String path = getPackagePath(packageName);
    try {
      // 找出mapper包下所有文件名,包括.class与.xml
      List<String> children = VFS.getInstance().list(path);
      for (String child : children) {
        if (child.endsWith(".class")) {
          // 通过.class文件获得对应的Class类对象并添加到matches的set集合
          addIfMatching(test, child);
        }
      }
    } catch (IOException ioe) {
      log.error("Could not read package: " + packageName, ioe);
    }
    return this;
  }

//VFS实例的list方法
public List<String> list(String path) throws IOException {
    List<String> names = new ArrayList<>();
    // getResources方法获取绝对路径Url,Url为一个对象,这里为:“/G:/xxx/../com/mamba/mapper”
    for (URL url : getResources(path)) {
      // 该list方法有子类实现
      names.addAll(list(url, path));
    }
    return names;
  }

// 子类DeafultVFS的list方法
public List<String> list(URL url, String path) throws IOException {
    InputStream is = null;
    try {
      List<String> resources = new ArrayList<>();
      URL jarUrl = findJarForResource(url);
      if (jarUrl != null) {
        // ...省略
      }
      else {
        List<String> children = new ArrayList<>();
        try {
          if (isJar(url)) {
            // ...省略
          }
          else {
            is = url.openStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
            List<String> lines = new ArrayList<>();
            // 遍历读取的文件夹下的所有文件名
            for (String line; (line = reader.readLine()) != null;) {
              lines.add(line);
              if (getResources(path + "/" + line).isEmpty()) {
                lines.clear();
                break;
              }
            }

            if (!lines.isEmpty()) {
              children.addAll(lines);
            }
          }
        } catch (FileNotFoundException e) {
         // ...省略
        }

        String prefix = url.toExternalForm();
        if (!prefix.endsWith("/")) {
          prefix = prefix + "/";
        }
		
		// 遍历递归,如果该文件夹下还有子文件夹
        for (String child : children) {
          String resourcePath = path + "/" + child;
          resources.add(resourcePath);
          URL childUrl = new URL(prefix + child);
          resources.addAll(list(childUrl, resourcePath));
        }
      }
      return resources;
    } finally {
      //省略....
    }
  }

// ResoulverUtil 下的addIfMatching方法:
protected void addIfMatching(Test test, String fqn) {
    try {
      // 去掉.class后缀并替换'/'为'.' 即变成这样: com.mamba.mapper.RolerMapper
      String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
      ClassLoader loader = getClassLoader();
      Class<?> type = loader.loadClass(externalName);
      // mataches方法主要调用Class类的isAssignableFrom(type)的方法判断type是否为调用者的子类或子接口
      if (test.matches(type)) {
        // 将当前类对象加入matches集合
        matches.add((Class<T>) type);
      }
    } catch (Throwable t) {
    }
}

MapperRegistry对象的addMapper方法

public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      // 判断当前KnownMappers集合中是否已经加载该接口的代理对象
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        // 实例化MapperPorxyFactory对象并加入集合,通过MapperPorxyFactory可以获取type接口的代理对象
        knownMappers.put(type, new MapperProxyFactory<>(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        // 解析type对应的mapper.xml文件
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

// MapperAnnotationBuilder的parse方法
public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      // 解析mapper.xml的方法
      loadXmlResource();
      // 解析之后会加入到Configuration的LoadedResource集合中,避免重复加载
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      Method[] methods = type.getMethods();
      for (Method method : methods) {
        try {
          // issue #237
          if (!method.isBridge()) {
            parseStatement(method);
          }
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
}

// 解析
private void loadXmlResource() {
    if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
      // 通过type的类对象获取对应的.xml文件,并将.xml文件变成流
      String xmlResource = type.getName().replace('.', '/') + ".xml";
      InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
      if (inputStream == null) {
        try {
          inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
        } catch (IOException e2) {
        }
      }
      if (inputStream != null) {
       // 通过XMLMapperBuilder 的parse来完成对各节点数据的映射封装
        XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
        xmlParser.parse();
      }
    }
  }  

2.2 resource与url方式

resource与Url无需通过MapperRegistry的addMapper方法,而是直接将指定的xml读取成流,然后通过new XMLMapperBuilder对象调用parse方法进行解析

2.3 class方式

class配置方式的解析与package方式大致一样,class方式省略了遍历包,会直接通过MapperRegistry对象的addMapper方法来解析。

2.4 四种方式梳理的流程图

在这里插入图片描述

3. XMLMapperBuilder对象对mapper.xml的真正解析

3.1 parse方法

public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      // 对各个元素节点的数据读取
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      // 通过nameSpace创建MapperProxyFactory对象
      bindMapperForNamespace();
    }

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

3.2 configurationElement() 方法

private void configurationElement(XNode context) {
    try {
      // 或取namespace值并设置到builderAssistant中
      String namespace = context.getStringAttribute("namespace");
      if (namespace == null || namespace.equals("")) {
        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 delete标签解析
      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);
    }
  }
3.2.1 cache-ref标签
<cache-ref namespace="com.someone.application.data.SomeMapper"/>

没有子节点,属性主要为namespace; 由cacheReflElement()方法解析

private void cacheRefElement(XNode context) {
    if (context != null) {
      // 将引用namespace加入configuration的CacheRef集合中,建立当前namespace与引用namespace的关系
      configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
      CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
      try {
        // 实际调用MapperBuilderAssistant的useCacheRef(),该方法可能会抛异常,即在Configuration中还不存在当前引用namespace的cache
        cacheRefResolver.resolveCacheRef();
      } catch (IncompleteElementException e) {
      	// 在上述抛异常的情况下,即未完成的情况下,会将cacheRefResolver加入到Configuration中的未完成的引用namespace的缓存集合中--
        configuration.addIncompleteCacheRef(cacheRefResolver);
      }
    }
  }

如图所示: cache 与 cacheRef 引用生成的关系
在这里插入图片描述

3.2.2 cache 标签
private void cacheElement(XNode context) {
    if (context != null) {
      // 可以指定type, 默认perpertual
      String type = context.getStringAttribute("type", "PERPETUAL");
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
      // 回收策略,默认LRU, 有FIFO, SOFT, WEAK四种
      // LRU移除最长时间不用的,FIFO按添加的顺序移除,SOFT基于垃圾收集器与软引用对象,WEAK基于垃圾收集器与弱应用规则对象
      String eviction = context.getStringAttribute("eviction", "LRU");
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      // 刷新策略
      Long flushInterval = context.getLongAttribute("flushInterval");
      // 引用数目,默认1024
      Integer size = context.getIntAttribute("size");
      // 默认false, 需要实体映射对象实现序列化,否则会抛异常
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }
3.2.3 resultMap 标签

resultMap的子节点标签是比较多的,属性有:{id, type, autoMapping,extends, ofType, resultType, javaType}, {type,ofType, resultType, javaType}是等价的,子元素节点有:

  • constructor{ idArg(column, javaType), arg }
  • id(property, column)
  • result(property, column)
  • association(property, javaType)
  • collection(property, ofType)
  • discriminator{case(value, resultType),(column, javaType)}

解析的大致流程如下:
在这里插入图片描述
resultMap节点的解析后对应的java对象为ResultMap;

public class ResultMap {
  private Configuration configuration;

  private String id;
  private Class<?> type;
  private List<ResultMapping> resultMappings;
  private List<ResultMapping> idResultMappings;
  private List<ResultMapping> constructorResultMappings;
  private List<ResultMapping> propertyResultMappings;
  private Set<String> mappedColumns;
  private Set<String> mappedProperties;
  private Discriminator discriminator;
  private boolean hasNestedResultMaps;
  private boolean hasNestedQueries;
  private Boolean autoMapping;
// 省略
}
3.2.4 sql 标签

sql标签解析的就保存在XmlMapperBuilder 的sqlFragments 集合中,没有给对应的Java对象。

3.2.5 select/insert/update/delete标签

这些标签解析后对应的java对象为MappedStatement;

public final class MappedStatement {
  private String resource;
  private Configuration configuration;
  private String id;
  private Integer fetchSize;
  private Integer timeout;
  private StatementType statementType;
  private ResultSetType resultSetType;
  private SqlSource sqlSource;
  private Cache cache;
  private ParameterMap parameterMap;
  private List<ResultMap> resultMaps;
  private boolean flushCacheRequired;
  private boolean useCache;
  private boolean resultOrdered;
  private SqlCommandType sqlCommandType;
  private KeyGenerator keyGenerator;
  private String[] keyProperties;
  private String[] keyColumns;
  private boolean hasNestedResultMaps;
  private String databaseId;
  private Log statementLog;
  private LanguageDriver lang;
  private String[] resultSets;
// 省略
}

由于代码太多,具体解析过程就不贴了。

3.3 各标签解析对应的java对象以及Configuration中的存储

在这里插入图片描述
Configuration对象中的关于存储解析的Mapper数据存储的代码片段:

  protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
      .conflictMessageProducer((savedValue, targetValue) ->
          ". please check " + savedValue.getResource() + " and " + targetValue.getResource());
  protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

  protected final Set<String> loadedResources = new HashSet<>();
  protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

到此,xml配置文件的解析及Configuration对象的实例化装配就完成了。

4. SqlSession的构建

SqlSession通过SqlSessionFactory的openSession()方法获得,openSession()是一个重载方法应对多种情况。以DefaultSqlSessionFactory为例,具体执行方法为openSessionFromDataSource和openSessionFromConnection两个。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // execType默认为Simple
      final Executor executor = configuration.newExecutor(tx, execType);
      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();
    }
  }

5. Executor

Executor在ExecutorType枚举类中定义了三种,为: SIMPLE, REUSE, BATCH,默认会采用SIMPLE。Executor的继承关系图如下:
在这里插入图片描述
三种实现类的方法,如下:
在这里插入图片描述
Configuration中实例化Executor的源码:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    // defaultExecutorType = ExecutorType.SIMPLE
    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再封装,CachingExecutor采用了装饰者模式
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }

6. StatementHandler

StatementHandler接口的具体实现类在StatementType中定义了三种,为:STATEMENT, PREPARED, CALLABLE,如图所示:
在这里插入图片描述
三种实现类的方法, 如图:
在这里插入图片描述
而RoutingStatementHandler类是这三种的装饰类,这是采用了装饰者的设计模式。源码片段:

// 构造方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }
  }

Configuration对象中实例化StatementHander的源码片段:

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) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

StatementType默认为StatementType.PREPARED,具体实现在MappedStatement类Builder构造方法中指定的。

7. ResultSetHandler与 ResultHandler处理查询结果

ResultHandler处理结果集采用了工厂方法,这个结果集的工厂方法可以自己定义,即在mybatis-config.xml中objectFactory标签里指定自定义的工厂类,mybatis默认采用DefaultObjectFactory。在Configuration中可以找到该默认的工厂类。
ResultSetHandler接口的主要实现类为DefualtResultSetHandler。该实例初始化的具体方法也是在Configuration中,源码如下:

  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) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

StatementHandler中持有ResultSetHandler属性,所有在StatementHandler实例化的时候就会调用该方法去实例化ResultSetHandler做为该属性的值。

处理结果集的大致流程如下,省略了许多细节:
在这里插入图片描述
storeObject之前还有一个getRowValue方法,该方法中调用了createResultObject()方法,该方法的具体实现里就用到了Configuration中的创建代理对象方法来创建一个Mapper接口的代理对象。storeObject()方法首先将该代理对象设置到ResultContext中,然后调用resultHander的handleResult()方法,将resultObject添加到ResultHandler的list集合中。

到此,执行流程的全过程就结束了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值