前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Mybatis 源码探究 (3)创建 SqlSessionFactory对象 执行sqlSession.getMapper()方法

Mybatis 源码探究 (3)创建 SqlSessionFactory对象 执行sqlSession.getMapper()方法

作者头像
宁在春
发布2022-10-31 14:40:14
4710
发布2022-10-31 14:40:14
举报
文章被收录于专栏:关于Java学习@宁在春

Mybatis 源码探究 (3)创建 SqlSessionFactory对象

时隔许久,终于又能接着来搞他啦。Mybatis 一起来探究吧。 先笑会再进入主题吧

开始啦

一、new SqlSessionFactoryBuilder().build(inputStream) 方法

代码语言:javascript
复制
String resource="mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
代码语言:javascript
复制
public SqlSessionFactory build(InputStream inputStream) {
    //在这里又接着调用了重载方法build 我们接着往下走啊 
    return build(inputStream, null, null);
}

二、SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) 方法

代码语言:javascript
复制
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
   //XMLConfigBuilder是对mybatis的配置文件进行解析的类,会对myabtis解析后的信息存放在Configuration对象中
    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.
    }
  }
}

一个一个讲啊,慢慢来。

1、 new XMLConfigBuilder(inputStream, environment, properties);

XMLConfigBuilder : 翻译过来就是XML配置器

XMLConfigBuilder是对mybatis的配置文件进行解析的类,会对myabtis解析后的信息存放在Configuration对象中,Configuration对象

会贯穿整个mybatis的执行流程,为mybatis的执行过程提供各种需要的配置信息。

代码语言:javascript
复制
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
    this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

XPathParser 和 XMLMapperEntityResolver 不是主要研究对象,我放在了文章最后,好奇的话,可以先滑到文末去看。

三、build(parser.parse()) 方法

先看parser.parse()方法 它的返回对象是核心配置对象Configuration

代码语言:javascript
复制
public Configuration parse() {
  if (parsed) {
    throw new BuilderException("Each XMLConfigBuilder can only be used once.");
  }
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}

进入到parseConfiguration(parser.evalNode("/configuration"))方法

可以看到 在这里就是将我们配置的好的文件 一步一步解析封装到Configuration对象去。

代码语言:javascript
复制
 private void parseConfiguration(XNode root) {
    try {
      // issue #117 read properties first
      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);
      // 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);
    }
  }

这一段代码主要作用就是将所有的配置文件加载到给定的对象。

我对于研究这个非常不熟练,结果就是走不出来。

mapperElement(root.evalNode("mappers"))里面要研究的东西不少,先略过哈。

四、return build(parser.parse()); 回到我们的入口处

我们可以知道 parser.parse() 构建出了一个 Configuration 对象,并成为了build()方法的入参。

这个build(parser.parse());

代码语言:javascript
复制
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

这个地方就是将之前已经赋值好的Configuration 放在里面

代码语言:javascript
复制
  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

课间休息

地点:长沙

五、SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

至此 我们获得了 SqlSessionFactory 对象,我们可以通过这获取到mybatis操作数据库的 SqlSession 对象。

代码语言:javascript
复制
 SqlSession sqlSession = sessionFactory.openSession();

openSession() 是SqlSessionFactory 接口下的方法

代码语言:javascript
复制
public interface SqlSessionFactory {

  SqlSession openSession();

  SqlSession openSession(boolean autoCommit);

  SqlSession openSession(Connection connection);

  SqlSession openSession(TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType);

  SqlSession openSession(ExecutorType execType, boolean autoCommit);

  SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);

  SqlSession openSession(ExecutorType execType, Connection connection);

  Configuration getConfiguration();
}

我们这里调用的方法是这个哈

代码语言:javascript
复制
  @Override
  public SqlSession openSession() {
 // openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) 
    return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
  }

最后一个参数就是是否提交,如果不为true,我们做了修改or删除,数据库并不会修改or删除。

接着往下看:

代码语言:javascript
复制
  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

最后返回mybatis操作数据库的 SqlSession 对象。

六、MyUserMapper mapper = sqlSession.getMapper(MyUserMapper.class);

代码语言:javascript
复制
  /**
  检索映射器。
  参数:
  type – 映射器接口类
  类型参数:
  <T> – 映射器类型
  返回:
  绑定到此 SqlSession 的映射器
   */
  <T> T getMapper(Class<T> type);

接下来开始套娃哈

我们实际调用的是它的实现类里的方法:

代码语言:javascript
复制
@Override
public <T> T getMapper(Class<T> type) {
  return configuration.getMapper(type, this);
}

