慢慢品,mybatis运行原理

// 这里主要是MyBatis的测试类,是源码阅读的入口:
@Test
public void test01() throws IOException {
    
    
    // 1、获取sqlSessionFactory对象
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = 
	new SqlSessionFactoryBuilder().build(inputStream);
    
    // 2、获取sqlSession对象
    SqlSession openSession = sqlSessionFactory.openSession();

    try {
    
    
        // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        UserMapper mapper = openSession.getMapper(UserMapper.class);
        // 4执行增删改查的方案
        User User = mapper.getUserById(1);
        System.out.println(mapper);
        System.out.println(User);
    } finally {
    
    
        openSession.close();
    }
}

一、获取SqlSessionFactory对象的过程

测试中的主要代码是:

SqlSessionFactory sqlSessionFactory = 
	new SqlSessionFactoryBuilder().build(inputStream);

流程图:

在这里插入图片描述

1.这里先是调用了SqlSessionFactoryBuilder的build()方法:
进入SqlSessionFactoryBuilder类的build

 /*
 build是一个重载的方法,实际都是都调用build(InputStream inputStream, String environment, Properties properties) 这个方法
 */
public SqlSessionFactory build(InputStream inputStream) {
    
    

  return build(inputStream, null, null);
}



/*
	实际调用的build(InputStream inputStream, String environment, Properties properties)
*/
public SqlSessionFactory build(InputStream inputStream, 
	String environment, Properties properties) {
    
    
  try {
    
    
  
// 这里创建了XMLConfigBuilder对象,用于解析xml文件的
    XMLConfigBuilder parser = 
		new XMLConfigBuilder(inputStream, environment, properties);

// 这里调用的XMLConfigBuilder的实例对象parser 的parse()方法
    return build(parser.parse());
  } catch (Exception e) {
    
    
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
  } finally {
    
    
    ErrorContext.instance().reset();
    try {
    
    
      inputStream.close();
    } catch (IOException e) {
    
    
      // Intentionally ignore. Prefer previous error.
    }
  }
}

进入XMLConfigBuilder类的 parse() 方法:

// 这是XMLConfigBuilder的parse方法,返回的Configuration对象
public Configuration parse() {
    
    
  if (parsed) {
    
    
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
 
 /* 
   调用同一个类中的parseConfiguration()方法中,调用解析器的evalNode()方法,解析MyBatis配置文件中的configuration
  */
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

在上面的 parse() 调用了同类的 parseConfiguration() 方法

// 这是XMLConfigBuilder的 parseConfiguration 方法
private void parseConfiguration(XNode root) {
    
    
  try {
    
    
/*
这里先找到根节点的每一子节点,像properties、typeAliases、plugins等依次进行解析并设置
*/
    propertiesElement(root.evalNode("properties")); //issue #117 read properties first
    typeAliasesElement(root.evalNode("typeAliases"));
/*
	这里是对全局配文件中的plugins进行解析并设置,
进入pluginElement()看一下
*/
    pluginElement(root.evalNode("plugins"));
    objectFactoryElement(root.evalNode("objectFactory"));
    objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
   /*
这里就是把配置文件中的setting进行解析并设置
*/
 settingsElement(root.evalNode("settings"));
    environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
    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);
  }
}

上面的parseConfiguration() 有对MyBatis配置文件的解析和设置,我们这里重点看一下三个

  1. plugins
  2. settings
  3. mappers

1、先是plugins标签属性的解析和设置:进入XMLConfigBuilder的**pluginElement()**方法

/*
	接下来是进入MLConfigBuilder 中的pluginElement()方法
*/
private void pluginElement(XNode parent) throws Exception {
    
    
  if (parent != null) {
    
    
	//如果配置了<plugins/>节点
    for (XNode child : parent.getChildren()) {
    
    
      
      // 解析plugins节点中的interceptor
      String interceptor = child.getStringAttribute("interceptor");
      //解析interceptor标签中的属性
      Properties properties = child.getChildrenAsProperties();
      
      //解析interceptor并创建相应的interceptor实现类的对象
      Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
      //解析相应的interceptor配置类的相应属性
      interceptorInstance.setProperties(properties);
      configuration.addInterceptor(interceptorInstance);
    }
  }
}

2、接着是settings标签解析和设置:进入XMLConfigBuilder的**settingsElement()**方法


/*----------------------------重点分割线:start---------------------------------------*/
/*
这里进入setingsElemnt()进行看一下,把在全局配置文件中可配置属性进行解析并设置,如果没有设置的属性,MyBatis会设置一个默认值。
*/
private void settingsElement(XNode context) throws Exception {
    
    
  if (context != null) {
    
    
    Properties props = context.getChildrenAsProperties();
    // Check that all settings are known to the configuration class
    MetaClass metaConfig = MetaClass.forClass(Configuration.class);
    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).");
      }
    }
