mybatis-spring源码分析(一)

文章目录

    • 了解
    • 配置
      • xml配置
      • java配置
    • SqlSessionFactoryBean
    • 往spring注册,生成mapper代理的MapperFactoryBean
    • 根据类型获取mapper代理类
    • 源码跟官网

了解

今天就大概讲一下mybatis-spring的主要执行流程;
mybatis-spring的官网http://mybatis.org/spring/zh/index.html;
作用,简单说,就是通过配置,生成mapper代理类,交给spring管理,

今天这篇文章主要讲:扫描mapper接口,注入spring容器,生成代理类的入口,具体怎么生成没有讲;

配置

xml配置

举个例子:xml配置需要配置SqlSessionFactoryBean,和MapperScannerConfigurer,MapperScannerConfigurer就是对路径进行解析的

    <!-- spring和MyBatis完美整合,不需要mybatis的配置映射文件 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- 自动扫描mapping.xml文件 -->
        <property name="mapperLocations" value="classpath:mapping/*.xml"></property>
    </bean>

    <!-- DAO接口所在包名,Spring会自动查找其下的类 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.txn.mapper" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    </bean>

MapperScannerConfigurer是实现了BeanDefinitionRegistryPostProcessor,通过postProcessBeanDefinitionRegistry方法获取到BeanDefinitionRegistry,然后还是通过ClassPathMapperScanner的scan方法进行注册bean,可以先看下面在回过头来看MapperScannerConfigurer类,是一样的…

java配置

@MapperScan(value = {"com.study.springbootplus.**.mapper"})

@Bean
public SqlSessionFactory sqlSessionFactory() {
  SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
  factoryBean.setDataSource(dataSource());
  return factoryBean.getObject();
}

通过MapperScan注解将通过注解引入MapperScannerConfigurer类,然后通过实现ImportBeanDefinitionRegistrar,获取MapperScan注解属性,然后new ClassPathMapperScanner来对mapper进行注册的,可以先看后面,然后在回过头来看;

SqlSessionFactoryBean

不管怎么配置,SqlSessionFactoryBean都是要单独配置的;
SqlSessionFactoryBean封装了mybatis的配置文件,数据源,解析mapperxml文件等等;

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。
而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。
通常,在 MyBatis-Spring 中,你不需要直接使用 SqlSessionFactoryBean 或对应的 SqlSessionFactory。相反,session 的工厂 bean 将会被注入到 MapperFactoryBean 或其它继承于 SqlSessionDaoSupport 的 DAO(Data Access Object,数据访问对象)中。

在官网中专门讲了SqlSessionFactoryBean,http://mybatis.org/spring/zh/factorybean.html

我的简单分析:https://yidajava.blog.csdn.net/article/details/108184170

往spring注册,生成mapper代理的MapperFactoryBean

@MapperScan(value = {"com.study.springbootplus.**.mapper"})

一般是通过mapperscan来扫描mapper接口,然后对接口生成对应的对应的代理类,注入spring容器中;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

注解使用了@Import(MapperScannerRegistrar.class),主要功能就看MapperScannerRegistrar类,MapperScannerRegistrar实现了两个接口,一个ImportBeanDefinitionRegistrar,顾名思义就是动态往spring容器注入bean,ResourceLoaderAware就是资源加载器,就是用来处理包扫描的,可以获取,工程的资源或者外部资源等等…
主要方法registerBeanDefinitions,能够拿到所有注解,和bean注册器.

