Mybatis源码浅析

Mybatis源码解析

解析开始准备

我们从一个测试类开始,具体的分析mybatis的执行流程
public class MybatisTest {
    public static void main(String[] args) throws IOException {
        //1. 读取mybatis-config.xml 文件,包含对与别名,数据库等配置定义
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");

        //2. 解析xml文件构建SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        //3. 通过SqlSessionFactory,获取SqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        //4. 获取Mapper类
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

        //5. 获取mapper 接口对象的方法操作数据库
        List<User> user = mapper.selectCount();
        System.out.println("查询用户数据量为:" + user.size());
    }
}

通过SqlSessionFactoryBuilder对xml配置文件进行解析,构建SqlSessionFactory

  • 如果是在整合spring之后,在通过MybatisPlusAutoConfiguration对mybatis进行自动配置时,类上添加了@ConditionalOnClass注解,表明在引入mybatis时需要构建SqlSessionFactory和SqlSessionFactoryBean在这里插入图片描述在这里插入图片描述
    而SqlSessionFactoryBean是一个InitializingBean,在Bean初始化之前会调用他的afterPropertiesSet方法,这里会调用buildSqlSessionFactory构建SqlSessionFactory

解析构建SqlSessionFactory

原生mybatis和spring整合mybatis的两种方法,最终都是通过XMLConfigBuilder对配置文件进行节点的解析,对配置文件中的每一个节点进行解析存入Configuration对象中(以下解析通过原生mybatis来说明)

  • 先通过SqlSessionFactoryBuilder.builder()----》XMLConfigBuilder.parse() ----> this.parseConfiguration(this.parser.evalNode(“/configuration”))
    • plugs解析到configuration的interceptorChain拦截器链中,所以mybatis的插件实际上就是拦截器(代理+责任链)
    • 解析别名
    • 设置参数类型处理器TypeHandler,默认会注册多个。
    • 解析二级缓存,详见二级缓存原理,使用了装饰器模式和责任链模式
    • 解析Mappers节点
      • 如果是通过mapper package="“的方式或者mapper class=”"的方式配置的,那么就会先将当前包路径下的所有Mapper类,构建成一个MapperProxyFactory代理类,添加到一个map中,我们后续通过sqlsession去拿对应的mapper,去执行指定方法的时候,实际上是拿的这个代理类。之后通过构建一个MapperAnnotationBuilder,加载相同路径下的xml文件【所以刚学mybatis的时候,说xml需要同mapper是相同的路径和名称】,并且通过XMLMapperBuilder解析对应路径下的xml文件中的节点。
      • 如果是通过mapper resource="“或者mapper url=”"的方式配置的,那么就通过XMLMapperBuilder解析对应路径下的xml文件中的节点
        • 当遇到select|delete|update|update节点的时候,通过XMLStatementBuilder,将各类标签解析成一个个SQLNode,封装成sqlSource,封装成mappedStatement,添加到configuration中专门存放整个statement的map中
  • 解析完成构建的configuration对象,设置到SqlsessionFactory

与Spring整合之后,SqlSessionFactory的构建主要通过自动配置类。比如我们解析Mapper时,添加@Mapper注解或者@MapperScan注解来批量指明具体的Mapper类,在Spring启动的时候会解析这些类【具体怎么解析的后续会出文章解读】,在生成beanDefinition的时候,会将其ClassType设置为MapperFactoryBean,这个类实际上是一个FactoryBean,属于一种特殊的bean,这种类型的bean,通过getObject()方法来创建它所包装的bean。并且MapperFactoryBean还继承了DaoSupport,这是实现的InitializingBean的类,所以MapperFactoryBean在bean初始化(初始化方法前)的时候,会调用其afterPropertiesSet方法。

  • afterPropertiesSet方法会给当前的Mapper类生成一个增强的代理工厂类MapperProxyFactory,并且存放到一个map中。当我们真正去获得实例的时候,会从这个map中获取对应的代理工厂类,生成代理对象MapperProxy,放入单例池中。同时会构建一个MapperAnnotationBuilder,使用XMLMapperBuilder解析同路径下的xml文件,解析各个节点,并且添加到configuration中。
  • 当我们通过XXXMapper.quey()进行方法调用的时候,实际上获取的是单例池中的代理类
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

