Mybatis-Spring源码分析(四) Mybatis的初始化

56 篇文章 3 订阅
7 篇文章 1 订阅

前言

当我们使用Mybatis调用Mapper接口中的方法的时候,可以确定Mybatis已经把SQL语句给拿到了,否则后续的步骤就无法进行了。那么Mybatis是在什么时候初始化Mapper并且拿到其中的内容的呢?本篇博客主要从源码上探究这个问题,本篇可能会涉及到【Mybatis-Spring源码分析(一) MapperScan】里面的部分知识点。更多Spring内容进入【Spring解读系列目录】

初始化流程

Mybatis实例化一个MapperFactoryBean的时候,Mybatis会首先执行一个初始化。初始化做的事情就是把Mapper接口里面的所有方法的注解拿出来,放到一个Map中,名字叫做Map<String, MappedStatement> mappedStatements。流程图大概就是下面的样子,这个有兴趣的同学可以跟着断点看下,笔者把断点的位置给大家放出来。
在这里插入图片描述

位置:org.apache.ibatis.session.Configuration#addMappedStatement
public void addMappedStatement(MappedStatement ms) {
  mappedStatements.put(ms.getId(), ms); //在这一行上断点
}

怎么做到的初始化

通过上面的流程可以看到,SQL语句的初始化是Mapper接口实例化以后立刻就被放到map中去了。那么问题就来了:一个类怎么样才能在初始化的时候做一些赋值或者拿到一些东西呢?之前笔者的帖子写过我们可以用@PostConstract或者实现BeanPostProcessor接口,这些都可以。但是Mybatis不是这么做的,它用的是另外一个方法afterPropertiesSet(),这个方法属于InitializingBean接口,用法示例如下。

@Service
public class CityService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("CityService.afterPropertiesSet()") ;
    }
}

InitializingBean

这个接口属于Spring框架,它的作用就是在Bean初始化的时候进行一些操作。从名字上看afterPropertiesSet,就是说在所有的属性和依赖都准备好以后再执行。

Mybatis初始化流程

之前的博客已经详细的说过,所有Mapper接口都会被转化为MapperfactoryBean。所以Mybatis初始化的流程是这样的。由于MapperfactoryBean继承自SqlSessionDaoSupport,而SqlSessionDaoSupport继承自DaoSupportDaoSupport又实现了InitializingBean
在这里插入图片描述
SqlSessionDaoSupportMybatis提供的类,在里面重写了checkDaoConfig()方法,由于SqlSessionDaoSupport是抽象类,因此这个方法只能由它的子类去实现。也就是说最终还是由MapperfactoryBean#checkDaoConfig()实现的。
那么这个逻辑是怎么走的呢?当MapperfactoryBean被实例化的时候,由于最终实现了InitializingBean接口,因此会先调用DaoSupport#afterPropertiesSet()方法:

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
   checkDaoConfig();
       //。。。。暂时无关,略
}

然后调用其本身的checkDaoConfig()方法,由于这是一个抽象方法:

protected abstract void checkDaoConfig() throws IllegalArgumentException;

因此又转给了它的子类SqlSessionDaoSupport#checkDaoConfig()去执行,但是SqlSessionDaoSupport本身就是抽象类:

public abstract class SqlSessionDaoSupport extends DaoSupport

所以就必须让MapperfactoryBean#checkDaoConfig()实现具体的逻辑:

protected void checkDaoConfig() {
  super.checkDaoConfig();
  notNull(this.mapperInterface, "Property 'mapperInterface' is required");
  Configuration configuration = getSqlSession().getConfiguration();
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
	//最终把Mapper接口添加到里面,并在这里面初始化Mapper接口
      configuration.addMapper(this.mapperInterface);
    } catch (Exception e) {
      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
      throw new IllegalArgumentException(e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}

继续进入Configuration#addMapper()里面:

public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

最终在MapperRegistry#addMapper()里面初始化这些类:

public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
	//put进去,并且给Mapper的type给上对应的value,也就是代理
      knownMappers.put(type, new MapperProxyFactory<>(type));
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

然后再用parser.parse();去解析这些注解,到此Mybatis的初始化流程结束。

总结

写了这么关于Mybatis的博客以后,终于可以总结一下了。Mybatis-Spring初始化分为在Spring初始化之前和Spring初始化之后。
Spring初始化之前:
通过分析源码可以得出@MapperScan主要做了三个工作:

  1. 扫描出所有的Mapper接口所对应的BeanDefinition
  2. Mapper接口转换为FactoryBean,或者说MapperFactoryBeanBeanDefinition
  3. BeanDefinition添加一个构造方法的值,并使用这个Class,在Spring示例化过程中根据这个Class返回相对应的代理对象。

Spring初始化之后:
Mybatis主要通过Spring的初始化方法扩展点来完成对Mapper接口信息的初始化,比如SQL语句的初始化等等。其实就是利用MapperFactoryBean实现了InitializingBean接口,然后使用AfterPropertiesSet()方法机制进行初始化。由于MapperFactoryBean其实就是一个Mapper,所以又可以理解为其就是一个Mapper信息的缓存,因为是被代理的并没有真正的代码。当所有的Mapper都被解析完毕以后,再缓存到一个Map<String, MappedStatement> mappedStatements中,共给后面调用时使用。

为什么Mybatis要使用这个方法去做这件事情呢?因为要等到代理Dao(Mapper)中所有的属性都初始化结束以后,也就是所有的依赖都构建完成以后,才开始解析这些注解。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值