MyBatis详细执行流程

MyBatis详细的执行流程


1. 创建加载核心配置文件的inputStream流
1.1 Recourse.getResourceAsStream(String resource)方法

调用Recourse类下的getResourceAsStream(String resource)方法,传递过来的resource值为在resources目录下的mybatis-config.xml文件的文件名。
getResourceAsStream(String resource)的源码

public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
  }

解释:在这个getResourceAsStream(String resource)方法很简单,直接返回一个InputStream 对象,这个InputStream 对象由getResourceAsStream(null, resource)方法中获得。


1.2. getResourceAsStream(null, resource)的方法

让我们进入getResourceAsStream的重载方法看看吧。

public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
      throw new IOException("Could not find resource " + resource);
    }
    return in;
}

解释:在这个方法一进来我们就看到了,新建了一个InputStream 对象 in。Okay!我们可以猜想这个 in 对象 可能就是要返回的给我们上一级的。让我们来看看它是怎么生成的!

在in 的生成中 调用了classLoaderWrapper.getResourceAsStream(resource, loader) 的方法。

老千层饼了,我们可以得知,in是调用了classLoaderWrapper对象getResourceAsStream(resource, loader)方法生成的。

ps: classLoaderWrapper在Resources类中是这样创建的:

private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();它是一个私有静态的对象哦!![有点饿汉模式的味道(设计模式中单例模式的一种)]。


1.3. getResourceAsStream(resource, loader)方法

既然都走到这里了那么我们就去探探getResourceAsStream(resource, loader)方法的深浅吧!
ps:到这里,我们传进来的
resource是一个"mybatis-config.xml"这样的字符串,
loader是null。

public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
   return getResourceAsStream(resource, getClassLoaders(classLoader));
}

解释:现在已经从Resource类跳到了ClassLoaderWrapper类了哦! 在这个getResourceAsStream(String resource, ClassLoader classLoader)方法里,它调用了一个重载的方法getResourceAsStream(resource, getClassLoaders(classLoader),把我们传进来的classLoader也就是值为null的ClassLoader 对象,传给了getClassLoaders方法,所以我们要去这个getClassLoaders方法里看看嘿嘿,好好追根朔源

1.4. getClassLoaders(classLoader)方法

我们可以从这个getClassLoaders方法里看到,这个方法返回了一个ClassLoader[]的数组,数组里面保存这个5个ClassLoader类对象。知道了这个我们就返回看这里 getResourceAsStream(resource, getClassLoaders(classLoader))
ps: 这个数组里的第一个classLoader对象是我们传进去的空对象,在这此调用中数组不会都是null值。

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
// 使用当前线程的ClassLoader  
// 使用当前类的ClassLoader 
// 使用系统ClassLoader,即系统的入口点所使用的ClassLoader。
    return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader
    };
}

1.5. 回到1.3的getResourceAsStream(resource, getClassLoaders(classLoader))方法

在这个方法里的源码我们可以看到,当遍历对象不为null的时候,将返回一个不为空的InputStream流。

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
 for (ClassLoader cl : classLoader) {
   if (null != cl) {

     // try to find the resource as passed
     InputStream returnValue = cl.getResourceAsStream(resource);

     // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
     if (null == returnValue) {
       returnValue = cl.getResourceAsStream("/" + resource);
     }

     if (null != returnValue) {
       return returnValue;
     }
   }
 }
 return null;
}

InputStream returnValue = cl.getResourceAsStream(resource),是将文件名传进去,如何去对应的加载器寻找资源,找得到就返回加载资源的输入流,找不到就返回一个空值。


1.6. 返回一个输入流

然后就层层返回,直到我们一开始的地方,这样我们的inputStream就新建好啦。

InputStream inputStream = Resources.getResourceAsStream(resource);

2 创建一个sqlSessionFactory对象
2.1 创建sqlSessionFactory代码用例

