mybatis源码解析-配置文件解析

mybatis作为一个orm框架,在几乎所有的公司都使用这一款orm框架,因为它非常的灵活易用,MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

 mybatis使用通常会有一个配置文件,即mybatis-config.xml,文件中包含了mybatis的所有的配置信息,比如数据库连接信息,别名,插件等等一系列的相关信息,来看看mybatis是如何来解析这些信息。首先回忆一下,mybatis的使用过程。由SqlSessionFactoryBulider去build一个SqlSessionFactory,在右SqlSessionFactory去创建一个SqlSession,SqlSession可以去执行sql语句,也可以去获取Mapper的对象,然后让Mapper对象去执行然后获取结果。

首先SqlSessionFactoryBulider定义了一系列的build方法,目的就是为了不同的加载配置文件的方式或者配置信息,而创建出对象。字符流的方法与字节流的方法类似,重点看以下两个方法。

 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
       //解析配置文件
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //调用重载的方法,创建出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) {
      }
    }
  }
    
  public SqlSessionFactory build(Configuration config) {
    //创建SqlSessionFactory对象
    return new DefaultSqlSessionFactory(config);
  }

先创建XMLConfigBuilder对象,并且将传入相关的配置信息,然后调用parser.parse()创建Configuartion对象,Configuartion意思就是配置,也就是这个类的所有属性所存的就是mybatis相关配置信息。

XMLConfigBuilder的构造器的第一件事就是先进行验证,验证这个文件的内容是不是符合mybatis的文件,所以先进行验证,即根据mybatis的dtd文件进行验证。

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
      //new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())先进行验证
      //XMLMapperEntityResolver对象就是mybatis的dtd文件,即验证规则
     this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //开始解析
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //将configuration中设置Properties对象,因为bulid方法可以传递Properties 对象,
    //注Properties 一般是设置数据源的相关信息
    this.configuration.setVariables(props);
    //这个属性标记文件是否已经解析过,配置文件只解析一次
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

Configuration对象的创建

typeAliasRegistry:类型别名注册器,里面储存着所有的类型与别名的对应关系,这里首先加载了mybatis中默认的,也就是在配置文件中你所填的那些配置信息的与它们的实现类的对应。

public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

第一步验证文件,以及加载一些默认信息到Configuration的任务就已经完成了,下一步就是加载配置文件的信息,回到SqlSessionFactoryBulider的bulid方法,在这里XMLConfigBuilder调用了parse进行配置文件的加载。然后先判断文件是否已经读取过,没有则解析内容,将所有的标签中的内容进行获取并且填充到Configuration中。由于内容都类似,后续就看看Properties的方法以及数据源的那个方法。

 public Configuration parse() {
	//判断文件是否已经读取过
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
	//设置文件以及读取
    parsed = true;
	//获取文件内容/configuration标签下的内容
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
	  //解析properties标签中的内容,
      propertiesElement(root.evalNode("properties"));
	  //设置mybatis的一些运行时的配置,比如懒加载等等之类
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(settings);
	  //解析别名,一般在实体类使用别名
      typeAliasesElement(root.evalNode("typeAliases"));
	  //插件
      pluginElement(root.evalNode("plugins"));
	  //MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
      objectFactoryElement(root.evalNode("objectFactory"));
	  // 为了统一不同数据库返回key值大小写不一致的问题,特自定义objectWrapperFactory来做统一的处理
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
	  //反射模块
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
	  //配置环境,这里一般会有数据源信息,以及事务管理类
      environmentsElement(root.evalNode("environments"));
          //数据库id
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
	  //类型转换器(几乎所有的类型转换都已经有),即jdbc的数据类型,java的那种类型
      typeHandlerElement(root.evalNode("typeHandlers"));
	  //解析并加装mapper文件
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

 

Properties标签内容的读取

private void propertiesElement(XNode context) throws Exception {
	//判断是否有这个内容
    if (context != null) {
      Properties defaults = context.getChildrenAsProperties();
      String resource = context.getStringAttribute("resource");
      String url = context.getStringAttribute("url");
      if (resource != null && url != null) {
        throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference.  Please specify one or the other.");
      }
	  //判断是传入的是相对路径
      if (resource != null) {
        defaults.putAll(Resources.getResourceAsProperties(resource));
      } else if (url != null) {//判断是传入的还是绝对路径
        defaults.putAll(Resources.getUrlAsProperties(url));
      }
	  //之前创建XMLConfigBuilder的时候已经传递过一次Properties文件的内容,
	  //是因为bulid方法可以传递这个Properties对象,所以要将这些内容都合并到一起
      Properties vars = configuration.getVariables();
      if (vars != null) {
        defaults.putAll(vars);
      }
      parser.setVariables(defaults);
	  //配置文件信息类覆盖之前的值
      configuration.setVariables(defaults);
    }
  }

 

设置数据源以及事务管理类方法,创建Environment对象

 private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
		//如果没标记使用其他的数据源,则使用默认节点的数据源
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
		//获取数据库id
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
		  //根据传递的数据,然后根据别名注册器中对应的类名,然后反射创建对象
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
		   //根据你传递的类型,创建数据源,比如是否使用连接池,jndi数据源等
          DataSource dataSource = dsFactory.getDataSource();
		  //创建Environment的Builder对象,Environment就是环境,存着数据库id,数据源,以及事务管理类
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
		  //设置配置信息类的环境。
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

至此,mybatis的配置文件的解析,首先先验证文件内容是否合标准,再此加载一些默认的别名到别名注册器上,创建Configuration对象,然后在解析文件的内容,新增别名,插件,创建数据源等等,并且将文件填入Configuration对象的属性中。最终创建出SqlSessionFactory对象。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Mybatis是一个轻量级的Java持久层开源框架,它封装了JDBC操作数据库的底层细节,提供了一个简单易用的数据库访问方式。 Mybatis源码分为核心模块和附加模块两部分,核心模块主要包括配置解析SQL解析SQL执行等功能,附加模块包括连接池、缓存、事务管理等功能。 在Mybatis源码中,配置解析是其中的关键部分。通过解析mybatis-config.xml配置文件,可以获取到数据库连接信息、映射器配置、插件配置等。在配置解析过程中,Mybatis会对配置文件进行校验,确保配置的正确性。 SQL解析Mybatis的另一个重要功能。Mybatis通过解析Mapper接口中的注解或XML配置文件中的SQL语句,将SQL语句解析为ParameterMapping、BoundSql等对象,并将其封装成一个MappedStatement对象,供后续的SQL执行使用。 SQL执行是Mybatis的核心功能之一。在SQL执行阶段,Mybatis会根据MappedStatement中的信息,获取数据库连接,并执行对应的SQL语句。在执行过程中,Mybatis会通过TypeHandler对参数进行类型转换,并使用ResultSetHandler将查询结果封装成Java对象。 除了核心模块,Mybatis源码还包括了连接池、缓存、事务管理等附加模块的实现。连接池模块负责管理数据库连接的获取和释放,缓存模块负责缓存查询结果以提高性能,而事务管理模块则负责管理数据库的事务处理。 总之,Mybatis源码解析涉及多个关键模块的实现,包括配置解析SQL解析SQL执行、连接池、缓存、事务管理等。通过了解这些模块的实现原理,我们可以更好地理解和使用Mybatis框架。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值