SpringBoot的starter到底是什么?

前言

我们都知道,Spring的功能非常强大,但也有些弊端。比如:我们需要手动去配置大量的参数,没有默认值,需要我们管理大量的jar包和它们的依赖。

创新互联主营祁阳网站建设的网络公司,主营网站建设方案,成都App制作,祁阳h5重庆小程序开发搭建,祁阳网站营销推广欢迎祁阳等地区企业咨询

为了提升Spring项目的开发效率,简化一些配置,Spring官方引入了SpringBoot。

当然,引入SpringBoot还有其他原因,在这里就不过多描述了。

本文重点跟大家一起聊聊SpringBoot的starter机制,因为它太重要了。

1 为什么要用starter?

在SpringBoot还没有出来之前,我们使用Spring开发项目。如果程序需要连接数据库,我们一般会使用Hibernate或Mybatis等ORM框架,这里我以Mybatis为例,具体的操作步骤如下:

  • 到maven仓库去找需要引入的mybatis jar包,选取合适的版本。
  • 到maven仓库去找mybatis-spring整合的jar包,选取合适的版本。
  • 在spring的applicationContext.xml文件中配置dataSource和mybatis相关信息。

当然有些朋友可能会指正,不是还需要引入数据库驱动包吗?

确实需要引入,但数据库驱动有很多,比如:mysql、oracle、sqlserver,这不属于mybatis的范畴,使用者可以根据项目的实际情况单独引入。

如果程序只是需要连接数据库这一个功能还好,按上面的步骤做基本可以满足需求。但是,连接数据库可能只是庞大的项目体系中一个环节,实际项目中往往更复杂,需要引入更多的功能,比如:连接redis、连接mongodb、使用rocketmq、使用excel功能等等。

引入这些功能的话,需要再把上面的步骤再重复一次,工作量无形当中增加了不少,而且有很多重复的工作。

另外,还是有个问题,每次到要到maven中找合适的版本,如果哪次找的mybatis.jar包 和 mybatis-spring.jar包版本不兼容,程序不是会出现问题?

SpringBoot为了解决以上两个问题引入了starter机制。

2 starter有哪些要素?

我们首先一起看看mybatis-spring-boot-starter.jar是如何定义的。

可以看到它的META-INF目录下只包含了:

  • pom.protperties  配置maven所需的项目version、groupId和artifactId。
  • pom.xml  配置所依赖的jar包。
  • MANIFEST.MF 这个文件描述了该Jar文件的很多信息。
  • spring.provides 配置所依赖的artifactId,给IDE使用的,没有其他的作用。

注意一下,没有一行代码。

我们重点看一下pom.xml,因为这个jar包里面除了这个没有啥重要的信息。



4.0.0

org.mybatis.spring.boot
mybatis-spring-boot
1.3.1

mybatis-spring-boot-starter
mybatis-spring-boot-starter


org.springframework.boot
spring-boot-starter


org.springframework.boot
spring-boot-starter-jdbc


org.mybatis.spring.boot
mybatis-spring-boot-autoconfigure


org.mybatis
mybatis


org.mybatis
mybatis-spring


从上面可以看出,pom.xml文件中会引入一些jar包,其中除了引入spring-boot-starter,之外重点看一下:mybatis-spring-boot-autoconfigure。

我们找到mybatis-spring-boot-autoconfigure.jar文件,打开这个文件。

里面包含如下文件:

  • pom.properties  配置maven所需的项目version、groupId和artifactId
  • pom.xml  配置所依赖的jar包
  • additional-spring-configuration-metadata.json  手动添加IDE提示功能
  • MANIFEST.MF 这个文件描述了该Jar文件的很多信息
  • spring.factories SPI会读取的文件
  • spring-configuration-metadata.json 系统自动生成的IDE提示功能
  • ConfigurationCustomizer 自定义Configuration回调接口
  • MybatisAutoConfiguration mybatis配置类
  • MybatisProperties mybatis属性类
  • SpringBootVFS 扫描嵌套的jar包中的类

spring-configuration-metadata.json和additional-spring-configuration-metadata.json的功能差不多,我们在applicationContext.properties文件中输入spring时,会自动出现下面的配置信息可供选择,就是这个功能了。

来自灵魂的一问:这两个文件有什么区别?

答:如果pom.xml中引入了spring-boot-configuration-processor包,则会自动生成spring-configuration-metadata.json。

如果需要手动修改里面的元数据,则可以在additional-spring-configuration-metadata.json中编辑,最终两个文件中的元数据会合并到一起。

MybatisProperties类是属性实体类:

