Spring整合MyBatis源码剖析


MyBatis整合MyBatis源码设计很多Spring IOC的内容,要想明白MyBatis如何整合进Spring需要对Spring IOC的加载流程和扩展点很熟悉,详情可以看SpringIOC容器加载流程和源码剖析

Demo

首先引入spring-mybatis依赖包

MyBatis-Spring MyBatis Spring Framework Spring Batch Java
2.0 3.5+ 5.0+ 4.0+ Java 8+
1.3 3.4+ 3.2.2+ 2.1+ Java 6+
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.3</version>
</dependency>

配置类

@Configuration
@MapperScan(basePackages = {"com.dm.mapper"})
@ComponentScan(basePackages = {"com.dm"})
@Repository
public class MyBatisConfig {
   @Bean
   public SqlSessionFactoryBean sqlSessionFactory() throws IOException {
      SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
      factoryBean.setDataSource(dataSource());
      // MyBatis配置文件路径
      factoryBean.setConfigLocation(new ClassPathResource("mybatis/mybatis-config.xml"));
      // Mapper映射文件路径
      factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/mapper/*.xml"));
      return factoryBean;
   }

    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("xxxxxxx");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://xx.xx.xx.xx:3306/test");
        return dataSource;
    }
}

User实体类

@Data
@Builder
@AllArgsConstructor
@ToString
public class User implements Serializable {

   private Integer id;

   private String name;

   private Integer age;

}

UserMapper.java

public interface UserMapper {

   User selectById(@Param(value = "id") Integer id);

    List<User> selectAllUser();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.dm.mapper.UserMapper">
   <resultMap id="result" type="com.dm.entity.User">
      <id column="id" jdbcType="INTEGER" property="id"/>
      <result column="name" jdbcType="VARCHAR" property="name"/>
      <result column="age" jdbcType="INTEGER" property="age"/>
   </resultMap>

   <select id="selectById" resultMap="result">
      select * from user
      <where>
         <if test="id > 0">
            and id=#{id}
         </if>
      </where>
   </select>

   <select id="selectAllUser" resultType="com.dm.entity.User">
        select * from user
    </select>

</mapper>

测试类

public class MainStarter {
   public static void main(String[] args) {
      // 加载spring上下文
      AnnotationConfigApplicationContext ioc = new AnnotationConfigApplicationContext(MyBatisConfig.class);
      UserMapper bean = (UserMapper) ioc.getBean("userMapper");
      System.out.println(bean.selectById(1));
   }
}

我们发现这和我们普通spring bean加载有2个区别,配置类上加了@MapperScan,和注入了一个SqlSessionFactoryBean(其实注入的bean不叫sqlSessionFactoryBean而是sqlSessionFactory)

注入SqlSessionFactoryBean源码

这里我们进入SqlSessionFactoryBean发现它实现了FactoryBean和InitializingBean

实现FactoryBean

实现了FactoryBean说明它可能更改了类型,我们来到getObject()方法

@Override
public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        afterPropertiesSet();
    }
    // 返回我们的sqlSessionFactory对象
    return this.sqlSessionFactory;
}

从这也可以看出容器中容器中有sqlSessionFactory 这个bean

实现InitializingBean

InitializingBean在bean初始化之后调用,调用afterPropertiesSet

具体调用逻辑AnnotationConfigApplicationContext->refresh->finishBeanFactoryInitialization->preInstantiateSingletons->getBean->doGetBean->createBean->doCreateBean->initializeBean->invokeInitMethods

可直接跟到AbstractAutowireCapableBeanFactory#invokeInitMethods

Override
public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
      "Property 'configuration' and 'configLocation' can not specified with together");
  this.sqlSessionFactory = buildSqlSessionFactory();
}
/**
 * 构建我们的sqlSessionFactory的实例
 */
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {

  // 声明一个Configuration对象用于保存mybatis的所有的配置信息
  final Configuration targetConfiguration;

  XMLConfigBuilder xmlConfigBuilder = null;
  // 判断当前的SqlSessionFactoryBean是否在配置@Bean的时候 factoryBean.setConfiguration();
  if (this.configuration != null) {
    // 把配置的SqlSessionFactoryBean配置的configuration 赋值给targetConfiguration
    targetConfiguration = this.configuration;
    if (targetConfiguration.getVariables() == null) {
      targetConfiguration.setVariables(this.configurationProperties);
    } else if (this.configurationProperties != null) {
      targetConfiguration.getVariables().putAll(this.configurationProperties);
    }
  }

  else if (this.configLocation != null) {
    //创建我们xml配置构建器对象,对mybatis/mybatis-config.xml配置文件进行解析 在这里以及把我们的mybaits-config.xml解析出要给document对象
    xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
    // 因为我们在创建XMLConfigBuilder的时候已经把我们的Configuration对象创建出来了
    targetConfiguration = xmlConfigBuilder.getConfiguration();
  } else {
    LOGGER.debug(
        () -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
    targetConfiguration = new Configuration();
    // 判断configurationProperties不为空,那么就调用targetConfiguration.set方法 把configurationProperties注入到Configuration对象中
    Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
  }

  // objectFactory不为空,那么就调用targetConfiguration.set方法 把objectFactory注入到Configuration对象
  Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
  // objectWrapperFactory不为空,那么就调用targetConfiguration.set方法把 ObjectWrapperFactory注入到Configuration对象中
  Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);

  // vfs不为空,那么就调用targetConfiguration.set方法把 vfs注入到Configuration对象中
  Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);

  if (hasLength(this.typeAliasesPackage)) {

    //第一步:扫描我们typeAliasesPackage 包路径下的所有的实体类的class类型 第二步:进行过滤,然后注册到Configuration的别名映射器中
    scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
        .filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
        .filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
  }

  /**
   * 判断我们SqlSessionFactory是否配置了typeAliases(class类型) 一般typeAliasesPackage配置好了 就没有必要配置typeAliases
   * 注册到Configuration的别名映射器中
   */
  if (!isEmpty(this.typeAliases)) {
    Stream.of(this.typeAliases).forEach(typeAlias -> {
      targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
      LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
    });
  }

  // 把我们自定义的插件注册到我们的mybatis的配置类上
  if (!isEmpty(this.plugins)) {
    Stream.of(this.plugins).forEach(plugin -> {
      targetConfiguration.addInterceptor(plugin);
      LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
    });
  }

  // 扫描我们自定义的类型处理器(用来处理我们的java类型和数据库类型的转化) 并且注册到我们的 targetConfiguration(批量注册)
  if (hasLength(this.typeHandlersPackage)) {
    scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
        .filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
        .forEach(targetConfiguration.getTypeHandlerRegistry()::register);
  }

  // 通过配置<TypeHandlers></TypeHandlers>的形式来注册我们的类型处理器对象
  if (!isEmpty(this.typeHandlers)) {
    Stream.of(this.typeHandlers).forEach(typeHandler -> {
      targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
      LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
    });
  }
    
  if (!isEmpty(this.scriptingLanguageDrivers)) {
    Stream.of(this.scriptingLanguageDrivers).forEach(languageDriver -> {
      targetConfiguration.getLanguageRegistry().register(languageDriver);
      LOGGER.debug(() -> "Registered scripting language driver: '" + languageDriver + "'");
    });
  }
  Optional.ofNullable(this.defaultScriptingLanguageDriver)
      .ifPresent(targetConfiguration::setDefaultScriptingLanguage);

  // 设置数据库厂商
  if (this.databaseIdProvider != null) {// fix #64 set databaseId before parse mapper xmls
    try {
      targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
    } catch (SQLException e) {
      throw new NestedIOException("Failed getting a databaseId", e);
    }
  }

  // 若二级缓存不为空,注册二级缓存
  Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);