这里新建了ClassPathMapperScanner ,不管是xml配置还是注解,解析都是靠这个类.

 @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    //获取MapperScan注解的属性值
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    //初始化ClassPathMapperScanner,主要用来通过这个类来生成对应的mapper代理类,实际是MapperFactoryBean,是一个fatoryBean,
    //当spring使用的时候,会调用对应的getObject,来生成代理类
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    // this check is needed in Spring 3.1
    if (resourceLoader != null) {
      scanner.setResourceLoader(resourceLoader);
    }

    //下面是获取MapperScan注解配置的值,获取之后加到ClassPathMapperScanner中

    /**
     *   annotationClass : 这个是基于包下面扫描所有的接口类并注册
     *   markInterface : 基于包下面扫描所有接口类并注册
     *   nameGenerator : 在Spring的容器中,将使用BeanNameGenerator去命名检测到的组件。
     *   factoryBean: 提前指定MapperFactoryBean
     *   sqlSessionTemplateRef: 指定使用sqlSessionTemplateRef
     *   sqlSessionFactoryRef : 指定使用sqlSessionFactoryRef
     *   value: 就是扫描包下所有的接口
     *   basepackages : 基于包下面的扫描MyBatis的接口。注意是,只有是接口的将会被扫描注册,如果是具体的类将会被忽略。
     *   basePackageClasses : 这是一个安全替代basePackages()作为指定组件的扫描包。包下面的所有接口都将会被扫描。
     */
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      scanner.setAnnotationClass(annotationClass);
    }

    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      scanner.setMarkerInterface(markerInterface);
    }

    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
    }

    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
    }

    scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
    scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));

    List<String> basePackages = new ArrayList<String>();
    for (String pkg : annoAttrs.getStringArray("value")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (String pkg : annoAttrs.getStringArray("basePackages")) {
      if (StringUtils.hasText(pkg)) {
        basePackages.add(pkg);
      }
    }
    for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
      basePackages.add(ClassUtils.getPackageName(clazz));
    }
    //对配置annotationClass,markInterface标记提前进行注册,用来过滤
    scanner.registerFilters();
    // 主要解析工作了
    scanner.doScan(StringUtils.toStringArray(basePackages));
  }

看看scan方法,

  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    //通过父类进行解析,将对应的包下类解析成BeanDefinition,BeanDefinitionHolder内部维护了BeanDefinition,
    // BeanDefinition就是spring的bean定义可以简单看做就是spring,bean的属性
    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 {
      //对bean定义进行处理
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }

父类的scan方法就不讲了,是spring的方法,就是扫描包下所有类生成BeanDefinition;并注入spring容器中,
mybatis-spring源码分析(一)_第1张图片
我们看对bean定义做的一些处理:
主要就是设置注入类的类型为mapperFactoryBean,

 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      // 取到BeanDefinitionHolder维护的Bean定义,具体的实现GenericBeanDefinition
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      // mapper接口是原始类,但是我们放在spring容器内部的实际是MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      definition.setBeanClass(this.mapperFactoryBean.getClass());

      // addToConfig属性好像没啥用...
      definition.getPropertyValues().add("addToConfig", this.addToConfig);

      boolean explicitFactoryUsed = false;
      // 前面有设置sqlSessionFactoryRef,如果设置了引用就用设置的
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }
      // 前面有设置sqlSessionTemplateRef,如果设置了引用就用设置的
      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      //如果都没有设置,就设置为类型注入
      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

根据类型获取mapper代理类

MapperFactoryBean类继承了SqlSessionDaoSupport,实现FactoryBean,继承SqlSessionDaoSupport重写了checkDaoConfig方法为了验证验证sqlSession,mapperInterface不为空,然后getObject方法就直接使用sqlSession来获取dao:

 @Override
  protected void checkDaoConfig() {
    //验证sqlSession,mapperInterface不为空
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        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();
      }
    }
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public T getObject() throws Exception {
    //获取SqlSession来获取mapper
    return getSqlSession().getMapper(this.mapperInterface);
  }

当获取对应的mapper的时候,实际是执行MapperFactoryBean的getObject,
mybatis-spring源码分析(一)_第2张图片
而getSqlSession返回的就是mybatis配置的sqlsessionFactory;然后生成代理类就是走的mybaits里面了,
我这篇文章主要为了分析mybatis-spring中间件的功能,生成代理就简单过一下好了,

走到sqlSessionTemplate类,然后通过类org.apache.ibatis.session.Configuration类,而Configuration维护了MapperRegistry类,然后通过MapperRegistry获取存储的MapperProxyFactory,代理工厂类生成代理类:这里,代理就生成了,我们使用的mapper代理就是这样生成的:
mybatis-spring源码分析(一)_第3张图片

mybatis-spring源码分析(一)_第4张图片
mybatis-spring源码分析(一)_第5张图片

源码跟官网

官网:http://mybatis.org/spring/zh/index.html
我的github源码:https://github.com/stackXu/spring
我是fork的,大家可以自己fork源码就好了,https://github.com/mybatis/spring

通过上面,mybatis-spring的功能就介绍完了,这时候回到最开始,
一种通过注解方式新建ClassPathMapperScanner,
一种通过配置方式来新建ClassPathMapperScanner…应该很明了把;
这篇文章主要讲mybatis-spring中间件,很多具体的没怎么讲,如果想要更深的了解,可以看如下文章:

你可能感兴趣的:(mybatis)