专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

mybatis源码解析

基于mybatis+spring

在我们写增删改查的时候,一般service依赖dao 写法是:

public class AService{
    @Autowire
    private BDao
    //调用Bdo的方法
}

一般BDo是一个接口,这里就有两个问题 1.接口是怎么被实例化,注入进去的 2.接口是怎么调用方法的,这个接口的实现类是什么,有什么内容 走进spring源码

从一个注解开始 @MapperScan(basePackages = ‘com.xx.dao’)

@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
.........
}

可以看到这个@MapperScan 注解其实是先当与实现了 @Improt 需要先了解下 @Import 的作用 详情见 tech.souyunku.com5f001f… 如果了解了@Import之后就知道,@MapperScan 这个注解的关键就是 MapperScannerRegistrar

MapperScannerRegistrar

MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar#registerBeanDefinitions

  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
  //拿到@MapperScan注解,后面会通过这个注解拿到需要扫描的包
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        //注册 MapperScannerConfigurer
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }

  void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
  //注册 MapperScannerConfigurer
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//.... BeanDefinitionBuilder 对 属性的设置,忽略,不然篇幅太长了
//.... 主要是设置比如说 msqlSessionFactoryBeanName,basePackages 等属性
//注册MapperScannerConfigurer
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  }

MapperScannerConfigurer

MapperScannerConfigurer 是什么,为什么要先注册这个类

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    }

MapperScannerConfigurer 继承了 BeanDefinitionRegistryPostProcessor# 是一种bean工厂后置处理器,在初始化普通bean之前就会执行 BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry 所以往spring注册MapperScannerConfigurer这个类之后,spring还会从beandefinition的集合中获取未加载的BeanDefinitionRegistryPostProcessor然后进行实例化执行postProcessBeanDefinitionRegistry方法

MapperScannerConfigurer#postProcessBeanDefinitionRegistry

这个后置处理器做了什么?

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    //.....
    //ClassPathMapperScanner 顾名思义就是类路径扫描器,后面分析
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
   //..去掉扫描器各种属性填充
   //开始进入扫描,basePackage这个就是我们在@MapperScan的包名
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.,basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

ClassPathMapperScanner

可以看到ClassPathMapperScanner继承了spring的ClassPathBeanDefinitionScanner

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
}

//下面是spring的ClassPathBeanDefinitionScanner的方法

public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
        //开始扫描包 ,调用的是mybatis的 ClassPathMapperScanner
        doScan(basePackages);
        //........
        //返回注册的bean数量
        return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    }

ClassPathMapperScanner的方法

  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
  //调用spring方法扫描出包,解析成 BeanDefinitionHolder
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    if (beanDefinitions.isEmpty()) {
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
          + "' package. Please check your configuration.");
    } else {
    //重点,开始对这些beanDefinition进行特殊处理
      processBeanDefinitions(beanDefinitions);
    }
    return beanDefinitions;
  }

  private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      //获取到 bean类名称
      String beanClassName = definition.getBeanClassName();

    //将bean类名称放到beanDefinition的构造方法里面,后面解析这个作用
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      //设置beanDefinition的class 其实就是 MapperFactoryBean 一种FactoryBean
      definition.setBeanClass(this.mapperFactoryBeanClass);
//.... 设置各种属性值
      if (!explicitFactoryUsed) {
//这蛮有意思的,设置自动装配的类型为 类型注入,假如有 setxx(xx xx)
//spring会去容器中找xx类,然后注入进去
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      //设置懒加载
      definition.setLazyInit(lazyInitialization);
    }
  }

所以到这里,其实可以看出来,扫描包之后往spring容器注入的是Class为MapperFactoryBean 所以接着看 MapperFactoryBean

MapperFactoryBean

是什么: 一种FactoryBean

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
  private Class<T> mapperInterface;
//刚才有看到往这个构造方法塞一个bean类名称,就是塞到这里的,这里就是我们平常写的xxDao
//把这个类名称放到这里干嘛呢,做动态代理,具体往下走
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
}

先看下 MapperFactoryBean<T> extends SqlSessionDaoSupport 这个有什么用呢 SqlSessionDaoSupport继承DaoSupportDaoSupport 又实现 InitializingBean 如果了解InitializingBean这个类就会知道,如果实现了InitializingBean就会在bean初始化的过程调用afterPropertiesSet方法。看下 DaoSupportafterPropertiesSet做了什么

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
        // Let abstract subclasses check their configuration.
        //检查dao配置,看下具体实现
        checkDaoConfig();
        //..........
    }

MapperFactoryBean#checkDaoConfig 中有实现