  if (xmlConfigBuilder != null) {
    try {
      // 真正的解析我们的配置(mybatis-config.xml)的document对象
      xmlConfigBuilder.parse();
      LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
    } catch (Exception ex) {
      throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  // 为我们的configuration设置一个环境变量
  targetConfiguration.setEnvironment(new Environment(this.environment,
      this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
      this.dataSource));

  // 循环我们的mapper.xml文件
  if (this.mapperLocations != null) {
    if (this.mapperLocations.length == 0) {
      LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
    } else {
      for (Resource mapperLocation : this.mapperLocations) {
        if (mapperLocation == null) {
          continue;
        }
        try {
          // 真正的循环我们的mapper.xml文件
          XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
              targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
          xmlMapperBuilder.parse();
        } catch (Exception e) {
          throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
        } finally {
          ErrorContext.instance().reset();
        }
        LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
      }
    }
  } else {
    LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
  }

  // 通过建造者模式构建我们的SqlSessionFactory对象 默认是DefaultSqlSessionFactory
  return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

@MapperScan源码

首先我们SqlSessionFactoryBean通过上面的@Bean注入进容器了,那么现在只差把UserMapper注入到容器中就可以了,但是问题来了,众所周知Mapper.java是接口类,而接口类是不可以解析成BeanDefinition,既然不可以解析成BeanDefinition也就不存在注入到容器,至于接口不能被注入大家可以来到ClassPathScanningCandidateComponentProvider#isCandidateComponent

protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
   AnnotationMetadata metadata = beanDefinition.getMetadata();
   // metadata.isIndependent()=顶级类、嵌套类、静态内部类
   // metadata.isConcrete() =非接口、非抽象类
   // metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName() = 抽象类并且必须方法中有@LookUp
   return (metadata.isIndependent() && (metadata.isConcrete() ||
         (metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}

我们来看下spring-mybatis是如何解决这个问题的

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)

从注解上看有个熟悉的@Import,它实现了ImportBeanDefinitionRegistrar接口,这个接口大家都知道是用来注册Bean定义的

我们来到实现了ImportBeanDefinitionRegistrar接口的registerBeanDefinitions

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

    // 从我们传入的配置类中来解析@MapperScan注解信息,然后吧MapperScan注解的属性转化为AnnotationAttributes类型
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    // 若上一步解析出来的mapperScanAttrs不为空(说明配置类上加了@MapperScan注解)
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }

void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {

    // 创建bean定义构造器 通过够构造器来构建出我们的bean定义<MapperScannerConfigurer> 应用到的设计模式[建造者模式]
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

    //手动为我们MapperScannerConfigurer 开启processPropertyPlaceHolders属性为true
    builder.addPropertyValue("processPropertyPlaceHolders", true);

    // 为我们的MapperScannerConfigurer 解析我们@MapperScanner 指定扫描的的注解类型
    Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
    if (!Annotation.class.equals(annotationClass)) {
      builder.addPropertyValue("annotationClass", annotationClass);
    }

    // 是否配置了标记接口
    Class<?> markerInterface = annoAttrs.getClass("markerInterface");
    if (!Class.class.equals(markerInterface)) {
      builder.addPropertyValue("markerInterface", markerInterface);
    }

    // 设置MapperScannerConfigurer的beanName 生成器对象
    Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
    if (!BeanNameGenerator.class.equals(generatorClass)) {
      builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
    }

    // 解析@MapperScan注解属性MapperFactoryBean 设置到MapperScannerConfigurer 声明一个自定义的MapperFactoryBean 返回一个代理对象
    Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
    if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
      builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
    }

    // 解析@MapperScan 的sqlSessionTemplateRef到底使用是哪个sqlSessionTemplate 设置到MapperScannerConfigurer 多数据源的情况下需要指定
    String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
    if (StringUtils.hasText(sqlSessionTemplateRef)) {
      builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
    }

    // 解析@MapperScan的sqlSessionFactoryRef属性 设置到 MapperScannerConfigurer 多数据情况下的话 ,需要指定使用哪个 sqlSessionFactory
    String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
    if (StringUtils.hasText(sqlSessionFactoryRef)) {
      builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
    }

    // 解析@MapperScan 扫描的的包或者是class对象
    List<String> basePackages = new ArrayList<>();
    basePackages.addAll(
        Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
        .collect(Collectors.toList()));

    basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
        .collect(Collectors.toList()));

    // 指定MapperScannerConfigurer 是否为懒加载
    String lazyInitialization = annoAttrs.getString("lazyInitialization");
    if (StringUtils.hasText(lazyInitialization)) {
      builder.addPropertyValue("lazyInitialization", lazyInitialization);
    }

    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));

    // 为我们的容器中注册了MapperScannerConfigurer的接口
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());

  }

