Mybatis代理对象的获取与其方法的调用过程

是Mybatis的使用过程中,我们只要配置成功,我们就可以使用定义好的接口类,直接使用他的方法,不用去实现他,这其中的原理是什么呢?下面开始探究其原理

一 代理对象是如何产生的
PersonMapper personMapper = session.getMapper(PersonMapper.class);
personMapper.getOne()

相信大家对上面的代码很熟悉,本文就从session.getMapper开始探究,其底层方法是MapperRegistry的getMapper方法

public class MapperRegistry {
   
  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 {
      //进入MapperProxyFactory的newInstance方法
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

/**************************************分界线********************************************************/

public class MapperProxyFactory<T> {

  private final Class<T> mapperInterface;//原接口class对象
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

  public T newInstance(SqlSession sqlSession) {
     //MapperProxy是一个InvocationHandler,
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

  protected T newInstance(MapperProxy<T> mapperProxy) {
    //利用jdk动态代理生产代理类
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  

}
二 代理对象是如何执行方法

通过上面的分析我们知道,mybatis代理对象是通过jdk动态代理生产,其动态代理类调用处理程序类是MapperProxy,所以执行代理类的方法会被转发到实现InvocationHandler接口类MapperProxy的invoke方法中类,

2.1 方法类别判断
public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        //如果调用的是Object的方法,就直接调用他,不用太关注
        return method.invoke(this, args);
      } else if (method.isDefault()) {
        //对默认方法的处理,不用太关注
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }


    //如果缓存中存在这个方法,从缓存中取,
    //如果没有,就从Configuration中获取相关信息,查询其方法和对应的sql语句建立,封装成MapperMethod类
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //执行方法,重点
    return mapperMethod.execute(sqlSession, args);
  }


/**************************************分界线********************************************************/


public class MapperMethod {

  private final SqlCommand command;//接口方法和对应的sql语句
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  //args是传入的参数
  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //command.getType()是sql语句的类型,如select,insert等
    switch (command.getType()) {
      case INSERT: { //insert语句,进入这个
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: { //update语句,进入这个
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: { //delete 语句,进入这个
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT: //select 语句,进入这个
        if (method.returnsVoid() && method.hasResultHandler()) {
         // 如果返回void 并且方法参数有ResultHandler
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          //如果返回多行结果
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
           //如果返回MAP
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
           //如果返回Cursor
          result = executeForCursor(sqlSession, args);
        } else {
          //否则就是查询单个对象
          //返回方法对应参数信息和其值
          Object param = method.convertArgsToSqlCommandParam(args);
          //执行方法,本文重点研究这个方法
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        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;
  }
2.2 方法参数解析过程

Mybatis是通过method.convertArgsToSqlCommandParam(args);获取方法参数及其值,其底层是MethodSignature的convertArgsToSqlCommandParam方法

//MethodSignature是MapperMethod的静态内部类
public static class MethodSignature {

    private final boolean returnsMany;
    private final boolean returnsMap;
    private final boolean returnsVoid;
    private final boolean returnsCursor;
    private final boolean returnsOptional;
    private final Class<?> returnType;
    private final String mapKey;
    private final Integer resultHandlerIndex;
    private final Integer rowBoundsIndex;
    private final ParamNameResolver paramNameResolver;

    public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
      .......
      //解析@Param注解
      this.paramNameResolver = new ParamNameResolver(configuration, method);
    }

    public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }
}


/********************************分界线***************************************************/
 
public class ParamNameResolver {

  private static final String GENERIC_NAME_PREFIX = "param";

  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;
  // 解析@Param注解,把解析结果放到names中,key是位置从0开始,vaule是@Param值,
  // 如果没有标注@Param注解,vaule就是arg+"key":如arg0
  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<>();
    int paramCount = paramAnnotations.length;
   
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    //没有参数,直接返回
    if (args == null || paramCount == 0) {
      return null;
    //没有标注@Param,且参数格式为1
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      //多个参数的情况
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      //循环遍历names
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        //如果标注@Param了,key就是@Param值,否则就是args0 args1 等
        //值是入参值
        param.put(entry.getValue(), args[entry.getKey()]);
        //从新添加新的key值规则,如param1  param2...
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

结论:Mybatis在方法参数解析时,如果是一个参数且参数上标注@Param,那么读取的参数名就是@Param的值,否则就是如args0 args1等;如果是多个参数,那么每个参数都有2个名字一个就是param+“i + 1”,如param1 param2。另一个的话,如果标注@Param,那么参数名就是@Param的值,否则就是如args0 args1等

2.3 select类型方法执行过程
public class DefaultSqlSession implements SqlSession {

  @Override
  public <T> T selectOne(String statement, Object parameter) {
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("..忽略.);
    } else {
      return null;
    }
  }
  
  @Override
  public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
  
  //statement是调用的方法名,parameter是入参
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //从全局配置值获取对应sql映射的包装对象
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用执行器Executor进行处理
      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();
    }
  }
2.4 执行器Executor对方法的处理

public class CachingExecutor implements Executor {

  private final Executor delegate;
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();

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

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    //二级缓存cache
    Cache cache = ms.getCache();
    if (cache != null) {
      //判断要不要刷新缓存
      flushCacheIfRequired(ms);
      //是否使用二级缓存,默认true
      if (ms.isUseCache() && resultHandler == null) {
        ensureNoOutParams(ms, boundSql);
        @SuppressWarnings("unchecked")
         //从事物管理器获取缓存
        List<E> list = (List<E>) tcm.getObject(cache, key);
        //如果没有缓存,就从数据库查
        if (list == null) {
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

}

public abstract class BaseExecutor implements Executor {

  @Override
  public <E> List<E> 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 <E> List<E> 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<E> list;
    try {
      queryStack++;
      //一级缓存,通过key获取结果
      list = resultHandler == null ? (List<E>) 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;
  }

private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //往缓存中储存一个一个临时 值,和对应的key
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
 //
 protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
      throws SQLException;
  • 进入子类的SimpleExecutor的doQuery方法
public class SimpleExecutor extends BaseExecutor {

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      //
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //获取Statement对象,并为?占位符赋值
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }

   private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    stmt = handler.prepare(connection, transaction.getTimeout());
    //参数类型解析
    handler.parameterize(stmt);
    return stmt;
  }
}
2.5 StatementHandler与数据库建立链接,并执行sql
public class PreparedStatementHandler extends BaseStatementHandler {
  @Override
  public void parameterize(Statement statement) throws SQLException {
     //底层是其子类DefaultParameterHandler的setParameters方法
    parameterHandler.setParameters((PreparedStatement) statement);
  }
}

/***********************分界线**************************************************/

public class DefaultParameterHandler implements ParameterHandler {

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}
2.6 StatementHandler与数据库建立链接,并执行sql
public class PreparedStatementHandler extends BaseStatementHandler {

  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //执行sql
    ps.execute();
    //利用ResultSetHandler处理结果
    return resultSetHandler.handleResultSets(ps);
  }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值