mybatis运行过程深度解析(二)

接着上面的一继续往下看:

SQLSession的执行过程,有了SQLSessionFactory对象就可以轻易的获取SQLSession。不过SQLSession也是一个接口。

在之前写的文章中,我们看到这段代码:

UserDao mapper=sqlSession.getMapper(UserDao.class);

从这句代码我们看出,我们只是给了一个Mapper接口便能获取到想要的对象了;不过在这里,我们获得的是代理对象,让我们看一下它到底怎么一步步获取到的:

首先我们看一下这个getMapper的方法,由于SQLSession是一个接口所以我们去它的实现类DefaultSqlSession中查看这个方法:

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

在这里我们能够看到,它运行到了configuration对象的getMapper方法来获取对应的接口对象的。继续跟进:

我看进入到Configuration的类中了,看一下方法:

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

它竟然在这里调用了映射器的注册器mapperRegistry的getMapper方法来获取对应接口对象的,那我们来看一下这个mapperRegistry注册器:

  @SuppressWarnings("unchecked")
  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 {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

首先通过 if (mapperProxyFactory == null)方法判断是否注册了一个Mapper,如果没有则会抛出异常;如果有,就会调用mapperProxyFactory对象来生成一个代理机制;继续跟进:

我们进入MapperProxyFactory类

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

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

看到这里是不是感觉很熟悉了,protected T newInstance(MapperProxy<T> mapperProxy)这个方法生成的是一个动态代理对象,因此mapper映射是通过动态代理来实现的。

我们来看一下代理方法:

  @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);
    return mapperMethod.execute(sqlSession, args);
  }

这个方法是被放在MapperProxy类中的,它首先判断了一下Mapper是否是一个类,这里肯定是一个接口;注意这里的代理是jdk动态代理;是不是问为什么会这么说,那我们来看一下这段代码:

public class MapperProxy<T> implements InvocationHandler, Serializable

看到了什么,不错是InvocationHandler,现在知道为什么他是jdk动态代理了吧!由于Mapper是一个接口,所以判断是false,会走

  final MapperMethod mapperMethod = cachedMapperMethod(method);

生成一个MapperMethod对象,最后执行它的execute方法,而且传递的只有SQLSession和当前运行参数。由于这个方法比较多,不重要的点就不截取了,

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

这里调用的 executeForMap(sqlSession, args);方法,那我们来看一下这个方法:

  private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }

到这里,就不在继续跟进了,我们停留在这个executeForMap这个方法的实现即可,它通过命令模式运行;通过源码我们能够知道它最后就是通过SQLSession对象去运行对象的sql的,其他的增删改这是类似于这样的操作;

这里我们就应该明白,mapper的xml配置文件命名空间对应的就是这个接口的全类名,而方法就是那条sql的id,这样mybatis就可以根据全类名和方法名,将其和代理绑定在一起;

这一节,我们看到了SQLSession大体的工作流程,就是初始化配置文件的sql,并向其中传递参数,执行sql语句并获取返回值;

那么它是怎么执行的,下一篇再细说;


猜你喜欢

转载自blog.csdn.net/hbl6016/article/details/80158566
今日推荐