/*----------------------------重点分割线:end---------------------------------------*/

    /*
	下面对在全局配置文件中可配置属性进行解析并设置,如果没有设置的属性,MyBatis会设置一个默认值
   */
    configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
    configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
    configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
    configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
    configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
    configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
    configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
    configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
    configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
    configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
    configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
    configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
    configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
    configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
    configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
    configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
    configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
    configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
    configuration.setLogPrefix(props.getProperty("logPrefix"));
    configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
    configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
  }
}

3、进入 mappers标签的解析和设置:进入XMLConfigBuildermapperElement() 方法

/*
这里进入mapperElement()进行看一下,把在全局配置文件中我们配置的Mapper相关的信息进行解析*/
private void mapperElement(XNode parent) throws Exception {
    
    
  if (parent != null) {
    
    
/*----------------------------重点分割线:start---------------------------------------*/
    for (XNode child : parent.getChildren()) {
    
    
       // 判断用了包扫描的方式
      if ("package".equals(child.getName())) {
    
    
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
    
    
        
        // 本测试中使用的是resource标签进行设置的
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) {
    
    
          ErrorContext.instance().resource(resource);
          InputStream inputStream = Resources.getResourceAsStream(resource);
		
		/*这里用了XMLMapperBuilder 的解析器,是对XxxMapper.xml文件进行解析*/
          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
          // 调用了XMLMapperBuilder类中的方法,下面进入parse()
          mapperParser.parse();
/*----------------------------重点分割线:end---------------------------------------*/

        } 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());
          mapperParser.parse();
        } else if (resource == null && url == null && mapperClass != null) {
    
    
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
    
    
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}

上面的解析mappers标签中调用了XMLMapperBuilder类中的 parse() 方法,下面进入parse()

public void parse() {
    
    
  if (!configuration.isResourceLoaded(resource)) {
    
    

    //这里调用同类中configurationElement()方法,接下来进入configurationElement()方法
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingChacheRefs();
  parsePendingStatements();
}

调用了XMLMapperBuilder类中的 configurationElement() 方法:

private void configurationElement(XNode context) {
    
    
  try {
    
    
    String namespace = context.getStringAttribute("namespace");
    if (namespace.equals("")) {
    
    
     throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    sqlElement(context.evalNodes("/mapper/sql"));
   
/* 这里返回了Mapper中select|insert|update|delete的List<XNode> list集合,
下面进入buildStatementFromContext方法*/
	buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    
    
    throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  }
}

上面调用了XMLMapperBuilderbuildStatementFromContext() 方法:

private void buildStatementFromContext(List<XNode> list) {
    
    
  if (configuration.getDatabaseId() != null) {
    
    
    buildStatementFromContext(list, configuration.getDatabaseId());
  }
  // 这里调用了 buildStatementFromContext的重载方法
  buildStatementFromContext(list, null);
}

// 重置的方法
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    
    
  for (XNode context : list) {
    
    
  
/* 
创建XMLStatementBuilder 对象,用于解析Mapper.xml中的增删改查的SQL语句
*/
    final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
    try {
    
    
     // 这里调用了SQL节点标签解析的方法:parseStatementNode()
      statementParser.parseStatementNode();
    } catch (IncompleteElementException e) {
    
    
      configuration.addIncompleteStatement(statementParser);
    }
  }
}

上面调用了XMLStatementBuilder 类中SQL节点标签解析的方法:parseStatementNode()

public void parseStatementNode() {
    
    
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

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

  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String parameterType = context.getStringAttribute("parameterType");
  Class<?> parameterTypeClass = resolveClass(parameterType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultType = context.getStringAttribute("resultType");
  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  Class<?> resultTypeClass = resolveClass(resultType);
  String resultSetType = context.getStringAttribute("resultSetType");
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

  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
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  // Parse selectKey after includes and remove them.
  processSelectKeyNodes(id, parameterTypeClass, langDriver);
  
  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  String resultSets = context.getStringAttribute("resultSets");
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  KeyGenerator keyGenerator;
  String keyStatementId = 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))
        ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
  }


/*----------------------------重点分割线:start---------------------------------------*/
  /*
	通过解析得到Mapper中statement语句的所有信息,
	调用addMappedStatement()添加一个MappedStatement,
	一个MappedStatement就代表一个增删改查方法相对应的标签的详细信息,
	这些最终都保存在configuation对象中。
 */
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered, 
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
/*----------------------------重点分割线:end---------------------------------------*/

上面使用到MapperBuilderAssistant 类的addMappedStatement()方法,该类继承BaseBuilder类

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 = applyCurrentNamespace(id, false);
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

  MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
  statementBuilder.resource(resource);
  statementBuilder.fetchSize(fetchSize);
  statementBuilder.statementType(statementType);
  statementBuilder.keyGenerator(keyGenerator);
  statementBuilder.keyProperty(keyProperty);
  statementBuilder.keyColumn(keyColumn);
  statementBuilder.databaseId(databaseId);
  statementBuilder.lang(lang);
  statementBuilder.resultOrdered(resultOrdered);
  statementBuilder.resulSets(resultSets);
  setStatementTimeout(timeout, statementBuilder);

  setStatementParameterMap(parameterMap, parameterType, statementBuilder);
  setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
  setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);

  MappedStatement statement = statementBuilder.build();
  
 /*----------------------------重点分割线:start---------------------------------------*/
   // 可以看到最终MappedStatement的实例还是保存在了configuration对象中
  configuration.addMappedStatement(statement);
  return statement;
}
 /*----------------------------重点分割线:end---------------------------------------*/

