SpringBoot自动配置
自动配置类可以捆绑在外部Jar中,并且仍然可以被Spring boot拾取管理。自动配置必须仅通过在导入文件中命名来加载(
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
)制作,确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描(@ComponentScan
)的目标。 此外,自动配置类不应该启用组件扫描以查找其他组件,而是使用@Import
注解指定特定组件。
命名规范
- 模块名称以xxx-spring-boot-starter命名,spring-boot-starter-xxx为官方的命名。
- 有配置属性,属性的第一个前缀使用与starter前缀,如
mybatis-plus-spring-boot-starter
配置属性前缀为:mybatis-plus
- 属性不以
the
或a
开头描述 - 对于
boolean
值,使用whether
或者enable
命名 - 多个单词以
-
连接,如mybatis-plus
- 时间属性使用
java.time.Duration
不使用long
,如果没有指定持续时间后缀,默认为秒 - 除非必须在运行时确定属性值,否则不要提供默认值
条件注解
@Conditional
注解修饰自动配置类,使其在指定的条件下才能自动配置或不配置
注解 | 描述 |
---|---|
[ConditionalOnBean ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnBean%E3%80%81@ConditionalOnMissingBean") |
仅当BeanFactory中已包含满足指定要求的bean时才匹配 |
[ConditionalOnMissingBean ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnBean%E3%80%81@ConditionalOnMissingBean") |
仅当BeanFactory中未包含满足指定要求的bean时才匹配 |
[@ConditionalOnProperty ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnProperty") |
检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue() 和matchIfMissing() 属性允许进一步的自定义 |
[@ConditionalOnResource ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnResource") |
只有当指定的资源在类路径上时才匹配 |
@ConditionalOnClass |
只有当指定的类在类路径上时才匹配 |
@ConditionalOnMissingClass |
只有当指定的类不在类路径上时才匹配 |
@ConditionalOnWebApplication |
当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 |
@ConditionalOnNotWebApplication |
当应用程序不是web应用程序时匹配 |
@ConditionalOnJava |
根据应用程序运行的JVM版本进行匹配 |
@ConditionalOnDefaultWebSecurity |
只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) |
[@ConditionalOnCloudPlatform ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnCloudPlatform") |
当指定的云平台处于活动状态时匹配 |
[@ConditionalOnEnabledResourceChain ](#注解 描述 ConditionalOnBean 仅当BeanFactory中已包含满足指定要求的bean时才匹配 ConditionalOnMissingBean 仅当BeanFactory中未包含满足指定要求的bean时才匹配 @ConditionalOnProperty 检查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用havingValue()和matchIfMissing()属性允许进一步的自定义 @ConditionalOnResource 只有当指定的资源在类路径上时才匹配 @ConditionalOnClass 只有当指定的类在类路径上时才匹配 @ConditionalOnMissingClass 只有当指定的类不在类路径上时才匹配 @ConditionalOnWebApplication 当应用程序是web应用程序时匹配。默认情况下,任何web应用程序都将匹配,但可以使用type()属性缩小范围 @ConditionalOnNotWebApplication 当应用程序不是web应用程序时匹配 @ConditionalOnJava 根据应用程序运行的JVM版本进行匹配 @ConditionalOnDefaultWebSecurity 只有当web security可用并且用户尚未定义自己的配置时才匹配(version 2.4) @ConditionalOnCloudPlatform 当指定的云平台处于活动状态时匹配 @ConditionalOnEnabledResourceChain 检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled()为true或者webjars-locator-core在类路径上 @ConditionalOnMissingFilterBean 仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean @ConditionalOnRepositoryType 仅当启用了特定类型的Spring Data存储库时才匹配 @ConditionalOnWarDeployment 当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false ConditionalOnNotWarDeployment 只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 "#@ConditionalOnEnabledResourceChain") |
检查Spring资源处理链是否已启用, 匹配WebProperties.Resources.Chain.getEnabled() 为true或者webjars-locator-core 在类路径上 |
@ConditionalOnMissingFilterBean |
仅当BeanFactory中不包含指定类型的Filter bean时才匹配。此条件将检测直接注册的Filterbean以及通过FilterRegistrationBean注册的FilterBean |
@ConditionalOnRepositoryType |
仅当启用了特定类型的Spring Data存储库时才匹配 |
@ConditionalOnWarDeployment |
当应用程序是传统的WAR部署时匹配。对于具有嵌入式服务器的应用程序,此条件将返回false |
ConditionalOnNotWarDeployment |
只有当应用程序不是传统的WAR部署时才匹配。对于具有嵌入式服务器的应用程序,此条件将返回true。 |
@ConditionalOnProperty
查指定属性是否具有特定值的条件。默认情况下,属性必须存在于环境中,并且不等于false。可以使用
havingValue()
和matchIfMissing()
属性允许进一步的自定义。
java
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
/**Alias for name()*/
String[] value() default {};
String prefix() default "";
String[] name() default {};
String havingValue() default "";
boolean matchIfMissing() default false;
}
- vlaue:属性名称值,与name不可以同时使用
- prefix:配置属性的前缀
- name:属性名称值
- havingValue:给属性名称添加限定值
- matchIfMissing:缺少属性值是否注入,默认为false,表示name中的属性不存在不可以注入,true表示缺属性依旧可以进行注入
starter模块自动配置代码:
java
@Bean
@ConditionalOnProperty(prefix = "study.datasource",name = "enable",havingValue = "true")
public DatabaseService databaseService(DatabaseProperties databaseProperties) {
return new DatabaseService(databaseProperties);
}
在类或方法使用该注解,在引用的该starter模块的程序中的application.yml添加以下配置:
yml
study:
datasource:
enable: true
@ConditionalOnResource
只有当指定的资源在类路径上时才匹配。
java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnResourceCondition.class)
public @interface ConditionalOnResource {
/**
* The resources that must be present.
* @return the resource paths that must be present.
*/
String[] resources() default {};
}
starter模块自动配置代码:
java
@Bean
@ConditionalOnResource(resources = "classpath:bootstrap.yml")
// @ConditionalOnResource(resources = "file:P:\\IDEA\\Learn\\java-skill-learn\\study-spring-boot-test\\src\\main\\resources\\bootstrap.yml")
public ResourceService resourceService() {
String resource = "classpath:bootstrap.yml";
return new ResourceService(resource);
}
在类或方法使用该注解,在引用的该starter模块的程序的resources路径下添加boostrap.yml文件,路径可以不加:classpath:
,默认加载,如果想加载不属于类路径的文件则加上前缀file:
@ConditionalOnNotWebApplication
当应用程序不是web应用程序时匹配。
java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnNotWebApplication {
}
starter模块自动配置代码:
java
@Bean
@ConditionalOnNotWebApplication
public WebService notWeService() {
return new WebService(WebApplicationType.NONE);
}
在类或方法使用该注解,在引用的该starter模块的程序的启动类设置web的类型。
java
@SpringBootApplication
public class StudyBootStarterApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(StudyBootStarterApplication.class)
// 设置不启用Web
.web(WebApplicationType.NONE)
.run(args);
}
}
@ConditionalOnEnabledResourceChain
检查Spring资源处理链是否已启用, 匹配
WebProperties.Resources.Chain.getEnabled()
为true或者webjars-locator-core
在类路径上。
java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnEnabledResourceChainCondition.class)
public @interface ConditionalOnEnabledResourceChain {
}
starter模块自动配置代码:
java
@Bean
@ConditionalOnEnabledResourceChain
public ResourceChainService resourceChainService() {
return new ResourceChainService();
}
在类或方法使用该注解,在引用的该starter模块的程序中的application.yml添加以下配置:
yml
spring:
web:
resources:
chain:
enabled: true
@ConditionalOnCloudPlatform
当指定的云平台处于活动状态时匹配。
java
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnCloudPlatformCondition.class)
public @interface ConditionalOnCloudPlatform {
CloudPlatform value();
}
starter模块自动配置代码:
java
@Bean
@ConditionalOnCloudPlatform(CloudPlatform.NONE)
public WebService notWeService() {
return new WebService(WebApplicationType.NONE);
}
在类或方法使用该注解,在引用的该starter模块的程序中的application.yml添加以下配置:
yml
spring:
main:
cloud-platform: none
如果没有添加,即便是CloudPlatform.NONE
没有云平台依旧不符合条件进行注入。
@ConditionalOnBean、@ConditionalOnMissingBean
@ConditionalOnBean: 仅当BeanFactory中已包含满足指定要求的bean时才匹配; @ConditionalOnMissingBean:仅当BeanFactory中已包含满足指定要求的bean时才匹配
@ConditionalOnBean
其中的三个属性:
- vlaue:要检查的bean的类类型
- type: 要检查的bean的类全限定性类型名称
- name:要检查的bean的名称,@Bean注解的
value
、name
属性
示例如下:
java
@Configuration
public class StudyAutoConfigurationB {
@Bean
public SortServiceB serviceB() {
return new SortServiceB();
}
@Bean
@ConditionalOnBean(name = "serviceB")
public SortServiceA serviceA() {
return new SortServiceA();
}}
注意: 如果是以下代码,那么serviceA将不会注入,因为Bean的加载是有顺序的:
java
@Configuration
public class StudyAutoConfigurationB {
@Bean
@ConditionalOnBean(name = "serviceB")
public SortServiceA serviceA() {
return new SortServiceA();
}}
@Bean
public SortServiceB serviceB() {
return new SortServiceB();
}
同一个配置类中,如无依赖关系,Bean自上往下注入,配置类顺序查看:本文的加载顺序
其他@Conditional注解
其他注解基本大同小异,@ConditionalOnBean
、@ConditionalOnMissingBean
、@ConditionalOnClass
、@ConditionalOnMissingClass
、@ConditionalOnProperty
使用较多。
基本使用
项目结构
- springboot版本:
2.7
- JDK 17
Autoconfigure模块
pom文件
xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>2.7.13</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring.boot.version}</version>
<optional>true</optional>
</dependency>
</dependencies>
配置属性类
typescript
package com.silvergravel.study.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
public class DatabaseProperties {
private String url;
private String username;
private String password;
private String driverClassName;
private Boolean enable;
public Boolean getEnable() {
return enable;
}
public void setEnable(Boolean enable) {
this.enable = enable;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
}
服务类
kotlin
package com.silvergravel.study.service;
import com.silvergravel.study.config.DatabaseProperties;
public class DatabaseService {
private final DatabaseProperties properties;
public DatabaseService(DatabaseProperties properties) {
this.properties = properties;
}
public void print() {
System.out.println("driver class name: " + properties.getDriverClassName());
System.out.println("datasource url: " + properties.getUrl());
System.out.println("username: " + properties.getUsername());
System.out.println("password: " + properties.getPassword());
}
}
配置类
less
package com.silvergravel.study;
import com.silvergravel.study.config.DatabaseProperties;
import com.silvergravel.study.service.DatabaseService;
import org.springframework.boot.autoconfigure.condition.ConditionalOnJava;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.system.JavaVersion;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author DawnStar
*/
@Configuration
@EnableConfigurationProperties(DatabaseProperties.class)
public class StudyAutoConfigurationApplication {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "study.datasource",name = "enable",havingValue = "true")
@ConditionalOnJava(value = JavaVersion.SEVENTEEN)
public DatabaseService databaseService(DatabaseProperties databaseProperties) {
return new DatabaseService(databaseProperties);
}
}
自动配置文件
SpringBoot 2.7
版本兼容两种形式:
-
在Resources下创建
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件 并在里面写入需要注入的配置类,如有多个则换行填写。推荐使用这种格式com.silvergravel.study.StudyAutoConfigurationApplication
-
在Resources下创建
META-INF/spring.factories
文件,按照属性文件格式进行配置,如果多个配置类,
分隔。2.7版本之前使用的格式。
ini
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.silvergravel.study.StudyAutoConfigurationApplication
确保autoconfigure模块targert生成spring-configuration-metadata.json
文件:
Starter模块
starter模块包含autoconfigure模块以及其他有用依赖,是一个Empty 模块。分成两个模块是没有必要的,如果没有多种
flavor
,optional
,options
,那么starter模块和autoconfigure模块完全可以合成一个模块。
pom文件
xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.silvergravel</groupId>
<artifactId>study-spring-boot-autoconfigure</artifactId>
<!--自己 install 的版本-->
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
Test 模块
- clean install autoconfigure模块
- clean install starter模块
pom文件
xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.silvergravel</groupId>
<artifactId>study-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
启动类
java
package com.silvergravel;
import com.silvergravel.study.service.DatabaseService;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
/**
* @author DawnStar
*/
@SpringBootApplication
public class StudyBootStarterApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder(StudyBootStarterApplication.class)
.web(WebApplicationType.NONE)
.run(args);
DatabaseService bean = context.getBean(DatabaseService.class);
bean.print();
}
}
application.yml
yaml
study:
datasource:
driver-class-name: org.postgresql.Driver
username: DawnSilverGravel
password: DawnSilverGravel
# url: jdbc:postgresql://localhost:5432/gravel
enable: true
由于使用了@ConditionalOnProperty(prefix = "study.datasource",name = "enable",havingValue = "true")
,所以需要enable
属性并且值为true才能自动配置。
自动配置大致流程
SpringbBoot 版本:2.7
@SpringBootApplication注解包含一个@EnableAutoConfiguration注解:
其中AutoConfigurationImportSelector
为自动配置的执行类,该类实现了ImportSelector
接口。ImportSelector
是由类型实现的接口,这些类型确定应根据给定的选择标准(通常是一个或多个注释属性)导入哪些 @Configuration
类。
下面是AutoConfigurationImportSelector
中的主要方法,用于获取被@Configuration
注解修饰类
-
执行
getAttributes(annotationMetadata)
方法获取元信息。 -
执行
getCandidateConfigurations(annotationMetadata, attributes)
获取META-INF/spring.factories
文件以及META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中的配置信息。 -
执行
removeDuplicates(configurations)
方法进行配置类去重。 -
执行
checkExcludedClasses(configurations, exclusions)
方法检查getExclusions(annotationMetadata, attributes)
方法获取的配置类是否合法。 -
执行
configurations.removeAll(exclusions)
方法删除排除的配置类。 -
执行
getConfigurationClassFilter().filter(configurations)
方法在读取自动配置类的字节码之前过滤限制的自动配置类。 -
执行
fireAutoConfigurationImportEvents
方法启动自动加载配置类。
加载顺序
配置类的加载顺序代码如下:
-
在
AutoConfigurationImportSelector
的私有静态内部类AutoConfigurationGroup
中,执行selectImports
方法获取经过排序的配置类。 -
执行
sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata())
处理配置类排序。 -
执行
getAutoConfigurationMetadata()
方法获取META-INF/spring-autoconfigure-metadata.properties
文件的配置信息: -
执行
getInPriorityOrder(configurations)
真正执行配置类排序 -
初始按字母顺序排序。
-
接着按
@AutoConfigureOrder
注解相对顺序再进行排序。 -
最后按着
@AutoConfigureAfter
注解和@AutoConfigureBefore
注解和@AutoConfiguration
相对排序再进行排序。
对于spring-autoconfigure-metadata.properties
,假设StudyAutoConfigurationB
配置在StudyAutoConfigurationApplication
配置类之前加载格式如下:
properties
com.silvergravel.study.StudyAutoConfigurationB=
com.silvergravel.study.StudyAutoConfigurationB.AutoConfigureBefore=com.silvergravel.study.StudyAutoConfigurationApplication
ps: 配置类中的Bean,如无依赖关系,则顺序加载,那么@ConditionalOnBean
以及@ConditionalOnMissingBean
注解则需要注意加载顺序。
@Enable模块驱动
所谓的模块是指具备相同领域功能组件的集合组合成一个独立的单元,比如AspectJ代理模块,Async模块等。
从自动配置@EnableAutoConfiguration
名称可以得知:启动自动配置。在该类中有一行代码:
java
@Import(AutoConfigurationImportSelector.class)
java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration @Configuration}, {@link ImportSelector},
* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.
*/
Class<?>[] value();
}
@Import
指示要导入的一个或多个组件类,通常为@Configuration类,允许导入@Configuration类、ImportSelector和ImportBeanDefinitionRegistrar实现。
ImportSelector
与ImportBeanDefinitionRegistrar
的异同:
- ImportSelector和ImportBeanDefinitionRegistrar都是基于动态注册bean对象到容器中,且都支持大量bean的导入。但两者在功能和操作上存在显著的差异。
- ImportSelector是一个接口,用户需要提供自己的实现类。在这个实现类中,用户需要返回要注册的bean的全限定名 数组。接着ConfigurationClassParser类中的precessImports方法会自动注册这些bean对象。
- ImportBeanDefinitionRegistrar也是一个接口,同样需要用户提供自己的实现类。但在这个实现类中,用户需要手动注册bean到容器中。
应用场景:
- 对于一些需要自定义控制bean注册过程的复杂场景,可能会更适合使用ImportBeanDefinitionRegistrar。
- 只是需要提供一个包含所有需要注册bean的列表,mportSelector可能会更方便。
基本使用
项目结构
- springboot版本:
2.7
- JDK 17
pom文件
xml
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>2.7.13</spring.boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure-processor</artifactId>
<optional>true</optional>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<optional>true</optional>
<version>${spring.boot.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
<version>${spring.boot.version}</version>
</dependency>
</dependencies>
@Configuration
java
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SilverGravelConfiguration.class)
public @interface EnableSilverGravel {
}
java
@Configuration
public class SilverGravelConfiguration {
@Bean
public String silverGravel() {
System.out.println("SilverGravel");
return "SilverGravel";
}
}
ImportSelector 与 ImportBeanDefinitionRegistrar实现
java
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
//@Import(ServerImportSelector.class)
@Import(ServerImportBeanDefinitionRegistrar.class)
public @interface EnableServer {
Server.Type type() default Server.Type.HTTP;
}
java
public interface Server {
void start();
void end();
enum Type {
/**
* 服务器类型
*/
HTTP,
DNS,
;
}
}
java
public class DnsServer implements Server {
@Override
public void start() {
System.out.println("DNS 服务器启动...");
}
@Override
public void end() {
System.out.println("DNS 服务器关闭...");
}
}
java
public class HttpServer implements Server {
@Override
public void start() {
System.out.println("HTTP 服务器启动...");
}
@Override
public void end() {
System.out.println("HTTP 服务器停止...");
}}
java
public class ServerImportSelector implements ImportSelector {
@Override
@SuppressWarnings("all")
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableServer.class.getName());
Server.Type type = (Server.Type) annotationAttributes.get("type");
return switch (type) {
case HTTP -> new String[]{HttpServer.class.getName()};
case DNS -> new String[]{DnsServer.class.getName()};
};
}
}
java
public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
@SuppressWarnings("all")
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnableServer.class.getName());
Server.Type type = (Server.Type) annotationAttributes.get("type");
String[] beanNames = switch (type) {
case DNS -> new String[]{DnsServer.class.getName()};
case HTTP -> new String[]{HttpServer.class.getName()};
}; List<AbstractBeanDefinition> abstractBeanDefinitions = Stream.of(beanNames)
.map(beanName -> BeanDefinitionBuilder
.genericBeanDefinition(beanName)
.getBeanDefinition())
.toList();
abstractBeanDefinitions.forEach(
abstractBeanDefinition -> BeanDefinitionReaderUtils.registerWithGeneratedName(abstractBeanDefinition, registry)
); }}
最后 clean intall,将该模块导入到其他模块使用即可。
github示例参考
study-spring-boot-autoconfigure(github.com)
参考文档
《Spring Boot编程思想(核心篇)》