Mybatis 实现原理

MyBatis 实现原理

  1. 读取 mybatis 配置信息,并创建 SqlSessionFactory 对象。

    通过 SqlSessionFactoryBuilder 对象构建 SqlSessionFactory

    public SqlSessionFactory build(InputStream inputStream) {
        return build(inputStream, null, null);
    }
    
    public SqlSessionFactory build(InputStream inputStream, String environment) {
        return build(inputStream, environment, null);
    }
    
    public SqlSessionFactory build(InputStream inputStream, Properties properties) {
        return build(inputStream, null, properties);
    }
    
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 拿到 XML 解析对象
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // 将解析对象解析为 Configuration 对象,并构建 SqlSessionFactory 对象
            return build(parser.parse());
        } catch (Exception e) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException e) {
                // Intentionally ignore. Prefer previous error.
            }
        }
    }
    
    public SqlSessionFactory build(Configuration config) {
        // 通过默认实现类构建 SqlSessionFactory
        return new DefaultSqlSessionFactory(config);
    }
    
  2. 通过 SqlSessionFactory 对象开启会话,获取 SqlSession 对象,默认开启事务(因为自动提交缺省值为 false)。

    @Override
    public SqlSession openSession() {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
    
    @Override
    public SqlSession openSession(boolean autoCommit) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, autoCommit);
    }
    
    @Override
    public SqlSession openSession(ExecutorType execType) {
        return openSessionFromDataSource(execType, null, false);
    }
    
    @Override
    public SqlSession openSession(TransactionIsolationLevel level) {
        return openSessionFromDataSource(configuration.getDefaultExecutorType(), level, false);
    }
    
    @Override
    public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
        return openSessionFromDataSource(execType, level, false);
    }
    
    @Override
    public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
        return openSessionFromDataSource(execType, null, autoCommit);
    }
    
    @Override
    public SqlSession openSession(Connection connection) {
        return openSessionFromConnection(configuration.getDefaultExecutorType(), connection);
    }
    
    @Override
    public SqlSession openSession(ExecutorType execType, Connection connection) {
        return openSessionFromConnection(execType, connection);
    }
    

    上面这些重载方法中,默认都是开启事务的,除了允许设置 autoCommit 属性值的方法我们可以通过调用可以修改为自动提交。

  3. 通过 SqlSession API 来与数据库交互(交互方式有两种)。

    1. 通过 SqlSession 对象调用 selectList, selectOne, insert, update, delete 等等的方法来与数据库交互,其中在调用方法中有一个很重要的参数就是 statement,指的是在 mapper.xml 中你定义的标签的 id 转换为 Mapper 接口中的方法。这种方式通过 statement 参数获取到指定的 MappedStatement 对象,然后再使用 MappedStatement 对象去取执行底层的 select, updateinsert, update, delete 均为 update)。

      这种方式没有使用动态代理。 具体可以查看一下 XMLStatementBuilder 这个类中的 parseStatementNode() 方法。

    2. 通过 SqlSession 对象调用 getMapper 方法来获取我们在程序中定义的 Mapper 接口,再使用 Mapper 接口中的方法与数据库交互。这种方式使用了动态代理。

      在使用 SqlSession 对象调用 getMapper 方法的时候,会调用默认实现类 DefaultSqlSession 中的 getMapper 方法,其内部使用的是 Configuration 对象的 getMapper 方法:

      // 该方法为 DefaultSqlSession 的方法
      @Override
      public <T> T getMapper(Class<T> type) {
          return configuration.getMapper(type, this);
      }
      

      跟进看一下 Configuration 对象的 getMapper 方法:

      // 该方法为 Configuration 的方法
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
          return mapperRegistry.getMapper(type, sqlSession);
      }
      

      跟进看一下 MapperRegistry 对象的 getMapper 方法:

      // 用于映射接口与代理工厂的关系
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
      
      // 该方法为 MapperRegistry 的方法
      @SuppressWarnings("unchecked")
      public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
          // 在这里可以看到要获取一个 MapperProxyFactory 对象,也就是 Mapper 代理工厂对象
          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);
          }
      }
      

      看到这里应该也很好奇,什么时候改变的 knownMappers 的堆内存(准确来说这里的关注点是堆数据是何时改变的,即何时做的 put 操作):

      // 先来看一下 MapperRegistry 对象的其他方法
      public <T> boolean hasMapper(Class<T> type) {
          return knownMappers.containsKey(type);
      }
      
      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<>(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);
                  }
              }
          }
      }
      
      /**
       * @since 3.2.2
       */
      public void addMappers(String packageName, Class<?> superType) {
          ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
          resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
          Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
          for (Class<?> mapperClass : mapperSet) {
              addMapper(mapperClass);
          }
      }
      
      /**
       * @since 3.2.2
       */
      public void addMappers(String packageName) {
          addMappers(packageName, Object.class);
      }
      
      /**
       * @since 3.2.2
       */
      public Collection<Class<?>> getMappers() {
          return Collections.unmodifiableCollection(knownMappers.keySet());
      }
      

      XMLConfigBuilder 类中:

      // XMLConfigBuilder 中的方法
      // 看到这里,回溯一下整体步骤1 中,构建 SqlSessionFactory 对象的时候调用了该方法
      public Configuration parse() {
          if (parsed) {
              throw new BuilderException("Each XMLConfigBuilder can only be used once.");
          }
          parsed = true;
          parseConfiguration(parser.evalNode("/configuration"));
          return configuration;
      }
      // 在 parse 方法中调用了该方法,用于解析 Configuration 对象
      private void parseConfiguration(XNode root) {
          try {
              //issue #117 read properties first
              propertiesElement(root.evalNode("properties"));
              Properties settings = settingsAsProperties(root.evalNode("settings"));
              loadCustomVfs(settings);
              loadCustomLogImpl(settings);
              typeAliasesElement(root.evalNode("typeAliases"));
              pluginElement(root.evalNode("plugins"));
              objectFactoryElement(root.evalNode("objectFactory"));
              objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
              reflectorFactoryElement(root.evalNode("reflectorFactory"));
              settingsElement(settings);
              // read it after objectFactory and objectWrapperFactory issue #631
              environmentsElement(root.evalNode("environments"));
              databaseIdProviderElement(root.evalNode("databaseIdProvider"));
              typeHandlerElement(root.evalNode("typeHandlers"));
              // 注意这里,这里是配置文件中解析 mappers 的配置
              mapperElement(root.evalNode("mappers"));
          } catch (Exception e) {
              throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
          }
      }
      // 解析 mappers 标签的具体实现
      private void mapperElement(XNode parent) throws Exception {
          if (parent != null) {
              for (XNode child : parent.getChildren()) {
                  if ("package".equals(child.getName())) {
                      String mapperPackage = child.getStringAttribute("name");
                      configuration.addMappers(mapperPackage);
                  } else {
                      String resource = child.getStringAttribute("resource");
                      String url = child.getStringAttribute("url");
                      String mapperClass = child.getStringAttribute("class");
                      // 下面要根据不同的配置形式来调用之前所说的 addMapper 的方法
                      if (resource != null && url == null && mapperClass == null) {
                          ErrorContext.instance().resource(resource);
                          InputStream inputStream = Resources.getResourceAsStream(resource);
                          // Configuration 中的 addMapper 内部调用的是 MapperRegistry 的 addMapper
                          // 而 Configuration 中的 addMapper 方法在两个类中使用,分别是 XMLConfigBuilder 与 XMLMapperBuilder
                          // XMLMapperBuilder 的具体调用就在 parse 方法中
                          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                          // 在 parse 方法中调用 addMapper
                          mapperParser.parse();
                      } else if (resource == null && url != null && mapperClass == null) {
                          ErrorContext.instance().resource(url);
                          InputStream inputStream = Resources.getUrlAsStream(url);
                          XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                          // 在 parse 方法中调用 addMapper
                          mapperParser.parse();
                      } else if (resource == null && url == null && mapperClass != null) {
                          Class<?> mapperInterface = Resources.classForName(mapperClass);
                          // 在 mapper 标签的 mapperClass 属性指定时,调用 addMapper
                          configuration.addMapper(mapperInterface);
                      } else {
                          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                      }
                  }
              }
          }
      }
      

      下面来具体看一下 XMLMapperBuilder 中的 parse 方法:

      // XMLMapperBuilder 中的方法
      public void parse() {
          if (!configuration.isResourceLoaded(resource)) {
              configurationElement(parser.evalNode("/mapper"));
              configuration.addLoadedResource(resource);
              // 在未加载 mapper 标签的 resource 属性时,需要绑定 XML 与 Mapper 接口的关系
              bindMapperForNamespace();
          }
      
          parsePendingResultMaps();
          parsePendingCacheRefs();
          parsePendingStatements();
      }
      // 绑定 XML 与 Mapper 的方法实现
      private void bindMapperForNamespace() {
          String namespace = builderAssistant.getCurrentNamespace();
          if (namespace != null) {
              Class<?> boundType = null;
              try {
                  boundType = Resources.classForName(namespace);
              } catch (ClassNotFoundException e) {
                  //ignore, bound type is not required
              }
              if (boundType != null) {
                  if (!configuration.hasMapper(boundType)) {
                      // Spring may not know the real resource name so we set a flag
                      // to prevent loading again this resource from the mapper interface
                      // look at MapperAnnotationBuilder#loadXmlResource
                      configuration.addLoadedResource("namespace:" + namespace);
                      // 如果 Configuration 对象中不包含反射的 Mapper 接口,则调用 addMapper
                      configuration.addMapper(boundType);
                  }
              }
          }
      }
      

      那么接下来的任务就是看一下 MapperProxyFactory 是如何创建 MapperProxy 对象的就 OK 了:

      public class MapperProxyFactory<T> {
      
          private final Class<T> mapperInterface;
          private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();
      
          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) {
              // 这里用到了 JDK 的动态代理
              return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
          }
      
          // 通过 SqlSession 对象创建 MapperProxy 对象
          public T newInstance(SqlSession sqlSession) {
              // methodCache 交由代理类维护
              final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
              return newInstance(mapperProxy);
          }
      
      }
      

      下面来看看 MapperProxy 这个代理类:

      // 使用 JDK 动态代理
      public class MapperProxy<T> implements InvocationHandler, Serializable {
      
          private static final long serialVersionUID = -6424540398559729838L;
          private final SqlSession sqlSession;
          private final Class<T> mapperInterface;
          private final Map<Method, MapperMethod> methodCache;
      
          public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
              this.sqlSession = sqlSession;
              this.mapperInterface = mapperInterface;
              this.methodCache = methodCache;
          }
      
          @Override
          public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
              try {
                  // 判断调用的是不是 Object 中定义的方法,toString,hashCode 这类。是的话直接放行。
                  if (Object.class.equals(method.getDeclaringClass())) {
                      return method.invoke(this, args);
                  } else if (method.isDefault()) {
                      return invokeDefaultMethod(proxy, method, args);
                  }
              } catch (Throwable t) {
                  throw ExceptionUtil.unwrapThrowable(t);
              }
              final MapperMethod mapperMethod = cachedMapperMethod(method);
              return mapperMethod.execute(sqlSession, args);
          }
      
          private MapperMethod cachedMapperMethod(Method method) {
              // 如果 method 不存在就构建一个新的 MapperMethod 对象返回
              return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
          }
      
          private Object invokeDefaultMethod(Object proxy, Method method, Object[] args)
              throws Throwable {
              final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class
                  .getDeclaredConstructor(Class.class, int.class);
              if (!constructor.isAccessible()) {
                  constructor.setAccessible(true);
              }
              final Class<?> declaringClass = method.getDeclaringClass();
              return constructor
                  .newInstance(declaringClass,
                               MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED
                               | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC)
                  .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
          }
      }
      

      下面看一下 MapperMethod 对象的 execute 方法:

      private final SqlCommand command;
      private final MethodSignature method;
      
      public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
          this.command = new SqlCommand(config, mapperInterface, method);
          this.method = new MethodSignature(config, mapperInterface, method);
      }
      
      public Object execute(SqlSession sqlSession, Object[] args) {
          Object result;
          // 具体执行 sql 返回结果
          switch (command.getType()) {
              case INSERT: {
                  Object param = method.convertArgsToSqlCommandParam(args);
                  // 这里可以看到最终调用的还是 SqlSession 的方法
                  result = rowCountResult(sqlSession.insert(command.getName(), param));
                  break;
              }
              case UPDATE: {
                  Object param = method.convertArgsToSqlCommandParam(args);
                  // 这里可以看到最终调用的还是 SqlSession 的方法
                  result = rowCountResult(sqlSession.update(command.getName(), param));
                  break;
              }
              case DELETE: {
                  Object param = method.convertArgsToSqlCommandParam(args);
                  // 这里可以看到最终调用的还是 SqlSession 的方法
                  result = rowCountResult(sqlSession.delete(command.getName(), param));
                  break;
              }
              case SELECT:
                  if (method.returnsVoid() && method.hasResultHandler()) {
                      executeWithResultHandler(sqlSession, args);
                      result = null;
                  } else if (method.returnsMany()) {
                      // 方法内部调用的也是 SqlSession 中的方法
                      result = executeForMany(sqlSession, args);
                  } else if (method.returnsMap()) {
                      // 方法内部调用的也是 SqlSession 中的方法
                      result = executeForMap(sqlSession, args);
                  } else if (method.returnsCursor()) {
                      // 方法内部调用的也是 SqlSession 中的方法
                      result = executeForCursor(sqlSession, args);
                  } else {
                      Object param = method.convertArgsToSqlCommandParam(args);
                      // 这里可以看到最终调用的还是 SqlSession 的方法
                      result = sqlSession.selectOne(command.getName(), param);
                      if (method.returnsOptional()
                          && (result == null || !method.getReturnType().equals(result.getClass()))) {
                          result = Optional.ofNullable(result);
                      }
                  }
                  break;
              case FLUSH:
                  // 这里可以看到最终调用的还是 SqlSession 的方法
                  result = sqlSession.flushStatements();
                  break;
              default:
                  throw new BindingException("Unknown execution method for: " + command.getName());
          }
          if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
              throw new BindingException("Mapper method '" + command.getName()
                                         + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
          }
          return result;
      }
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值