protected void checkDaoConfig() {
//....
//获取mybatis 配置类
    Configuration configuration = getSqlSession().getConfiguration();
//重点来了 往配置类添加mapper,这个mapper是最开始往BeanDefinition传来的
//真实的xxDao,看下这个做了什么事情
        configuration.addMapper(this.mapperInterface);
  }

  public <T> void addMapper(Class<T> type) {
  //将xxdao类放到  MapperProxyFactory 然后放到 knownMappers 这个缓存中
  knownMappers.put(type, new MapperProxyFactory<>(type));
  //往mapper注册器添加mapper,接着往下
    mapperRegistry.addMapper(type);
  }

  public <T> void addMapper(Class<T> type) {
   //去掉不影响主流程的.........
   //解析xxxDao,主要是解析类
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
    //主要逻辑,往下    
        parser.parse();
    //..........
  }


public void parse() { //........去掉无关主流程代码 //获取xxxDao的所有方法 Method[] methods = type.getMethods(); for (Method method : methods) { try { if (!method.isBridge()) { //重要,往下走 parseStatement(method); } } } parsePendingMethods(); }

void parseStatement(Method method) { //.....主要是根据方法名称去获取sql,然后将这些信息封装成 //MappedStatement 然后放到 configuration 中 assistant.addMappedStatement(...); } }
//最后是将 放到 configuration中的`mappedStatements`这个map
//后面如果执行方法的时候,直接从这个map就可以获得相应的sql等参数 
    configuration.addMappedStatement(statement);

所以 checkDaoConfig 这个方法一层套一层,最后是想把我们写的XXDao解析出来,以方法的维度,包含着方法名称,sql,出入参类型等等,然后放到configurationmappedStatements 接下来看更重要的方法。

FactoryBean最重要的就是getObject方法 往下看MapperFactoryBean

  public T getObject() throws Exception {
  //获取sqlsession,在spring环境中是 sqlSessionTemplate
  //往下看 sqlSessionTemplate 的getMapper方法
    return getSqlSession().getMapper(this.mapperInterface);
  }

  public <T> T getMapper(Class<T> type) {
  //调用Configuration#getMapper,又开始一层套一层,往下看
    return getConfiguration().getMapper(type, this);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //又是这个 mapperRegistry,往下走,注意这里的 sqlSession 还是 sqlSessionTemplate
    return mapperRegistry.getMapper(type, sqlSession);
  }

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  //刚才在addMapper的时候就放进去 MapperProxyFactory 现在拿出来
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
//.....
    try {
    //可以看出来是返回代理类了,具体往下走
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public T newInstance(SqlSession sqlSession) {
  // MapperProxy 真正的代理类
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    //使用 jdk 动态代理,就不进去看了
    return newInstance(mapperProxy);
  }

到这里为止算是将xxxDao以动态代理的形式交给spring容器管理

MapperProxy#invoke 做了什么

MapperProxy#invoke 是真正执行xxDao的方法,对xxDao方法进行代理

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//...忽略对Object方法的执行
//从缓存中拿到  MapperMethod
//一般是new 一个 MapperMethod。MapperMethod里面的参数基本都是从
//Configuration 的 mappedStatements 获取,也就是addMappers的时候放进去
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //往下走看,这个方法做了什么
    return mapperMethod.execute(sqlSession, args);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
        //...忽略各种判断是update,还是select,还是delete等,直接看seleOne
        //因为流程都差不多
        //往下走,这时候的 sqlSession 还是 SqlSessionTemplate
          result = sqlSession.selectOne(command.getName(), param);
     //......
    return result;
  }

  public <T> T selectOne(String statement, Object parameter) {
  //调用 sqlSessionProxy 的方法,一看就知道这个是sqlSession的代理类
  //在SqlSessionTemplate 实例化方法进行代理的
  //往下看代理类 SqlSessionInterceptor 的invoke
    return this.sqlSessionProxy.selectOne(statement, parameter);
  }

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //获取到真正干活的 sqlSession: DefaultSqlSession
      SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
      try {
      //这个方法要注意不是我们xxxDao的方法,是上面那个selectOne方法
      //执行对象是SqlSession,所以要去看 DefaultSqlSession#SelectOne
        Object result = method.invoke(sqlSession, args);
//....
        return result;
      } catch (Throwable t) {
      //.....
      } finally {
        if (sqlSession != null) {
        //重要,所以每次mapper执行完方法,都会关闭session,这就导致了
        //spring+mybatis mybatis的一级缓存失效
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }

  public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    //调用 selecList往下看
    List<T> list = this.selectList(statement, parameter);
//........
  }

  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
    //从 configuration 获取 MappedStatement(包含着sql,出参入参,等等) 
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用执行器,往下看
      return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  //相当于获取sql
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //往下看
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
   //....
   //往下看,顾名思义,从数据库获取数据
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    //.....
    return list;
  }

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
//......
//往下走,模板方法,调用子类方法,SimpleExecutor
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
//....
    return list;
  }

快完了,做个标志 org.apache.ibatis.executor.SimpleExecutor#doQuery 下次接着写

  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

未完待续………

文章永久链接:https://tech.souyunku.com/26699

未经允许不得转载:搜云库技术团队 » mybatis源码解析

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们