Mybatis原理剖析之自动映射器(三)

概述

抛开mybatis-spring.jar这个开发包,一个原生的获取mapper代码如下

File file = new File("mybatis-config.xml");
InputStream inputStream = new FileInputStream(file);

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

SqlSession sqlSession = sqlSessionFactory.openSession();
ProxyMapper proxyMapper  = sqlSession.getMapper(ProxyMapper.class);

String rules = proxyMapper.getRules();
System.out.println("rules = " + rules);

流程分析

  • 前三行代码 : 根据XML构造IO流,并作为参数构建SqlSessionFactory,由 Mybatis原理剖析之Configuration(二)文章可知,SqlSessionFactory只是一个接口,实际生成的工厂类为DefaultSqlSessionFactory,这个工厂类持有configuration对象。
  • 第四行代码 : sqlSessionFactory.openSession(),本意为打开一个Session,我们看一下代码
 @Override
  public SqlSession openSession() {
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }
  
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      
      ..此处省略..
      
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
     	....
    } finally {
      	....
    }
 }
  • 看最后一行代码 : return new DefaultSqlSession(configuration, executor, autoCommit),生成了一个DefaultSqlSession对象,这个对象持有configuration和executor。executor其实就是最终执行sql语句的执行器,这个我们后期再讲。
  • 第五第六行代码 :
  ProxyMapper proxyMapper  = sqlSession.getMapper(ProxyMapper.class);
  String rules = proxyMapper.getRules();
其实这两行代码才是我们最疑惑的,ProxyMapper明明是一个接口,我们并没有定义它的任何实现类,系统是如何构造了一个ProxyMapper对象,并且调用getRules方法并获取结果了呢?

不急我们接下往下看,打开sqlSession的getMapper方法,即DefaultSqlSession的getMapper方法。

代码如下

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

好吧,并不是DefaultSqlSession获取的Mapper,DefaultSqlSession持有configuration对象,调用了configuration的getMapper方法获取mapper。接下来看一下configuration的getMapper()方法。
注:看到没,框架里边处处皆是泛型,泛型的好处,咱们下回单讲哈。

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

好吧,还是得往下看,configuration调用mapperRegistry的getMapper方法获取Mapper,其中两个参数分别为

  • type,需要获取的Mapper的Class对象。
  • sqlSession,为什么要传sqlSession呢,因为sqlSession持有Executor,后期需要调用sqlSession的Executor执行sql啊。

接着看mapperRegistry的getMapper方法。

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) {
    	....
    }
}

流程分析

  • 到这里,如果你看过我之前的一篇文章Mybatis原理剖析之Configuration(二),就会有印象,解析XML阶段,根据Mapper.xml的配置的namespace值,我们向knownMappers添加了键值对,键为namespace(其实就是Mapper的路径)对应接口的Class对象,值为一个MapperProxyFactory对象。那么现在,我们传入了一个Mapper的Class对象,当然就可以获取一个MapperProxyFactory对象啊。
  • 接下往下看,对mapperProxyFactory判空,若空抛异常。
  • 再往下看,mapperProxyFactory.newInstance(sqlSession)。

mapperProxyFactory.newInstance代码如下

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

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

看到这里,如果你看过我另外一篇文章设计模式之JDK动态代理,那你一定会很熟悉。是的,首先构造了一个mapperProxy对象,而这个对象作为代理对象,最后执行这个对象的invoke方法。那被代理对象是谁呢,当然是我们的mapper。

这也就解释了为什么sqlSession获取的Mapper是一个接口,却能查询数据库,并返回结果,原因你调用的每一个Mapper的方法,最后都被代理到了MapperProxy的invoke方法内,然后执行sqlSession的Executor相应方法,执行数据库的增删改查操作。

接下来,我们看一下MapperProxy的invoke方法实现。

代码如下

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

流程分析

  • 首先,判断执行的方法是不是属于Object所定义,为什么这么做呢,比如你调用了一个toString方法,我们当然不能帮你执行sql操作啊,而是执行Object的默认实现。
  • 然后,调用cachedMapperMethod方法,相当于做了一次缓存吧,也算是代码优化的一种,因为构造MapperMethod是需要耗费时间的。
  • 最后调用execute方法执行sql操作,具体是如何操作的呢,我们下回分解。

温馨提示

  • 如果您对本文有疑问,请在评论部分留言,我会在最短时间回复。
  • 如果本文帮助了您,也请评论,作为对我的一份鼓励。
  • 如果您感觉我写的有问题,也请批评指正,我会尽量修改。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值