简介
- 可插拔插件
- 与jar包区别:starter能实现自动配置
- 可以大幅提升开发效率
动态搭建starter
新建Maven项目工程
pom.xml
<?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 http://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.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>demo-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
</dependencies>
</project>
新建Bean对象
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 天气属性
* @Author: tswine
* @Date: 2021/1/18 21:59
*/
@ConfigurationProperties(prefix = "weather")
public class WeatherSource {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
新建Service对象,暴露接口
public class WeatherSourceService {
private WeatherSource weatherSource;
public WeatherSourceService(WeatherSource weatherSource) {
this.weatherSource = weatherSource;
}
public String getType() {
return this.weatherSource.getType();
}
}
构建配置类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: tswine
* @Date: 2021/1/18 22:01
*/
@Configuration
@EnableConfigurationProperties(WeatherSource.class)
@ConditionalOnProperty(name = "weather.enable", havingValue = "true")
public class WeatherAutoConfiguration {
@Autowired
private WeatherSource weatherSource;
@Bean
@ConditionalOnMissingBean(WeatherSourceService.class)
public WeatherSourceService weatherSourceService() {
return new WeatherSourceService(weatherSource);
}
}
META-INF 增加配置
src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.demo.WeatherAutoConfiguration
打包发布到仓库,然后使用方引入
测试代码
@Autowired
private WeatherSourceService weatherSourceService;
@RequestMapping(value = "/starter")
private String starter() {
return weatherSourceService.getType();
}
此时在配置文件中没有配置属性,则会启动报错,需要在application.yml中配置属性,则才会被自动注入
weather.enable: true
weather.type: 下雨
新建starter步骤
- 新建SpringBoot项目
- 引入spring-boot-autoconfigure
- 编写属性源及自动配置类
- 在META-INF/spring.factories中添加自动配置类实现
Starter原理
入口
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
通过getCandidateConfigurations 获取spring.factories中EnableAutoConfiguration的所有实现类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
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;
}
获取实现的方法名后,进行了一系列的排除,然后通过fireAutoConfigurationImportEvents方法发送AutoConfigurationImportEvent事件进行广播,然后进行自动注入
fireAutoConfigurationImportEvents
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) {
List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners();
if (!listeners.isEmpty()) {
AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions);
for (AutoConfigurationImportListener listener : listeners) {
invokeAwareMethods(listener);
listener.onAutoConfigurationImportEvent(event);
}
}
}
自动注入流程
- 依赖启动类上@SpringBootApplication
- 引入AutoConfigurationImportSelector
- ConfigurationClassParser中处理
- 获取spring.factories中EnableAutoConfiguration实现类
- 自动配置类过滤(@ConditionalOnProperty)
- 自动配置类导入