您的当前位置:首页正文

【spring】spring集成Mybatis源码分析

2024-11-25 来源:个人技术集锦

spring集成Mybatis简单介绍

  • 本文mybatis以Mybatis-Spring 1.3.2为主!
  • 集成的核心思路:成为spring的Bean。
  • 集成的实现方式:基于spring的FactoryBean。

Mybatis-Spring 1.3.2集成的思路

  • 通过@MapperScan导入了MapperScannerRegistrar类。
  • MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法 。
  • 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper。
  • 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中默认是不会扫描接口的。
  • 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component。我们重写所有的接口不加@Component也可以被扫描到。
  • 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition。
  • 把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType(用来读取set方法)。
  • 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean。
  • 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean。
  • sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生。
  • MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
  • 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性 。
  • 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象 。
  • 到这里,有了Mybatis的代理对象,并且通过FactoryBean将其放入到spring的Bean中。

Mybatis-Spring 2.0.6集成的思路

  • 通过@MapperScan导入了MapperScannerRegistrar类。
  • MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法。
  • 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition。
  • 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法。
  • 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
  • 剩余逻辑和1.3.2版本一样。
// 可以通过@Bean的方式注入
@Bean 
public MapperScannerConfigurer mapperScannerConfigurer() {
    erScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    mapperScannerConfigurer.setBasePackage("com.zhangwei");
    return mapperScannerConfigurer; 
}

源码分析:@MapperScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class) // 导入了MapperScannerRegistrar类
@Repeatable(MapperScans.class)
public @interface MapperScan {

  // 传入的要扫描的包路径
  String[] value() default {};
    
  ......
}

MapperScannerRegistrar源码分析

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    // spring启动会调用的方法
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    // 得到MapperScan的注解信息
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    ......
    // 默认使用MapperFactoryBean
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }
    ......
        
    // 设置被扫描到的都可以成为Bean:IncludeFilter直接返回true
    scanner.registerFilters();
    // 进行扫描。这里面扫描完成后会设置definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);去保证MapperFactoryBean的set方法可以执行。
    // definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());使用构造方法,指定一个参数的!
    // definition.setBeanClass(this.mapperFactoryBean.getClass());设置具体的Beanclass
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }
}

让spring可以扫描接口

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {
  // 重新方法,让接口也可以成为Bean
  protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
  }
}

MapperFactoryBean源码分析

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    //intentionally empty 
  }
  
  // 构造方法传入这个参数
  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  ......

  /**
   * Bean的对象为sqlSession.getMapper去获取的Mybatis的代理对象
   */
  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

  /**
   * 返回的类型为前面BeanDefinition传入的类型
   */
  @Override
  public Class<T> getObjectType() {
    return this.mapperInterface;
  }

  ......
}

MapperFactoryBean继承的SqlSessionDaoSupport源码分析

/**
 * MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set 方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是 根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的 bean或者SqlSessionTemplate类型的bean。
 */
public abstract class SqlSessionDaoSupport extends DaoSupport {
  private SqlSession sqlSession;

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

  public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
    this.sqlSession = sqlSessionTemplate;
    this.externalSqlSession = true;
  }
  // 将sqlSession进行依赖注入。之前代码设置为AUTOWIRE_BY_TYPE进行触发。
  public SqlSession getSqlSession() {
    return this.sqlSession;
  }
}   

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
显示全文