跟着源码学mybatis(一):启动流程解读

      首先画个重点,不了解的自己去去百度下:

          1. 两个重要的接口:SqlSession接口SqlSessionFactory接口

          2. 两个重要的类:DefaultSqlSession类DefaultSqlSessionFactory类

          3. mybatis能够实现通过mapper接口就可以查询的原因是:动态代理!

     好了,开始正文。首先要讨论的当然是启动入口。Mybatis集成到系统中后,有两种获取入口方式。第一种是手动获取:如下:

           Reader reader = Resources.getResourceAsReader("Mybatis配置文件路径");

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

           SqlSession sqlSession = sqlSessionFactory.openSession();

      也可以这样获取:

                 Reader reader = Resources.getResourceAsReader("Mybatis配置文件路径");

           SqlSession sqlSession = SqlSessionManager.newInstance(reader).openSession();

      然后就是通过sqlSession.getMapper(“接口名称”)获取接口实例交互数据库了。

      第二种是通过spring自动注入的方式。在使用@Autowired注解自动注入mapper接口实例时自动调用。

      启动入口什么的,是mybatis暴露给开发人员的使用方法,按理来说是挺简单的,就不废话了。接下来分析一下从启动入口出发,mybatis都干了些什么。如果是通过手动获取启动入口,直接Ctrl键进入SqlSessionFactoryBuilder的build方法,会发现它创建了一个XMLConfigBuilder对象,然后调用了它的parse()方法,并把parse()方法的返回结果作为参数给了SqlSessionFactoryBuilder的另一个build方法。

      我擦嘞,什么玩意?相信你一定会二脸懵逼的。初次看到这玩意,我都惊呆了。好吧,耐着性子看下去。我们先说结果,还记得手动获取入口时得到的Reader文件流吗?对,就是这玩意,实例XMLConfigBuilder时,将reader作为参数传进去,然后就给我们返回了一个Configuration对象。上文出现的SqlSessionFactoryBuilder的第二个build方法根据这个Configuration对象给我们返回了一个SqlSessionFactory对象。

     嗯......还是不懂。好吧,XMLConfigBuilder是一个xml配置文件解析工具,将我们的配置文件解析为Configuration对象。SqlSessionFactoryBuilder根据这个Configuration对象为我们返回了SqlSessionFactory工厂。到这里,mybatis的大门已经在眼前了。可以进去看看了。

    XMLConfigBuilder类作为一个工具类,封装了大量解析mybatis配置文件的方法。它会将mybatis-config.property文件中的标签键值映射为java类型对象。最后,给我们返回了Configuration对象。

   而SqlSessionFactoryBuilder的第二个build方法中,创建了一个DefaultSqlSessionFactory对象,就像上文提到的,Configuration对象作为参数传入了DefaultSqlSessionFactory的构造方法中。于是Configuration对象就变成了DefaultSqlSessionFactory对象的属性值。DefaultSqlSessionFactory是SqlSessionFactory接口的实现类。Ctrl键进入SqlSessionFactory接口,你会看到一堆openSession方法,这就是文章开头开到的openSession方法了

   到这里,这一切是简单不过的,对吧?只是简单的属性方法调用。接下来该划重点了。通过DefaultSqlSessionFactory类直接获取入口的方式,会让我们的程序看着很乱。比如,每次都需要openSession()。最重要的是,它不是线程安全的。what?线程不安全?那mybatis还能用吗?现在还有单线程可以完成的程序吗?

   庆幸的是,我们有了另一种思路,那就是让它变成线程安全的。SqlSessionManager就是mybatis官方为我们实现的一种方案。那就是利用ThreadLocal为每个线程建立单独的SqlSession副本。并且,用代理模式和反射机制来控制每个SqlSession连接与数据库的访问。这块内容看源码就可以了。

   然鹅,实际开发中,我们肯定不愿意在每次与数据库交互前都先openSession()下,这样的方式会让开发变得很繁重。在计算机中,当同一事件大量发生时,就该为之做一个优化了。没错,spring的控制反转机制为mybatis提供了这样的可能。

   mybatis-spring包中有一个SqlSessionFactoryBean类,是mybatis为spring提供的。spring自身有一种机制是将spring的接口暴露给第三方使用,然后回收它们实现自动注入。SqlSessionFactoryBean继承了spring的InitializingBean接口,所以mybatis会在spring初始化时完成自身的初始化。SqlSessionFactoryBean也实现了spring的FactoryBean接口,该接口会在实现类完成属性设置后执行自身的afterPropertiesSet方法。如此,便可以将mybatis的初始化放在这里完成。

   好了,我们基本上可以推测出mybatis-spring包的作用了。就是将mybatis自身的配置启动步骤打包到一个spring的bean中,然后依赖于spring强大的回收机制启动mybatis自身。在SqlSessionFactoryBean的afterPropertiesSet方法中,一路追下去,进入了buildSqlSessionFactory方法,在它的的最后,又可以看到一个熟悉的东西:

   return this.sqlSessionFactoryBuilder.build(configuration);

    有没有很熟悉呢?没错,buildSqlSessionFactory方法就是那个帮我们自动完成SqlSessionFactory获取的方法了!它将我们配置到spring的application.xml文件中的mybatis-config.xml相关的配置经XmlConfigBuilder读取后转化为Configuration对象,然后创建SQLSessionFactory工厂。到这里,mybatis随spring启动和完成配置映射的步骤都完成了!

   那么,spring通过@Autowired注解自动注入mapper接口的实例或者我们手动getBean时,又做了些什么呢?mybatis-spring包下有一个MapperFactoryBean类,它继承了SqlSessionDaoSupport类,也直接实现FactoryBean接口.MapperFactoryBean有一个getObject方法,此方法调用SqlSessionDaoSupport中父类方法得到了SqlSession对象,并从SqlSession中根据接口名称取出了对应的mapper实现类。也就是说,spring的getBean方法注入mapper接口时,本质上调用的是MapperFactoryBean类的getObject方法。getObject又从SqlSession中取出了该接口的实现类。

   而SqlSessionDaoSupport类中的SqlSession从哪来呢?这就要说到SqlSessionTemplate类了。在mybatis官方包中,共有三类实现了SqlSession接口,分别是DefaultSqlSession类、SqlSessionManager类和SqlSessionTemplate。在文章开头我们看到了,手动获取SqlSession也有两种方式,一种是通过DefaultSqlSessionFactory直接获取,另一种就是将SqlSessionFactory交给SqlSessionManager管理,它会生成SqlSession的代理对象来达到线程安全的目的。但是呢,SqlSessionManager已经被官方废弃了。目前mybatis和spring整合后,都是通过SqlSessionTemplate类来管理SqlSession了。SqlSessionTemplate中的SqlSessionFactory参数则由spring来自动注入,也就是application.xml中声明的org.mybatis.spring.SqlSessionFactoryBean.

   是不是有点懵?也就是说,mybatis官方提供了两种SqlSession线程安全的解决方案,SqlSessionManager和SqlSessionTemplate。SqlSessionManager出于设计上的考虑被废弃了。SqlSessionTemplate是SqlSessionManager思想的一种更优雅的实现。同样的,也是基于动态代理和反射机制,将SqlSession的获取拦截到SqlSessionTemplate中,然后统一进行管理。我们可以做很多的设想,怎么更优雅的把一块大面包分给一群饥渴的人。当人数不确定时,为了分配均匀,我们必须把这块面包管理起来,然后根据人数分配。用户请求就是饥渴的人,数据库连接就是大面包,合理的调度会让服务器的压力更小些。

    回到我们最开始提到的两个接口和两个实现类。SqlSession接口和实现类是数据库连接代表商品,SqlSessionFactory和实现类是创建连接SqlSession的工厂代表工厂。SqlSessionManager和SqlSessionTemplate是管理者代表代理商。当商品供不应求时,代理商的存在会让一切变得尽然有序。

   到这里,一条主线基本被理了出来:mybatis实现了sring的接口,将自己暴露给spring。spring将mybatis当做自身的元素完成mybatis初始化,并将SqlSessionFactory交给SqlSessionTemplate来生成代理。之后在spring中引入mybatis中的bean元素(mapper实现类)时,其实是从SqlSessionTemplate中得到代理SqlSession来取出其中的mapper接口实现类。

   有没有觉得很奇怪?那SqlSession中的mapper接口实现类又是哪里来的?我们可没有看到过它们的实现类哦!其实,在我们将配置文件流交给SqlSessionFactoryBuilder().build()处理时,它调用XmlConfigBuilder的parse方法解析时,返回的Configuration对象中已经将我们的mapper.xml文件也解析完成了。并且呢,存放到了MapperRegistery类中。MapperRegistery就是个大容器,看名字也知道是注册mapper相关内容的。Ctrl键进入MapperRegistery中,会发现它就是个大Map。MapperRegistery中存放的是mapper命名空间转化的类名或mapper接口作为key,mapper代理类作为value的键值对。

  所以,SqlSession中的getMapper方法最后会调用configuration中的getMapper方法,configuration会调用MapperRegistery的getMapper方法。然后,我们就得到了MapperRegistery中保存的mapper接口的实现类(代理)。

   如此,一张清晰的mybatis地图便绘制完成了。

猜你喜欢

转载自blog.csdn.net/qq_28802119/article/details/80914928