Mybatis源码-MapperProxy分析(一)

    前两天公司谈绩效,我有讲到,最近在看源码。说到在看Mybatis源码的时候,当领导问到我,Mybatis源码中有没有什么在我们项目中能够借鉴的地方,我一愣。愣是没有想出来。这个问题,发人深省。

    谈到源码,对于我这种1-3年的菜鸟来说,会自豪的说我看过这个这个的源码,那个那个的源码。现在想来,太可怕了。我似乎走入了一个误区。看源码的目的到底是什么?从看源码的过程中,我能学到什么?

    今天,就来分享下,我看Mybatis源码后的一些思考。这篇重点说一下,动态代理模式,在Mybatis框架中的应用。

    了解动态代理的,都知道,正常的动态代理。是这个样子滴

而用过Mybatis的,都会发现,它的动态代理是这样的

显然,Mybatis的动态代理被阉割了。。。dao层是只有接口没有实现类的。为什么这么做呢?因为,Mybatis不需要dao层接口有实现类,为什么呢?下面带大家来深入源码,来发掘这个特点所带来的好处。

    干看源码,对整体的调用关系不是非常清晰,我这里贴一张我整理的时序图,虽然啊 虽然很简略,但可以清晰看出代码的关系,如下

    我们通过configuration创建了一个sqlSession,调用getMapper对sqlSession进行填充,通过动态代理调用selectOne方法,调用Executor执行query方法,从而完成一次Query的JDBC。下面我们来看具体细节。

    当然,我们先看MapperProxy,为什么?因为调用dao层方法时,下一步就是这个类啊···。上源码

//解释下,一个是动态代理接口InvocationHandler,一个是序列化接口Serializable
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;
  //在sqlSession调用getMapper时,实例化的当前类。
  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      try {
        return method.invoke(this, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //重点看下,mapperMethod都做了什么呢?
    return mapperMethod.execute(sqlSession, args);
  }

  private MapperMethod cachedMapperMethod(Method method) {
    MapperMethod mapperMethod = methodCache.get(method);
    if (mapperMethod == null) {
      //此处new了一个MapperMethod,并将method存入了methodCache,Key为method,value为mapperMethod
      mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
      methodCache.put(method, mapperMethod);
    }
    return mapperMethod;
  }

}

MapperProxy类是Mybatis动态代理的枢纽类。都做了什么呢1、初始化了MapperMethod,并调用了execute方法执行了sql。下面来看Mybatis动态代理的核心类:MapperMethod

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    //与sql相关的解析操作,咳咳后面详细分析
    this.command = new SqlCommand(config, mapperInterface, method);
    //同样,与调用方法相关操作,后面详细分析
    this.method = new MethodSignature(config, mapperInterface, method);
  }
  //主要看execute方法。分CRUD,我的demo调用的是SELECT的selectOne方法
  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 {
          //字面意思,将数组参数转成SqlComman类型的参数
          Object param = method.convertArgsToSqlCommandParam(args);
          //通过sqlSession调用dao方法,并传入相应参数
          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;
  }
}

今天呢。我们不谈细节,明天接着聊。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值