二、获取sqlSession对象的过程

测试中的代码:

		 // 3、获取接口的实现类对象
        //会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
        UserMapper mapper = openSession.getMapper(UserMapper.class);

整个流程的解析(流程图):

在这里插入图片描述

现在进入源码部分:
进入了DefaultSqlSessionFactory类,该类实现了SqlSessionFactory,进入:openSession() 方法

public SqlSession openSession() {
    
    

/* 调用openSessionFromDataSource()*/
  return openSessionFromDataSource(
	configuration.getDefaultExecutorType(), null, false);
}

实际调用的DefaultSqlSessionFactory类下面的重载的openSession() 方法

/ 在该类中Mapper进行了重载,最终实际调用的是下面的方法
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    
    
  Transaction tx = null;
  try {
    
    
/*从configuration中获取当前的环境*/
    final Environment environment = configuration.getEnvironment();
    final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
// 创建事务
    tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

 /*----------------------------重点分割线:start---------------------------------------*/
/*
	创建一个Executor实现类的对象 ,这个对象非常重要。
	Executor是一个接口
	有SimpleExecutor、BatchExecutor和ReuseExecutor等实现类
	是MyBatis中四个Executor、StatementHandler、ParamHandler、ResultSetHandler重要的对象之一.
*/
    final Executor executor = configuration.newExecutor(tx, execType);
    // 创建并返回了一个包含configuration、executor和autoCommit相关信息的DefaultSqlSession
   return new DefaultSqlSession(configuration, executor, autoCommit);
 /*----------------------------重点分割线:end---------------------------------------*/   
 
  } 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();
  }
}

上面调用了Configuration中的newExecutor() 方法:

public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    
    
  executorType = executorType == null ? defaultExecutorType : executorType;
  executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
  Executor executor;

