首页 > 其他分享 >【Mybatis-Plus】Mybatis-Plus 是如何整合到 SpringBoot 中的呢?

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

时间:2024-02-26 10:00:28浏览次数:25  
标签:return SpringBoot factory targetConfiguration Mybatis Plus null class

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 的来看。

标签:return,SpringBoot,factory,targetConfiguration,Mybatis,Plus,null,class
From: https://www.cnblogs.com/kukuxjx/p/18033610

相关文章

  • SpringBoot:通过实现自定义接口获取实现类的@RequestMapping注解请求路径
    1.自定义接口//什么都不用写,就定义一个空接口publicinterfaceMyMark{}2.Controller接口类实现自定义接口@RestControllerpublicclassDayControllerimplementsMyMark{@RequestMapping("/day1")publicStringget1(){return"day1";}......
  • 【Mybatis】Mybatis 是如何整合到 Spring 中的呢?
    1 前言当你把Spring、SpringBoot、Mybatis或者Mybatis-Plus的源码都看过后,那有没有想过比如Mybatis如何整合到Spring或者SpringBoot的呢?就是思考框架跟框架之间的融合,那么这节我们就来看看单纯的Mybatis是如何融合到SpringBoot的。融合Spring的就不看了,毕竟大......
  • mybatis
    MyBatis概述MyBatis是一个实现了数据持久化的开源框架,简单理解就是对JDBC进行封装MyBatis优点与JDBC相比,减少了50%以上的代码量。MyBatis是最简单的持久化框架,小巧并且简单易学。MyBatis相当灵活,不会对应用程序或者数据库的现有设计强加任何影响,SQL写在XML里,从程序代码中彻......
  • SpringBoot结合Liquibase实现数据库变更管理
    https://juejin.cn/post/7171232605478584328 https://juejin.cn/post/7170857098538909732  前言研发过程中经常涉及到数据库变更,对表结构的修复及对数据的修改,为了保证各环境都能正确的进行变更,我们可能需要维护一个数据库升级文档来保存这些记录,有需要升级的环境按......
  • Linux离线部署SpringBoot前后端分离项目
    本文介绍了在内网下的纯离线环境中部署SpringBoot前后端分离项目,由于是个前端仔,并未接触过linux,在经历诸多错误和踩坑之后,终于部署成功(大哭),在此记录一下。工具选择选择合适的工具进行远程连接,如Xshell、Xftp、putty、Terminus等Xshell:连接远程服务器的命令终端Xftp:连接远......
  • JWT(Token令牌)整合 SpringBoot
     前言:JWT(JSONWebToken)可以被称为令牌(token)。JWT是一种在网络应用中广泛使用的令牌格式,用于在用户和服务器之间传递安全可靠的信息。JWT通常包含了用户的身份信息和一些其他的元数据,被用作身份验证和授权。因此,人们经常将JWT简称为令牌(token)。 代码整合:1.导......
  • Kafka 集成SpringBoot
    1.环境准备1.Kafka集群环境准备1.准备一个Kafka集群环境并启动Kafka3.6.1集群安装与部署2.创建firstTopic/usr/kafka/kafka_2.13-3.6.1/bin/kafka-topics.sh--bootstrap-server192.168.58.130:9092--create--partitions1--replication-factor3--topicfirst2.Sp......
  • SpringBoot + Redis 的配置及使用
    一、SpringBoot配置Redis1.1pom引入spring-boot-starter-data-redis包<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></......
  • mybatis和jdbc有啥区别
    JDBC是Java提供的一个操作数据库的API,是一种规范;mybatis是一个持久层框架,用java编写的.他封装了jdbc操作的很多细节,使开发者只需要关注sql语句本身,而无需关注注册驱动,创建连接等繁杂过程.他使用了ORM思想实现了结果集的封装(ORM:ObjectRelationalMapping对象关系映射简单......
  • mybatis—mapper.xml里的多种写法举例
    mybatis—mapper.xml里的多种写法举例目录mybatis—mapper.xml里的多种写法举例向一个表里插入另一个表的一些数据并进行取舍需要查询的select后的字段如果有不少语句都一致的话可以考虑单独提出来更新删除in语句面对集合和字符串<trim>标签<choose>标签判断<when>、<other......