写在前面

在前面我们对SpringBoot的自动装配原理有了一个较为深刻的研究,那么接下来我们就分析一下其中的场景启动器(starter),并尝试自定义一个属于自己的场景启动器(starter)。

场景启动器简介

场景启动器(starter),其实就是一个个的功能。SpringBoot会将用户常用的一些功能抽离出来,做成一个个的场景启动器,这些场景启动器会导入实现这些功能所需的全部依赖组件,这样开发者只需在项目中引入这些场景启动器,那么相应的依赖就会加载进来。开发者只需通过修改配置文件,就能实现使用相应功能这一目的。

父场景启动器介绍

当我们新建一个SpringBoot项目时,POM文件中会自动添加一个父依赖,该依赖名称为spring-boot-starter-parent

1
2
3
4
5
6
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

可以看到这个spring-boot-starter-parent是一个场景启动器,点进去查看一下这个依赖,可以看到里面又有一个名为spring-boot-dependencies的依赖,用于进行项目依赖的版本管理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.6.7</version>
</parent>
<artifactId>spring-boot-starter-parent</artifactId>
<packaging>pom</packaging>
<name>spring-boot-starter-parent</name>
<description>Parent pom providing dependency and plugin management for applications built with Maven</description>
<properties>
<java.version>1.8</java.version>
<resource.delimiter>@</resource.delimiter>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>

同时里面配置了6个默认的配置信息,如下所示:

1
2
3
4
5
6
7
8
9
<!--默认Java版本1.8-->
<java.version>1.8</java.version>
<!--默认资源分隔符为@-->
<resource.delimiter>@</resource.delimiter>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<!--默认编码格式使用UTF-8-->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

实际上这个spring-boot-starter-parent还提供了默认的资源过滤和插件配置。

再来看一下这个spring-boot-dependencies依赖的源码,如下所示:

1
2
3
4
5
6
7
8
9
<properties>
<activemq.version>5.16.4</activemq.version>
<antlr2.version>2.7.7</antlr2.version>
<appengine-sdk.version>1.9.96</appengine-sdk.version>
<artemis.version>2.19.1</artemis.version>
<aspectj.version>1.9.7</aspectj.version>
<assertj.version>3.21.0</assertj.version>
......
</properties>

可以看到它定义了所需场景启动器的版本信息,因此开发者在需要某场景启动器时,只需导入对应的场景启动器名称,无需再指定具体的版本号,这里已经指定并解决了各个场景启动器版本之间的冲突问题。

场景启动器原理

当开发者导入所需的starter之后,SpringBoot会将该starter所依赖的组件自动导入并自动配置,这就是我们常说的SpringBoot的自动配置。

自动配置原理

自动配置类的获取与注入

尽管在前一篇我们已经分析了SpringBoot的自动装配原理,但是笔者没有结合项目和一些图片来介绍,所以可能比较难以理解,这里再分析一次,可以加深理解。

首先查看一下项目的启动类源码,如下所示:

1
2
3
4
5
6
@SpringBootApplication
public class KenBingThoughtsApplication {
public static void main(String[] args) {
SpringApplication.run(KenBingThoughtsApplication.class, args);
}
}

通过分析可以知道这个@SpringBootApplication注解内部的结构:

这里面有一个非常重要的类AutoConfigurationImportSelector,该类的作用图中已经说清楚了,不过说一下其中一个名为selectImports的方法:

1
2
3
4
5
6
7
8
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}

可以看到它返回了一个字符串数组,下图是它的处理过程:

接下来再来看一下这个spring.factories文件,这里以spring-boot-autoconfigure包为例进行查阅:

可以看到这里面所有以xxxAutoConfiguration结尾的类都是SpringBoot官方为所有场景启动器所提供的自动配置类。这里以常用的WebMvcAutoConfiguration为例进行介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
......
}

可以看到这个类上使用了很多条件注解,以及配置注解表明这个类会向Spring容器中注册,同时也是有条件的生效。

【自动配置类的获取与注入原理】
(1)SpringBoot项目在启动时会扫描入口类所在包及其子包下的所有类并注入到Spring容器中;
(2)同时也会扫描类路径下META-INF/spring.factories文件,并从中加载所有以xxxAutoConfiguration结尾的自动配置类,这样就完成了自动配置类的获取与注入工作。

