生活工程体验信仰哲学精神
投稿投诉
精神世界
探索历史
哲学文学
艺术价值
信仰创造
境界审美
体验技术
技能工具
工程信息
医学生产
生活运用
操作能力

彻底搞懂SpringBoot中的starter机制

12月8日 尘世客投稿
  前言
  我们都知道,Spring的功能非常强大,但也有些弊端。比如:我们需要手动去配置大量的参数,没有默认值,需要我们管理大量的jar包和它们的依赖。
  为了提升Spring项目的开发效率,简化一些配置,Spring官方引入了SpringBoot。
  当然,引入SpringBoot还有其他原因,在这里就不过多描述了。
  本文重点跟大家一起聊聊SpringBoot的starter机制,因为它太重要了。
  1为什么要用starter?
  在SpringBoot还没有出来之前,我们使用Spring开发项目。如果程序需要连接数据库,我们一般会使用Hibernate或Mybatis等ORM框架,这里我以Mybatis为例,具体的操作步骤如下:到maven仓库去找需要引入的mybatisjar包,选取合适的版本。到maven仓库去找mybatisspring整合的jar包,选取合适的版本。在spring的applicationContext。xml文件中配置dataSource和mybatis相关信息。
  当然有些朋友可能会指正,不是还需要引入数据库驱动包吗?
  确实需要引入,但数据库驱动有很多,比如:mysql、oracle、sqlserver,这不属于mybatis的范畴,使用者可以根据项目的实际情况单独引入。
  如果程序只是需要连接数据库这一个功能还好,按上面的步骤做基本可以满足需求。但是,连接数据库可能只是庞大的项目体系中一个环节,实际项目中往往更复杂,需要引入更多的功能,比如:连接redis、连接mongodb、使用rocketmq、使用excel功能等等。
  引入这些功能的话,需要再把上面的步骤再重复一次,工作量无形当中增加了不少,而且有很多重复的工作。
  另外,还是有个问题,每次到要到maven中找合适的版本,如果哪次找的mybatis。jar包和mybatisspring。jar包版本不兼容,程序不是会出现问题?
  SpringBoot为了解决以上两个问题引入了starter机制。2starter有哪些要素?
  我们首先一起看看mybatisspringbootstarter。jar是如何定义的。
  可以看到它的METAINF目录下只包含了:pom。protperties配置maven所需的项目version、groupId和artifactId。pom。xml配置所依赖的jar包。MANIFEST。MF这个文件描述了该Jar文件的很多信息。spring。provides配置所依赖的artifactId,给IDE使用的,没有其他的作用。
  注意一下,没有一行代码。
  我们重点看一下pom。xml,因为这个jar包里面除了这个没有啥重要的信息?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionparentgroupIdorg。mybatis。spring。bootgroupIdmybatisspringbootartifactIdversion1。3。1versionparentmybatisspringbootstarterartifactIdnamemybatisspringbootstarternamedependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootstarterartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootstarterjdbcartifactIddependencydependencygroupIdorg。mybatis。spring。bootgroupIdmybatisspringbootautoconfigureartifactIddependencydependencygroupIdorg。mybatisgroupIdmybatisartifactIddependencydependencygroupIdorg。mybatisgroupIdmybatisspringartifactIddependencydependenciesproject
  从上面可以看出,pom。xml文件中会引入一些jar包,其中除了引入springbootstarter,之外重点看一下:mybatisspringbootautoconfigure。
  我们找到mybatisspringbootautoconfigure。jar文件,打开这个文件。
  里面包含如下文件:pom。properties配置maven所需的项目version、groupId和artifactIdpom。xml配置所依赖的jar包additionalspringconfigurationmetadata。json手动添加IDE提示功能MANIFEST。MF这个文件描述了该Jar文件的很多信息spring。factoriesSPI会读取的文件springconfigurationmetadata。json系统自动生成的IDE提示功能ConfigurationCustomizer自定义Configuration回调接口MybatisAutoConfigurationmybatis配置类MybatisPropertiesmybatis属性类SpringBootVFS扫描嵌套的jar包中的类
  springconfigurationmetadata。json和additionalspringconfigurationmetadata。json的功能差不多,我们在applicationContext。properties文件中输入spring时,会自动出现下面的配置信息可供选择,就是这个功能了。
  来自灵魂的一问:这两个文件有什么区别?
  答:如果pom。xml中引入了springbootconfigurationprocessor包,则会自动生成springconfigurationmetadata。json。
  如果需要手动修改里面的元数据,则可以在additionalspringconfigurationmetadata。json中编辑,最终两个文件中的元数据会合并到一起。
  MybatisProperties类是属性实体类:ConfigurationProperties(prefixMybatisProperties。MYBATISPREFIX)publicclassMybatisProperties{publicstaticfinalStringMYBATISPREFIXprivateStringconfigLprivateString〔〕mapperLprivateStringtypeAliasesPprivateStringtypeHandlersPprivatebooleancheckConfigLprivateExecutorTypeexecutorTprivatePropertiesconfigurationPNestedConfigurationPropertyprivateCpublicStringgetConfigLocation(){returnthis。configL}publicvoidsetConfigLocation(StringconfigLocation){this。configLocationconfigL}DeprecatedpublicStringgetConfig(){returnthis。configL}DeprecatedpublicvoidsetConfig(Stringconfig){this。configL}publicString〔〕getMapperLocations(){returnthis。mapperL}publicvoidsetMapperLocations(String〔〕mapperLocations){this。mapperLocationsmapperL}publicStringgetTypeHandlersPackage(){returnthis。typeHandlersP}publicvoidsetTypeHandlersPackage(StringtypeHandlersPackage){this。typeHandlersPackagetypeHandlersP}publicStringgetTypeAliasesPackage(){returnthis。typeAliasesP}publicvoidsetTypeAliasesPackage(StringtypeAliasesPackage){this。typeAliasesPackagetypeAliasesP}publicbooleanisCheckConfigLocation(){returnthis。checkConfigL}publicvoidsetCheckConfigLocation(booleancheckConfigLocation){this。checkConfigLocationcheckConfigL}publicExecutorTypegetExecutorType(){returnthis。executorT}publicvoidsetExecutorType(ExecutorTypeexecutorType){this。executorTypeexecutorT}publicPropertiesgetConfigurationProperties(){returnconfigurationP}publicvoidsetConfigurationProperties(PropertiesconfigurationProperties){this。configurationPropertiesconfigurationP}publicConfigurationgetConfiguration(){}publicvoidsetConfiguration(Configurationconfiguration){this。}publicResource〔〕resolveMapperLocations(){ResourcePatternResolverresourceResolvernewPathMatchingResourcePatternResolver();ListResourceresourcesnewArrayListResource();if(this。mapperLocations!null){for(StringmapperLocation:this。mapperLocations){try{Resource〔〕mappersresourceResolver。getResources(mapperLocation);resources。addAll(Arrays。asList(mappers));}catch(IOExceptione){ignore}}}returnresources。toArray(newResource〔resources。size()〕);}}
  可以看到Mybatis初始化所需要的很多属性都在这里,相当于一个JavaBean。
  下面重点看一下MybatisAutoConfiguration的代码:org。springframework。context。annotation。ConfigurationConditionalOnClass({SqlSessionFactory。class,SqlSessionFactoryBean。class})ConditionalOnBean(DataSource。class)EnableConfigurationProperties(MybatisProperties。class)AutoConfigureAfter(DataSourceAutoConfiguration。class)publicclassMybatisAutoConfiguration{privatestaticfinalLoggerloggerLoggerFactory。getLogger(MybatisAutoConfiguration。class);privatefinalMybatisPprivatefinalInterceptor〔〕privatefinalResourceLoaderresourceLprivatefinalDatabaseIdProviderdatabaseIdPprivatefinalListConfigurationCustomizerconfigurationCpublicMybatisAutoConfiguration(MybatisPropertiesproperties,ObjectProviderInterceptor〔〕interceptorsProvider,ResourceLoaderresourceLoader,ObjectProviderDatabaseIdProviderdatabaseIdProvider,ObjectProviderListConfigurationCustomizerconfigurationCustomizersProvider){this。this。interceptorsinterceptorsProvider。getIfAvailable();this。resourceLoaderresourceLthis。databaseIdProviderdatabaseIdProvider。getIfAvailable();this。configurationCustomizersconfigurationCustomizersProvider。getIfAvailable();}PostConstructpublicvoidcheckConfigFileExists(){if(this。properties。isCheckConfigLocation()StringUtils。hasText(this。properties。getConfigLocation())){Resourceresourcethis。resourceLoader。getResource(this。properties。getConfigLocation());Assert。state(resource。exists(),Cannotfindconfiglocation:resource(pleaseaddconfigfileorcheckyourMybatisconfiguration));}}BeanConditionalOnMissingBeanpublicSqlSessionFactorysqlSessionFactory(DataSourcedataSource)throwsException{SqlSessionFactoryBeanfactorynewSqlSessionFactoryBean();factory。setDataSource(dataSource);factory。setVfs(SpringBootVFS。class);if(StringUtils。hasText(this。properties。getConfigLocation())){factory。setConfigLocation(this。resourceLoader。getResource(this。properties。getConfigLocation()));}Configurationconfigurationthis。properties。getConfiguration();if(configurationnull!StringUtils。hasText(this。properties。getConfigLocation())){configurationnewConfiguration();}if(configuration!null!CollectionUtils。isEmpty(this。configurationCustomizers)){for(ConfigurationCustomizercustomizer:this。configurationCustomizers){customizer。customize(configuration);}}factory。setConfiguration(configuration);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(StringUtils。hasLength(this。properties。getTypeHandlersPackage())){factory。setTypeHandlersPackage(this。properties。getTypeHandlersPackage());}if(!ObjectUtils。isEmpty(this。properties。resolveMapperLocations())){factory。setMapperLocations(this。properties。resolveMapperLocations());}returnfactory。getObject();}BeanConditionalOnMissingBeanpublicSqlSessionTemplatesqlSessionTemplate(SqlSessionFactorysqlSessionFactory){ExecutorTypeexecutorTypethis。properties。getExecutorType();if(executorType!null){returnnewSqlSessionTemplate(sqlSessionFactory,executorType);}else{returnnewSqlSessionTemplate(sqlSessionFactory);}}publicstaticclassAutoConfiguredMapperScannerRegistrarimplementsBeanFactoryAware,ImportBeanDefinitionRegistrar,ResourceLoaderAware{privateBeanFactorybeanFprivateResourceLoaderresourceLOverridepublicvoidregisterBeanDefinitions(AnnotationMetadataimportingClassMetadata,BeanDefinitionRegistryregistry){ClassPathMapperScannerscannernewClassPathMapperScanner(registry);try{if(this。resourceLoader!null){scanner。setResourceLoader(this。resourceLoader);}ListStringpackagesAutoConfigurationPackages。get(this。beanFactory);if(logger。isDebugEnabled()){for(Stringpkg:packages){logger。debug(Usingautoconfigurationbasepackage{},pkg);}}scanner。setAnnotationClass(Mapper。class);scanner。registerFilters();scanner。doScan(StringUtils。toStringArray(packages));}catch(IllegalStateExceptionex){logger。debug(Couldnotdetermineautoconfigurationpackage,automaticmapperscanningdisabled。,ex);}}OverridepublicvoidsetBeanFactory(BeanFactorybeanFactory)throwsBeansException{this。beanFactorybeanF}OverridepublicvoidsetResourceLoader(ResourceLoaderresourceLoader){this。resourceLoaderresourceL}}org。springframework。context。annotation。ConfigurationImport({AutoConfiguredMapperScannerRegistrar。class})ConditionalOnMissingBean(MapperFactoryBean。class)publicstaticclassMapperScannerRegistrarNotFoundConfiguration{PostConstructpublicvoidafterPropertiesSet(){logger。debug(No{}found。,MapperFactoryBean。class。getName());}}}
  这个类就是一个Configuration(配置类),它里面定义很多bean,其中最重要的就是SqlSessionFactory的bean实例,该实例是Mybatis的核心功能,用它创建SqlSession,对数据库进行CRUD操作。
  除此之外,MybatisAutoConfiguration类还包含了:ConditionalOnClass配置了只有包含SqlSessionFactory。class和SqlSessionFactoryBean。class,该配置类才生效。ConditionalOnBean配置了只有包含dataSource实例时,该配置类才生效。EnableConfigurationProperties该注解会自动填充MybatisProperties实例中的属性。AutoConfigureAfter配置了该配置类在DataSourceAutoConfiguration类之后自动配置。
  这些注解都是一些辅助功能,决定Configuration是否生效,当然这些注解不是必须的。
  接下来,重点看看spring。factories文件有啥内容:org。springframework。boot。autoconfigure。EnableAutoConfigurationorg。mybatis。spring。boot。autoconfigure。MybatisAutoConfiguration
  里面只有一行配置,即key为EnableAutoConfiguration,value为MybatisAutoConfiguration。
  好了,介绍了这么多东西,现在我们来总结一下,
  starter几个要素如下图所示:
  那么,编写starter需要哪些步骤?1。需要定义一个名称为xxxspringbootstarter的空项目,里面不包含任何代码,可以有pom。xml和pom。properties文件。2。pom。xml文件中包含了名称为xxxspringbootautoconfigure的项目。3。xxxspringbootautoconfigure项目中包含了名称为xxxAutoConfiguration的类,该类可以定义一些bean实例。当然,Configuration类上可以打一些如:ConditionalOnClass、ConditionalOnBean、EnableConfigurationProperties等注解。4。需要在spring。factories文件中增加key为EnableAutoConfiguration,value为xxxAutoConfiguration。
  我们试着按照这四步,自己编写一个starter看看能否成功,验证一下总结的内容是否正确。3如何定义自己的starter?3。1先创建一个空项目
  该项目名称为idgeneratestarter,注意为了方便我把项目重命名了,原本应该是叫idgeneratespringbootstarter的,如下图所示:
  pom。xml文件定义如下:?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionversion1。3。1versiongroupIdcom。suegroupIdidgeneratespringbootstarterartifactIdnameidgeneratespringbootstarternamedependenciesdependencygroupIdcom。suegroupIdidgeneratespringbootautoconfigureartifactIdversion1。3。1versiondependencydependenciesproject
  我们看到,它只引入了idgeneratespringbootautoconfigure。当然如果有需要这里还可以引入多个autoconfigure或者多个其他jar包或者。3。2创建idgenerateautoconfigure
  同样为了方便我把项目重命名了,原本是叫idgeneratespringbootautoconfigure,如下图所示:
  该项目当中包含:pom。xml、spring。factories、IdGenerateAutoConfiguration、IdGenerateService和IdProperties这5个关键文件,下面我们逐一看看。
  先从pom。?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdparentgroupIdorg。springframework。bootgroupIdspringbootstarterparentartifactIdversion2。0。4。RELEASEversionparentmodelVersion4。0。0modelVersionversion1。3。1versiongroupIdcom。suegroupIdidgeneratespringbootautoconfigureartifactIdnameidgeneratespringbootautoconfigurenamedependenciesdependencygroupIdorg。springframework。bootgroupIdspringbootstarterartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootautoconfigureartifactIddependencydependencygroupIdorg。springframework。bootgroupIdspringbootconfigurationprocessorartifactIdoptionaltrueoptionaldependencydependenciesbuildpluginsplugingroupIdorg。apache。maven。pluginsgroupIdmavencompilerpluginartifactIdconfigurationsource1。8sourcetarget1。8targetconfigurationpluginpluginsbuildproject
  我们可以看到,这个文件比较简单就引入了:springbootstarter:springboot的相关jar包。springbootautoconfigure:springboot自动配置相关jar包。springbootconfigurationprocessor:springboot生成IDE提示功能相关jar包。
  重点看看spring。factories文件:org。springframework。boot。autoconfigure。EnableAutoConfigurationcom。sue。IdGenerateAutoConfiguration
  它里面只包含一行配置,其中key是EnableAutoConfiguration,value是IdGenerateAutoConfiguration。
  再重点看一下IdGenerateAutoConfigurationConditionalOnClass(IdProperties。class)EnableConfigurationProperties(IdProperties。class)ConfigurationpublicclassIdGenerateAutoConfiguration{AutowiredprivateIdPBeanpublicIdGenerateServiceidGenerateService(){returnnewIdGenerateService(properties。getWorkId());}}
  该类是一个使用了Configuration注解标记为了配置类,生效的条件是ConditionalOnClass注解中检测到包含IdProperties。class。并且使用EnableConfigurationProperties注解会自动注入IdProperties的实例。
  此外,最关键的点是该类里面创建了idGenerateService的bean实例,这是自动配置的精髓。
  再看看IdGenerateServicepublicclassIdGenerateService{privateLongworkId;publicIdGenerateService(LongworkId){this。workIdworkId;}publicLonggenerate(){returnnewRandom()。nextInt(100)this。workId;}}
  我们可以看到它是一个普通的类,甚至都没有使用Service注解,里面有个generate方法,根据workId的值和随机数动态生成id。
  最后看看IdPropertiesConfigurationProperties(prefixIdProperties。PREFIX)publicclassIdProperties{publicstaticfinalStringPREFIXprivateLongworkId;publicLonggetWorkId(){returnworkId;}publicvoidsetWorkId(LongworkId){this。workIdworkId;}}
  它是一个配置实体类,里面包含了相关的配置文件。使用ConfigurationProperties注解,会自动把application。properties文件中以sue开通的,参数名称跟IdProperties中一样的参数值,自动注入到IdProperties对象中。3。3创建idgeneratetest
  这个项目主要用于测试。
  该项目里面包含:pom。xml、application。properties、Application和TestRunner文件。
  先看看pom。xml文件?xmlversion1。0encodingUTF8?projectxmlnshttp:maven。apache。orgPOM4。0。0xmlns:xsihttp:www。w3。org2001XMLSchemainstancexsi:schemaLocationhttp:maven。apache。orgPOM4。0。0http:maven。apache。orgxsdmaven4。0。0。xsdmodelVersion4。0。0modelVersionversion1。3。1versiongroupIdcom。suegroupIdspringbootidgeneratetestartifactIdnamespringbootidgeneratetestnamedependenciesdependencygroupIdcom。suegroupIdidgeneratespringbootstarterartifactIdversion1。3。1versiondependencydependenciesproject
  由于只测试刚刚定义的id生成功能,所以只引入的idgeneratespringbootstarterjar包。
  application。properties配置资源文件sue。workId123
  只有一行配置,因为我们的IdProperties中目前只需要这一个参数。
  Application是测试程序启动类SpringBootApplicationpublicclassApplication{publicstaticvoidmain(String〔〕args){SpringApplication。run(Application。class,args);}}
  很简单,就是一个普通的springboot启动类
  TestRunner是我们的测试类ComponentpublicclassTestRunnerimplementsApplicationRunner{AutowiredprivateIdGenerateServiceidGenerateSpublicvoidrun(ApplicationArgumentsargs)throwsException{LongsysNoidGenerateService。generate();System。out。println(sysNo);}}
  它实现了ApplicationRunner接口,所以在springboot启动的时候会调用该类的run方法。
  好了,所有自定义starter的代码和测试代码都已经就绪。接下,运行一下Application类的main方法。
  运行结果:176
  完美,验证成功了。
  接下来,我们分析一下starter的底层实现原理。4starter的底层原理是什么?
  通过上面编写自己的starter的例子,相信大家对starter的认识更进一步了,现在跟大家一起看看starter的底层是如何实现的。
  idgeneratestarter。jar其实是一个空项目,依赖于idgenerateautoconfiguration。jar。
  idgeneratestarter。jar是一个入口,我们给他取一个更优雅的名字:门面模式,其他业务系统想引入相应的功能,必须要通过这个门面。
  我们重点分析一下idgenerateautoconfiguration。jar
  该jar包核心内容是:IdGenerateConfiguration,这个配置类中创建了IdGenerateService对象,IdGenerateService是我们所需要自动配置的具体功能。
  接下来一个最重要的问题:IdGenerateConfiguration为什么会自动加载的呢?
  还记得我们定义的spring。factories文件不?org。springframework。boot。autoconfigure。EnableAutoConfigurationcom。sue。IdGenerateAutoConfiguration
  它里面只包含一行配置,其中key是EnableAutoConfiguration,value是IdGenerateAutoConfiguration。
  要搞明白这个过程,要从Application类的SpringBootApplication注解开始:Target(ElementType。TYPE)Retention(RetentionPolicy。RUNTIME)DocumentedInheritedSpringBootConfigurationEnableAutoConfigurationComponentScan(excludeFilters{Filter(typeFilterType。CUSTOM,classesTypeExcludeFilter。class),Filter(typeFilterType。CUSTOM,classesAutoConfigurationExcludeFilter。class)})publicinterfaceSpringBootApplication{AliasFor(annotationEnableAutoConfiguration。class)C?〔〕exclude()default{};AliasFor(annotationEnableAutoConfiguration。class)String〔〕excludeName()default{};AliasFor(annotationComponentScan。class,attributebasePackages)String〔〕scanBasePackages()default{};AliasFor(annotationComponentScan。class,attributebasePackageClasses)C?〔〕scanBasePackageClasses()default{};}
  从上面可以看出该注解里面包含了EnableAutoConfiguration注解。Target(ElementType。TYPE)Retention(RetentionPolicy。RUNTIME)DocumentedInheritedAutoConfigurationPackageImport(AutoConfigurationImportSelector。class)publicinterfaceEnableAutoConfiguration{StringENABLEDOVERRIDEPROPERTYspring。boot。C?〔〕exclude()default{};String〔〕excludeName()default{};}
  EnableAutoConfiguration注解会引入AutoConfigurationImportSelector类。
  该类的selectImports方法一个关键方法:OverridepublicString〔〕selectImports(AnnotationMetadataannotationMetadata){配置有没有配置spring。boot。enableautoconfiguration开关,默认为true如果为false,则不执行自动配置的功能,直接返回if(!isEnabled(annotationMetadata)){returnNOIMPORTS;}找springautoconfiguremetadata。properties中的元素AutoConfigurationMetadataautoConfigurationMetadataAutoConfigurationMetadataLoader。loadMetadata(this。beanClassLoader);获取EnableAutoConfiguration注解中的属性AnnotationAttributesattributesgetAttributes(annotationMetadata);获取工程下所有配置key为EnableAutoConfiguration的值,即IdGenerateConfiguration等类。ListStringconfigurationsgetCandidateConfigurations(annotationMetadata,attributes);删除重复的值configurationsremoveDuplicates(configurations);获取需要排除的规则列表SetStringexclusionsgetExclusions(annotationMetadata,attributes);检查checkExcludedClasses(configurations,exclusions);删除需要排除的值configurations。removeAll(exclusions);根据配置文件中配置的开关,过滤一部分不满足条件的值configurationsfilter(configurations,autoConfigurationMetadata);fireAutoConfigurationImportEvents(configurations,exclusions);returnStringUtils。toStringArray(configurations);}
  这里就是starter能够自动配置的秘密。
  此外,有些朋友看其他人定义的springbootstarter可能会有疑惑。
  先看看druidspringbootstarter
  alibaba定义的druidspringbootstarter只有xxxspringbootstarter。jar文件,而没有xxxspringbootautoconfigure。jar文件。
  再看看springbootstarterjdbc:
  更神奇的是这个文件中连pom。xml都没有,一脸懵逼
  是不是我讲错了?
  答:其实没有。
  SpringBoot的原则是约定优于配置。
  从springbootstarterjdbc内部空实现来看,它的约定是要把xxxspringbootstarter。jar和xxxspringbootautoconfigure。jar区分开的。个人认为,alibaba定义得并不好,没有遵照springboot的约定,虽然功能不受影响。(这个地方欢迎一起探讨一下)
  而springboot自己定义的springbootstarterjdbc为什么连pom。xml文件也没有呢?
  它不需要依赖xxxspringbootautoconfigure。jar文件吗?
  因为springboot把所有的自动配置的类都统一放到springbootautoconfigure。jar下面了:
  spring。factories文件内容如下:
  SpringBoot这样集中管理自动配置,而不需要从各个子包中遍历,我个人认为是为了查找效率。
  我们最后再看看springcloudstarteropenfegin
  明显看到,它是遵循了我们说的原则的。
  除此之外,还有一个原则一顺便提一下。
  SpringBoot和SpringCloud系列定义jar包的名称是:springbootstarterxxx。jarspringcloudstarterxxx。jar
  而我们自己的项目定义的jar应该是:xxxspringbootstarter。jar
  原文链接:https:mp。weixin。qq。comsPcTF2id8ooT2qfqb5Qe2NA
