Mybatis深入之初始化过程

Mybatis深入之初始化过程

一:简介

    这篇开始是根据Mybatis源码来对Mybatis进行更深入的学习、当然、精力有限、还做不到学习的面面俱到。
    Mybatis初始化过程可以用一句话概括:就是将Mybatis的配置信息加载到一个类中、供后面Mybatis进行各种操作时使用、这个类叫:Configuration——见名知意。当然这个类的功能并不仅限与存放配置文件信息。

二:整体流程

下面是一段正常情况下从加载配置到执行sql语句的代码:
        String mybatisConfigPath = "config/mybatis/mybatis.xml";
        InputStream inputStream = Resources.getResourceAsStream(mybatisConfigPath);
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int count = (Integer)sqlSession.selectOne("org.alien.mybatis.samples.mapper.AuthorMapper.getAllAuthorsCount");
        System.out.println(count);

初始化过程在上面代码中就是获取SqlSessionFactory的过程。

初始化过程流程图:

参照流程图、初始化大致步骤:

  1. 加载配置文件
  2. 解析配置文件、将配置文件中的信息装载到Configuration中。
  3. 根据Configuration创建SqlSessionFactory并返回。

三:详细过程

3.1 加载配置文件

这一步很简单、从代码层面上来看就是将配置文件以流的形式读取到程序中、并将其作为参数传递给SqlSessionFactoryBuilder以供后面创建SqlSessionFactory。其提供了许多重载的方法供我们选择:

但是其最后都是调用核心方法(从这里也可以看出、初始化过程就是构造填充Configuration过程):

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      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) {
    return new DefaultSqlSessionFactory(config);
  }

3.2解析配置文件

解析配置文件的入口是在SqlSessionFactoryBuilder中的:public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties)

流程图:

3.2.1 整理流程:

  1. 创建MybatisDTD文件实体类:XMLMapperEntityResolver.
  2. 根据配置文件流信息和上一步创建的EntityResolver创建配置文件解析类:XPathParser用于解析配置文件内容.
  3. 将前两部创建的对象作为XMLConfigBuilder的构造函数参数传递、创建XMLConfigBuiler对象.
  4. 调用XMLConfigBuilder.parse()创建Configuration对象并将配置文件信息装配到Configuration对象中.

    下面从代码的角度来看上面流程主要代码。这里从代码执行角度进行分析。

3.2.2 代码流程

  1. SqlSessionFactoryBuilder.build()开始
  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      //创建解析文件并装配Configuration的类
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //这里分开写、清楚一点。解析配置文件、装配Configuration并返回
      Configuration configuration = parser.parse();
      //根据Configuration创建SqlSessionFactory并返回
      return build(configuration);
    } 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.
      }
    }
  }

先看XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);到底创建了一个什么样的XMLConfigBuilder。

具体构造方法:

  public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
  }
  • 这里只关心inputstream参数、其他的可以自行研究、其实是不同build方法传递的不同参数。
  • 从上面可以看出要想先构建XMLConfigBuilder、首先需要创建XMLMapperEntityResolver、并以其作为创建XPathParser对象的参数之一。

2、XMLMapperEntityResolver的创建:new XMLMapperEntityResolver()、即只需调用其无参构造函数即可。其源码就不再贴了、就是将Mybatis的DTD文件加载到一个私有集合中private static final Map<String, String> doctypeMap = new HashMap<String, String>();并向外提供一个用户获取DTD的InputSource的方法public InputSource resolveEntity(String publicId, String systemId);

