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

发布了385 篇原创文章 · 获赞 1471 · 访问量 90万+

猜你喜欢

转载自blog.csdn.net/zzti_erlie/article/details/104505144