【Mybatis-Plus】Mybatis-Plus 是如何整合到 SpringBoot 中的呢?

1  前言

上次我们看了 Mybatis 是如何整合到 SpringBoot 的,那么平时我们可能直接用的就是 Mybatis-Plus,那么它又是如何结合到的 SpringBoot 的呢?原理其实是一样的,这节我们就来看看。

看的过程中,其实会深深体会 Mybatis-Plus 就是对 Mybatis 的增强,类似 SpringBoot 对 Spring 的增强。

2  环境准备

还是老样子,首先我们新建个工程,然后引入 Mybatis-Plus,大家要知道一点引入 Mybatis-Plus 的依赖,就不需要主动引入 Mybatis 的了,因为它自己会依赖的:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.postgresql</groupId>
    <artifactId>postgresql</artifactId>
    <version>42.2.7</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>
spring.datasource.driver-class-name = org.postgresql.Driver
spring.datasource.url = jdbc:postgresql://localhost:5432/test
spring.datasource.username = postgres
spring.datasource.password = xxx

mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath*:/mapper/**/*.xml

还有一个变化就是我们的 Mapper 接口,可以直接继承 Plus 提供的,就可以完成基础的 CRUD:

@Mapper
public interface AccountMapper extends BaseMapper<AccountPo> {
    
}

效果:

 

3  源码分析

我们还是直接从 Starter 看起(mybatis-plus-boot-starter-3.3.0.jar / META-INF / spring.factories):

3.1  MybatisPlusAutoConfiguration

对比着来看,Mybatis 那边是MybatisAutoConfiguration, Mybatis-Plus 是MybatisPlusAutoConfiguration:

@Configuration
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties({MybatisPlusProperties.class})
@AutoConfigureAfter({DataSourceAutoConfiguration.class, MybatisPlusLanguageDriverAutoConfiguration.class})
public class MybatisPlusAutoConfiguration implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(MybatisPlusAutoConfiguration.class);
    private final MybatisPlusProperties properties;

对于 SqlSessionFactory 的创建,Mybatis 那边是 SqlSessionFactoryBean, Mybatis-Plus 是 MybatisSqlSessionFactoryBean:

@Bean
@ConditionalOnMissingBean
public 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.typeHandlers)) {
        factory.setTypeHandlers(this.typeHandlers);
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
        factory.setMapperLocations(this.properties.resolveMapperLocations());
    }
    Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
    if (!ObjectUtils.isEmpty(this.languageDrivers)) {
        factory.setScriptingLanguageDrivers(this.languageDrivers);
    }
    Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);
    if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
        factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
    }
    GlobalConfig globalConfig = this.properties.getGlobalConfig();
    this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
    this.getBeanThen(IKeyGenerator.class, (i) -> {
        globalConfig.getDbConfig().setKeyGenerator(i);
    });
    this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
    this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
    factory.setGlobalConfig(globalConfig);
    return factory.getObject();
}

基本大差不差,我们看看 getObject()方法:

public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
        this.afterPropertiesSet();
    }
    return this.sqlSessionFactory;
}

还是继续 afterPropertiesSet()方法:

public void afterPropertiesSet() throws Exception {
    // 必须校验
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
    this.sqlSessionFactory = this.buildSqlSessionFactory();
}

继续 buildSqlSessionFactory()方法:

protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
    MybatisXMLConfigBuilder xmlConfigBuilder = null;
    MybatisConfiguration targetConfiguration;
    if (this.configuration != null) {
        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) {
        xmlConfigBuilder = new MybatisXMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);
        targetConfiguration = xmlConfigBuilder.getConfiguration();
    } else {
        LOGGER.debug(() -> {
            return "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration";
        });
        targetConfiguration = new MybatisConfiguration();
        Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
    }
    this.globalConfig = (GlobalConfig)Optional.ofNullable(this.globalConfig).orElseGet(GlobalConfigUtils::defaults);
    this.globalConfig.setDbConfig((DbConfig)Optional.ofNullable(this.globalConfig.getDbConfig()).orElseGet(DbConfig::new));
    targetConfiguration.setGlobalConfig(this.globalConfig);
    if (StringUtils.hasLength(this.typeEnumsPackage)) {
        Object classes;
        if (this.typeEnumsPackage.contains("*") && !this.typeEnumsPackage.contains(",") && !this.typeEnumsPackage.contains(";")) {
            classes = this.scanClasses(this.typeEnumsPackage, (Class)null);
            if (((Set)classes).isEmpty()) {
                LOGGER.warn(() -> {
                    return "Can't find class in '[" + this.typeEnumsPackage + "]' package. Please check your configuration.";
                });
            }
        } else {
            classes = new HashSet();
            String[] typeEnumsPackageArray = StringUtils.tokenizeToStringArray(this.typeEnumsPackage, ",; \t\n");
            com.baomidou.mybatisplus.core.toolkit.Assert.notNull(typeEnumsPackageArray, "not find typeEnumsPackage:" + this.typeEnumsPackage, new Object[0]);
            Stream.of(typeEnumsPackageArray).forEach((typePackage) -> {
                try {
                    Set<Class<?>> scanTypePackage = this.scanClasses(typePackage, (Class)null);
                    if (scanTypePackage.isEmpty()) {
                        LOGGER.warn(() -> {
                            return "Can't find class in '[" + typePackage + "]' package. Please check your configuration.";
                        });
                    } else {
                        classes.addAll(scanTypePackage);
                    }
                } catch (IOException var4) {
                    throw new MybatisPlusException("Cannot scan class in '[" + typePackage + "]' package", var4);
                }
            });
        }
        TypeHandlerRegistry typeHandlerRegistry = targetConfiguration.getTypeHandlerRegistry();
        ((Set)classes).stream().filter(Class::isEnum).filter((cls) -> {
            return IEnum.class.isAssignableFrom(cls) || MybatisEnumTypeHandler.dealEnumType(cls).isPresent();
        }).forEach((cls) -> {
            typeHandlerRegistry.register(cls, MybatisEnumTypeHandler.class);
        });
    }
    Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
    Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
    Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
    Stream var10000;
    if (StringUtils.hasLength(this.typeAliasesPackage)) {
        var10000 = this.scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream().filter((clazz) -> {
            return !clazz.isAnonymousClass();
        }).filter((clazz) -> {
            return !clazz.isInterface();
        }).filter((clazz) -> {
            return !clazz.isMemberClass();
        });
        TypeAliasRegistry var10001 = targetConfiguration.getTypeAliasRegistry();
        var10000.forEach(var10001::registerAlias);
    }
    if (!ObjectUtils.isEmpty(this.typeAliases)) {
        Stream.of(this.typeAliases).forEach((typeAlias) -> {
            targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
            LOGGER.debug(() -> {
                return "Registered type alias: '" + typeAlias + "'";
            });
        });
    }
    if (!ObjectUtils.isEmpty(this.plugins)) {
        Stream.of(this.plugins).forEach((plugin) -> {
            targetConfiguration.addInterceptor(plugin);
            LOGGER.debug(() -> {
                return "Registered plugin: '" + plugin + "'";
            });
        });
    }
    if (StringUtils.hasLength(this.typeHandlersPackage)) {
        var10000 = this.scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter((clazz) -> {
            return !clazz.isAnonymousClass();
        }).filter((clazz) -> {
            return !clazz.isInterface();
        }).filter((clazz) -> {
            return !Modifier.isAbstract(clazz.getModifiers());
        }).filter((clazz) -> {
            return ClassUtils.getConstructorIfAvailable(clazz, new Class[0]) != null;
        });
        TypeHandlerRegistry var28 = targetConfiguration.getTypeHandlerRegistry();
        var10000.forEach(var28::register);
    }
    if (!ObjectUtils.isEmpty(this.typeHandlers)) {
        Stream.of(this.typeHandlers).forEach((typeHandler) -> {
            targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
            LOGGER.debug(() -> {
                return "Registered type handler: '" + typeHandler + "'";
            });
        });
    }
    if (!ObjectUtils.isEmpty(this.scriptingLanguageDrivers)) {
        Stream.of(this.scriptingLanguageDrivers).forEach((languageDriver) -> {
            targetConfiguration.getLanguageRegistry().register(languageDriver);
            LOGGER.debug(() -> {
                return "Registered scripting language driver: '" + languageDriver + "'";
            });
        });
    }
    Optional.ofNullable(this.defaultScriptingLanguageDriver).ifPresent(targetConfiguration::setDefaultScriptingLanguage);
    if (this.databaseIdProvider != null) {
        try {
            targetConfiguration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
        } catch (SQLException var23) {
            throw new NestedIOException("Failed getting a databaseId", var23);
        }
    }
    Optional.ofNullable(this.cache).ifPresent(targetConfiguration::addCache);
    if (xmlConfigBuilder != null) {
        try {
            xmlConfigBuilder.parse();
            LOGGER.debug(() -> {
                return "Parsed configuration file: '" + this.configLocation + "'";
            });
        } catch (Exception var21) {
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var21);
        } finally {
            ErrorContext.instance().reset();
        }
    }
    targetConfiguration.setEnvironment(new Environment(MybatisSqlSessionFactoryBean.class.getSimpleName(), (TransactionFactory)(this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory), this.dataSource));
    if (this.mapperLocations != null) {
        if (this.mapperLocations.length == 0) {
            LOGGER.warn(() -> {
                return "Property 'mapperLocations' was specified but matching resources are not found.";
            });
        } else {
            Resource[] var24 = this.mapperLocations;
            int var27 = var24.length;
            for(int var5 = 0; var5 < var27; ++var5) {
                Resource mapperLocation = var24[var5];
                if (mapperLocation != null) {
                    try {
                        XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
                        xmlMapperBuilder.parse();
                    } catch (Exception var19) {
                        throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var19);
                    } finally {
                        ErrorContext.instance().reset();
                    }
                    LOGGER.debug(() -> {
                        return "Parsed mapper file: '" + mapperLocation + "'";
                    });
                }
            }
        }
    } else {
        LOGGER.debug(() -> {
            return "Property 'mapperLocations' was not specified.";
        });
    }
    SqlSessionFactory sqlSessionFactory = (new MybatisSqlSessionFactoryBuilder()).build(targetConfiguration);
    SqlHelper.FACTORY = sqlSessionFactory;
    // 这就是我们启动的时候,打日志的源头
    if (this.globalConfig.isBanner()) {
        System.out.println(" _ _   |_  _ _|_. ___ _ |    _ ");
        System.out.println("| | |\\/|_)(_| | |_\\  |_)||_|_\\ ");
        System.out.println("     /               |         ");
        System.out.println("                        " + MybatisPlusVersion.getVersion() + " ");
    }
    return sqlSessionFactory;
}

基本上差不多,多了一个打印启动日志的。

3.2  MybatisPlusProperties

对比着来看,Mybatis 那边是 MybatisProperties(属性是 mybatis 开头), Mybatis-Plus 是 MybatisPlusProperties(属性是mybatis-plus 开头):

@ConfigurationProperties(
    prefix = "mybatis-plus"
)
public class MybatisPlusProperties {
    private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
    private String configLocation;
    private String[] mapperLocations = new String[]{"classpath*:/mapper/**/*.xml"};

可以看到属性配置 mybatis-plus 开头,并且 mapper.xml 的位置也帮我们初始化了一个默认的目录,其它的就不细看了,本节主要看关系的串联哈。

还有 @Mapper 扫描,跟 Mybatis 一致,直接复制的:

@Configuration
@Import({MybatisPlusAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class, MapperScannerConfigurer.class})
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
    public MapperScannerRegistrarNotFoundConfiguration() {
    }
    public void afterPropertiesSet() {
        MybatisPlusAutoConfiguration.logger.debug("Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
    }
}

4  小结

好啦,就看到这里了,对比着上一节的 Mybatis 的来看。

posted @ 2024-02-26 09:56  酷酷-  阅读(72)  评论(0编辑  收藏  举报