接下来进行的是,一个调用SqlSessionFactoryBuilder().build(inputStream)方法生成一个SqlSessionFactory的过程。

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.2 build(inputStream)方法

新建了一个SqlSessionFactoryBuilder实例,调用的是系统隐式提供的无参构造方法。利用该对象并调用了带字节流的build方法。从其源码可以看出,又跳到了重载方法build(InputStream inputStream, String environment, Properties properties)里去了。

public SqlSessionFactory build(InputStream inputStream) {
	  return build(inputStream, null, null);
}
2.3 build(InputStream inputStream, String environment, Properties properties)

从这个新的build方法一进来,我们就调用了XMLConfigBuilder类的构造方法新建了一个XMLConfigBuilder对象。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
  //inputStream为我们传进去的输入流,
  //environment, properties都是空对象
    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.
    }
  }
}

注意:inputStream为我们传进去的输入流,environment, properties都是空对象


2.4 XMLConfigBuilder构造方法。
  • 具体的XMLConfigBuilder构造方法。进来之后又调用了自己的另一个构造方法private XMLConfigBuilder(XPathParser parser, String environment, Properties props).从源码中我们可以看到,我们新建了一个XPathParser对象,在这个对象里又新建了一个new XMLMapperEntityResolver() ,它是MyBatis DTD文件的脱机实体解析程序类,里面定义了一些dtd文件名。接下来我们要把一个加载核心配置文件的输入流、一个值为true的布尔值、一个为空对象的props,和一个装着一些属性是常量值为dtd文件名的类拿去构造一个XPathParser对象
    ps:在这个构造方法中它把自己的一个parsed属性设置为false。下面会用到。
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
 	 this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

解释:就是给之前新建的XMLConfigBuilder里面的属性赋值的过程。例如说:XMLConfigBuilder建新的XPathParser对象,再在XPathParser对象给他的document属性new一个新的document对象以及给XPathParser其他属性的赋值过程。


2.5 new XPathParser(inputStream, true, props, new XMLMapperEntityResolver())

进来之后我们发现构造方法中有两个东西。

  1. 一个是名为commonConstructor的方法,
  2. 另一个方法是createDocument,用于给XPathParser里的document属性赋值的方法。
    源码
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
 	 commonConstructor(validation, variables, entityResolver);
 	 this.document = createDocument(new InputSource(inputStream));
}

2.5.1 commonConstructor(validation, variables, entityResolver)

参数validation:true,variables:null,entityResolver是刚刚创建的MyBatis DTD文件的脱机实体解析程序类
commonConstructor源码

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();
}

从上面的源码可以看出这个commonConstructor()存在的目的是给PathParse对象里的属性赋值。
让我们看看PathParse类里有什么对象。

public class XPathParser {
  private final Document document;
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;
}

从中我们可以得知,我们还有一个document对象没有赋值,所以也就会再使用一个createDocument(new InputSource(inputStream))方法;


2.5.2 createDocument(new InputSource(inputStream));

ps:new InputSource(inputStream),只是新建了一个InputSource对象,并把这个对象里面的其中一个字节流属性赋值为inputStream。
InputSource对象的具体属性在下面。然后调用这个新建的InputSource对象使用了createDocument方法。
我们可以从源码中看到,这个方法用于给XPathParser对象里的属性赋值,具体属性请看下方。
createDocument方法代码

