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,参考如下链接:
在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
评论区