[疫情期间复习] mybatis源码分析系列(二) SqlSessionFactory创建过程

3 篇文章 0 订阅
3 篇文章 0 订阅

强调一些分析的mybatis3
第一个问题 mybatis是如何加载配置文件的 如果数据源 别名等信息
从测试用例我们能看到这一段代码

 @BeforeAll
  static void setUp() throws Exception {
    // 读取配置文件数据流
    try (Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")) {
      // 获取到数据流 -> 创建sqlSessionFactory
      sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    }
  }

很明显先读取了指定path的xml 然后创建sqlSessionFactory 顺利成章我们就需要跟进build()方法了

SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 有9个 build() 方法,每一种都允许你从不同的资源中创建一个 SqlSessionFactory 实例。
其实说是9种个人感觉说5种个合适。

SqlSessionFactory build(InputStream inputStream)
SqlSessionFactory build(Reader reader)
第一种方法是xml时代最常用的,它接受一个指向 XML 文件(也就是之前讨论的 mybatis-config.xml 文件)的 InputStream/reader 实例。
SqlSessionFactory build(InputStream inputStream, String environment)
SqlSessionFactory build(Reader reader, String environment)

第二种 指定xml文件的实例和配置文件,environment 决定加载哪种环境
SqlSessionFactory build(InputStream inputStream, Properties properties)
SqlSessionFactory build(Reader reader, Properties properties)
第三种 指定xml文件的实例和配置文件,properties 包括数据源和事务管理器
SqlSessionFactory build(InputStream inputStream, String env, Properties props)
SqlSessionFactory build(Reader reader, String env, Properties props)
第四种 指定xml文件的实例和配置文件,environment 决定加载哪种环境,properties 包括数据源和事务管理器
SqlSessionFactory build(Configuration config)
第五种 注入config文件

跟进代码发现这个方法

public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      //加载xml所有节点信息 
      //初始化系统别名信息等
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
       //获取到源数据 开始解析数据
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

跟进parser.parse()方法 XMLConfigBuilder类下parse()方法

 public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  private void parseConfiguration(XNode root) {
    try {
      // 获取各个节点信息 
      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);
      //获取数据源信息
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

root数据 这就是我的配置文件啊
在这里插入图片描述

是否似曾相识 哈哈 这不就是xml中我们配置的吗?
在这里插入图片描述
分析一下数据库解析流程 其他都是同理

private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      //获取所有节点
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
         //创建事务管理器
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //创建数据源
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

//事务工厂
  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
    //获取配置文件的事务类型
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      //在内存中找到我初始化是写入配置文件的type 然后通过反射创建工厂
      TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }
  //构建数据源工厂
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      //反射创建实例 resolveClass(type)获取类地址
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }
//TypeAliasRegistry 类下的
  public <T> Class<T> resolveAlias(String string) {
    try {
      if (string == null) {
        return null;
      }
      // issue #748
      String key = string.toLowerCase(Locale.ENGLISH);
      Class<T> value;
      if (typeAliases.containsKey(key)) {
        value = (Class<T>) typeAliases.get(key);
      } else {
        value = (Class<T>) Resources.classForName(string);
      }
      return value;
    } catch (ClassNotFoundException e) {
      throw new TypeException("Could not resolve type alias '" + string + "'.  Cause: " + e, e);
    }
  }

我们定义的数据源信息
在这里插入图片描述
如何加载我们定义的sql语句呢?
映射器(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 {
          //看到这是不是应该有一个面试题 ?mapper 支持几种形式制定?
          //获取resource节点
          String resource = child.getStringAttribute("resource");
          //获取url节点
          String url = child.getStringAttribute("url");
          //获取class节点
          String mapperClass = child.getStringAttribute("class");
          //配置的员信息
          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());
            mapperParser.parse();
          }
          //url配置
          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());
            mapperParser.parse();
          }
          //类配置
          else if (resource == null && url == null && mapperClass != null) {
            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.");
          }
        }
      }
    }
  }

把所有的数据全部放入Configuration中,创建SqlSessionFactory

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

这样SqlSessionFactory创建过程 完毕分析完毕。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值