Mybatis苞米豆源碼分析二-方法執行


執行具體過程(集成到spring)

  1. 找到掃包類ClassPathMapperScanner,和以往的掃包形式一樣,掃描包下所有類, 並獲得BeanDefinition
  2. 基於BeanDefinition,通過設置definition.setBeanClass,然后在spring 容器中通過getBean的方式獲取Mapper對象(此時是基礎對象下面要繼續織入插件)
  3. Mapper對象只有簡單持有sqlSession來做數據庫操作的能力, 而Mybatis提供了插件的功能, 就需要已實例化的Mapper對象進行再次代理, 將插件能力用方法攔截的方式編織到進去(插件要編織代碼具體執行位置依據實際情況)

Mybatis知識: 重重代理之后最終操作數據庫執行鏈 Executor -> StatementHandler -> statement.excute()-> ResultHander.handleResultSets(statement)

//spring-mybatis掃包注解 實例化ClassPathMapperScanner 並設置所需屬性值, 屬spring 范疇,忽略掉,直接關注ClassPathMapperScanner
@MapperScan("com.moredian.audit.dao.mapper")
public class MybatisPlusConfig {
    忽略.......
}

查看最關心的doScan與processBeanDefinitions方法

ClassPathMapperScanner

 @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //使用spring自帶掃包方式 先得到所有定義類
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
    } else {
      // 具體Bean處理方法 接着往下看
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }
  
  //實際是對定義類的一系列設置
  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  
    忽略若干行......
    
    //definition.setBeanClass 很眼熟的代碼, 設置FactoryBean 后續spring容器通過FactoryBean的getObject()方法得到具體代理對象
    //getObject的實現在 MapperFactoryBean中實現, 接下來所有的東西都是圍繞MapperFactoryBean的getObject來實現的
    definition.setBeanClass(this.mapperFactoryBean.getClass());
    
    忽略若干行.......
    
  }

實例化Mapper對象

MapperFactoryBean

@Override
  public T getObject() throws Exception {
    //這里使用spring-mybatis集成 使用的是getSqlSession()=SqlSessionTemplate
    return getSqlSession().getMapper(this.mapperInterface);
  }

委托SqlSessionTemplate來實例化Mapper

SqlSessionTemplate

@Override
  public <T> T getMapper(Class<T> type) {
    //使用mybatis configuration來實例化Mapper
    return getConfiguration().getMapper(type, this);
  }

  @Override
  public Configuration getConfiguration() {
    //獲取sqlSessionFactory中的configuration
    //在上一節MybatisPlusAutoConfiguration初始化過程中 sqlSessionFactory的configuration設置為苞米豆重寫類
    return this.sqlSessionFactory.getConfiguration();
  }

繼續調用方法getMapper

MybatisConfiguration

@Override
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mybatisMapperRegistry.getMapper(type, sqlSession);
    }

MybatisMapperRegistry

@Override
    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 MybatisPlusMapperRegistry.");
        }
        try {
            //在這里會通過代理實例化一個Mapper對象 最終使用MapperProxy實例化代理對象
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

直接看invoke方法,Mapper每個方法都會被此方法代理執行, jdk代理方式 不過多解釋

MapperProxy

@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);
    }
    //此處為執行方法類 這個方法很重要, 后面查詢時, 對於是否查詢條目邊界, 就在cachedMapperMethod方法中設置, 判斷Mapper方法參數是否含有有RowBounds子類(舉例:Page extends RowBounds)
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

到這里 Mapper代理實例化方式和方法執行過程基本結束, 接下來看Mapper方法的具體執行, 接上面最后一行 mapperMethod.execute(sqlSession, args) 以查詢列表為例查看源碼

MapperMethod

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      //通過看前面代碼可只此處sqlSession的實現為Spring的 SqlSessionTemplate, 但其實SqlSessionTemplate並沒有實現Sqlsession的功能,而是委托給Mybatis自帶的DefaultSqlSession來完成操作
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
     //通過看前面代碼可只此處sqlSession的實現為Spring的 SqlSessionTemplate, 但其實SqlSessionTemplate並沒有實現Sqlsession的功能,而是委托給Mybatis自帶的DefaultSqlSession來完成操作
      result = sqlSession.<E>selectList(command.getName(), param);
    }
   
    忽略.......
    return result;
  }

實際執行者為DefaultSqlSession, 接着往下看selectList

DefaultSqlSession

@Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //在上一節解析XML的時候 已經將所有的statement設置到configuration 此處直接取用
      MappedStatement ms = configuration.getMappedStatement(statement);
      //executor 有兩個實現類BaseExecutor 和 cachedExecutor(二級緩存), 為了方便代碼追蹤, 不開二級緩存,使用BaseExecutor來執行
      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();
    }
  }

我們更關注的是從數據庫取數據的執行過程, 直接跳到queryFromDatabase方法

BaseExecutor

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    
    忽略緩存代碼....
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    忽略緩存代碼....
    return list;
  }
  
  
  //下面這段代碼為此篇文章最為重點的代碼
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException {
    Statement stmt = null;
    try {
      //啥也沒干 不需要關心
      flushStatements();
      //獲取 苞米豆Configuration
      Configuration configuration = ms.getConfiguration();
      //這里是mybatis插件核心所在 非常重要 直接進入方法
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection, transaction.getTimeout());
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

實例化具體Statement執行器

Configuration

//實例化Statement執行器
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    //創建RoutingStatementHandler對象 用於執行Statement, 其實從名字可以看出RoutingStatementHandler 並不做真正的處理, 而是將處理過程交給其他基礎StatementHandler實現類
    StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
    //這里就是Mybatis的精髓所在了 將創建的RoutingStatementHandler對象再次代理, 添加插件執行功能, 進入interceptorChain.pluginAll 看具體如何代理的
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }

通過jdk代理方式 植入插件調用鏈

InterceptorChain

  //將所有的插件interceptors 通過代理方式植入到目標對象中
  public Object pluginAll(Object target) {
    for (Interceptor interceptor : interceptors) {
      // 從下面這段代碼可以簡單看出,要么給target通過反射方式設置屬性,要么通過jdk重新代理當前target, 這里根據自己需要實現攔截器(也即插件) 
      // 下面從苞米豆分頁插件(PaginationInterceptor)為例子來看怎么實現
      target = interceptor.plugin(target);
    }
    return target;
  }

分頁插件織入

PaginationInterceptor

 @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            //具體包裝交給Plugin工具類來做的 直接看工具類wrap方法
            return Plugin.wrap(target, this);
        }
        return target;
    }

mybatis插件執行包裝

Plugin

public static Object wrap(Object target, Interceptor interceptor) {
    Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
    Class<?> type = target.getClass();
    Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
    if (interfaces.length > 0) {
      //這段代碼表明 代理方法實現在Plugin 直接進Plugin查看invoke方法
      return Proxy.newProxyInstance(
          type.getClassLoader(),
          interfaces,
          new Plugin(target, interceptor, signatureMap));
    }
    return target;
  }
  
  
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //獲取當前類 所有需要攔截的方法
      Set<Method> methods = signatureMap.get(method.getDeclaringClass());
      //若方法需要攔截 則前置執行攔截器方法  
      if (methods != null && methods.contains(method)) {
        //具體的方法執行Invocation在intercept中執行, 當然這取決於是否真的需要執行
        return interceptor.intercept(new Invocation(target, method, args));
      }
      //若方法不需要攔截 則直接執行方法
      return method.invoke(target, args);
    } catch (Exception e) {
      throw ExceptionUtil.unwrapThrowable(e);
    }
  }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM