SpringBoot自动配置与@Enable模式

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
  • 属性不以thea开头描述
  • 对于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注解的valuename属性

示例如下:

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版本兼容两种形式:

  1. 在Resources下创建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件 并在里面写入需要注入的配置类,如有多个则换行填写。推荐使用这种格式

    com.silvergravel.study.StudyAutoConfigurationApplication

  2. 在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 模块。分成两个模块是没有必要的,如果没有多种flavoroptionaloptions,那么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 模块

  1. clean install autoconfigure模块
  2. 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注解修饰类

  1. 执行getAttributes(annotationMetadata)方法获取元信息。

  2. 执行getCandidateConfigurations(annotationMetadata, attributes)获取META-INF/spring.factories文件以及META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的配置信息。

  3. 执行removeDuplicates(configurations)方法进行配置类去重。

  4. 执行checkExcludedClasses(configurations, exclusions)方法检查getExclusions(annotationMetadata, attributes)方法获取的配置类是否合法。

  5. 执行configurations.removeAll(exclusions)方法删除排除的配置类。

  6. 执行getConfigurationClassFilter().filter(configurations)方法在读取自动配置类的字节码之前过滤限制的自动配置类。

  7. 执行fireAutoConfigurationImportEvents方法启动自动加载配置类。

加载顺序

配置类的加载顺序代码如下:

  1. AutoConfigurationImportSelector的私有静态内部类AutoConfigurationGroup中,执行selectImports方法获取经过排序的配置类。

  2. 执行sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata())处理配置类排序。

  3. 执行getAutoConfigurationMetadata()方法获取META-INF/spring-autoconfigure-metadata.properties文件的配置信息:

  4. 执行getInPriorityOrder(configurations)真正执行配置类排序

  5. 初始按字母顺序排序。

  6. 接着按@AutoConfigureOrder注解相对顺序再进行排序。

  7. 最后按着@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实现。

ImportSelectorImportBeanDefinitionRegistrar的异同:

  1. ImportSelector和ImportBeanDefinitionRegistrar都是基于动态注册bean对象到容器中,且都支持大量bean的导入。但两者在功能和操作上存在显著的差异。
  2. ImportSelector是一个接口,用户需要提供自己的实现类。在这个实现类中,用户需要返回要注册的bean的全限定名 数组。接着ConfigurationClassParser类中的precessImports方法会自动注册这些bean对象。
  3. 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)

参考文档

SpringBoot2.7官方文档.pdf

《Spring Boot编程思想(核心篇)》

相关推荐
考虑考虑30 分钟前
Jpa使用union all
java·spring boot·后端
用户3721574261351 小时前
Java 实现 Excel 与 TXT 文本高效互转
java
浮游本尊2 小时前
Java学习第22天 - 云原生与容器化
java
渣哥4 小时前
原来 Java 里线程安全集合有这么多种
java
间彧4 小时前
Spring Boot集成Spring Security完整指南
java
间彧4 小时前
Spring Secutiy基本原理及工作流程
java
Java水解5 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
洛小豆7 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学8 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端
ytadpole8 小时前
Spring Cloud Gateway:一次不规范 URL 引发的路由转发404问题排查
java·后端