• mybatis源码分析——数据的绑定


      orm的演进过程,jdbc——ibatis——mybatis,jdbc最基础的访问数据库的方式,ibatis基于jdbc进行了封装,程序员

    可以直接在xml里面写sql,通过调用dao中的方法执行数据库的操作,mybatis省略了dao的步骤,只需要mapper中的方法与

    mapper.xml中的sqlId映射上,就可以直接调用。

      1:通过mybatis源码看一下mybatis的实现原理

    实现原理就是动态代理,动态代理有两种,jdk代理和cglib代理,jdk代理必须基于接口,在内存中动态生成接口的子类来实现

    在上一节中,解析mapper.xml的时候会解析命名空间

      public void parse() {
        if (!configuration.isResourceLoaded(resource)) {
          // 解析sql,生成mapperStatement
          configurationElement(parser.evalNode("/mapper"));
          configuration.addLoadedResource(resource);
          // 解析命名空间,绑定代理工厂
          bindMapperForNamespace();
        }
    
        parsePendingResultMaps();
        parsePendingChacheRefs();
        parsePendingStatements();
      }
    

      

     调用configuration的addMapper方法,将命名空间的class对象传入

      public <T> void addMapper(Class<T> type) {
        mapperRegistry.addMapper(type);
      }
    

      

    首先判断这个类型是否已经注册,如果已经注册,抛出一个已经绑定的异常,

    如果没有注册,则放入knownMappers中缓存,key为类型,value为MapperProxyFactory对象

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

      

    下面来看一下这个类:MapperProxyFactory

    维护一个mapperInterface的接口,有方法newInstance(SqlSession sqlSession)

    public class MapperProxyFactory<T> {
    
      private final Class<T> mapperInterface;
      private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
    
      public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      public Class<T> getMapperInterface() {
        return mapperInterface;
      }
    
      public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
      }
    
      @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);
      }
    
    }
    

      

    以上是addMapper的使用,注册了命名空间的类型到内存中。

    下面看一下绑定后的使用过程:

    从configuration对象中getMapper,实际上是从mapperRegistry中获取,因为在上一节解析命名空间的时候已经注册了

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

      

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

      

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

      

    拿到代理工厂MapperProxyFactory对象,然后调用newInstance方法创建代理

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

      

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

      

    最后将代理对象返回,我们得到了UserMapper对象,实际上是一个代理

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

    这里可以看到,所谓的动态代理,就是在程序运行过程中生成接口的实现对象,而不是通过代码实现的。

     

     通过debug可以看到proxy 代理对象

    所以当我们调用listUsers时,会调到代理的invoke方法

    到这里通过代理实现的mybatis的数据绑定就结束了

    总结:

    两个主要的方法:addMapper、getMapper

    addMapper:在解析命名空间的时候会注册到Configuration类中的MapperRegistry中,MapperRegistry类中有个knowMappers的缓存,key为命名空间的Class对象,value为MapperProxyFactory

    getMapper:通过SqlSession获取接口实现的时候会调到,然后回到Configuration中的MapperRegistry中拿到MapperProxyFactory对象(根据Class对象),然后生成代理对象,并且返回

    当调用方法时,实际上调用的是代理的方法,然后就会调到切面的MapperProxy中的invoke

    这一节主要就是分析了一下通过代理实现的数据绑定,这里忽略了最重要的一个类SqlSession,它起到一个承上启下的作用,非常重要,下一点来分析一下SqlSession回话的源码

  • 相关阅读:
    0223_模拟2011
    0223_模拟2011
    0223_模拟2011
    0223_模拟2011
    12c DG broker DMON自动重启过程分析
    12c DG broker DMON自动重启过程分析
    12c DG broker DMON自动重启过程分析
    12c DG broker DMON自动重启过程分析
    CITA架构设计
    跨链合约示例
  • 原文地址:https://www.cnblogs.com/warrior4236/p/13085618.html
Copyright © 2020-2023  润新知