自动配置过程

前面以xxxAutoConfiguration结尾的自动配置类被注册到容器中后,会完成自动导入和自动配置工作。首先我们来查看一下SpringBoot中配置文件与POJO对象之间的映射关系,这也是实现自动配置的基础。

SpringBoot采用了配置集中化管理这一理念,即所有的配置信息都可以在一个名为application.yml或者application.properties的配置文件中进行设置。实际上这个配置文件可以通过@ConfigurationProperties注解来与定义的属性POJO类进行关联,注意POJO类的命名格式建议为xxxProperties,不一定非得是这样,但是无论怎样都要与在@EnableConfigurationProperties(xxxProperties.class)注解中设置的类名保持一致,还有这个POJO类中各个属性字段都要有默认值。这其实就是SpringBoot所推崇的约定大于配置这一理念,在尽可能减少用户配置的同时又不减少灵活性,即用户可以随心所欲的覆盖这些属性的默认值。

这里我们在配置文件中定义一个名为person的前缀,然后又定义了一个名为Person的实体类,那么接下来两者就可以通过@ConfigurationProperties(prefix="person")注解进行配置映射:

当然了,在实际使用时还需要配合@EnableConfigurationProperties({person.class})注解,这样才会自动将与配置文件绑定好的POJO类注入到Spring容器中。

接下来我们以最常用的HttpEncodingAutoConfiguration(http编码自动配置类)为例,介绍这个自动配置类进行自动配置的整个过程。先查阅HttpEncodingAutoConfiguration自动配置类源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//表示此类为一个配置类,可以被Spring扫描的到
@Configuration(
proxyBeanMethods = false
)
//表示将与配置文件绑定好的ServerProperties POJO类注入到IOC容器中,使之生效
@EnableConfigurationProperties({ServerProperties.class})
//@Conditional是条件注解,会根据不同的判断条件,如果满足指定的条件,该配置类中的配置才会生效
//判断是否为web应用,是的话当前配置类中的配置才会生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//判断是否存在CharacterEncodingFilter这个类,如果有的话当前配置类中的配置才会生效
@ConditionalOnClass({CharacterEncodingFilter.class})
//判断是否存在server.servlet.encoding.enabled属性,如果matchIfMissing = true表示即使我们不配置属性值为true,该配置类默认也会生效
@ConditionalOnProperty(
prefix = "server.servlet.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
//注入与配置文件属性相绑定的属性配置类
private final Encoding properties;

//提供一个有参的构造方法,并在HttpEncodingAutoConfiguration对象实例化的时候注入其中
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}

//当系统中没有提供CharacterEncodingFilter这一实例的时候,系统会默认提供一个CharacterEncodingFilter实例
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
return filter;
}

//自定义本地语言字符编码映射器
@Bean
public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
}

static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final Encoding properties;

LocaleCharsetMappingsCustomizer(Encoding properties) {
this.properties = properties;
}

public void customize(ConfigurableServletWebServerFactory factory) {
if (this.properties.getMapping() != null) {
factory.setLocaleCharsetMappings(this.properties.getMapping());
}

}

public int getOrder() {
return 0;
}
}
}

接下来通过一张图来了解自动配置的整个过程:

【自动配置过程】
(1)容器会根据当前不同注解中的条件来决定当前这个自动配置类是否生效;
(2)如果当前自动配置类生效,那么就会给容器中添加所需的对应组件;
(3)这些对应组件的属性是从对应的属性POJO类中获取,而这些属性POJO类则是与配置文件相绑定的;
(4)配置文件中可以配置项的项都是在属性POJO类中定义的,后续开发者可以根据名称及前缀进行判断。

一句话总结:配置文件与POJO类进行属性映射绑定,自动配置类确定是否生效,生效则添加对应的POJO类。

小结

SpringBoot在启动时会加载大量的自动配置类,如果开发者所需要的功能在默认自动配置类中有则会进行自动导入和自动配置工作;如果没有则需要手动添加对应的组件。一般需要定义一个属性POJO类完成与application.yml或者application.properties配置文件的属性映射绑定工作,然后再定义一个以xxxAutoConfiguration结尾的自动配置类,根据条件来选择性的让自动配置类生效,并注入自定义的属性POJO类。

