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