mybatis的启动流程源码

目录

前言

一、主程序

二、SqlSessionFactory创建

1.SqlSessionFactory的创建过程

2.mapper.xml加载

3.解析xml文件对应的接口

 三、SqlSession创建

 四、Mapper代理对象创建

总结


前言

       mybatis的启动流程主要包括sqlSessionFactory,selSession和mapper代理对象的创建,本文将从源码角度分析,在接下来的文章中还会分析在spring参与的情况下启动流程的变化。


一、主程序

       本文基于mybatis3.5.5版本,首先基于config文件创建了SqlSessionFactory,然后创建SqlSession,最后获取Mapper的代理对象,执行相关的查询,修改方法,本文主要对获取Mapper代理对象的过程进行分析,关于Mapper方法执行流程会在以后的文章中给出。

  public static void main(String[] args) throws Exception{
    //1、读取配置文件
    String resource = "config/mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);

    //2、初始化mybatis,创建SqlSessionFactory类实例
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    SqlSession session = sqlSessionFactory.openSession(true);

    UserMapper userMapper = session.getMapper(UserMapper.class);
}

二、SqlSessionFactory创建

1.SqlSessionFactory的创建过程

       SqlSession的创建首先需要对config文件进行解析,在解析的过程中基本就是对mybatis-config.xml文件中的所有元素进行解析,然后生成configure,本文针对其中的Mapper解析过程进行分析。

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"));
      /*
          解析Mapper  获取执行的sql语句
       */
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

2.mapper.xml加载

        mapper可以通过package,resource,url和mapperClass方式进行配置,本文以resource方式为例。

  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");
          // 从resource中获取
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            //解析注解注入的sql语句 @Select
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //从url中获取
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            // 从class中获取
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

       首先判断当前资源是否已经被加载过,如果加载过不做处理,没有加载过,加载文件,解析其中的mapperStatement,设置当前资源为已加载,最后解析mapperk.xml对应的nameSpace。

  public void parse() {
    //判断当前资源是否加载过,避免重复加载
    if (!configuration.isResourceLoaded(resource)) {
      //将mapper.xml文件中的语句添加到configure的statements中
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      //解析mapper对应的nameSpace
      bindMapperForNamespace();
    }

    parsePendingResultMaps();
    parsePendingCacheRefs();
    parsePendingStatements();
  }

3.解析xml文件对应的接口

         解析mapper.xml中的nameSpace对应的java接口,这里也有避免重复加载的逻辑。

  private void bindMapperForNamespace() {
    String namespace = builderAssistant.getCurrentNamespace();
    if (namespace != null) {
      Class<?> boundType = null;
      try {//根据namespace找到对应的java类
        boundType = Resources.classForName(namespace);
      } catch (ClassNotFoundException e) {
        // ignore, bound type is not required
      }
      //避免重复加载
      if (boundType != null && !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);
        //加载java类对应的mapperStatement
        configuration.addMapper(boundType);
      }
    }
  }

        对mapper接口进行解析,首先加载当前mapper接口对应的mapper.xml,到这里可以看到,在解析mapper.xml时会对其对应的mapper接口进行解析,在解析mapper接口的时候也会解析mapper.xml,所以mybatis在这里使用了避免重复加载的逻辑。遍历接口中的每个方法,如果方法的上面带有@Select,@update等相关注解,生成mapperStatement,加入到configure中。

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        //解析mapper类
        parser.parse();
        loadCompleted = true;
    }
  }
  public void parse() {
    String resource = type.toString();
    if (!configuration.isResourceLoaded(resource)) {
      //加载java类对应的xml资源,并进行解析
      loadXmlResource();
      configuration.addLoadedResource(resource);
      assistant.setCurrentNamespace(type.getName());
      parseCache();
      parseCacheRef();
      for (Method method : type.getMethods()) {
        if (!canHaveStatement(method)) {
          continue;
        }
        if (getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent()
            && method.getAnnotation(ResultMap.class) == null) {
          parseResultMap(method);
        }
        try {
          //对方法上的注解中的sql语句进行处理
          parseStatement(method);
        } catch (IncompleteElementException e) {
          configuration.addIncompleteMethod(new MethodResolver(this, method));
        }
      }
    }
    parsePendingMethods();
  }

 三、SqlSession创建

          在获取sqlSession的过程中,首先是创建事务,设置相关属性。

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      // 获取transaction 事务
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      // 创建执行器 executor
      /*
        mybatis有三种执行器
        SIMPLE(默认), REUSE, BATCH
       */
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

 四、Mapper代理对象创建

       最后使用mapperProxyFactory创建mapper接口的代理对象,关于代理对象的详细内容会在以后的文章中给出。

  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 {
      //创建mapper的代理对象
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

总结

         提示本文主要对mybatis的启动流程进行分析,主要是SqlSessionFactory,SqlSession和Mapper代理对象的创建过程,接下来将分析在有spring参与的情况下,相关流程的变化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值