这里我们再提一下这个由@Conditional注解所派生出来的注解,作用是只有满足这些注解的条件,才会给容器中注入所需的组件,里面的配置内容才会生效。一些常用的由@Conditional派生注解如下表所示:

@Conditional派生注解 作用(判断是否满足指定条件)
@ConditionalOnNotWebApplication 当前不是Web环境
@ConditionalOnWebApplication 当前是Web环境
@ConditionalOnResource 类路径下是否有指定资源文件
@ConditionalOnExpression 满足SPEL表达式
@ConditionalOnMissingBean 容器中不存在指定的Bean
@ConditionalOnBean 容器中存在指定的Bean
@ConditionalOnSingleCandidate 容器中只有一个指定的Bean或者这个Bean是首选Bean
@ConditionalOnJava 系统的Java版本是否满足指定
@ConditionalOnMissingClass 系统中没有指定的类
@ConditionalOnClass 系统中有指定的类
@ConditionalOnProperty 系统中指定的属性是否有指定的值

别看上面有这么多自动配置类,但是这些配置类都需要在一定的条件下才会生效,即加载了这么多的配置类,生效的都是满足了指定条件。开发者可以通过在application.yml或者application.properties配置文件中添加debug=true属性来开启SpringBoot的调试类,并让控制台输出条件评估报告:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//条件评估报告
============================
CONDITIONS EVALUATION REPORT
============================

//已启用的自动配置类(正匹配)
Positive matches:
-----------------

AopAutoConfiguration.ClassProxyingConfiguration matched:
- @ConditionalOnMissingClass did not find unwanted class 'org.aspectj.weaver.Advice' (OnClassCondition)
- @ConditionalOnProperty (spring.aop.proxy-target-class=true) matched (OnPropertyCondition)

JmxAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.jmx.export.MBeanExporter' (OnClassCondition)
- @ConditionalOnProperty (spring.jmx.enabled=true) matched (OnPropertyCondition)

TaskSchedulingAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler' (OnClassCondition)

//未启用的自动配置类(负匹配)
Negative matches:
-----------------

ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)
......

//排除的的自动配置类
Exclusions:
-----------

None

//没有限定条件的自动配置类
Unconditional classes:
----------------------

org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
......

自定义场景启动器步骤

starter命名约定

SpringBoot遵循约定大于配置这一原则,因此对于starter的命名也有一定的约定规则,了解这些规则可便于开发者知悉这些starter功能及作用。

对于官方提供的starter而言,命名规则为:spring-boot-starter-+模块名,如spring-boot-starter-web

对于非官方(即自定义)提供的starter而言,命名规则为:模块名+spring-boot-starter,如mybatis-spring-boot-starter

starter模块整体架构

通过前面的分析,我们知道starter模块整体主要由xxxAutoConfigurationxxxProperties组成。

xxxAutoConfiguration是自动配置类,用于自动注入所需的一些组件,并使用xxxProperties类来获取组件相关的一些配置信息。xxxProperties是属性配置类,即从配置文件中获取对应的属性值以覆盖默认值。请注意,由于starter的作用只是依赖聚合,因此不建议直接在starter内部通过代码来更改属性值。

一般来说,具体的逻辑应当由其他模块来实现,然后由starter导入相应的模块,即starter只起导入依赖的作用,因此starter模块整体架构如下图所示:

从图中可以看到,starter模块依赖了两部分,一部分是常用依赖,另一部分则是自动配置模块所需的依赖。前面说的xxxAutoConfigurationxxxProperties实际上则是自动配置模块的具体实现,starter通过该模块来对外提供相应功能。

autoconfigure模块开发

第一步,POM文件引入相应依赖。明确所有的starter都需要引入spring-boot-autoconfigure依赖,该依赖中包含很多与自动配置相关的注解及定义;其次还可以选择是否引入spring-boot-configuration-processor依赖,该依赖可以让开发者在配置文件中输入想修改的配置项信息时,能给出相应的提示信息。这里的配置文件不仅仅是以.properties结尾的,还可以是以.yml结尾的,不过SpringBoot中默认使用application.yml文件:

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