投诉 评论 转载

我的抠门老公之旅游年卡我的抠门老公,在我的威逼利诱下,终于买了张武汉职工惠游年卡,一百块钱,83个景点,能玩一年,他居然跟我斗争了半年,才心如刀割般买了。今天他耷拉着像鞋拔子的脸,跟我出门了。……怎么就没想到前几天,我和几个女同事到阳朔去游玩,做服装生意的表姐也跟着一同前往。回来的路上,我发现表姐的手上多了一串崭新的串珠,挺漂亮的,忍不住向她打听价格。表姐自豪地说:只花了18……晚睡对孩子的危害你知道有多大吗孩子折腾到半夜,大人也跟着晚睡,当然现在很多年轻的父母,自己本身睡的就很晚,所以对于孩子晚睡,并不是特别重视,那么,晚睡对孩子的危害,你知道有多大吗?就让本站的小编和您一起去了……潜在的危机由于网络数据传输同样被压缩了近两千倍,采用这种编码技术以后,原本4G最高的100Mbps秒,一下子就可以扩展到接近200Gbps秒,使得5G引以为傲的最高10Gbps秒的速度就……世乒赛女团小组赛今日收官,国乒力争4连胜,陈梦或将休赛一场您好,我是乒谈。今天是10月4日,成都世乒赛继续进行。女团小组赛将在今天全部收官,国乒迎战最后一个对手马来西亚队,争取全胜晋级淘汰赛!以下是具体的赛程,男团赛程已经……我在锂电厂打工刚毕业就拿18万年终奖,猎头电话铺天盖地本文来源:时代财经作者:贺晴编者按:在全球汽车产业电气化转型加速的当下,锂电产业链正上演疯狂的造富神话,其中尤以上游锂矿、锂盐商最甚。凭借前期布局,家里有矿的上游企……感冒期间不要吃这些食物太辣的食物。辣椒等刺激性食物会让脆弱的胃肠功能更加紊乱,甚至引发恶心、呕吐的风险。高盐食物。实验数据显示,减少盐的摄入,可提高唾液中的溶菌酶含量,保护口腔、咽……彻底搞懂SpringBoot中的starter机制前言我们都知道,Spring的功能非常强大,但也有些弊端。比如:我们需要手动去配置大量的参数,没有默认值,需要我们管理大量的jar包和它们的依赖。为了提升Sprin……大年夜大年三十,我们一家回福州过年。道路两旁张灯结彩,可热闹了。晚上,我们去酒店吃年夜饭。厨师们大显身手,忙忙碌碌。开饭喽!爷爷一声令下。一道道美食摆在我面前,一阵阵香气扑鼻而……小公举夺口红是怎么赚钱的这个网赚骗局有人收割百万在网赚这个圈子里,从来不缺乏新事物的诞生,因为人都是喜新厌旧的,也只有新才能抓住人的眼球,但很多时候的新也只不过是换了一个躯壳而已,其内在本质并没有变。例如最近突如起来火了一波……究竟晨跑午跑夜跑哪种最好究竟晨跑、午跑、夜跑哪种最好?其实,适合自己的就是最好的,而清楚了解各时间段跑步的利与弊,才能选择最适合的。晨跑晨跑优点:使一天精神饱满晨跑缺点:加大心血管负……和你在一起你问我,和你在一起是什么感觉。为什么想到问这个问题呢?看着你灿烂的笑靥一如盛开的栀子花,深深的酒窝里装满期待。我说和你在一起很快乐,很温暖。你问;还有呢?是啊,还有……
妊娠糖尿病吃鱼好吗2023刚开始就把难度挑到地狱级了吃樟脑丸会致人死亡吗莫兰特吹牛皮后立刻五战四败,这次打脸的速度比他的突破更快辣椒对人的危害是什么花生榨汁能补充镁吗饮水机真的卫生吗长期喝饮水机的热水好不好初中学历男创业打拼一年成火锅店店长年薪万坐月子吃虾好吗纯中药染发剂有什么牌子对探春的历史评价是什么中国四大海味鲍翅肚参有钱人才吃得起的吃这些食物会降低性欲!前台工作总结二十年后的家乡500字优秀作文带你认识鼻咽癌天空并不是我们的界限孕妇有霉菌性阴炎会影响胎儿吗美国神助攻,中国热捧RISCV芯片,ARM的饭突然不美味了任挑一个都完爆辽宁!广东引援4大目标正式曝光,内线全联盟最强男人肾不好多吃这种食物让你重振雄风火箭首秀败北,谁之过?流浪儿童安全教育有哪些问题

友情链接:中准网聚热点快百科快传网快生活快软网快好知文好找