这里做的事情很简单-使用建造者模式构建MapperScannerConfigurer并注册到BeanDefinition,然后进入MapperScannerConfigurer类中发现它实现了BeanDefinitionRegistryPostProcessor,InitializingBean(这里并没有什么用)

大家都知道BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry会在refresh->invokeBeanFactoryPostProcessors中会实例化刚刚注册的MapperScannerConfigurer bean定义并且会调用postProcessBeanDefinitionRegistry

MapperScannerConfigurer#postProcessBeanDefinitionRegistry

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
  // 若MapperScannerConfigurer属性的processPropertyPlaceHolders为ture的时候,就进行processPropertyPlaceHolders();
  if (this.processPropertyPlaceHolders) {
    processPropertyPlaceHolders();
  }

  // 显示的new 一个ClassPathMapperScanner 包扫描器对象 这个对象是mybaits继承了spring的ClassPathBeanDefinitionScanner为我们扫描器指定@MapperScan属性
  ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
  scanner.setAddToConfig(this.addToConfig);
  scanner.setAnnotationClass(this.annotationClass);
  scanner.setMarkerInterface(this.markerInterface);
  scanner.setSqlSessionFactory(this.sqlSessionFactory);
  scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
  scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
  scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
  scanner.setResourceLoader(this.applicationContext);
  scanner.setBeanNameGenerator(this.nameGenerator);
  scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
  if (StringUtils.hasText(lazyInitialization)) {
    scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
  }

  // 扫描规则过滤
  scanner.registerFilters();
  // 扫描我们@MapperScan指定的路径下的bean定义信息 先会去调用ClassPathMapperScanner.scan()方法然后调用ClassPathMapperScanner#doScan方法然后会调用到父类的ClassPathBeanDefinitionScanner#doScan,在父类的doScan中会调用isCandidateComponent(AnnotatedBeanDefinition beanDefinition)方法,而这个方法在子类被重写了所以这里调到子类isCandidateComponent,而子类中的不会过滤掉接口,所以接口可以被扫描注册成beanDefinition了
  scanner.scan(
      StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
    return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}