@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {

public static final String MYBATIS_PREFIX = "mybatis";
private String configLocation;
private String[] mapperLocations;
private String typeAliasesPackage;
private String typeHandlersPackage;
private boolean checkConfigLocation = false;
private ExecutorType executorType;
private Properties configurationProperties;
@NestedConfigurationProperty
private Configuration configuration;

public String getConfigLocation() {
return this.configLocation;
}

public void setConfigLocation(String configLocation) {
this.configLocation = configLocation;
}

@Deprecated
public String getConfig() {
return this.configLocation;
}

@Deprecated
public void setConfig(String config) {
this.configLocation = config;
}

public String[] getMapperLocations() {
return this.mapperLocations;
}

public void setMapperLocations(String[] mapperLocations) {
this.mapperLocations = mapperLocations;
}

public String getTypeHandlersPackage() {
return this.typeHandlersPackage;
}

public void setTypeHandlersPackage(String typeHandlersPackage) {
this.typeHandlersPackage = typeHandlersPackage;
}

public String getTypeAliasesPackage() {
return this.typeAliasesPackage;
}

public void setTypeAliasesPackage(String typeAliasesPackage) {
this.typeAliasesPackage = typeAliasesPackage;
}

public boolean isCheckConfigLocation() {
return this.checkConfigLocation;
}

public void setCheckConfigLocation(boolean checkConfigLocation) {
this.checkConfigLocation = checkConfigLocation;
}

public ExecutorType getExecutorType() {
return this.executorType;
}

public void setExecutorType(ExecutorType executorType) {
this.executorType = executorType;
}

public Properties getConfigurationProperties() {
return configurationProperties;
}

public void setConfigurationProperties(Properties configurationProperties) {
this.configurationProperties = configurationProperties;
}

public Configuration getConfiguration() {
return configuration;
}

public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}

public Resource[] resolveMapperLocations() {
ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
List resources = new ArrayList();
if (this.mapperLocations != null) {
for (String mapperLocation : this.mapperLocations) {
try {
Resource[] mappers = resourceResolver.getResources(mapperLocation);
resources.addAll(Arrays.asList(mappers));
} catch (IOException e) {

}
}
}
return resources.toArray(new Resource[resources.size()]);
}
}

可以看到Mybatis初始化所需要的很多属性都在这里,相当于一个JavaBean。

下面重点看一下MybatisAutoConfiguration的代码:

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {

private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List configurationCustomizers;
public MybatisAutoConfiguration(MybatisProperties properties,
ObjectProvider interceptorsProvider,
ResourceLoader resourceLoader,
ObjectProvider databaseIdProvider,
ObjectProvider> configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}

@PostConstruct
public 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
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(this.properties.getConfigLocation())) {
factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
}
Configuration configuration = this.properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
configuration = new Configuration();
}
if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
for (ConfigurationCustomizer customizer : 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());
}

return factory.getObject();
}

@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;

@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}

List packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
for (String pkg : packages) {
logger.debug("Using auto-configuration base package '{}'", pkg);
}
}

scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}

@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}

@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration {

@PostConstruct
public void afterPropertiesSet() {
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.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

里面只有一行配置,即key为EnableAutoConfiguration,value为MybatisAutoConfiguration。

好了,介绍了这么多东西,现在我们来总结一下。

starter几个要素如下图所示:

那么,编写starter需要哪些步骤?

  • 需要定义一个名称为xxx-spring-boot-starter的空项目,里面不包含任何代码,可以有pom.xml和pom.properties文件。
  • pom.xml文件中包含了名称为xxx-spring-boot-autoconfigure的项目。
  • xxx-spring-boot-autoconfigure项目中包含了名称为xxxAutoConfiguration的类,该类可以定义一些bean实例。当然,Configuration类上可以打一些如:ConditionalOnClass、ConditionalOnBean、EnableConfigurationProperties等注解。
  • 需要在spring.factories文件中增加key为EnableAutoConfiguration,value为xxxAutoConfiguration。

我们试着按照这四步,自己编写一个starter看看能否成功,验证一下总结的内容是否正确。

3 如何定义自己的starter?

3.1 先创建一个空项目

该项目名称为id-generate-starter,注意为了方便我把项目重命名了,原本应该是叫id-generate-spring-boot-starter的,如下图所示:

pom.xml文件定义如下:



4.0.0
1.3.1
com.sue
id-generate-spring-boot-starter
id-generate-spring-boot-starter


com.sue
id-generate-spring-boot-autoconfigure
1.3.1


我们看到,它只引入了id-generate-spring-boot-autoconfigure。当然如果有需要这里还可以引入多个autoconfigure或者多个其他jar包或者。

3.2 创建id-generate-autoconfigure

同样为了方便我把项目重命名了,原本是叫id-generate-spring-boot-autoconfigure,如下图所示:

该项目当中包含:pom.xml、spring.factories、IdGenerateAutoConfiguration、IdGenerateService 和 IdProperties 这5个关键文件,下面我们逐一看看。

先从pom.xml




org.springframework.boot
spring-boot-starter-parent
2.0.4.RELEASE

4.0.0
1.3.1
com.sue
id-generate-spring-boot-autoconfigure
id-generate-spring-boot-autoconfigure



orgSpringBoot的starter到底是什么?
浏览地址:http://www.hantingmc.com/qtweb/news1/273151.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联