Mybatis:源码解析

发布于 2023-10-19  1322 次阅读


Mybatis使用也有好多年了,还没仔细扒一下源码,前段时间有空,看了一下源码,做个简单的记录。源码解析包括了一些设计思路,设计模式,和一些自己的想法。

1 入手-入口

// 加载Mybatis配置文件    
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
// 通过构建工厂,构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 创建一个sqlSession
SqlSession session = sqlSessionFactory.openSession();
    try {
        // 通过sqlSession先获取到Mapper接口对象
        BlogMapper mapper = session.getMapper(Mapper.class);
        // 执行查询语句
        Blog one = mapper.selectOne();
        System.out.println(one);
    } finally {
      // 关闭当前session
      session.close();
    }

通过入口配置,我们要知道整个Mybatis运行的流程,代码注释上已写明。接下来我们一步一步的分析。

2 加载配置文件

Resources.getResourceAsStream("mybatis-config.xml")

这个代码,只是用来加载配置文件,并没有做解析。

我们点进去看下源码,发现首先是获取所有的可能得类加载器,然后使用类加载器加载配置文件

public InputStream getResourceAsStream(String resource) {
    return getResourceAsStream(resource, getClassLoaders(null));
  }

使用类加载器加载文件后直接返回InputStream,第一步文件加载就完成了

 InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {
      if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) {
          returnValue = cl.getResourceAsStream("/" + resource);
        }

        if (null != returnValue) {
          return returnValue;
        }
      }
    }
    return null;
  }

3 构建sqlSessionFactory工厂

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

这个代码就是将上一步的inputStream流作为参数传进去,build()方法有很多重载,可以配置其他的参数,这里使用的是一个参数的。这里有个设计模式是建造者模式。

建造者模式:使用多个简单的对象一步一步构建成一个复杂的对象

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 {
        if (inputStream != null) {
          inputStream.close();
        }
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

构建XMLConfigBuilder对象用于解析XML,XPathParser是XMLConfigBuilder调用的解析工具,最后通过

  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    commonConstructor(validation, variables, entityResolver);
    this.document = createDocument(new InputSource(inputStream));
  }

createDocument方法是最终创建文档的方法,创建好文档等候,就是解析,调用parseConfiguration方法解析document里面的节点

  private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      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);
      // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }

解下好的参数会封装到Configuration configuration配置参数中,便于后面使用。

解析好的configuration会调用build方法,生成SqlSessionFactory返回,至此SqlSessionFactory创建成功。

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

4 创建SqlSession

使用DefaultSqlSessionFactory的openSessionFromDataSource()方案创建sqlSession,其中ExecutorType默认是SIMPLE普通类型。

SqlSession session = sqlSessionFactory.openSession();

根据Configuration配置的信息(Environment环境,数据源,事务等信息)(Executor执行器,默认是SimpleExecutor用于执行查询语句的)返回创建好的DefaultSqlSession

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

下面使用sqlSession就可以加载mapper对象,操作数据库了。

5 加载Mapper,执行查询

通过反射和类型创建Mapper代理对象,从前面xml配置好的mapper中读取(mapperRegistry)

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    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);
    }
  }
  1. 从DefaultSqlSession中的Configuration属性中获取数据
  2. Configuration中有mapperRegistry对象
  3. mapperRegistry中knownMappers是类型是Map<Class<?>, MapperProxyFactory<?>>的map对象
  4. knownMappers.get(type)获取mapper的代理对象
  5. 获取到的对象类型是MapperProxyFactory
  6. MapperProxyFactory的方法缓存(methodCache)中又包含MapperMethodInvoker,这个就是具体的代理执行器,然后创建MapperProxy
  7. MapperProxy中有一个cachedInvoker方法
  private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
    try {
      return MapUtil.computeIfAbsent(methodCache, method, m -> {
        if (!m.isDefault()) {
          return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
        }
        try {
          if (privateLookupInMethod == null) {
            return new DefaultMethodInvoker(getMethodHandleJava8(method));
          } else {
            return new DefaultMethodInvoker(getMethodHandleJava9(method));
          }
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
          throw new RuntimeException(e);
        }
      });
    } catch (RuntimeException re) {
      Throwable cause = re.getCause();
      throw cause == null ? re : cause;
    }
  }

MapperMethod方法会解析mapper中的语句

SqlCommand方法会调用resolveMappedStatement方法生成MappedStatement

6 MapperProxy代理

这个类中有一个invoke方法是具体执行查询语句的方法

    public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
      return mapperMethod.execute(sqlSession, args);
    }

会自动判断类型,执行响应的查询语句

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        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;
  }

然后执行流程大概就是:

  1. 执行响应的handler处理对应的查询语句
  2. handler调用执行器executor只能执行语句
  3. 如果是CachingExecutor会调用createCacheKey创建缓存key放入map
  4. 然后对查询的结果封装结果集进行返回

7. CachingExecutor的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, boundSql);
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

TransactionalCacheManager事务缓存管理器

tcm.getObject(cache, key)更具缓存和key获取已缓存的数据返回,这就是二级缓存

8. 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 {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

localCache就是一级缓存

开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询,二级缓存中有数据就直接返回,没有就查一级缓存,还没有就查询数据库


欢迎欢迎~热烈欢迎~