ソースコード分析のMyBatisのフロー分析:動的プロキシステージ

GitHubのアドレス

ソース決意アドレス
https://github.com/erlieStar/mybatis-3

プロジェクトでのデバッグソースコード
https://github.com/erlieStar/mybatis-examples

主に四つの成分に関連した動的プロキシMyBatisのステージ、約

  1. エグゼキュータ:キャッシュとエグゼキュータに実装され、二次キャッシュ
  2. StatementHandler:使用声明かのprepareStatement JDBCが提供する操作を実行、接続の役割を果たしています
  3. ParameterHandler:プリコンパイルされたSQLのパラメータ設定
  4. ResultSetHandler:データベースに戻された結果セット(ResultSetの)カプセル化された、ユーザ指定のリターンエンティティのタイプ

コードを回復するために、実行の代理ステージは、この図に含まれています。このプロセスのダイナミックを得ることができます
ここに画像を挿入説明

戻り値動的プロキシオブジェクト

例としては、ステップチェイスバイステップのプロセスをデバッグするために、動的プロキシを過ごします

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

前の記事では、sqlSessionFactory実装クラスはDefaultSqlSessionFactoryなので、のOpenSessionはDefaultSqlSessionを返し、getMapper方法を追いかけています

MapperRegistryクラスアップではなく、初期化中にキャッチマッパーインタフェースオブジェクトおよび対応するマッピングを行うMapperProxyFactory保存しましたか?

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

あなたはそれを得た、それはプロキシオブジェクトマッパーインターフェイスにこの背面かららしいです

  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、オリジナルのプロキシクラスマッパーインタフェースはMapperProxyああです。

動的プロキシアプローチを実行

実行するとき、以下の動作がMapperProxyクラスを呼び出し()関数をジャンプします

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(アクチュエータ)

  @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の着信キュータは、以下の方法で返されます

  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で、装飾SimpleExecutor CachingExecutorで使用されます

次いで、この方法は、doUpateのSimpleExecutorに入ります

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

調製したMappedStatementするStatementType型クラスから分かるように我々は、PreparedStatementHandlerを作成しました

mappedStatement.statementType = StatementType.PREPARED;

そして、PreparedStatementのPreparedStatementHandlerから取得

handler.parameterize(stmt);

SQL ParameterHandlerの次のPreparedStatementHandlerパラメータの設定

更新方法にSimpleStatementHandler

  @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()

このように、除去プロセスが完了しました

以下により実行される全体的なフローチャート

詳細エグゼキュータ

SimpleExecutor:デフォルトの設定は、データベースにアクセスするためのPreparedStatementオブジェクトを使用して、新しいPreparedStatementオブジェクトを作成する必要があり、各訪問
ReuseExecutor:アクセスするデータベースをPreparedStatementオブジェクトを使用するには、文オブジェクトのアクセス再利用する
バッチ処理を:する能力を達成するために複数のSQL文を実行します

概要

以下のコールのリンク

MapperProxy:迎撃マッパーメソッド
MapperMethod:呼び出しDefaultSqlSession文の種類に応じて
DefaultSqlSession:エグゼキュータへの権利の実現
キュータ:StatementHandler生成
ParameterHandlerを:StatementHandler生成されたSQLプリコンパイラ
ResultSetHandler:データベースを返す結果セット(ResultSetが)でしたパッケージ、ユーザー指定のリターンエンティティのタイプ

参考ブログ

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

公開された385元の記事 ウォンの賞賛1471 ビュー90万+

おすすめ

転載: blog.csdn.net/zzti_erlie/article/details/104505144