一、自定义 starter
1. 命名
官方命名:
- spring-boot-starter-XXX
- spring-boot-starter-jdbc
- spring-boot-starter-data-redis
非官方命名:
- XXX-spring-boot-starter
- mybatis-spring-boot-starter
- druid-spring-boot-starter
2.maven 依赖
spring-boot-autoconfigure
是必须的
spring-boot-configuration-processor
在编译后会生成 META-INF/spring-configuration-metadata.json
文件,起到提示的功能
<groupId>com.example</groupId>
<artifactId>animal-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
</dependencies>
3.创建参数类
@ConfigurationProperties(prefix = "animal")
public class AnimalProperties {
private String type = "animal";
private final Dog dog = new Dog();
private final Cat cat = new Cat();
public static class Dog {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static class Cat {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
}
4.创建业务实现类
public class AnimalService {
private AnimalProperties animalProperties;
public AnimalService(AnimalProperties animalProperties) {
this.animalProperties = animalProperties;
}
public String getMsg() {
return String.format("type: %s; dog: %s; cat: %s;",
animalProperties.getType(),
animalProperties.getDog().getName(),
animalProperties.getCat().getName());
}
}
5.创建自动配置类
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(AnimalProperties.class)
@ConditionalOnClass(AnimalProperties.class)
public class AnimalAutoConfiguration {
@Autowired
private AnimalProperties animalProperties;
@Bean
@ConditionalOnMissingBean(AnimalService.class)
public AnimalService animalService() {
return new AnimalService(animalProperties);
}
}
6.配置spring.factories
新建META-INF目录,然后在META-INF目录下创建spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.AnimalAutoConfiguration
二、使用 starter
1.maven 依赖
<groupId>com.example</groupId>
<artifactId>spring-boot-service</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>animal-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
2.配置文件
application.yml 文件
animal:
type: dog and cat
dog:
name: xiao qiang
cat:
name: xiao hua
3.启动类
@SpringBootApplication
public class Boot {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Boot.class, args);
AnimalService service = context.getBean(AnimalService.class);
String msg = service.getMsg();
System.out.println(msg);
}
}
结果:
type: dog and cat; dog: xiao qiang; cat: xiao hua;
三、原理
启动类有 @SpringBootApplication
注解, @SpringBootApplication
有 @EnableAutoConfiguration
注解,@EnableAutoConfiguration
引入 AutoConfigurationImportSelector
// SpringBootApplication.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
// EnableAutoConfiguration.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
SpringBoot 会解析 AutoConfigurationImportSelector,调用 AutoConfigurationImportSelector$AutoConfigurationGroup 的 process 方法
SpringFactoriesLoader.loadFactoryNames 会读取 META-INF/spring.factories
文件内容,读取到
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.AnimalAutoConfiguration
注册 AnimalAutoConfiguration 到 spring
// AutoConfigurationImportSelector.java
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;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
@Override
public Class<? extends Group> getImportGroup() {
return AutoConfigurationGroup.class;
}
// AutoConfigurationImportSelector$AutoConfigurationGroup.java
@Override
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}