目 录CONTENT

文章目录

spring-boot集成mybatis逻辑分析

FatFish1
2025-11-04 / 0 评论 / 0 点赞 / 14 阅读 / 0 字 / 正在检测是否收录...

SpringBoot的JDBC自动化配置

DataSourceAutoConfiguration导入的自动配置

SpringBoot的JDBC配置类需要集成坐标:

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>

对应的配置类是:org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration

其中,分别构造了两个bean:EmbeddedDatabaseConfiguration和PooledDataSourceConfiguration

@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}

@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ DataSourceConfiguration.Hikari.class, DataSourceConfiguration.Tomcat.class,
       DataSourceConfiguration.Dbcp2.class, DataSourceConfiguration.OracleUcp.class,
       DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}

首先EmbeddedDatabaseConfiguration的条件是:

  • 符合EmbeddedDatabaseCondition输出

  • DataSource、XADataSource对应的bean还没有创建

对应创建的bean是EmbeddedDataSourceConfiguration

PooledDataSourceConfiguration的条件是:

  • 符合PooledDataSourceCondition输出

  • DataSource、XADataSource对应的bean还没有创建

对应创建的bean是DataSourceConfiguration.Hikari.class、DataSourceConfiguration.Tomcat.class、DataSourceConfiguration.Dbcp2.class、DataSourceConfiguration.OracleUcp.class、DataSourceConfiguration.Generic.class, DataSourceJmxConfiguration.class

其中Hikari数据源是默认的,这就要到DataSourceConfiguration去看了

看下两个条件:

EmbeddedDatabaseCondition

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    ConditionMessage.Builder message = ConditionMessage.forCondition("EmbeddedDataSource");
    if (hasDataSourceUrlProperty(context)) {
       return ConditionOutcome.noMatch(message.because(DATASOURCE_URL_PROPERTY + " is set"));
    }
    if (anyMatches(context, metadata, this.pooledCondition)) {
       return ConditionOutcome.noMatch(message.foundExactly("supported pooled data source"));
    }
    EmbeddedDatabaseType type = EmbeddedDatabaseConnection.get(context.getClassLoader()).getType();
    if (type == null) {
       return ConditionOutcome.noMatch(message.didNotFind("embedded database").atAll());
    }
    return ConditionOutcome.match(message.found("embedded database").items(type));
}

使用内嵌数据库,必须满足

  • 不能有url配置

  • 指定type符合内嵌数据库类型

而PooledDataSourceCondition的条件就简单多了,及有对应的ClassLoader即可

@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    ConditionMessage.Builder message = ConditionMessage.forCondition("PooledDataSource");
    if (DataSourceBuilder.findType(context.getClassLoader()) != null) {
       return ConditionOutcome.match(message.foundExactly("supported DataSource"));
    }
    return ConditionOutcome.noMatch(message.didNotFind("supported DataSource").atAll());
}

生成DataSourceProperties

在DataSourceAutoConfiguration中另一个比较核心的点就是:

@EnableConfigurationProperties(DataSourceProperties.class)

@EnableConfigurationProperties 注解是将后面的Properties类基于配置文件自动装配成bean

可以看它的调用点:

// org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar#getTypes
private Set<Class<?>> getTypes(AnnotationMetadata metadata) {
    return metadata.getAnnotations().stream(EnableConfigurationProperties.class)
          .flatMap((annotation) -> Arrays.stream(annotation.getClassArray(MergedAnnotation.VALUE)))
          .filter((type) -> void.class != type).collect(Collectors.toSet());
}

大致意思就是找到所有的@EnableConfigurationProperties注解,取其value,过滤掉为空的,以Set形式返回

继续向上追溯:

// org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
    registerInfrastructureBeans(registry);
    registerMethodValidationExcludeFilter(registry);
    ConfigurationPropertiesBeanRegistrar beanRegistrar = new ConfigurationPropertiesBeanRegistrar(registry);
    getTypes(metadata).forEach(beanRegistrar::register);
}

在这里获取到上面getTypes方法拿到的Set<Class>,执行ConfigurationPropertiesBeanRegistrar#register(java.lang.Class<?>) 完成beanDefinition注册

继续向上找它的调用点,实际上是在上级接口里面:

// org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry,
       BeanNameGenerator importBeanNameGenerator) {
    registerBeanDefinitions(importingClassMetadata, registry);
}

而该方法的调用路线就比较明确了:

是在ConfigurationClassPostProcessor,即BeanDefinitionRegistryPostProcessor的实现类,在BeanFactory加载完BD后,额外加载一批通过注解或其他方式导入的BD

而DataSourceProperties.class的前置条件是spring.datasource属性

@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties implements BeanClassLoaderAware, InitializingBean {
    ……
}

同时还实现了Aware,和InitializingBean用于初始化完成之后做后置处理

