Mybatis源码阅读4 --- 只写接口就能调用?MapperProxy

City city = sqlSession.selectOne("com.hhg.jerry.dao.CityDao.getById",1L);

Mybatis通过Session执行sql操作,参数为statement(mapper的namespace+select的id)和改statement所需参数,直接用接口调用当然更好:

public class App {
    public static void main( String[] args ) throws Exception{
        InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        City city = null;
        CityDao cityDao = sqlSession.getMapper(CityDao.class);
        city = cityDao.getById(1L);
//        city = sqlSession.selectOne("com.hhg.jerry.dao.CityDao.getById",1L);
        System.out.println(city);
    }
}

CityDao只是一个接口,sqlSession.getMapper返回的肯定是个代理类,下面通过代码一看究竟。

发现是MapperRegister,提供了getMapper、hasMaper、addMapper3个方法,先不看如何add,且看怎么get:

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

很简单,knowMappers取到factory,factory.newInstanse就是Mapper代理类,看下newInstance方法:

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

先new了一个mapperProxy对象(注意MapperProxy的接口InvocationHandler),然后调用Proxy.newProxyInstance生成了mapperInterface(CityDao)的代理类。

简单梳理下:

1、session.getMapper ,参数为Mapper的class对象(例如:CityDao.class)

2、mapregister通过class对象获取MapperProxyFactory

3、MapperProxyFactory生成MapperProxy对象(InvokeHandler),并使用MapperProxy生成代理对象

get后,就看knowMappers如何add:

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<T>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

addMapper这个方法很简单,方法内部用MapperAnnotationBuiler去解析注解,看来是支持注解的,mark了。

addMapper在哪被调用了呢?自己找找吧。

调用Mapper的方法过程是怎样?先看看这个InvokeHandler对象的invoke方法:


如果是Object声明的方法(toString)就直接invoke,如果isDefaultMethod(构造函数吗?没看懂)就invodeDefaultMethod,当然我们调用city.getById就会生成MapperMethod,看下MapperMethod的execute方法:


switch了command的type,一会要看看如何生成不同的type,我们的case是select,下面又又很多根据method的判断,我们是走到最后那个else,然后调用sqlSession的selectOne方法。

简单总结下:构建mybatis的mapper时根据Mapper.xml的namespace找对应接口CityDao,并生成相应动态代理对象存放在knowMappers(key为class),sqlSession.getMapper返回改代理类,代理类执行调用mapperMethod对象(有command和method属性),mapperMethod对象再去调用sqlSession的方法(sqlSession的那个方法取决于command和method),下面看看细节:SqlCommand和MethodSignature,SqlCommand很简单,通过MappedStatement获取改方法的type(select or insert or update or delete),MappedStatement如何获取知道sql type?


接着看MethodSignature:


就是获取方法的一些信息(返回类型,是否返回空,是否返回map等),由于我们只有一个方法getById且返回的是City,不建议细看MethodSignature这个类,只看和getById相关即可。注意Type,ParameterizedType,不了解的建议去研究下。

回顾下,从sqlSession或者mapper接口开始,到返回结果,整个流程已经debug过了,当然内部还有很多细节没看。Mybatis操作数据库也是借助JDBC,下面看下JDBC部分

猜你喜欢

转载自blog.csdn.net/lnlllnn/article/details/80711809