你看又牵扯到configuration对象了哈,知道它多重要了吧。

代码语言:javascript
复制
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
}

到了mapperRegistry 这个对象拉。 mapperRegistry的翻译就是映射器注册表 这个地方我目前还没有搞懂它是什么时候注册进去的哈。

代码语言:javascript
复制
  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      // 这里是去获得 我们传过来的 MyUserMapper.class的代理类的
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

接下来继续看这个 mapperProxyFactory.newInstance(sqlSession) 方法

代码语言:javascript
复制
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

接着套丫

代码语言:javascript
复制
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

当我们看到这里的时候就知道mybatis是通过反射和代理来进行这些操作的哈。

真实执行对象并不在,而是在 MapperProxy mapperProxy Mapper的代理对象中。

那个具体执行的,等下一篇博客哈。我还有很多没懂的。

七、XPathParser

代码语言:javascript
复制
public class XPathParser {

  private final Document document;
  private boolean validation;
  private EntityResolver entityResolver;
  private Properties variables;
  private XPath xpath;
}
  • Document(Document对象) Document 对象代表整个 XML 文档,是一棵文档树的根,可为我们提供对文档数据的最初(或最顶层)的访问入口。
  • validation(是否开启验证标记) 该标记表示设置解析器在解析文档的时候是否校验文档,在创建DocumentBuilderFactory实例对象时进行设置。
  • EntityResolver (加载本地的DTD文件 如果解析mybatis-config.xml 配置文件,默认联网加载http://mybatis.org/dtd/mybatis-3- config.dtd 这个DTD 文档,当网络比较慢时会导致验证过程缓慢。在实践中往往会提前设置EntityResolver 接口对象加载本地的DTD 文件,从而避免联网加载DTD文件。
  • XPath (XPath对象) XPath 是一种为查询XML 文档而设计的语言,它可以与DOM 解析方式配合使用,实现对XML 文档的解析。
  • variables(配置参数集合)。 对应配置文件中节点下定义的键值对集合,包括通过url或者resource读取的键值对集合。

八、XMLMapperEntityResolver

对这个我没有深究。doc注释就是这么一句话:MyBatis DTD 的离线实体解析器 说实话DTD都是我第一次见 🤦‍

当然百度是强大的。

它是EntityResolver子类,xml 的解析会基于事件触发对应的 Resolver 或 Handler,当解析到 dtd 等外部资源时会

EntityResolverresolveEntity方法。在XMLMapperEntityResolver.resolveEntity中,当解析到 mybatis-3-config.dtd、

mybatis-3-mapper.dtd 等资源时,会直接从 classpath 下的 org/apache/ibatis/builder/xml/ 路径获取资源,而不需要通过 url 获取。

九、XMLMapperBuilder类

涉及到的类包括:

MapperBuilderAssistant:Mapper文件解析辅助类,包括了解析各种节点的方法。

BaseBuilder:XMLMapperBuilder与MapperBuilderAssistant的父类,保存了Configuration、TypeAliasRegistry、TypeHandlerRegistry三个属性。

TypeAliasRegistry:别名对应关系,保存了字符串与类的对应信息,例如“String”字符串对应 String.class

TypeHandlerRegistry:注册java.lang与java.sql类型对应关系

Configuration:Mapper文件解析节点完成后,存储解析后的属性信息。

XMLStatementBuilder:解析SQL语句节点类,解析后存储在Configuration下的mappedStatements属性

XMLMapperBuilder用于解析Mapper文件,包括namespace、parameterMap、resultMap以及各种SQL(insert/uppdate/delete/select)

解析过程中主要是parse()方法,

解析Mapper文件后会存储在MapperBuilderAssistant以及它父类BaseBuilder的实例下,主要是Configuration类

自言自语

我这算是自己的阅读笔记哈,无聊读着看看。

算是我第一次读源码的记录。

大家一起加油哦。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-07-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Mybatis 源码探究 (3)创建 SqlSessionFactory对象
    • 一、new SqlSessionFactoryBuilder().build(inputStream) 方法
      • 二、SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) 方法
        • 1、 new XMLConfigBuilder(inputStream, environment, properties);
      • 三、build(parser.parse()) 方法
        • 进入到parseConfiguration(parser.evalNode("/configuration"))方法
      • 四、return build(parser.parse()); 回到我们的入口处
        • 课间休息
          • 五、SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            • 六、MyUserMapper mapper = sqlSession.getMapper(MyUserMapper.class);
              • 七、XPathParser
                • 八、XMLMapperEntityResolver
                  • 九、XMLMapperBuilder类
                    • 自言自语
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档