Mybatis源碼解析之流程解析:動態代理階段

GitHub地址

源碼解析地址
https://github.com/erlieStar/mybatis-3

debug源碼用項目
https://github.com/erlieStar/mybatis-examples

關於Mybatis的動態代理階段,主要涉及到4個組件

  1. Executor:一級緩存和二級緩存的實現都在Executor中
  2. StatementHandler:使用JDBC提供的Statement或PrepareStatement執行操作,起承上啓下的作用
  3. ParameterHandler:對預編譯的SQL進行參數設置
  4. ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型

這個動態代理階段執行的流程都包含在這張圖中,依次追一下代碼就能搞定
在這裏插入圖片描述

返回動態代理對象

用上一節的例子來debug動態代理的過程,一步一步追

sqlSession = sqlSessionFactory.openSession(true);
BookMapper bookMapper = sqlSession.getMapper(BookMapper.class);
int num = bookMapper.deleteById(1);

上一篇文章說到sqlSessionFactory的實現類是DefaultSqlSessionFactory,所以openSession返回的是DefaultSqlSession,追getMapper方法

追到了MapperRegistry類,這個類不是在初始化的時候保存了mapper接口對象和相應的MapperProxyFactory的映射關係嗎?

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();

答對了,看來要從這放回mapper接口的代理對象了

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

發現生成代理的工作都交給MapperProxyFactory類了,沒事,接着追

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
  protected T newInstance(MapperProxy<T> mapperProxy) {
    // 實現了mapper接口的動態代理對象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

到這終於追完了,對Proxy.newProxyInstance這個動態代理函數不瞭解的看如下文章

Mybatis只寫了接口,爲什麼能運行?

看Proxy.newProxyInstance()方法中的最後一個參數是啥?MapperProxy,原來mapper接口的代理類就是MapperProxy啊。

執行動態代理方法

當執行如下操作的時候,會跳進MapperProxy類的invoke()函數

int num = bookMapper.deleteById(1);
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

    // 代理以後,所有Mapper方法調用時,都會執行這個invoke方法
    try {
      // 如果是Object本身的方法不增強
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        // 針對java7以上版本對動態類型語言的支持,暫不分析
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    // 從緩存中獲取mapperMethod對象,如果緩存中沒有,則創建一個,並添加到緩存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

執行操作給了MapperMethod類,繼續追

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

根據sql語句的不同類型,將執行權交給了Sqlsession

DefaultSqlSession交給Executor(執行器)

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      // statement爲命名空間+方法名
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

接着調用會依次經過CachingExecutor和SimpleExecutor
因爲我們發現DefaultSqlSession傳入的Executor通過如下方法返回的

  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    // 防止粗心大意的人將defaultExecutorType設爲null
    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) interceptorChain.pluginAll(executor);
    return executor;
  }

方法傳入的executorType爲defaultExecutorType

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

而cacheEnabled(控制二級緩存)默認值爲true,所用會用CachingExecutor裝飾SimpleExecutor

接着進入SimpleExecutor的doUpate方法

  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);
    }
  }

創建了一個PreparedStatementHandler,因爲從MappedStatement類中可以看到statementType類型爲PREPARED

mappedStatement.statementType = StatementType.PREPARED;

並且從PreparedStatementHandler得到一個PreparedStatement

handler.parameterize(stmt);

接着PreparedStatementHandler使用ParameterHandler設置SQL中的參數

到了SimpleStatementHandler的update方法

  @Override
  public int update(Statement statement) throws SQLException {
    String sql = boundSql.getSql();
    Object parameterObject = boundSql.getParameterObject();
    KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
    int rows;
    if (keyGenerator instanceof Jdbc3KeyGenerator) {
      statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
      rows = statement.getUpdateCount();
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else if (keyGenerator instanceof SelectKeyGenerator) {
      statement.execute(sql);
      rows = statement.getUpdateCount();
      keyGenerator.processAfter(executor, mappedStatement, statement, parameterObject);
    } else {
      statement.execute(sql);
      rows = statement.getUpdateCount();
    }
    return rows;
  }

終於看到JDBC的原生寫法

statement.execute()

至此,刪除流程完畢

整個執行的流程圖如下

Executor詳解

SimpleExecutor:默認配置,使用PreparedStatement對象訪問數據庫,每次訪問都要創建新的PreparedStatement對象
ReuseExecutor:使用PreparedStatement對象訪問數據庫,訪問時會重用statement對象
BatchExecutor:實現執行多條SQL語句的能力

總結

調用鏈路如下

MapperProxy:攔截Mapper方法
MapperMethod:根據語句的不同類型調用DefaultSqlSession
DefaultSqlSession:將執行權交給Executor
Executor:生成StatementHandler
ParameterHandler:將生成的StatementHandler中的SQL進行預編譯
ResultSetHandler:對數據庫返回的結果集(ResultSet)進行封裝,返回用戶指定的實體類型

參考博客

[1]https://www.jianshu.com/p/46c6e56d9774

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章