// org.springframework.boot.autoconfigure.jdbc.DataSourceProperties#afterPropertiesSet
@Override
public void afterPropertiesSet() throws Exception {
    if (this.embeddedDatabaseConnection == null) {
       this.embeddedDatabaseConnection = EmbeddedDatabaseConnection.get(this.classLoader);
    }
}

DataSourceConfiguration-创建数据源

提供了Tomcat、Hikari、Dbcp2、OracleUCP、Generic这几种配置类,其中Hikari是默认的数据源类型

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
       matchIfMissing = true)
static class Hikari {
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    HikariDataSource dataSource(DataSourceProperties properties) {
       HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
       if (StringUtils.hasText(properties.getName())) {
          dataSource.setPoolName(properties.getName());
       }
       return dataSource;

因为它头上的条件是:

  • 项目中有HikariDataSource类

  • 存在spring.datasource.type配置,但是matchIfMissing = true

即有HikariDataSource类时,不写spring.datasource.type,默认就会使用该类型的数据源

MyBatis集成SpringBoot注入DataSource

可以知道,MyBatis的配置核心就是MapperScannerConfigure和SqlSessionFactoryBean,参考如下链接:

http://www.chymfatfish.cn/archives/configformybatis

在SpringBoot环境下,就是自动创建这些bean,自动配置类是MybatisAutoConfiguration,坐标如下:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

创建SqlSessionFactoryBean和SqlSessionFactory在下面:

// org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration#sqlSessionFactory
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
  factory.setDataSource(dataSource);
  factory.setVfs(SpringBootVFS.class);
  if (StringUtils.hasText(this.properties.getConfigLocation())) {
    factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  }
  applyConfiguration(factory);
  if (this.properties.getConfigurationProperties() != null) {
    factory.setConfigurationProperties(this.properties.getConfigurationProperties());
  }
  if (!ObjectUtils.isEmpty(this.interceptors)) {
    factory.setPlugins(this.interceptors);
  }
  if (this.databaseIdProvider != null) {
    factory.setDatabaseIdProvider(this.databaseIdProvider);
  }
  if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
    factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
  }
  if (this.properties.getTypeAliasesSuperType() != null) {
    factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
  }
  if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
    factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
  }
  if (!ObjectUtils.isEmpty(this.typeHandlers)) {
    factory.setTypeHandlers(this.typeHandlers);
  }
  if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
    factory.setMapperLocations(this.properties.resolveMapperLocations());
  }
  Set<String> factoryPropertyNames = Stream
      .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
      .collect(Collectors.toSet());
  Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
  if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
    // Need to mybatis-spring 2.0.2+
    factory.setScriptingLanguageDrivers(this.languageDrivers);
    if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
      defaultLanguageDriver = this.languageDrivers[0].getClass();
    }
  }
  if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
    // Need to mybatis-spring 2.0.2+
    factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
  }
  applySqlSessionFactoryBeanCustomizers(factory);
  return factory.getObject();
}

而生成MapperScannerConfigurer在内部类org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar

这个内部类也继承了ImportBeanDefinitionRegistrar,跟上面DataSourceProperties生成的逻辑差不多

可见,Mybatis也可以基于Spring配置文件编写,其编写逻辑类似:

# mybatis
mybatis:
    type-aliases-package: com.tm.sbia.feature.domain
    check-config-location: false
    mapper-locations: classpath*:mybatis/**/*mapper.xml
    type-handlers-package: 
    configuration:
        # 全局映射器启用缓存
        cache-enabled: true
        # 查询时,关闭关联对象即时加载以提高性能
        lazy-loading-enabled: true
        # 设置关联对象加载的形态,此处为按需加载字段(加载字段由SQL指 定),不会加载关联表的所有字段,以提高性能
        aggressive-lazy-loading: false
        # 对于未知的SQL查询,允许返回不同的结果集以达到通用的效果
        multiple-result-sets-enabled: true
        # 允许使用列标签代替列名
        use-column-label: true
        #允许使用自定义的主键值(比如由程序生成的UUID 32位编码作为键值),数据表的PK生成策略将被覆盖
        #use-generated-keys: true
        # 给予被嵌套的resultMap以字段-属性的映射支持
        auto-mapping-behavior: FULL
        # 对于批量更新操作缓存SQL以提高性能
        #default-executor-type: BATCH
        # 数据库超过25000秒仍未响应则超时
        default-statement-timeout: 0
        # 使用驼峰规则
        map-underscore-to-camel-case: true
        # 默认的枚举处理类
        default-enum-type-handler: 

MyBatisPlus集成SpringBoot

MyBatisPlus与MyBatis逻辑差不多

坐标是:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>

对应的配置类是com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration

其中通过注解加载了MybatisPlusProperties

@EnableConfigurationProperties(MybatisPlusProperties.class)

配置

# Mybatis配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: true
    defaultEnumTypeHandler: com.test.TestHandler
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.test.domain

0

评论区