mybatis调用过程

一、简单说一下mapper初始化

mybatis在应用启动的时候,会默认扫描指定路径的mapper,然后生成代理类。入口在@MapperScan注解上,有个@Import(MapperScannerRegistrar.class)。我们重点看下MapperScannerRegistrar类,它会注册mapper的BeanDef,这里就不介绍了。重点介绍mapper接口封装后的类MapperFactoryBean,它的getObject就是咱们的mapper之后的代理类。如下:

 protected T newInstance(MapperProxy mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

发现最终通过jdk动态代理生成实现类,注意这里的sqlSession是传进来的。这个sqlSession并不是真正sql执行的session,它只是一个session代理,初始化阶段并不会创建sqlSession。这里下节就会介绍。我们继续mapper代理类的初始化,看下MapperProxy类:

public class MapperProxy implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class mapperInterface;
  private final Map methodCache;

public MapperProxy(SqlSession sqlSession, Class mapperInterface, Map methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

上面只截取了代码片段。这里看到MapperProxy实现了InvocationHandler,说明以后mapper的每次sql调用都会调用到这里的invoke方法。启动工程,抓取mapper的代理类的实现:

public final class $Proxy159 extends Proxy implements JournalLineMapper {
    private static Method m1;
    private static Method m7;
    private static Method m6;
    private static Method m2;
    private static Method m5;
    private static Method m3;
    private static Method m4;
    private static Method m8;
    private static Method m9;
    private static Method m0;

    public $Proxy159(InvocationHandler var1) {
        super(var1);
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m7 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getJournalLinePOBySearchTxt", Class.forName("java.lang.Long"), Class.forName("java.lang.String"), Class.forName("java.sql.Date"), Class.forName("java.sql.Date"), Class.forName("java.lang.Long"), Class.forName("java.lang.Integer"), Class.forName("org.apache.ibatis.session.RowBounds"));
            m6 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("sequenceJournal", Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.math.BigDecimal"), Class.forName("java.math.BigDecimal"), Class.forName("java.lang.String"), Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getAccountSummaryRTWithChildrenValue", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.lang.Integer"));
            m3 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getSummaryBySobIdAndLedgerId", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"));
            m4 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getSummaryWithChildren", Class.forName("java.lang.Long"), Class.forName("java.lang.Long"), Class.forName("java.lang.String"));
            m8 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getJournalLinePOCount", Class.forName("java.lang.Long"), Class.forName("java.lang.String"), Class.forName("java.sql.Date"), Class.forName("java.sql.Date"), Class.forName("java.lang.Long"), Class.forName("java.lang.Integer"));
            m9 = Class.forName("com.xxxx.query.mapper.JournalLineMapper").getMethod("getMultiLedgerAccountSummaryRTWithChildrenValue", Class.forName("java.lang.Long"), Class.forName("java.util.List"), Class.forName("java.util.List"), Class.forName("java.lang.Integer"));
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public final boolean equals(Object var1) {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final List getSummaryBySobIdAndLedgerId(Long var1, Long var2) {
        try {
            return (List)super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final JournalLineSummaryDTO getSummaryWithChildren(Long var1, Long var2, String var3) {
        try {
            return (JournalLineSummaryDTO)super.h.invoke(this, m4, new Object[]{var1, var2, var3});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final JournalLineAccountSummaryDTO getAccountSummaryRTWithChildrenValue(Long var1, Long var2, List var3, Integer var4) {
        try {
            return (JournalLineAccountSummaryDTO)super.h.invoke(this, m5, new Object[]{var1, var2, var3, var4});
        } catch (RuntimeException | Error var6) {
            throw var6;
        } catch (Throwable var7) {
            throw new UndeclaredThrowableException(var7);
        }
    }

上面反编译的源码,也能看到每个mapper接口方法的实现,其实调用的都是super.h.invoke方法。验证上面说的结论。

二、sqlSession在mapper代理类初始化阶段,是怎么注入的?

上面也提到了在MapperFactoryBean.getObject方法实现里面,有段代码:

@Override
  public  T getMapper(Class type) {
    return getConfiguration().getMapper(type, this);
  }

把this传递进去了,这个this是SqlSessionTemplate对象,看下这个类:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;

  private final ExecutorType executorType;

  private final SqlSession sqlSessionProxy;

  private final PersistenceExceptionTranslator exceptionTranslator;

发现没,成员有个叫SqlSession类型,看下它的构造函数,看下SqlSession创建的是啥?

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

sqlSessionProxy是通过jdk动态代理生成的SqlSession接口实现类。说明传递给mapper代理类的这个sqlSession并不是真正的session,而是一个代理类。后面sql在执行的时候,所有SqlSession接口的方法调用,都会被委托给这个SqlSessionInterceptor的invoke方法。SqlSessionInterceptor的源码如下:

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

可以在invoke里面看到真正调用执行的时候,sqlSession是在invoke方法里面通过getSqlSession得到。

三、mapper方法执行过程

入口在mapper的代理类

return (Long)super.h.invoke(this, m8, new Object[]{var1, var2, var3, var4, var5, var6});

看下org.apache.ibatis.binding.MapperProxy#invoke源码:

 @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

进入mapperMethod.execute(sqlSession, args),如下:

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

最终会调用到sqlSession.selectXXX方法,而这个sqlSession就是初始化的时候传递进去的sqlSessionProxy并不是真正的session,它没有执行能力。它会委托给org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor类的invoke方法,上面已经讲过了。看下它的invoke方法:

 private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

获取真正的sqlSession,然后反射调用。
method.invoke(sqlSession, args) 这句话就是 sqlSession.method(args),只不过这里要用反射来调用,所以必须得用前面那种写法。
我们继续看session.method方法,比如随机选择一个session的方法,selectOne好了。
org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)的源码:

@Override
  public  T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

到这里,终于看到了session维度了。继续追踪:


  @Override
  public  List selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

看到executor了吧,它是session的属性,而session是每次调用的时候创建。说明executor也是每次sql调用都会重新创建新的对象。进入executor看看:

 @Override
  public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }

  @SuppressWarnings("unchecked")
  @Override
  public  List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List list;
    try {
      queryStack++;
      list = resultHandler == null ? (List) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

选择BaseExecutor,看到里面有localcache,这里就不在展开了,上一篇已经讲过了。

到这里,我们可以后面再执行,就是拿Connection去真正执行了。。。以后有时间在补充

你可能感兴趣的:(mybatis调用过程)