其他依赖则按需进行引入,这里就没什么好说的了。

第二步,xxxAutoConfiguration自动配置类实现。autoconfigure模块中xxxAutoConfiguration自动配置类的编写非常重要,它可以帮助开发者实现组件的自动装配与自动注入,进而让开发者更加专注于需要什么组件,如何去配置它们:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Configuration 
@ConditionalOnxxx //限定自动配置类生效的一些条件
@EnableConfigurationProperties(xxxProperties.class)
public class xxxAutoConfiguration {
@Autowired
private xxxProperties properties;

@Bean
public YouNeedBean youNeedBean() {
YouNeedBean bean = new YouNeedBean()
bean.setField1(properties.get(field1));
bean.setField2(properties.get(field2));
bean.setField3(properties.get(field3));
......
}
}

这里我们在自动配置类中添加了一些自动配置类生效的条件,这些在实际开发过程中还是非常重要的。

第三步,xxxProperties属性配置类实现。请注意这个属性配置类的命名要与在第二步中,使用@EnableConfigurationProperties(xxxProperties.class)注解中配置的文件名保持一致。同时这个xxxProperties属性配置类用于从外部的以.properties或者.yml结尾的配置文件中读取对应的配置信息,以覆盖默认的属性值:

1
2
3
4
5
6
7
8
9
10
11
12
13
@ConfigurationProperties(prefix = "yourproperties") 
public class xxxProperties {

private boolean enabled = true;

private String clientId;

private String beanName;

private String scanBasePackage;

private String path;
}

可以看到这里我们使用@ConfigurationProperties注解来绑定配置文件,并从中获取以yourproperties为前缀的配置项信息。

第四步,配置spring.factories文件。在介绍原理的时候就说过,它会扫描这个spring.factories文件中的xxxAutoConfiguration自动配置类并加载到Spring容器中。在项目的resource目录下新建一个名为META-INF的目录,然后在该目录下新建一个名为spring.factories的配置文件,将在第二步定义好的xxxAutoConfiguration自动配置类的全路径放在里面:

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kenbingthoughts.mystarter.xxxAutoConfiguration

自定义场景启动器实战

第一步,使用Maven创建一个名为mystarter-spring-boot-starter的项目,然后在其POM文件中新增spring-boot-autoconfigurespring-boot-configuration-processor依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.7</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.kenbingthoughts</groupId>
<artifactId>mystarter-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>mystarter-spring-boot-starter</name>
<description>自定义Starter启动器</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>11</source>
<target>11</target>
<compilerVersion>11</compilerVersion>
</configuration>
</plugin>
</plugins>
</build>
</project>

当然了,由于笔者使用的JDK版本为11,同时使用了spring-boot-starter-parent父场景启动器,因此这里覆盖了默认的1.8版本。请注意,此项目中由于不包含main方法,因此不能添加如下配置项:

1
2
3
4
5
6
7
8
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

这个配置项会加载main方法,导致后续应用程序无法打包。

第二步,新建一个名为KenBingProperties的属性配置类。这里使用了@ConfigurationProperties()这一注解来实现类型安全的属性注入,即将application.properties 配置文件中以kenbing为前缀的属性注入到这个类所对应的属性上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@ConfigurationProperties(prefix = "kenbing")
public class KenBingProperties {
private static final String DEFAULT_NAME = "啃饼思录";
private static final String DEFAULT_WEBSITE = "啃饼网";

private String name = DEFAULT_NAME;

private String website = DEFAULT_WEBSITE;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getWebsite() {
return website;
}

public void setWebsite(String website) {
this.website = website;
}
}

第三步,自定义application.properties 配置文件中以kenbing为前缀的对应配置项:

1
2
kenbing.name=kenbingthoughts
kenbing.website=kenbingthoughts.top

第四步,定义一个用于提供实际功能的业务逻辑类KenBingService,这里我们逻辑比较简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class KenBingService {
private String name;

private String website;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getWebsite() {
return website;
}

public void setWebsite(String website) {
this.website = website;
}

public String index(){
return "hello,welcome to "+this.website+",i am "+ this.name;
}
}

