Mybatis flow analysis of the source code Analysis: Dynamic Proxy stage

GitHub address

Source resolve the address
https://github.com/erlieStar/mybatis-3

debug source code with project
https://github.com/erlieStar/mybatis-examples

About dynamic proxy Mybatis stage, mainly related to the four components

  1. Executor: cache and secondary cache implemented in the Executor
  2. StatementHandler: Use Statement or perform operations PrepareStatement JDBC provided, play the role of connecting
  3. ParameterHandler: pre-compiled SQL parameter settings
  4. ResultSetHandler: result set returned to the database (the ResultSet) encapsulated, the type of user-specified return entities

This process dynamic proxy stages of execution are included in this figure, in order to recover the code can get
Here Insert Picture Description

Returns the dynamic proxy object

Examples spend a dynamic proxy to debug the process, step by step chase

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

Previous article comes sqlSessionFactory implementation class is DefaultSqlSessionFactory, so openSession returns DefaultSqlSession, chasing getMapper method

Catch up MapperRegistry class, not during initialization saved mapper interface objects and the corresponding mapping MapperProxyFactory do?

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

You got it, it seems from this back into the proxy object mapper interface

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

I found to generate the proxy of the work to MapperProxyFactory class, all right, then chase

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

This chase to finally over, this dynamic proxy function of Proxy.newProxyInstance see the following articles do not understand

Mybatis write-only interface, why run?

Look Proxy.newProxyInstance () method in the last parameter is valid? MapperProxy, the original proxy class mapper interface is MapperProxy ah.

Perform dynamic proxy approach

When performing the following operation will jump invoke MapperProxy class () function

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

Perform operations to the MapperMethod class, continue to chase

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

Depending on the type of sql statements, the implementation of the right to a Sqlsession

DefaultSqlSession to the Executor (actuators)

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

Then in turn calls through CachingExecutor and SimpleExecutor
because we found DefaultSqlSession incoming Executor returned by the following method

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

The method is passed executorType defaultExecutorType

protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;

And cacheEnabled (secondary cache control) The default value is true, will be used with decorative SimpleExecutor CachingExecutor

The method then enters SimpleExecutor of 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);
    }
  }

We created a PreparedStatementHandler, as can be seen from MappedStatement statementType type class as PREPARED

mappedStatement.statementType = StatementType.PREPARED;

And obtained from a PreparedStatement PreparedStatementHandler

handler.parameterize(stmt);

Next PreparedStatementHandler parameter settings in SQL ParameterHandler

To update method 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;
  }

Finally see JDBC native wording

statement.execute()

Thus, the removal process is completed

The overall flow chart executed by the following

Detailed Executor

SimpleExecutor: the default configuration, use the PreparedStatement object to access the database, each visit must create a new PreparedStatement object
ReuseExecutor: using PreparedStatement object to access the database, reuses the statement object access
BatchExecutor: execute multiple SQL statements to achieve the ability to

to sum up

Call link below

MapperProxy: interception Mapper method
MapperMethod: Depending on the type of call DefaultSqlSession statement
DefaultSqlSession: the implementation of the right to Executor
Executor: generating StatementHandler
ParameterHandler: The StatementHandler the generated SQL pre-compiler
ResultSetHandler: the database returns a result set (ResultSet) were package, the type of user-specified return entities

Reference blog

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

Published 385 original articles · won praise 1471 · Views 900,000 +

Guess you like

Origin blog.csdn.net/zzti_erlie/article/details/104505144
Recommended