自定义starter
# 自定义starter
# 为什么要自定义starter
Starter的作用是简化依赖管理和自动配置
,让用户只需引入一个依赖就能使用特定功能。
虽然 Spring Boot 已经提供了很多常用的 starter,但是有些特殊的需求可能并没有被满足。这时候,开发者就可以自定义自己的 starter 来满足这些特殊需求。以下是一些可能需要自定义 starter 的情况:
- 整合第三方库:有时候,我们需要整合一些第三方的库或者框架到 Spring 应用中。这时候,我们可以自定义一个 starter,将这个第三方库封装成一个 bean,然后在应用中使用。
- 优化常用配置:Spring Boot 虽然提供了很多默认的配置,但是有些情况下,我们需要对一些常用的配置进行优化。这时候,我们可以自定义一个 starter,对这些常用的配置进行封装和优化。
- 提供特殊功能:有时候,我们需要提供一些特殊的功能,例如日志收集、性能监控等。这时候,我们可以自定义一个 starter,将这些特殊的功能封装成一个 bean,然后在应用中使用。
- 简化配置:有时候,我们需要使用一些复杂的配置来启动 Spring 应用。这时候,我们可以自定义一个 starter,将这些复杂的配置封装成一个简单的配置方式,然后在应用中使用。
总的来说,自定义一个 starter 可以帮助我们简化应用的配置和开发过程,提高开发效率和应用性能。
# 探究starter
怎么编写一个自定义的starter,最好的方法,就是照着现有的starter研究一遍,然后依葫芦画瓢去实现。一开始功能可以简单一些,重点在于弄清流程是怎么样的。
通常SpringBoot项目中引入了很多很多的starter,那项目是如何将其配置成Bean的呢?
一切的关键要从启动类上的注解*@SpringBootApplication*说起。点开它你会发现,它是多个注解的组合,
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication{
// .....
}
2
3
4
5
6
7
8
9
10
11
12
13
14
其中需要关注的是@EnableAutoConfiguration
,字面意思就是允许自动装配。那自动装配咱们继续-点开看。
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
// ...
}
2
3
4
5
@AutoConfigurationPackage
主要是为了确定哪个包下的组件会被Spring Boot自动扫描和注册。@Import({AutoConfigurationImportSelector.class})
则是为了触发Spring Boot的自动配置过程,根据项目的依赖情况自动引入必要的配置类,从而简化开发者的工作量,使得项目可以“开箱即用”。也就是将pom坐标中,别人写好的starter注入到spring容器中。
我们引入pom坐标后,之所以能使用starter,通过bean的方式,就是靠的它,那我们继续看它——AutoConfigurationImportSelector.class.
AutoConfigurationImportSelector
类中的 getCandidateConfigurations
方法是 Spring Boot 自动配置机制的核心部分之一。它负责从项目的类路径中加载所有可能的自动配置类,这些配置类通常定义在 META-INF/spring.factories
文件中。
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
2
3
4
5
AutoConfigurationImportSelector
类中的 getCandidateConfigurations
方法是 Spring Boot 自动配置机制的核心部分之一。它负责从项目的类路径中加载所有可能的自动配置类,这些配置类通常定义在 META-INF/spring.factories
文件中。
AutoConfigurationImportSelector
是 Spring Boot 自动配置机制的关键部分,它的工作流程包括以下几个方面:
- 发现自动配置类:通过
getCandidateConfigurations
方法从所有依赖的META-INF/spring.factories
文件中读取自动配置类的名字列表。这些自动配置类通常位于各个 starter 库中,并且它们被设计为在满足特定条件时自动配置相关的组件。- 条件评估:虽然
getCandidateConfigurations
方法本身不执行条件评估,但它返回的自动配置类将会由 Spring Boot 进行进一步处理。Spring Boot 使用条件注解(如@ConditionalOnClass
,@ConditionalOnMissingBean
,@ConditionalOnProperty
等)来决定哪些自动配置类应该被真正加载到应用上下文中。只有当这些条件都满足时,相应的自动配置类才会生效。- 注册 Bean:一旦某个自动配置类被确定要加载,它就会像普通的 Spring 配置类一样工作,负责注册所需的 Bean 到 Spring 应用上下文中。这可能包括创建并配置数据源、设置 MVC 视图解析器、配置安全策略等。
然后,方法中的 SpringFactoriesLoader,有一个属性FACTORIES_RESOURCE_LOCATION
,
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap();
// ....
}
2
3
4
5
6
SpringFactoriesLoader
类在 Spring Boot 中扮演了一个非常重要的角色,它负责从 META-INF/spring.factories
文件中加载配置类,并且这些配置类通常用于实现自动配置和扩展点。
换句话说,它是从类路径下的这个文件中,去找寻该类,并尝试为其实现自动配置。
按照这个思路,我们要定义一个starter也应该有一个META-INF/spring.factories
文件,且其中有自动配置类的-全路径类名。
# 准备
Starter的核心概念
- 自动配置模块:包含
@Configuration
类、条件注解和配置属性绑定。 - Starter模块:一个空的Maven模块,仅用于聚合依赖(依赖自动配置模块和第三方库)
引入对应依赖
<!-- your-starter-autoconfigure/pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.1.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>3.1.5</version>
<optional>true</optional>
</dependency>
</dependencies>
2
3
4
5
6
7
8
9
10
11
12
13
14
配置类的编写
// YourServiceProperties.java
@ConfigurationProperties(prefix = "your.service")
public class YourServiceProperties {
private String apiKey;
private int timeout = 5000;
// Getters and Setters
}
2
3
4
5
6
7
8
@ConfigurationProperties
作用:跟 对应的配置文件 yaml进行数据绑定
定义自动配置类
// YourServiceAutoConfiguration.java
@Configuration
@ConditionalOnClass(YourService.class) // 当类路径存在YourService时生效
@EnableConfigurationProperties(YourServiceProperties.class)
public class YourServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean // 当容器中没有YourService时创建Bean
public YourService yourService(YourServiceProperties properties) {
return new YourService(properties.getApiKey(), properties.getTimeout());
}
}
2
3
4
5
6
7
8
9
10
11
12
写入配置文件信息
在src/main/resources/META-INF/spring/
目录下创建文件spring.factories
:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.YourServiceAutoConfiguration
2
# 命名规则
SpringBoot官方提供的starter以spring-boot-starter-xxx
的方式命名
官方建议自定义的starter使用xxx-spring-boot-starter
的方式命名。用以区分官方与三方。
# 实现
自定义starter,大致可以分为如下几步:
- 创建项目,在pom文件中引入
autoconfigure
包 - 编写自己的代码
- 进行 META-INF/spring.factories 配置文件的编写,使得项目启动能加载指定的自动配置类
第三点,SpringBoot 2.7之后的有改动
# 1.创建项目并引入对应架包
在创建项目,设置artifactId
时,记得遵守上文说到的命名规则。
引入自动配置包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
2
3
4
# 2 编写自己需要的代码
以下是简单的一个示例:
- service包中的类:
public class HelloService {
@Autowired
HelloProperties helloProperties;
public String sayHello(String name){
return name + helloProperties.getName();
}
}
2
3
4
5
6
7
8
9
之所以不将其(HelloService)注入到容器中,是为了实现按需使用。可能你自定义的starter中有很多bean对象,但开发中可能不是每个都需要,于是我们将使用权交给使用者,关于它的配置我们交给自动配置类
- Bean包中的配置类
@ConfigurationProperties(prefix = "man.sugar")
public class HelloProperties {
@Value("loo")
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
与配置文件中的值进行绑定。
- Config包中的自动配置类
@Configuration
@EnableConfigurationProperties(HelloProperties.class)
public class HelloServiceAutoConfiguration {
@Bean
// @ConditionalOnMissingBean ...
public HelloService helloService(){
return new HelloService();
}
}
2
3
4
5
6
7
8
9
10
在创建bean组件的时候,可以按照自己的需求,通过@ConditionalXXxx
定义一些条件,实现根据条件创建。条件可以放在类上,也可以放在方法上。
使用@EnableConfigurationProperties(HelloProperties.class)
会将HelloProperties放到容器中,因为HelloService的创建,需要该属性
- 配置文件application.properties
man.sugar.name = Drink plenty of water
到此自定义starter基本实现完成,但还差最重要的一步。因为此时的自动配置类不会生效。根据自动配置原理可以了解到,我们还需要在配置META-INF/spring.factories
文件中进行配置,这样才能使得项目在启动的时候加载指定的自动配置类。
# 3 配置文件的编写
在项目的资源文件中创建META-INF/spring.factories
,并对spring.factories
进行编写,将需要自动加载的类写入配置
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sugar.demo1.config.HelloServiceAutoConfiguration
2
3
配置自己的自动配置类路径
提示
springboot在2.7及以后版本不推荐使用/META-INF/spring.factories文件,并且在Spring Boot 3将移除对/META-INF/spring.factories的支持。
新的写法是/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.sugar.demo1.config.HelloServiceAutoConfiguration
2
# 使用自定义starter
在将之前项目通过maven进行install后,即可开始使用。
# 引入自定义的starter
<dependency>
<groupId>com.sugar.demo1</groupId>
<artifactId>use-custom-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
2
3
4
5
成功引入后,即可在项目中使用对应的bean对象
@RestController
public class HepController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello(){
String sugar = helloService.sayHello("sugar");
return "sth = "+sugar;
}
}
2
3
4
5
6
7
8
9
10
11
12
同时你也可以在配置文件中对helloService中的HelloProperties的值进行更改
man.sugar.name=like water