Mybatis中,SQLSessionFactoryBuilder使用build方法时做了哪些事?

5 篇文章 0 订阅
5 篇文章 0 订阅

目录

探究Configuration

探究parse方法

启动断点调试

过程

总结


        当我们上手mybatis时,对于mybatis如何读取xml配置文件,获取SQLSessionFactory的底层源码还是处于一个比较模糊的状态,作者本人也是比较懵,所以本文尝试一下读一下SQLSessionFactoryBuilder的build方法源码,探索一下mybatis如何初始化数据连接以及读取配置文件的。

        首先我们拿出一段正常mybatis执行sql语句的基本流程

@Test
    public void test1() throws IOException {
        //定义mybatis核心配置文件在classes下的路径
        String mybatisPath = "mybatisConfig.xml";
        //根据路径获取字节输入流对象
        InputStream is = Resources.getResourceAsStream(mybatisPath);
        //创建SqlSessionFactoryBuilder对象
        SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
        //通过SqlSessionFactoryBuilder对象的build方法创建SqlSessionFactory对象
        SqlSessionFactory factory = sqlSessionFactoryBuilder.build(is);
        //通过工厂类的openSession方法获取sql执行对象
        SqlSession sqlSession = factory.openSession();
        //sql执行对象的selectOne方法
        //获取第一个参数,即执行id:由namespace + . + SQL语句标签id
        String id = "com.ling.mybatis.dao.UserDao" + "." + "findUserById";
        User user = sqlSession.selectOne(id);
        System.out.println(user);
        //关闭sqlSession
        sqlSession.close();
    }

        我们在使用build方法获取SQLSessionFactory对象时,方法传入了字节输入流,那么此时传入的字节输入流一定有着他的意义。我们向上看,字节输入流是我们读取好的mybatis中心配置文件,里面配置了与数据库连接有关的driver,url,username,password等信息,同时还配置了各个dao层配置sql的xml配置文件地址,那么这时我们心理有数了:这步实际上完成了数据文件的读取操作。

        按住Ctrl点击build方法进入源码。

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

        首先光标默认停靠的位置就是上面的方法,但是此方法明显调用了本类重载的build方法,我们根据返回值类型SqlSessionFactory,以及方法参数,上下寻找,最终我们找到了方法内调用的真正执行的方法,如下:

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

        我们尝试着分析一下源码,该方法目的十分明确,第一步定义SQLSessionFactory的变量引用,然后将配置文件的字节流作为构造器参数,创建了一个XMLConfiguration的对象,对象名字仿佛就在疯狂的暗示:“看我看我!我就是负责处理XML配置文件的对象!”。最后调用了parse(英文是解析的意思)方法,该方法返回值:Configuration。调用完该方法后就给引用赋上了值。那么我们可以断定,该方法就是将字节输入流中包含的信息读取出的方法。

探究Configuration

        很明显,将一个InputStream传入对象构造器,然后调用对象parse解析方法得到一个Configuration对象,就是把字节码信息转化成了对象的信息。我们此时可以大胆的猜测,该对象处理了xml配置文件,利用对象的属性存储了配置文件信息,并且使用parse方法进行解析。我们先看看这个对象有什么属性。

        他有四个成员变量,environment是环境,字符串类型,那么它会不会存储了一些信息呢?这个我也不明白,所以只能留个悬念了以后遇到了再解决了。