3、XPathParser的创建:

  public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
    //填充XPathParser 部分私有属性
    commonConstructor(validation, variables, entityResolver);
    //根据InputStream来创建Document对象用于后面操作配置文件。
    this.document = createDocument(new InputSource(inputStream));
  }
  • EntityResolver就是前面的XMLMapperEntityResolver
  • InputStream则是配置文件流信息
  private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
    this.validation = validation;
    this.entityResolver = entityResolver;
    this.variables = variables;
    XPathFactory factory = XPathFactory.newInstance();
    this.xpath = factory.newXPath();
  }
  • 设置解析xml文件时使用的属性
  private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      factory.setValidating(validation);

      factory.setNamespaceAware(false);
      factory.setIgnoringComments(true);
      factory.setIgnoringElementContentWhitespace(false);
      factory.setCoalescing(false);
      factory.setExpandEntityReferences(true);

      DocumentBuilder builder = factory.newDocumentBuilder();
      builder.setEntityResolver(entityResolver);
      builder.setErrorHandler(new ErrorHandler() {
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

        public void fatalError(SAXParseException exception) throws SAXException {
          throw exception;
        }

        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }
  • 根据InputSource创建Document

    3、当XPathParser创建完成之后、回到真正执行XMLConfigBuilder创建的方法:

  private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //解析文件代码只能执行一次、当解析之后此值将变为true
    this.parsed = false;
    this.environment = environment;
    //前面实例化好的XPathParser
    this.parser = parser;
  }
  • 调用Configuration无参构造方法创建其实例对象,
  • 设置XMLConfigBuilder解析装配Configuration需要用到的属性、其中最关键的this.parser = parser也就是前面实例化好的XPathParser。
  • 有兴趣的可以看一眼Configuration实例化时初始化了哪些东西基本Mybatis的默认配置在这里都能找到

4、当XMLConfigBuilder实例化好之后、接下来就是解析配置文件、装配Configuration。

  public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
//对于parser.evalNode(String node)如何执行的、这里不关注。只需要知道parser.evalNode(String node)是干嘛的就行。    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }
  • 获取配置文件中configuration节点所有信息包括其子节点。
private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      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);
    }
  }
  • 从这里很明显的就看出此方法是将Mybatis配置文件各个配置项解析并装配到Configuration对象中。

5、这里只看其中一个最简单的过程——将Mybatis配置文件中的<settings>...<setting name="xxx" value="xxx"/>...<settings>解析并设置到Configuration中、其他的等后面涉及到会深入其过程之中

  private void settingsElement(XNode context) throws Exception {
    if (context != null) {
      Properties props = context.getChildrenAsProperties();
      // Check that all settings are known to the configuration class
      MetaClass metaConfig = MetaClass.forClass(Configuration.class);
      for (Object key : props.keySet()) {
        if (!metaConfig.hasSetter(String.valueOf(key))) {
          throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
        }
      }
      configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
      configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
      configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
      configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
      configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
      configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
      configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
      configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
      configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
      configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
      configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
      configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
      configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
      configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
      configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
      configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
      configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
      configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
      configuration.setLogPrefix(props.getProperty("logPrefix"));
      configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
      configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
    }
  }

6、解析装配完成之后、返回Configuration

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
  • 返回最终生成的DefaultSqlSessionFactory

    到这里整个解析过程也就结束了。

补充:

多提一句、网上有的说SqlSessionFactory的创建用到了创建者模式、觉得并不是那么恰当、建造者模式的核心是有一个调度员来根据不同的场景来调度不同的创建者创建具体对象。而这里并没有。个人觉得只是方法的一系列的重载、来方便使用者根据不同的场景或者喜好来创建SqlSessionFactory。

更多内容:Mybatis 目录

时间: 2024-08-09 06:35:02

Mybatis深入之初始化过程的相关文章

MyBatis的初始化过程。

对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XML配置文件创建Configuration对象的过程 3. 手动加载XML配置文件创建Configuration对象完成初始化,创建并使用SqlSessionFactory对象 4. 涉及到的设计模式 一. MyBatis的初始化做了什么 任何框架的初始化,无非是加载自己运行时所需要的配置信息.MyBati

MyBatis初始化过程解析----广西11选5平台出租源码解析

