干翻Mybatis源码系列之第五篇:Mybatis中核心运行机制超级详细版

简介: 干翻Mybatis源码系列之第五篇:Mybatis中核心运行机制超级详细版

一:引言

1:本文目的

熟悉花哥的都知道,花哥是该粗的地方粗该细的地方细,写文章的话咱追求的还是细致和全面,所以本文意在将Mybatis从SqlSessionFactory被创建出来到Dao被执行成功,我们会将绝大部分的细节写的到位,这是我们本专栏也是本篇文章的目的。

2:首要知识回顾

1:在我们使用原生Mybatis的时候,我们获取UserDao的实现类对象,还得向下面这么写。基于Spring整合Mybatis之后,我们也就只会拿着userDao.xxx()这样进行操作了。然而,我们要知道下文中几行代码,才能体现Mybatis真正的运行过程。

//通过这一步将Mybatis配置文件读取到我们的JVM中。
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
//接下来就该创建SqlSessionFactory和SqlSession对象了。
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStram);
SqlSession sql = sqlSessionFactory.openSession);
//获取到具体的UserDao接口的代理对象
UserDao userDao = sqlSession.getMapper(UserDao.class);
//基于实际的JDBC的
int result = userDao.save();
List<User> users= userDao.query();

3:知识补充

1):Mybatis如何处理XML文件

1:任何配置读取到JVM当中一定是以对象的形式体现的,封装成一个一个存储类型的对象,存储在JVM的用户内存中。

2:Mybatis的核心配置文件Mybatis-config.xml会被封装成Configuration对象,读取XML相关内容。

3:Mybatis中的Mapper.xml文件则是被打散了,这种标签则是被封装到MappedStatement对象中,剩余的标签则是被封装到

各种类型的对象基于放到Map当中,key是nameSpace.id的形式,并且浙西对象都保存到了Configuration当中,Configuration和MappedStatement是双向引用。

4:Java当中XML解析的方式有三种:DOM SAX XPath,Mybatis在处理XML的过程当中使用的是Xpath

5:XPathParser解析了Xml之后初步解析成为一个XNode对象。

6:Configuration和MappedStatement是Mybatis当中的最核心的两个存储类对象。

2):测试使用Mybatis中API解析XML

XPathParser xpathPaser = new XPathParser(InputStream)

通过标签Node的方式就可以获取到一个一个标签的具体的内容。

@Test
    public void testXml() throws IOException {
        //InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        //Reader reader = Resources.getResourceAsReader("users.xml");
        InputStream inputStream = Resources.getResourceAsStream("users.xml");
        XPathParser xPathParser = new XPathParser(inputStream);
        //xNodes 里面的xNode 对应 <user>
        //  /configuration/*,要读取这个根标签下的所有的内容
        List<XNode> xNodes = xPathParser.evalNodes("/users/*");
        //要读取这个/users标签中下的所有的标签。
        List<com.baizhiedu.xml.User> users = new ArrayList<>();
        //这里面每一个xNode对应就是一个<user>
        for (XNode xNode : xNodes) {
            com.baizhiedu.xml.User user = new com.baizhiedu.xml.User();
            List<XNode> children = xNode.getChildren();
            user.setName(children.get(0).getStringBody());
            user.setPassword(children.get(1).getStringBody());
            users.add(user);
        }
        for (com.baizhiedu.xml.User user : users) {
            System.out.println("user = " + user);
        }
    }
<users>
    <user>
        <name>sunshuai</name>
        <password>2222</password>
    </user>
    <user>
        <name>xiaohei</name>
        <password>2222</password>
    </user>
</users>

以上这个内容是在对应到Mybatis框架当中是在:build方法当中完成的InputStream – XNode – Configuration的解析。

//----------------------------删除------------------------------------//

整个Mybatis的操作就是两大块,一个是获取Dao之后的操作,另外一个就是获取SqlSession的操作,把这几大块内容搞清楚了,我们大致的东西就搞明白了。

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

这块是最没有歧义的,这块就是通过IO的方式通过获取Mybatis-config.xml和xxxMapper.xml的中的配置内容。

我们上边虽然只读取了Mybatis.xml配置文件当中的内容,但是顺便也会把Mapper.xml当中的脚本,这是基于Mybatis-config.xml当中的Mapper标签,将所有的Mapper.xml找到,读取到JVM内存中,封装到对应的对象当中。

//----------------------------删除------------------------------------//

三:Mybatis核心运行过程

1:new SqlSessionFactoryBuilder().build(inputStram)详解

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStram);

build方法是来自于SqlSessionFactoryBuilder,SqlSessionFactory 也是需要SqlSessionFactoryBuilder来进行创建的。

点击去build方法之后我们可以看到:

public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }

这个方法调用的是下面这个重载的方法:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            //parser.parse()的返回值:Configuration 
            //var5 是SqlSessionFactory 
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();
            try {
                inputStream.close();
            } catch (IOException var13) {
            }
        }
        return var5;
    }

