MyBatis源码解析(四):初始化Mapper

        前沿:在MyBatis源码解析(三):初始化SqlSession博客中,已经对 openSession() 底层进行解析,并获取到 SqlSession 对象;接着,需要通过 SqlSession.getMapper(),获取 Mapper 的代理对象去执行 Statement,getMapper()分析会与之前已经分析过的addMapper() 相比较进行分析,以确保流程清晰,具体 addMapper() 流程可参考MyBatis源码解析(二):初始化SqlSessionFactory

一,初始化 Mapper 流程图

        1,流程图

    2,流程解析

        * 在 SqlSessionFactory 解析中,MyBatis会将配置文件解析到的 Mapper 文件添加到 Configuration对象中,Configuration对调用 MapperRegistery 对象保存 Mapper 实例其内部一 Map 容器中;

        * getMapper()时,会根据上一步 addMapper(),相同方式走一遍 getMapper()流程,并最终通过JDK动态代理初始化代理对象

二,初始化步骤

    1,获取 Mapper 实例

        * 通过 DefaultSqlSession 来 getMapper()

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

        * DefaultSqlSession 从 Configuration 中 getMapper()

@Override
  public <T> T getMapper(Class<T> type) {
    // 触发Mapper获取, 从Configuration配置对象中获取Mapper
    // 在初始化配置信息时, Configuration.addMapper会调用MapperRegistery存储Mapper到Map容器中
    // 存储过程可以参考前一篇博客, 初始化SqlSessionFactory
    return configuration.<T>getMapper(type, this);
  }

        * Configuration 从 MapperRegistery 中 getMapper()

             -- 初始化 SqlSessionFactory 时, 通过调用 addMapper() 已经存储 Mapper 到 MapperRegistery 中

public <T> void addMapper(Class<T> type) {
    // 存储时调用MapperRegistry存储
    mapperRegistry.addMapper(type);
  }

             -- getMapper() 时,同样根据 MapperRegistery 进行获取

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 获取时同样调用MapperRegistry进行获取
    return mapperRegistry.getMapper(type, sqlSession);
  }

        * MapperRegistery 根据传递的 Mapper 类型从 Map 容器中获取已经初始化的 MapperProxyFactory 对象

             -- 初始化 SqlSessionFactory 时,已经添加 Mapper 映射到 Map容器中,并初始化 MapperProxyFactory 传递了 Mapper 类型

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 {
        // 添加Mapper将Mapper添加到Map容器中, 并初始化value为MapperProxyFactory
        // 此时MapperProxyFactory初始化已经传递Mapper类型到工厂类中
        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);
        }
      }
    }
  }

             -- getMapper()时,直接从 MapperRegistery 的 Map容器中根据类型,获取 Mapper 的对应 MapperProxyFactory 对象,并进行初始化

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 从Map容器中获取该对象, 获取初始化的MapperProxyFactory
    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中实例化一个Mapper代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

    2,通过JDK动态代理初始化代理对象

        * 从上一步获取的 MapperProxyFactory 中初始化实例

public T newInstance(SqlSession sqlSession) {
    // 初始化MapperProxy, 该类为InvocationHandler实现类, 动态代理最终执行该类的Invoke方法
    // mapperInterface 在初始化传递过程中已经初始化为当前Mapper类型
    // methodCache可以理解为statement缓存容器, 实例为ConcurrentHashMap
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

        * 调用JDK动态代理最终生成 Mapper 代理对象并返回

protected T newInstance(MapperProxy<T> mapperProxy) {
    // 通过JDK动态代理方式, 生成Mapper的代理对象,
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值