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

 

 
posted @ 2020-01-03 10:23  蟹烟客  阅读(1404)  评论(0编辑  收藏  举报