MybatisPlusAutoConfiguration源码阅读
代码如下:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//package com.baomidou.mybatisplus.autoconfigure;import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import java.util.Iterator;
import java.util.List;
import javax.sql.DataSource;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.mapper.ClassPathMapperScanner;
import org.mybatis.spring.mapper.MapperFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisPlusProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);private final MybatisPlusProperties properties;private final Interceptor[] interceptors;private final ResourceLoader resourceLoader;private final DatabaseIdProvider databaseIdProvider;private final List<ConfigurationCustomizer> configurationCustomizers;private final ApplicationContext applicationContext;public MybatisPlusAutoConfiguration(MybatisPlusProperties properties, ObjectProvider<Interceptor[]> interceptorsProvider, ResourceLoader resourceLoader, ObjectProvider<DatabaseIdProvider> databaseIdProvider, ObjectProvider<List<ConfigurationCustomizer>> configurationCustomizersProvider, ApplicationContext applicationContext) {this.properties = properties;this.interceptors = (Interceptor[])interceptorsProvider.getIfAvailable();this.resourceLoader = resourceLoader;this.databaseIdProvider = (DatabaseIdProvider)databaseIdProvider.getIfAvailable();this.configurationCustomizers = (List)configurationCustomizersProvider.getIfAvailable();this.applicationContext = applicationContext;}public void afterPropertiesSet() {this.checkConfigFileExists();}private void checkConfigFileExists() {if (this.properties.isCheckConfigLocation() && StringUtils.hasText(this.properties.getConfigLocation())) {Resource resource = this.resourceLoader.getResource(this.properties.getConfigLocation());Assert.state(resource.exists(), "Cannot find config location: " + resource + " (please add config file or check your Mybatis configuration)");}}@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();factory.setDataSource(dataSource);factory.setVfs(SpringBootVFS.class);if (StringUtils.hasText(this.properties.getConfigLocation())) {factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));}this.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.properties.resolveMapperLocations())) {factory.setMapperLocations(this.properties.resolveMapperLocations());}if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());}GlobalConfig globalConfig = this.properties.getGlobalConfig();if (this.applicationContext.getBeanNamesForType(MetaObjectHandler.class, false, false).length > 0) {MetaObjectHandler metaObjectHandler = (MetaObjectHandler)this.applicationContext.getBean(MetaObjectHandler.class);globalConfig.setMetaObjectHandler(metaObjectHandler);}if (this.applicationContext.getBeanNamesForType(IKeyGenerator.class, false, false).length > 0) {IKeyGenerator keyGenerator = (IKeyGenerator)this.applicationContext.getBean(IKeyGenerator.class);globalConfig.getDbConfig().setKeyGenerator(keyGenerator);}if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false, false).length > 0) {ISqlInjector iSqlInjector = (ISqlInjector)this.applicationContext.getBean(ISqlInjector.class);globalConfig.setSqlInjector(iSqlInjector);}factory.setGlobalConfig(globalConfig);return factory.getObject();}private void applyConfiguration(MybatisSqlSessionFactoryBean factory) {MybatisConfiguration configuration = this.properties.getConfiguration();if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {configuration = new MybatisConfiguration();}if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {Iterator var3 = this.configurationCustomizers.iterator();while(var3.hasNext()) {ConfigurationCustomizer customizer = (ConfigurationCustomizer)var3.next();customizer.customize(configuration);}}factory.setConfiguration(configuration);}@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {ExecutorType executorType = this.properties.getExecutorType();return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);}@Configuration@Import({AutoConfiguredMapperScannerRegistrar.class})@ConditionalOnMissingBean({MapperFactoryBean.class})public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {public MapperScannerRegistrarNotFoundConfiguration() {}public void afterPropertiesSet() {MybatisPlusAutoConfiguration.logger.debug("No {} found.", MapperFactoryBean.class.getName());}}public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {private BeanFactory beanFactory;private ResourceLoader resourceLoader;public AutoConfiguredMapperScannerRegistrar() {}public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {if (!AutoConfigurationPackages.has(this.beanFactory)) {MybatisPlusAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");} else {MybatisPlusAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");List<String> packages = AutoConfigurationPackages.get(this.beanFactory);if (MybatisPlusAutoConfiguration.logger.isDebugEnabled()) {packages.forEach((pkg) -> {MybatisPlusAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);});}ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);if (this.resourceLoader != null) {scanner.setResourceLoader(this.resourceLoader);}scanner.setAnnotationClass(Mapper.class);scanner.registerFilters();scanner.doScan(StringUtils.toStringArray(packages));}}public void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}public void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}}
}
一.注解分析
1.@Configuration
该类是一个配置类
2.@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
SqlSessionFactory和SqlSessionFactoryBean两个类必须存在
3. @ConditionalOnSingleCandidate(DataSource.class)
DataSource类只有一个类型,确保单一数据源
4.@EnableConfigurationProperties({MybatisPlusProperties.class})
通过MybatisPlusProperties导入自动配置
该类如下:
通过配置文件进行数据绑定。下面是一个项目中的数据库的配置文件
# MyBatis-Plus配置
# 配置Mapper XML文件的扫描路径,支持通配符匹配多个路径下的Mapper文件
mybatis-plus.mapper-locations: classpath*:/mapper/*Mapper.xml# 实体扫描,多个 package 用逗号或者分号分隔
mybatis-plus.typeAliasesPackage: com.test.common.*.entity# 数据库相关配置
# 主键类型 AUTO:"数据库ID自增", INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
mybatis-plus.global-config.db-config.id-type: AUTO# 字段策略 IGNORED:"忽略判断",NOT_NULL:"非 NULL 判断"),NOT_EMPTY:"非空判断"
mybatis-plus.global-config.db-config.field-strategy: not_empty# 驼峰下划线转换
mybatis-plus.global-config.db-config.column-underline: true# 数据库大写下划线转换
#mybatis-plus.global-config.db-config.capital-mode: true# 逻辑删除配置
# 逻辑删除的值,当记录被逻辑删除时,数据库字段将被设置为该值
mybatis-plus.global-config.db-config.logic-delete-value: 0
# 逻辑未删除的值,表示记录未被删除时数据库字段的值
mybatis-plus.global-config.db-config.logic-not-delete-value: 1# 刷新 mapper
mybatis-plus.global-config.refresh: false# 是否显示MyBatis-Plus启动时的banner信息
mybatis-plus.global-config.banner: false
上面以mybatis-plus为前缀的属性值会和MybatisPlusProperties中的配置类中的属性进行绑定。
5.@AutoConfigureAfter({DataSourceAutoConfiguration.class})
该类会在DataSourceAutoConfiguration类配置后在进行配置,确保配置顺序正确,在数据源配置加载之后再加载数据库配置。
二.代码核心思路分析
1. 该类实现了Spring框架的InitializingBean接口,
在afterPropertiesSet()初始化方法里面使用checkConfigFileExists()方法校验配置文件是否存在。
2.缺失类的自动配置
如果SqlSessionFactory,SqlSessionTemplate类缺失会自动配置
@Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
@Bean@ConditionalOnMissingBeanpublic SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
3.使用配置
应用MyBatis配置到SqlSessionFactoryBean中,如果有定制化配置使用定制化的配置。
4.处理缺失MapperFactoryBean类的场景
当缺失MapperFactoryBean类(MapperFactoryBean类用于创建MyBatis的Mapper接口代理实例),使用@Import({AutoConfiguredMapperScannerRegistrar.class})导入注册类。AutoConfiguredMapperScannerRegistrar负责自动扫描和注册MyBatis的Mapper接口。
/*** 自动配置的Mapper扫描注册器* 实现BeanFactoryAware、ImportBeanDefinitionRegistrar和ResourceLoaderAware接口,* 用于自动扫描和注册标记了@Mapper注解的Mapper接口*/public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {private BeanFactory beanFactory;private ResourceLoader resourceLoader;public AutoConfiguredMapperScannerRegistrar() {}/*** 注册Bean定义,扫描并注册Mapper接口* @param importingClassMetadata 导入类的元数据信息* @param registry Bean定义注册器*/public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 检查是否存在自动配置包,如果不存在则禁用自动Mapper扫描if (!AutoConfigurationPackages.has(this.beanFactory)) {MybatisPlusAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");} else {MybatisPlusAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");List<String> packages = AutoConfigurationPackages.get(this.beanFactory);if (MybatisPlusAutoConfiguration.logger.isDebugEnabled()) {packages.forEach((pkg) -> {MybatisPlusAutoConfiguration.logger.debug("Using auto-configuration base package '{}'", pkg);});}// 创建类路径Mapper扫描器并进行配置ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);if (this.resourceLoader != null) {scanner.setResourceLoader(this.resourceLoader);}// 设置要扫描的注解类型为@Mapper,并执行扫描scanner.setAnnotationClass(Mapper.class);scanner.registerFilters();scanner.doScan(StringUtils.toStringArray(packages));}}/*** 设置Bean工厂* @param beanFactory Bean工厂实例*/public void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}/*** 设置资源加载器* @param resourceLoader 资源加载器实例*/public void setResourceLoader(ResourceLoader resourceLoader) {this.resourceLoader = resourceLoader;}}