public class XMLConfigBuilder extends BaseBuilder {
    private boolean parsed;
    private final XPathParser parser;
    private String environment;
    private final ReflectorFactory localReflectorFactory;
}

        看不懂,哈哈哈哈,不要慌,我们还是看看他在构造方法中到底做了什么事。

        下面是build方法中创建对象时调用的构造器,很明显他使用了其他构造器。参数我们都是一路跟过来的,所以只有InputStream不是null,其他两个是null,所以我们只需要关注InputStream即可,该构造器将InputStream传入了XPathParser对象中。具体做了什么我们先不看。

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

        继续ctrl跟进,查看这个构造器里卖的是什么药。

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        this.localReflectorFactory = new DefaultReflectorFactory();
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }

        原来如此,他首先继承了父类Configuration,这个对象顾名思义,就是配置对象,那么里面肯定用于存储一系列配置信息。跟进Configuration对象,看看有没有我们想要的东西(我们想要的东西也就是能够存储读取出来的配置文件信息的容器

        点进去give me a jump,吓我一跳,但是我的嘴角微微上扬,因为我看到了一个很敏感的词Environment,也就是第一个属性,这叫环境,也就是我们在mybatis中心配置文件中配置时用的那个标签名,心中的期待一下子提高了不少,Ctrl进入Environment看看里面什么样。

public class Configuration {
    protected Environment environment;
    protected boolean safeRowBoundsEnabled;
    protected boolean safeResultHandlerEnabled;
    protected boolean mapUnderscoreToCamelCase;
    protected boolean aggressiveLazyLoading;
    protected boolean multipleResultSetsEnabled;
    protected boolean useGeneratedKeys;
    protected boolean useColumnLabel;
    protected boolean cacheEnabled;
    protected boolean callSettersOnNulls;
    protected boolean useActualParamName;
    protected boolean returnInstanceForEmptyRow;
    protected String logPrefix;
    protected Class<? extends Log> logImpl;
    protected Class<? extends VFS> vfsImpl;
    protected LocalCacheScope localCacheScope;
    protected JdbcType jdbcTypeForNull;
    protected Set<String> lazyLoadTriggerMethods;
    protected Integer defaultStatementTimeout;
    protected Integer defaultFetchSize;
    protected ResultSetType defaultResultSetType;
    protected ExecutorType defaultExecutorType;
    protected AutoMappingBehavior autoMappingBehavior;
    protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior;
    protected Properties variables;
    protected ReflectorFactory reflectorFactory;
    protected ObjectFactory objectFactory;
    protected ObjectWrapperFactory objectWrapperFactory;
    protected boolean lazyLoadingEnabled;
    protected ProxyFactory proxyFactory;
    protected String databaseId;
    protected Class<?> configurationFactory;
    protected final MapperRegistry mapperRegistry;
    protected final InterceptorChain interceptorChain;
    protected final TypeHandlerRegistry typeHandlerRegistry;
    protected final TypeAliasRegistry typeAliasRegistry;
    protected final LanguageDriverRegistry languageRegistry;
    protected final Map<String, MappedStatement> mappedStatements;
    protected final Map<String, Cache> caches;
    protected final Map<String, ResultMap> resultMaps;
    protected final Map<String, ParameterMap> parameterMaps;
    protected final Map<String, KeyGenerator> keyGenerators;
    protected final Set<String> loadedResources;
    protected final Map<String, XNode> sqlFragments;
    protected final Collection<XMLStatementBuilder> incompleteStatements;
    protected final Collection<CacheRefResolver> incompleteCacheRefs;
    protected final Collection<ResultMapResolver> incompleteResultMaps;
    protected final Collection<MethodResolver> incompleteMethods;
    protected final Map<String, String> cacheRefMap;
}

        啊,我的天,这是啥:id,dataSource,太熟悉了,这就是数据库初始化的时用到了DataSource,在学习Spring的时候DataSource估计诸位没少配过吧哈哈哈。

public final class Environment {
    private final String id;
    private final TransactionFactory transactionFactory;
    private final DataSource dataSource;
}

        终于经过一层一层的向内挖,我们终于找到了存储信息的容器:来自XMLConfiguration父类的Configuration的Environment属性。阶段目的达到了,我们回到XMLConfiguration对象调用parse作为build方法参数的那一段。

探究parse方法

        鼠标放到parse方法上面,我们发现该方法返回了一个Configuration类型的参数,ohhhhhh,我们此时就可以断定parse方法将字节输入流中的内容已经存放到Configuration对象中,解析读取阶段已经完成了。

         此时我们就可以放心的进入parse源码,看看程序到底是怎么读取xml配置文件的。

        一个简单的判断语句,如果成员变量是true的话抛异常,看来重点就在else了。他调用了parseConfiguration方法,并且直接返回了存储配置文件信息的configuration属性,那么该方法调用时就将字节流信息存储到configuration属性中,该方法就是核心方法,我看一下参数:evalNode方法,虽然我不知道是干嘛的,但是看见Node,咦,这不是结点吗!!?原来操作了文件中的节点,那究竟是什么结点呢?看后面的字符串:configuration结点。啊,这不是mybatis中心配置文件的根标签吗,原来在这开始解析文件的,我们看看parseConfiguration方法怎么解析的。

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

        向里跟进,原来是一个控制中心方法,通过各种各样的处理方法最终完成了解析操作,我们可以看出该方法中调用了数个本类对象的方法,而方法的参数中字符串内容是按照顺序排布的mybatis中心配置文件的标签!解析xml配置文件的方法有很多,我们在javaweb学习阶段了解了dom4j解析,这个我们不需要做太多了解。我们知道了是XMLConfiguration中的parseConfiguration方法完成了解析。

    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.loadCustomLogImpl(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

        接下来就是看一下究竟是不是该方法将解析的结果存储到XMLConfiguration的configuration属性中的。

启动断点调试

        在使用parseConfiguration方法前configuration的Environment属性是空的

         执行完parseConfiguration方法后,environment属性就有值

         此时的DataSource也有了信息

        既然拿到了信息(存储在Configuration对象中,也就是parse方法的返回值),那么build方法就可以根据信息建立连接了。

        我们在此回到调用parse方法那一块,进入build方法源码,看看解析完xml配置文件后,build方法拿到了信息做了什么。

        得到以下代码。返回了一个SQLSessionFactory的实现类DefaultSQLSessionFactory,并且把配置文件相关信息交给了构造器。不行了,好奇心驱使我看一看这个构造器有没有对无辜的配置文件信息做些什么。

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

        啊这,看来使我们多疑了,该构造器只是简单的给configuration属性完成了初始化过程。人家是清白的。

public class DefaultSqlSessionFactory implements SqlSessionFactory {
    private final Configuration configuration;

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

        到这里读取build文件源码过程就结束了,下面我们来做个总结吧~

 过程

        首先我们进入了build方法,发现build方法中创建了XMLConfiguration对象用于解析配置文件的操作。

        然后我们了解了XMLConfiguration如果解析了文件,那么解析后的结果究竟放在了哪里,经过调查我们发现,XMLConfiguration继承了BaseBuilder的configuration成员变量,而configuration对象的environment属性就是用于存储中心配置文件有关的信息。

        接着我们回到了刚进入build时的源码,查看parse,发现其返回值正是Configuration,跟进parse方法,发现其调用了parseConfiguration方法,参数为XNode root,很明显是根节点的意思,该方法就是用于读取mybatis中心配置文件的方法。该方法内调用了多个本类的方法,没有多余的操作,并且按照中心配置文件中的标签顺序依次调用了对应的解析方法,并将结果存储在成员变量configuration中。此时完成了读取解析字节输入流的操作,并将字节码信息转化为对象属性。

        最后我们查看了build方法,发现他只是new了一个SQLSessionFactory的实现类DefaultSQLSessionFactory,并且将configuration属性赋值到DefaultSQLSessionFactory的configuration属性上。

总结

SQLSessionFactoryBuilder功能:

        1. 解析字节输入流对应mybatis中心配置文件

                核心方法:XMLConfiguration的parse方法

                方法作用:把字节输入流中的信息通过parseConfiguration方法解析到XMLConfiguration对象的configuration属性中,并且将该属性作为返回值返回。

        2.创建SQLSessionFactory的实现类对象DefaultSQLSessionFactory

                注意点:DefaultSQLSessionFactory中存储了配置文件信息。

  • 13
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Aristocrat l

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值