通过Mapper方法查询数据 (原生Mybatis方式的源码)

  • 构建完SqlsessionFactory之后,我们通过SqlsessionFactory.opensession()获取到一个sqlsession,这里会按照具体情况委托给对应的执行器处理,如果我们开启了二级缓存,那么将会委托给CachingExecutor,如果未指定那么默认是SIMPLE,继承自BaseExecutor。当然如果我们为执行器添加了插件,那么将会在这里执行【插件执行原理详见下一篇】。
  • sqlsession.getMapper(),我们去获取对应的Mapper类,实际上是从configuration的map中获取对应的代理类MapperProxy
  • 执行具体的方法的时候,实际上就会执行代理类的invoke方法,判断当前的方法是增删查改中的哪个类型再执行(以select举例)。
    • 会从map中获取到对应方法所代表的MappedStatement,然后用指定的执行器执行。
    • 解析sql语句,并且将其参数进行预处理,将#和$的符号进行替换
    • 对sql语句创建缓存
    • 进行具体方法的调用,如果开启了二级缓存,从二级缓存中获取一下,没有的话,就会调用具体执行器的方法去执行(三个执行器的父类就是baseExecutor,这里面实现了一级缓存),从一级缓存中获取一下,没有的话,就会创建一个StatementHandler对象,这个对象中同时会封装ParameterHandlerResultSetHandler对象。可以执行对其增强的插件
    • 数据库查询获得结果集,将其放入一级缓存中,然后放入二级缓存。将数据返回
      在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
      在这里插入图片描述

通过Mapper方法查询数据(Spring整合Mybatis之后的源码解析)

在Spring整合Mybtis之后,通过Spring与Mybatis的整合快速入门,我们可以看出,我们并不需要通过SqlsessionFactory.opensession(),然后通过session获取具体的Mapper实例,我们只需要注入Mapper就可以直接调用方法查询。

为什么不需要通过SqlsessionFactory.opensession()就能获取到sqlsession呢?
  • 当注入Mapper使用具体方法查询数据库时,实际上我们拿到的是一个Mapper实例实际上是一个MapperFactoryBean的代理类,但是它构造出一个mapper需要借助SqlSession,这里使用的SqlSession其实是SqlSessionTemplate。我们指导MybatisAutoConfiguration会让容器中注入一个SqlSessionTemplate,那么spring是如何把这个SqlSessionTemplate设置到mapperFactoryBean的属性上的昵?
    • 实际上是在Bean进行属性赋值的时候,MapperFactoryBean继承了SqlSessionDaoSupport,需要一个sqlSessionTemplate属性,所以在进行属性赋值的时候会从拿到之前注入的实例,通过反射调用set方法注入
  • 我们拿MapperFactoryBean实例后,最终时是会调用其getObject()方法,this.getSqlSession().getMapper(this.mapperInterface),最终获得一个MapperProxy的代理对象,当mapper被调用其接口中声明的方法的时候,会调用MapperProxy#invoke
  • 而其真正方法的调用又委托给了它的内部类PlainMethodInvoker的invoke方法,最后调用的是MapperMethod#execute,这个方法会根据方法调用的类型(增删改查)调用MapperProxy中的属性SqlSession(根据上面知道sqlSession实现类是SqlSessionTemplate)`对应的方法
  • (拿update举例)实际上调用的是sqlsessionTemplate.update()—》this.sqlSessionProxy.update()。这里的sqlSessionProxy是一个代理类new SqlSessionTemplate.SqlSessionInterceptor()。当获取的时候会执行其invoke方法。会根据SqlSessionUtils.getSqlSession拿到当前请求的sqlsession【具体怎么拿到的还有待分析】。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值