// ExecutorType有三种类型的分写SIMPLE、BATCH 、REUSE ,可以通过配置文件进行配置
  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);
  }

 /*----------------------------重点分割线:start---------------------------------------*/
/*
如果全局配置中配置了缓存,使用CachingExecutor对之前的对象进行包装
CachingExecutor在遇到查询的方法前,会现在本地缓存查询一下是否有数据,如果返回缓存中的数据,
如果没有,所有底层还是调用SimpleExecutor的对应方法。

*/
  if (cacheEnabled) {
    
    
    executor = new CachingExecutor(executor);
  }

/* 这里调用了interceptorChain的pluginAll() */
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}
 /*----------------------------重点分割线:end---------------------------------------*/

上面创建完executor后还调用了interceptorChain.pluginAll(executor),对它进行包装,现在进入
InterceptorChain 查看:

public class InterceptorChain {
    
    

  private final List<Interceptor> interceptors = new ArrayList<Interceptor>();
 
  // 会用Exector进行包装进入配置的每个interceptor 实现类中
  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);
  }

}

三、获取接口的实现类对象

测试中的关键代码:

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

流程图:
在这里插入图片描述
进入SqlSessionFactory类的getMapper() 方法:

public <T> T getMapper(Class<T> type) {
    
    
  // 实际调用了Configuration的getMapper()方法
  return configuration.<T>getMapper(type, this);
}

上面实际调用了ConfigurationgetMapper() 方法:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    
    
  // 实际调用的MapperRegistry对象的getMapper方法
  return mapperRegistry.getMapper(type, sqlSession);
}

上面实际调用的MapperRegistry对象getMapper() 方法

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    
    
  /*Configuation中的MapperRegistry中的knownMappers获取到我们需要的
Mapper 类型的mapperProxyFactory 
*/
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);
  }
}

进入MapperProxyFactorynewInstance() 方法:

public class MapperProxyFactory<T> {
    
    

   // 这个是上面调用的newInstance()方法
  public T newInstance(SqlSession sqlSession) {
    
    
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

// 最终实际调用的是这个重载的方法	
  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    
    
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] {
    
     mapperInterface }, mapperProxy);
  }
}

查看一下MapperProxy类,发现它实现了InvocationHandler接口,说明这个类是用于产生动态代理对象的类:

