当前位置:   article > 正文

Mybatis中sqlSession.getMapper背后的原理

Mybatis中sqlSession.getMapper背后的原理

在通过MyBatis操作数据库之前我们一定先通过Session对象获取指定Mappper接口的代理对象。如下代码所示:

public class UserMapper{
  @Select(value="SELECT * FROM user")
  public List<User> findAll();
}
  • 1
  • 2
  • 3
  • 4

public static void main(String [] args){

   Configuration configuration = new Configuration();
   configuration.addMapper(UserMapper.class);
   SqlSessionFactory sqlSessionFactory = new DefaultSqlSessionFactory(configuration);

   // 获取SqlSession对象
   try(SqlSession sqlSession = sqlSessionFactory.openSession();){
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = userMapper.findAll();
       }catch(Exception e){
       //TODO 异常信息
       }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

调用链

1.DefaultSqlSession.调用getMapper方法 -> 2.Configuration.调用getMapper方法 -> 3.MapperRegistry调用getMapper方法
                                                                                        |
                                                                                        |
  |-------------------------------------------------------------------------------------|
  |
  |
  |-->4.判断knownMappers缓存是否已经解析过指定的Mapper接口,没解析过抛错(在调用addMapper方法时就将接口添加到缓存中并对接口中的方法进行解析).解析过创建代理对象
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);

        if (Objects.isNull(mapperProxyFactory)){
            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);
        }
    }
  |
  |
  |-->  5.创建代理对象,MapperProxy实现了InvocationHandler接口,使用JDK动态代理创建
        public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
        protected T newInstance(MapperProxy<T> mapperProxy) {
        // 用JDK自带的动态代理生成映射器
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

注意第5代逻辑:

   // 已解析过的Mapper接口及对应的代理工厂缓存
   private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();


  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (Objects.isNull(mapperProxyFactory)) {
      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);
    }
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

先从缓存中获取接口对应的代理工厂,并判断是否存储代理工厂。不存在时抛错,表示此接口没有注册过无法创建代理对象.
何时向缓存中添加接口及对应的代理工厂呢?如下代码:

  public <T> void addMapper(Class<T> type) {
 // mapper必须是接口!才会添加
 if (type.isInterface()) {
   if ( knownMappers.containsKey((type)) {
     // 如果重复添加了,报错
     throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
   }

   boolean loadCompleted = false;

   try {
     // 将mapper接口放入缓存并为其创建独立的mapper接口代理对象工厂
     // 每个Mapper接口有一个独立的代理工厂
     knownMappers.put(type, new MapperProxyFactory<T>(type));

     // 在运行分析器之前添加类型非常重要
     // 否则,绑定可能会由
     // 映射器解析器。如果类型已知,则不会尝试。
     MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
     // 解析mapper接口及相应的mapper.xml文件
     parser.parse();
     loadCompleted = true;
   } finally {
     // 如果加载过程中出现异常需要再将这个mapper从mybatis中删除,这种方式比较丑陋吧,难道是不得已而为之?
     if (!loadCompleted) {
       knownMappers.remove(type);
     }
   }
 }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

在注册Mapper 接口时,会向缓存中添加接口对应的代理工厂,以便后面通过接口获取代理对象调用方法。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/409494
推荐阅读
相关标签
  

闽ICP备14008679号