准备工作 为了看清楚广西11选5平台出租的 Q1446595067 整个初始化过程,先创建一个简单的Java项目,目录结构如下图所示: 1.1 Product 产品实体类 public class Product { private long id; private String productName; private String productContent; private String price; private int sort; private int falseSales; p

java代码的初始化过程研究

刚刚在ITeye上看到一篇关于java代码初始化的文章,看到代码我试着推理了下结果,虽然是大学时代学的知识了,没想到还能做对.(看来自己大学时掌握的基础还算不错,(*^__^*) 嘻嘻--)但是博主写的不够详细具体,我想在这详细谈一下java代码的具体初始化过程. 首先要清楚,初始化分为两个过程:类初始化.对象初始化. 类初始化是指类加载器将类加载到内存时,对类成员的初始化过程,也就是有static修饰的变量.对于加载完的类,它的类变量都会赋一个默认值,即使你定义时就赋值了.比如int类型就是0

JAVA对象的初始化过程

出处:http://blog.csdn.net/andrew323/article/details/4665379 下面我们通过两个例题来说明对象的实例化过程. 例1:   编译并运行该程序会有以下输出 Static Block Employee Company:china soft Non-Static Block Employee phone:0755-51595599 Employee(String) Empoloyee() 下面我们来对结果做分析: 1 在代码34行实例化对象时, 先对给

Java对象相关元素的初始化过程

1.类的成员变量.构造函数.成员方法的初始化过程 当一个类使用new关键字来创建新的对象的时候,比如Person per = new Person();JVM根据Person()寻找匹配的类,然后找到这个类相匹配的构造方法,这里是无参构造,如果程序中没有给出任何构造方法,则JVM默认会给出一个无参构造.当创建一个对象的时候一定对调用该类的构造方法,构造方法就是为了对对象的数据进行初始化.JVM会对给这个对象分配内存空间,也就是对类的成员变量进行分配内存空间,如果类中在定义成员变量就赋值的话,就按

对Socket CAN的理解(5)——【Socket CAN控制器的初始化过程】

转载请注明出处:http://blog.csdn.net/Righthek 谢谢! 对于一般的CAN模块,进行初始化时,最关键的是以下两步: 1.  配置CAN的位时序: 2.  配置CAN的消息报文: 下面,我们来详细分析上面提到的关键两步. 一.初始化步骤: 1.  第一步,进入初始化模式,在CAN控制寄存器中,将Init位置1: 2.  第二步,在CAN控制寄存器中,将CCE位置1: 3.  第三步,等待Init位置1,此步聚为了确保已经进入初始化模式: 4.  第四步,将位时序的值写入到

IOC容器的初始化过程

1.ClassPathXmlApplicationContext类体系结构 左边的黄色部分是ApplicationContext体系继承结构,右边是BeanFactory结构体系,两个体系是典型的模板方法设计模式的使用. 从该继承体系可以看出: (1)BeanFactory是一个bean工厂的最基本定义,里面包含了一个bean工厂的几个最基本方法:getBean(),containsBean()等,是一个很纯粹的bean工厂,不关注资源.资源位置.事件等. ApplicationContext是

Java初始化过程

以下程序执行的结果是: class X{ Y y=new Y(); public X(){ System.out.print("X"); } } class Y{ public Y(){ System.out.print("Y"); } } public class Z extends X{ Y y=new Y(); public Z(){ System.out.print("Z"); } public static void main(Stri

启动期间的内存管理之初始化过程概述----Linux内存管理(九)

日期 内核版本 架构 作者 GitHub CSDN 2016-06-14 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 在内存管理的上下文中, 初始化(initialization)可以有多种含义. 在许多CPU上, 必须显式设置适用于Linux内核的内存模型. 例如在x86_32上需要切换到保护模式, 然后内核才能检测到可用内存和寄存器. 而我们今天要讲的boot阶段就是系统初始化阶段使用的内存分配器. 1 前景回顾 1.1