public class MapperProxy<T> implements InvocationHandler, Serializable {
    
    

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    
    
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
    if (Object.class.equals(method.getDeclaringClass())) {
    
    
      try {
    
    
        return method.invoke(this, args);
      } catch (Throwable t) {
    
    
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    
    
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
    
    
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

最后返回了MapperProxy的对象(即接口的代理对象

四、使用Mapper接口的代理对象进行增删改查的流程

测试中的代码:

	// 4执行增删改查的方案
    User User = mapper.getUserById(1);

流程图:

进入源码部分:
进入MapperProxy类,执行invoke() 方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
  /*
	如果Object类的方法,就直接放行,
 */
  if (Object.class.equals(method.getDeclaringClass())) {
    
    
    try {
    
    
      return method.invoke(this, args);
    } catch (Throwable t) {
    
    
      throw ExceptionUtil.unwrapThrowable(t);
    }
  }

 /*method包装成一个MapperMethod对象*/
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  
  // 这里调用MapperMethod的execute()方法
  return mapperMethod.execute(sqlSession, args);
}

上面调用了MapperMethod类,调用execute() 方法:

public Object execute(SqlSession sqlSession, Object[] args) {
    
    
  Object result;


/*这是SQL语句类型进行判断INSERT 、UPDATE、DELETE、SELECT  */
  if (SqlCommandType.INSERT == command.getType()) {
    
    
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.insert(command.getName(), param));
  } else if (SqlCommandType.UPDATE == command.getType()) {
    
    
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.update(command.getName(), param));
  } else if (SqlCommandType.DELETE == command.getType()) {
    
    
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.delete(command.getName(), param));
  } else if (SqlCommandType.SELECT == command.getType()) {
    
    
/*在查询(SELECT)类型中会对返回类型进行检查*/
    if (method.returnsVoid() && method.hasResultHandler()) {
    
    
      executeWithResultHandler(sqlSession, args);
      result = null;
    } else if (method.returnsMany()) {
    
    
  /*这是返回多个的结果*/
      result = executeForMany(sqlSession, args);
    } else if (method.returnsMap()) {
    
    
/*这是返回Map类型的*/
      result = executeForMap(sqlSession, args);
    } else {
    
    
      Object param = method.convertArgsToSqlCommandParam(args);
  /*这是查询单个的,返回单个实体类对象的,调用的DefaultSqlSession
的selectOne()方法*/
      result = sqlSession.selectOne(command.getName(), param);
    }
  } else {
    
    
    throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    
    
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

上面调用了DefaultSqlSession 类的selectOne() 方法:

public <T> T selectOne(String statement, Object parameter) {
    
    
  // Popular vote was to return null on 0 results and throw exception on too many.
  
  /*在这里看到实际是调用自己的selectList()方法*/
  List<T> list = this.<T>selectList(statement, parameter);
  if (list.size() == 1) {
    
    
/*放回集合中的第一个元素*/
    return list.get(0);
  } else if (list.size() > 1) {
    
    
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    
    
    return null;
  }
}

进入该类的selectList()方法,该方法有很多的重载方法,最后调用的是下面的
selectList(String statement, Object parameter, RowBounds rowBounds) 方法:

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    
    
  try {
    
    
/*
	先从configuation中获取对应的MappedStatement,
	然后传给executor对象,调用query进行查询
	 wrapCollection(parameter):这里对我们传入的参数进行包装
 */
    MappedStatement ms = configuration.getMappedStatement(statement);
    List<E> result = executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    return result;
  } catch (Exception e) {
    
    
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    
    
    ErrorContext.instance().reset();
  }
}

看一下是如何包装我们传入的对象的:

private Object wrapCollection(final Object object) {
    
    
 
  if (object instanceof List) {
    
    
    StrictMap<Object> map = new StrictMap<Object>();
     // 如果传入的集合对象是List集合,map中的key使用的是"list"
    map.put("list", object);
    return map;
  } else if (object != null && object.getClass().isArray()) {
    
    
    StrictMap<Object> map = new StrictMap<Object>();
    // 如果传入的集合对象是数组,map中的key使用的是"array"
    map.put("array", object);
    return map;
  }
  return object;
}

上面对传入的参数包装后使用的Executorquery()的查询方法
Executor的实现类有
BaseExecutor
SimpleExecutorCachExecutor等,而调用query()实际是调用的是BaseExecutor的:

// BaseExecutor类中query()方案
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    
    
   /*
   这里获取到了BoundSql 对象,
   BoundSql :包含了SQL语句,参数信息、元数据参数等
   */
   BoundSql boundSql = ms.getBoundSql(parameter);
   CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
   return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}

如果MyBatis中使用了缓存,会将SimpleExecutor包装成了CacheExecutor:进入CacheExecutor的Query方法:

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
    
    
  
  Cache cache = ms.getCache();
  if (cache != null) {
    
    
    flushCacheIfRequired(ms);
    if (ms.isUseCache() && resultHandler == null) {
    
    
      ensureNoOutParams(ms, parameterObject, boundSql);
      @SuppressWarnings("unchecked")
      List<E> list = (List<E>) tcm.getObject(cache, key);
      if (list == null) {
    
    
        list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
      }
      return list;
    }
  }
  // 底层还是调用SimpleExecutor的query方法
  return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

最终还是调用了BaseExecutor类中重载的Query方法:

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    
    
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) throw new ExecutorException("Executor was closed.");
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    
    
    clearLocalCache();
  }
  List<E> list;
  try {
    
    
    queryStack++;
/*
先从本地缓存中拿数据,*/
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
    
    
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
    
    

/*本地缓存中没有数据,调用queryFromDatabase方法*/
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    
    
    queryStack--;
  }
  if (queryStack == 0) {
    
    
    for (DeferredLoad deferredLoad : deferredLoads) {
    
    
      deferredLoad.load();
    }
    deferredLoads.clear(); // issue #601
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
    
    
      clearLocalCache(); // issue #482
    }
  }
  return list;
}