到这里我们可以清楚的看到,parser.parse()方法已经将核心存储类Configuration创建出来了,build(Configuration) 将SqlSessionFactory对象创建出来了。

1):new XMLConfigBuilder()

对于处理XML文件Mybatis当中有两个核心的类:XPathParser和XMLConfigBuilder,经过一系列的读取之后,将Mybatis核心配置文件的内容暂时放置到了XMLConfigBuilder.XPathParser.Document当中:

XMLConfigBuilder:

public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

XPathParser:

private final Document document;
    public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
        this.commonConstructor(validation, variables, entityResolver);
        this.document = this.createDocument(new InputSource(inputStream));
    }

2):XMLConfigBuilder.parse()

public Configuration parse() {
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            //parser.evalNode("/configuration")这个是将Mybatis-config.xml中的configuration标签整个拿了出来。
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }

parser.evalNode(“/configuration”)这个是将Mybatis-config.xml中的configuration标签整个拿了出来,返回值是一个XNode对象。parseConfiguration(XNode)的返回值是核心的Configuration对象,并且在此处也将关键的 MappedStatement也创建了出来,并在

Configuration对象当中以:

protected final Map<String, MappedStatement> mappedStatements;

存在,至此Mybatis存存储类对象Configuration+MappedStatement都已经创建完毕!

补充说明:此处对于Configuration+MappedStatement的说明不够详细,我们后续会将详解版专门写一篇文章,链接至于此处。

2):this.build(parser.parse());

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

在这里SqlSessionFactory也基于重载build(Configuration config)方法被创建出来。

2:sqlSessionFactory.openSession详解

待补充!

3:sqlSession.getMapper(UserDao.class)详解

我们基于sqlSessionFactory.openSession()创建的SqlSession的类型是:DefaultSqlSession,基于上述代码路径一路走到了MapperProxyFactory的类中,这是Mybatis当中创建UserDao代理对象的核心对象之一,另外一个是MapperProxy,基于二者Mybatis创建出来了UserDao的代理对象,最终代码如下:

protected T newInstance(MapperProxy<T> mapperProxy) {
        return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
    }

MapperProxy是InvocationHandler的代理类

public class MapperProxy<T> implements InvocationHandler, Serializable {
  ...
}

Mybatis再次创建接口的代理对象正是基于JDK提供的Proxy.newInstance()的方式:

Proxy.newProxyInstance(ClassLoader classLoader, Class[] clazzs, InvocationHandler invocationHandler);

我们知道JDK创建代理对象的方式是将额外功能书写到了InvocationHandler.invoke()方法中。

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

这里值得注意的是

4:userDao.query()详解

UserDao userDao = sqlSession.getMapper(UserDao.class);以下部分是我们的关注点,我们通过动态代理构建了Dao的实现类,并创建了他的对象。

Mybatis在这里是通过两种关键类型做的

1:MapperProxyFactory(JDK创建的代理 )
2:MapperProxy代理的就是MapperProxy的代理。
      3:SqlCommand
              |-- type [insert update select delete]
              |-- name  namespace.id
      4:MethodSingnatrue
                      |-- 方法的返回值
              |-- 方法的参数
  | -- sqlSession.update();
              |-- Excutor (SimpleExcutor,ReuseExcutor....)
                  |-- StatementHandler
                      |-- ParameterHandler  ResultSetHandler
                            TypeHandler

备注:此文章未完待续…

相关文章
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(User.java)
mybatis简单案例源码详细【注释全面】——实体层(User.java)
14 0
|
6天前
|
SQL Java 数据库连接
一文细说Mybatis八大核心源码
以上 是V哥给大家整理的8大核心组件的全部内容,为什么说选择 Java 就是选择未来,真正爱 Java 的人,一定喜欢深入研究,学习源码只是第一步,要有一杆子捅到操作系统才够刺激。
|
6天前
|
SQL 缓存 Java
|
6天前
|
XML Java 数据库连接
探秘MyBatis:手写Mapper代理的源码解析与实现
探秘MyBatis:手写Mapper代理的源码解析与实现
21 1
|
6天前
|
SQL Java 数据库连接
深入源码:解密MyBatis数据源设计的精妙机制
深入源码:解密MyBatis数据源设计的精妙机制
34 1
深入源码:解密MyBatis数据源设计的精妙机制
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
mybatis简单案例源码详细【注释全面】——Utils层(MybatisUtils.java)
14 0
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——测试层(UserMapperTest.java)
mybatis简单案例源码详细【注释全面】——测试层(UserMapperTest.java)
10 0
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Dao层映射文件(UserMapper.xml)【重要】
mybatis简单案例源码详细【注释全面】——Dao层映射文件(UserMapper.xml)【重要】
11 0
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——Dao层接口(UserMapper.java)
mybatis简单案例源码详细【注释全面】——Dao层接口(UserMapper.java)
10 0
|
6天前
|
Java 数据库连接 mybatis
mybatis简单案例源码详细【注释全面】——实体层(Role.java)
mybatis简单案例源码详细【注释全面】——实体层(Role.java)
8 0