private Document createDocument(InputSource inputSource) {
    // important: this must only be called AFTER common constructor
    try {
    //用于创建一个DocumentBuilderFactory 对象。
      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
      //给factory对象设置它的属性值。
      
      //指定由此工厂生成的解析器是否将在分析文档时验证文档。
      factory.setValidating(validation);	
      
    //指定此工厂生成的解析器是否将提供对XML命名空间的支持。
      factory.setNamespaceAware(false);
      
      //指定此工厂生成的分析器是否将忽略注释。
      factory.setIgnoringComments(true);
      
      /*指定此工厂创建的解析器在解析XML文档时
      是否必须消除元素内容中的空白(有时松散地称为“可忽略的空白”)
      (请参见XML Rec 2.10)。
      */
      factory.setIgnoringElementContentWhitespace(false);
      
      //指定此工厂生成的解析器是否将CDATA节点转换为文本节点,
      //并将其附加到相邻的(如果有)文本节点上。
      factory.setCoalescing(false);
      
		//指定此工厂生成的解析器是否将展开实体引用节点。
      factory.setExpandEntityReferences(true);

	//通过factory创建一个DocumentBuilder 对象
      DocumentBuilder builder = factory.newDocumentBuilder();
      //entityResolver 是之前传进来的2.5中的new值。
      builder.setEntityResolver(entityResolver);
      //设置报错处理器
      builder.setErrorHandler(new ErrorHandler() {
        @Override
        public void error(SAXParseException exception) throws SAXException {
          throw exception;
        }

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

        @Override
        public void warning(SAXParseException exception) throws SAXException {
        }
      });
      //调用DocumentBuilder类的对象builder里的parse方法
      //作用:将给定输入源的内容解析为XML文档,
      //	 并返回一个新的DOM{@linkdocument}对象。
      return builder.parse(inputSource);
    } catch (Exception e) {
      throw new BuilderException("Error creating document instance.  Cause: " + e, e);
    }
  }

这样我们就成功给XPathParse对象里的属性都赋值上了,其中builder.parse(inputSource);这个方法还可以往下探,但是真的太多了,现在看不太明白,留着以后有空再补上。

InputSource对象里的属性

public class InputSource{
    private String publicId;
    private String systemId;
    private InputStream byteStream;//把inputStream赋值给它
    private String encoding;
    private Reader characterStream;
}

2.6 返回2.4的XMLConfigBuilder构造方法

建完XPathParse对象之后返回2.4的XMLConfigBuilder构造方法
XMLConfigBuilder构造方法

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
 	 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");
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
}

从这里我们可以看到新建的XMLConfigBuilder对象中有一个parsed属性值为false。


2.7 返回2.3新建完一个XMLConfigBuilder对象之后
  • 2.3中的部分代码
  //inputStream为我们传进去的输入流,
  //environment, properties都是空对象
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    return build(parser.parse());

从中我们可以知道:
现在创建了一个新的XMLConfigBuilder对象后,接着我们调用这个对象的parse方法(),也就是这一段return build(parser.parse());。这个parse()方法将返回一个Configuration对象。通过这个方法给Configuration对象里的属性赋值。然后返回这个Configuration对象。用于build()方法。下面是parse()方法

public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

parsed默认值是false,在创建XMLConfigBuilder对象时设置的,在2.6中有提及到; 所以我们这里就会执行parseConfiguration(parser.evalNode("/configuration"));


2.8 parseConfiguration方法

parser.evalNode("/configuration")返回了一个根节点类,evalNode里又以之前赋值的document对象,和“/configuration”字符串作为参数最后返回一个XNode类

private void parseConfiguration(XNode root) {
    try {
      //issue #117 read properties first
      propertiesElement(root.evalNode("properties"));
      Properties settings = settingsAsProperties(root.evalNode("settings"));
      loadCustomVfs(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"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}

在这个类中会设置核心配置文件对照的类属性节点。


2.9 build(Configuration config)

到这个时候,我们就带着这个赋好值的Configuration对象到build方法里。,直接新建一个DefaultSqlSessionFactory对象传入config对象,赋值给DefaultSqlSessionFactory对象里的Configuration属性并且返回DefaultSqlSessionFactory对象。

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}
//new DefaultSqlSessionFactory(config);具体内容
public class DefaultSqlSessionFactory implements SqlSessionFactory {

   private final Configuration configuration;

   public DefaultSqlSessionFactory(Configuration configuration) {
      this.configuration = configuration;
   }
}

到此处,我们就拿到了一个sqlSessionFactory对象。第二次修改,还是只整出了一个sqlSessionFactory对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值