调用同一个类中的queryFromDatabase() 方法:

/*
	进入queryFromDatabase方法
*/
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    
    
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
    
    
  
/*调用doQuery进行查一下*/
        list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    
    
    localCache.removeObject(key);
  }
  
  // 将查出的数据存入本地缓存中
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    
    
    localOutputParameterCache.putObject(key, parameter);
  }
//返回查询的数据
  return list;
}

BaseExecutor

//进入doQuery方法,该方法是抽象方法,在SimpleExecutor中进行了实现
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
    throws SQLException;

因为SimpleExecutor类的实现doQuery方法:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    
    
 
 //从这里可以看出MyBatis的底层还是使用的原生的JDBC
  Statement stmt = null;
  try {
    
    
    Configuration configuration = ms.getConfiguration();
   
/*
创建一个StatementHandler对象,
是mybatis中四个重要对象之一,用于创建Statement对象
在创建的StatementHandler的时候,底层也同时创建创建的ParamHandler和ResultSetHandler这两个对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
/**/
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.<E>query(stmt, resultHandler);
  } finally {
    
    
    closeStatement(stmt);
  }
}

StatementHandler对象是在Configuation对象中创建的,进入Configuation的newStatementHandler方法:

public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    
  /*这里创建了一个RoutingStatementHandler对象*/
StatementHandler statementHandler = new 	RoutingStatementHandler(executor, 
		mappedStatement, 
		parameterObject, 
		rowBounds, 
		resultHandler, 
		boundSql);
  statementHandler = (StatementHandler) 
/*
   对象创建完了之后,还是会使用拦截器来包装StatementHandler
*/
interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

进入RoutingStatementHandler,查看是如创建的

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    
  
  switch (ms.getStatementType()) {
    
    
// 这是statement,没有进行预编译的,不能防止SQL注入攻击
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
// 这是预编译的PrepareStatement,默认的是PREPARED
    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());
  }

}

创建PrepaStatementHandler后,使用它创建出Statement,调用SimpleExecutor的prepareStatement方法,进行预编译:

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    
    
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection);
  handler.parameterize(stmt);
  return stmt;
}

上面调用了PrepareStatementHandler类中parameterize方法:

public void parameterize(Statement statement) throws SQLException {
    
    
    /*
	调用parameterHandler的setParameters,进行预编译参数设置
	(parameterHandler是之前创建StatementHandler对象的时候就创建好了的)
    parameterHandler是接口,实际是DefaultParameterHandler类的对象
*/
     parameterHandler.setParameters((PreparedStatement) statement);
}

调用了DefaultParameterHandler类的setParameters:

public void setParameters(PreparedStatement ps) throws SQLException {
    
    
  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 对象的setParameter方法进行设置预编译参数。*/
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) jdbcType = configuration.getJdbcTypeForNull();
        typeHandler.setParameter(ps, i + 1, value, jdbcType);
      }
    }
  }
}

调用DefaultResultSetHandler类的handleResultSets()方法:

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

  int resultSetCount = 0;
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  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.getResulSets();
  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++;
    }
  }

handleResultSets方法最终有间接调用到getPropertyMappingValue,代码中可以说明在处理结果映射的时候还是用的TypeHandler对象来处理返回结果的:

private Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix)
    throws SQLException {
    
    
  if (propertyMapping.getNestedQueryId() != null) {
    
    
    return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
  } else if (propertyMapping.getResultSet() != null) {
    
    
    addPendingChildRelation(rs, metaResultObject, propertyMapping);
    return NO_VALUE;
  } else if (propertyMapping.getNestedResultMapId() != null) {
    
    
    // the user added a column attribute to a nested result map, ignore it
    return NO_VALUE;
  } else {
    
    
	
	// 这里使用了TypeHandler类来处理结果集的映射
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
    return typeHandler.getResult(rs, column);
  }
}

猜你喜欢

转载自blog.csdn.net/Hicodden/article/details/106457538
今日推荐