ClassPathMapperScanner#doScan

@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    // 调用父类ClassPathBeanDefinitionScanner 来进行扫描
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);

    // 若扫描后 我们mapper包下有接口类,那么扫描bean定义就不会为空
    if (beanDefinitions.isEmpty()) {
        LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
                    + "' package. Please check your configuration.");
    } else {
        
        // 这里很重要
        processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
}

ClassPathMapperScanner#processBeanDefinitions(beanDefinitions);

Mapper生成代理对象

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
  GenericBeanDefinition definition;
  // 循环我们所有扫描出mapper的bean定义出来
  for (BeanDefinitionHolder holder : beanDefinitions) {
    // 获取我们的bean定义
    definition = (GenericBeanDefinition) holder.getBeanDefinition();
    // 获取我们的bean定义的名称
    String beanClassName = definition.getBeanClassName();
    LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
        + "' mapperInterface");

    // 设置ConstructorArgumentValues 会通过构造器初始化对象
    definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
    // 设置成factoryBean
    definition.setBeanClass(this.mapperFactoryBeanClass);

    definition.getPropertyValues().add("addToConfig", this.addToConfig);

    /**
     * 为我们的Mapper对象绑定我们的sqlSessionFactory引用 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionFactory的属性)
     * 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为我么你的UserMapper(MapperFactoryBean)
     * 的sqlSessionFactory赋值(调用set方法)
     */
    boolean explicitFactoryUsed = false;
    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;
    }
    /**
     * 为我们的Mapper对象绑定我们的sqlSessionTemplate属性对象
     * 说白了就是我们的UserMapper(实际上是就是为我们的MapperFactoryBean添加一个sqlSessionTemplate的属性)
     * 然后SpringIoc在实例话我们的MapperFactoryBean的时候会经历populate()方法为我么你的UserMapper(MapperFactoryBean)
     * 的sqlSessionTemplate赋值(调用set方法)
     */
    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.");
      }
      // 将sqlSessionTemplate通过AUTOWIRE_BY_TYPE自动装配
      definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
      explicitFactoryUsed = true;
    }

    if (!explicitFactoryUsed) {
      LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
      definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
    }
    // 设置bean定义的加载模型(是否为懒加载)
    definition.setLazyInit(lazyInitialization);
  }
}

此时Mapper集成进Spring中了。

总结

Spring整合Mybatis最重要的点就是怎么把代理Mapper注入到spring容器中。要解决注入问题首先解决2个问题。

  1. 首先Mapper是一个接口,无法生成BeanDefinition
  2. Mapper需要生成代理对象

1,Mybatis利用@Import注解注入一个BeanDefinitionRegistryPostProcessor,而这个BeanDefinition是包含注册功能的,在这个BeanDefinitionRegistryPostProcessor我们可以实现扫描功能并重写isCandidateComponent,使接口可以被扫描并注册成一个bean定义。

2,Mapper生成代理对象,利用FactoryBean进行改写,使只要getBean,便拿到了getObject改写的代理对象MapperProxy


文章作者: dm
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 dm !
评论
 上一篇
ELK单机集群搭建,增量索引方案 ELK单机集群搭建,增量索引方案
环境准备java版本:无需准备(es7.x内置JDK12) 操作系统:CentOS7 安装包:https://www.elastic.co/cn/downloads/past-releases#elasticsearch 我这里选择的是7.
2022-09-14
下一篇 
MyBatis核心源码剖析 MyBatis核心源码剖析
MyBatis是一款ORM框架,它解决的问题是针对JDBC操作数据库和封装数据集繁琐的问题。下面我们看一下传统JDBC来创建连接和获取数据集的方式。 JDBCmaven依赖 <!-- mysql驱动 --> <dependenc
2022-08-11
  目录