Mybatis analyse des flux du code source Analyse: scène dynamique Proxy

adresse GitHub

détermination de la source l'adresse
https://github.com/erlieStar/mybatis-3

débogage du code source avec le projet
https://github.com/erlieStar/mybatis-examples

A propos de l'étape mybatis dynamique de proxy, principalement lié aux quatre composantes

  1. Exécuteur: cache et cache secondaire mis en œuvre dans le Exécuteur
  2. StatementHandler: utilisation Déclaration ou d'effectuer des opérations PrepareStatement JDBC fournies, jouer le rôle de connexion
  3. ParameterHandler: compilé pré-réglages des paramètres SQL
  4. ResultSetHandler: jeu de résultats est retourné à la base de données (ResultSet) encapsulé, le type d'entités de retour spécifiées par l'utilisateur

Cette dynamique de processus étapes proxy d'exécution sont inclus dans ce chiffre, afin de récupérer le code peut obtenir
Insérer ici l'image Description

Renvoie l'objet proxy dynamique

Les exemples passent un proxy dynamique pour déboguer le processus, étape par étape chase

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

Article précédent est sqlSessionFactory classe d'implémentation est DefaultSqlSessionFactory, donc retourne openSession DefaultSqlSession, chassant méthode getMapper

Rattrapage classe MapperRegistry, pas lors de l'initialisation enregistrée objets d'interface Mapper et le mappage correspondant MapperProxyFactory faire?

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

Vous l'avez, il semble de ce retour dans l'objet proxy interface mappeur

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

J'ai trouvé pour générer le proxy du travail à la classe MapperProxyFactory, tout droit, puis chasse

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

Cette chasse enfin terminée, cette fonction proxy dynamique de Proxy.newProxyInstance voir les articles suivants ne comprennent pas

Mybatis l'interface écriture seule, pourquoi courir?

méthode Rechercher Proxy.newProxyInstance () dans le dernier paramètre est valide? MapperProxy, l'interface mappeur de classe proxy d'origine est MapperProxy ah.

Exécution d'une approche proxy dynamique

Lorsque vous effectuez l'opération suivante sautera Invoke fonction classe 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);
  }

Effectuer des opérations à la classe MapperMethod, continuer à chasser

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

En fonction du type d'instructions SQL, la mise en œuvre du droit à un Sqlsession

DefaultSqlSession à l'exécuteur (actionneurs)

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

Puis , dans les appels à son tour par CachingExecutor et SimpleExecutor
parce que nous avons trouvé DefaultSqlSession Exécuteur entrant retourné par la méthode suivante

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

Le procédé est transmis executorType defaultExecutorType

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

Et cacheEnabled (contrôle du cache secondaire) La valeur par défaut est vrai, sera utilisé avec décoratif SimpleExecutor CachingExecutor

Le procédé passe ensuite SimpleExecutor de 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);
    }
  }

Nous avons créé un PreparedStatementHandler, comme on peut le voir de la classe de type MappedStatement statementType comme PREPARE

mappedStatement.statementType = StatementType.PREPARED;

Et obtenu à partir d'un PreparedStatement PreparedStatementHandler

handler.parameterize(stmt);

Suivant les réglages des paramètres PreparedStatementHandler dans SQL ParameterHandler

Pour la méthode mise à jour 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;
  }

Enfin voir libellé natif JDBC

statement.execute()

Ainsi, le processus de suppression est terminé

Le tableau d'ensemble de flux exécuté par la suite

exécuteur détaillée

SimpleExecutor: la configuration par défaut, utilisez l'objet PreparedStatement pour accéder à la base de données, chaque visite doit créer un nouvel objet PreparedStatement
ReuseExecutor: en utilisant l' objet PreparedStatement pour accéder à la base de données, réutilise l'accès objet déclaration
BatchExecutor: exécuter plusieurs instructions SQL pour obtenir la capacité de

résumé

lien ci-dessous appel

MapperProxy: interception méthode Mapper
MapperMethod: En fonction du type de déclaration DefaultSqlSession d'appel
DefaultSqlSession: la mise en œuvre du droit à l' Executor
Executor: générer StatementHandler
ParameterHandler: Le StatementHandler généré pré-compilateur SQL
ResultSetHandler: la base de données renvoie un jeu de résultats (ResultSet) ont été paquet, le type d'entités de retour spécifiées par l' utilisateur

Blog de référence

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

Publié 385 articles originaux · louange won 1471 · Vues 900000 +

Je suppose que tu aimes

Origine blog.csdn.net/zzti_erlie/article/details/104505144
conseillé
Classement