定义两个属性,然后提供对应的getter和setter方法,以及一个用于输出信息的index方法。

第五步,自定义xxxAutoConfiguration自动配置类。这里定义一个名为KenBingServiceAutoConfiguration的自动配置类,里面的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Configuration
@EnableConfigurationProperties(KenBingProperties.class)
@ConditionalOnClass(KenBingService.class)
public class KenBingServiceAutoConfiguration {
@Autowired
private KenBingProperties kenBingProperties;

@Bean
KenBingService kenBingService(){
KenBingService kenBingService = new KenBingService();
kenBingService.setName(kenBingProperties.getName());
kenBingService.setWebsite(kenBingProperties.getWebsite());
return kenBingService;
}
}

简单解释一下上述代码的含义:
(1)@Configuration注解表明这是一个配置类,可以被Spring容器扫描的到;
(2)使用@EnableConfigurationProperties(KenBingProperties.class)注解并绑定之前定义的属性配置类,表示让之前定义的被@ConfigurationProperties(prefix = "kenbing")注解所修饰的KenBingProperties类生效,让从配置文件中读取的属性值可以成功的绑定到KenBingProperties类上;
(3)@ConditionalOnClass(KenBingService.class)是一个条件注解,表示只有项目当前classpath环境下存在KenBingService类时,这个类中的业务逻辑才会生效;
(4)使用@Autowired注解将业务逻辑所需要的KenBingProperties对象给注入进来;
(5)提供一个KenBingService对象,并将这个对象的值从application.properties 配置文件中获取后返回。

第六步,定义spring.factories文件。在项目的resource目录下新建一个名为META-INF的目录,然后在该目录下新建一个名为spring.factories的配置文件,将在第五步定义好的KenBingServiceAutoConfiguration自动配置类的全路径放在里面:

1
2
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.kenbingthoughts.mystarterspringbootstarter.config.KenBingServiceAutoConfiguration

这样我们就完成了自定义场景启动器的定义工作。

第七步,打包自定义场景启动器。一般来说我们会将自定义的场景启动器打包,然后上传到Maven私服,以供其他同事使用,这里笔者就不上传了,直接本地打包并安装了。点击IDEA中的Maven插件,选择Lifecycle,然后先clean一下,再install一下,这样自定义场景启动器就安装到本地仓库了。

第八步,新建一个SpringBoot项目,然后在POM文件中引入刚才自定义场景启动器mystarter-spring-boot-starter

1
2
3
4
5
6
<!--引入自定义的starter-->
<dependency>
<groupId>com.kenbingthoughts</groupId>
<artifactId>mystarter-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

然后刷新一下Maven,可以看到扩展包里面已经出现了自定义的场景启动器:

第九步,测试。实际上现在项目中已经有一个默认的KenBingService实例,而且里面还有默认值。我们可以测试一下,出于简单考虑直接在项目测试类中进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
@RunWith(SpringRunner.class)
@SpringBootTest
class LearnFreemarkerApplicationTests {
private static final Logger logger = LoggerFactory.getLogger(LearnFreemarkerApplicationTests.class);

@Autowired
private KenBingService kenBingService;

@Test
void contextLoads() {
logger.info(kenBingService.index());
}
}

运行上述测试方法,可以看到控制台输出如下信息:

1
hello,welcome to 啃饼网,i am 啃饼思录

这些信息可以从引入的mystarter-spring-boot-starter的包下一个名为spring-configuration-metadata.json中得到答案:

当然了,实际上这些数据来源于KenBingConfig类中的值。不过开发者也可以通过修改application.properties 配置文件中以kenbing为前缀的对应配置项来覆盖默认值:

1
2
kenbing.name=kenbingthoughts
kenbing.website=kenbingthoughts.top

之后再重新启动一下测试类,可以看到控制台输出如下信息:

1
hello,welcome to kenbingthoughts.top,i am kenbingthoughts

总结

本篇通过对场景启动器的深度分析,然后学习了自定义场景启动器的步骤,最后在此基础上自定义了一个自己的场景启动器。

参考文章:图文并茂,Spring Boot Starter 万字详解!还有谁不会?@Conditional注解常规使用方法,感谢大佬的解惑。