SpringBoot 3.x开发自定义自动配置二方包

292 阅读3分钟

SpringBoot的自动配置有什么优点

  1. 开箱即用:许多常用的企业级服务和中间件,如数据库连接、消息队列、缓存等,都可以通过引入相应的“Starters”来自动配置,实现开箱即用,开发者无需进行复杂的XML配置或编写大量的Java配置模版代码,Spring Boot 通过自动配置机制提供了大量默认配置,简化了项目设置。
  2. 灵活定制:尽管提供了自动配置,Spring Boot 也允许开发者通过简单的注解或配置文件来覆盖自动配置的默认行为,以满足特定的业务需求。

如何使用

  1. 创建配置类:首先,你需要创建一个带有@AutoConfiguration注解的类,这个类将包含你的自定义配置,配置类里主要负责利用配置(可选)来创建bean。
    1.1 定义外部配置(可选):当需要外部配置时或者依赖方要调整参数时,使用@ConfigurationProperties注解标注在配置类对应的POJO上,同时可以指定默认值。
    1.2 定义Bean:在配置类中使用@Bean注解来定义你需要的Bean,往往会使用到上一步中的配置值。
    2.创建imports文件:在META-INF/spring目录下创建org.springframework.boot.autoconfigure.AutoConfiguration.imports文件。这个文件用于指定Spring Boot启动时需要加载的自动配置类,将你的自动配置类的全限定名添加到AutoConfiguration.imports文件中,每个类名占一行。
    3.构建JAR包:将你的自定义自动配置类所在的项目打包到一个JAR文件中。
    4 使用自定义自动配置:在其他Spring Boot项目中,通过添加对前面创建的JAR包的依赖,即可使用你的自定义自动配置。,可以在application.propertiesapplication.yml中添加相应的配置项。

demo

自动配置所在的项目(My-Print-Starter)

项目目录

image.png

pom

<?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 https://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>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.xxx</groupId>
    <artifactId>MySpringBootStarter</artifactId>
    <version>0.0.3-SNAPSHOT</version>
    <name>MySpringBootStarter</name>
    <description>MySpringBootStarter</description>
    <packaging>jar</packaging>
    <url/>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>
    <build>
        <!-- 将自定义starter中的默认配置文件也打包 -->
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.yml</include>
                    <include>**/*.yaml</include>
                    <include>**/*.json</include>
                    <include>**/*.imports</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
</project>

外部配置类POJO

// 一般反射都需要无参和setter方法
@Data
// 指定配置和`application.properties`或`application.yml`中数据的映射关系
@ConfigurationProperties(prefix = "print")
public class PrintProperties {
    // 指定默认值
    private String name = "LLLL";
}

自动配置类

@AutoConfiguration
@EnableConfigurationProperties(PrintProperties.class)
public class MyConfiguration {

    @Bean
    // 防止bean注入重复或者冲突
    @ConditionalOnMissingBean(PrintService.class)
    // 入参时EnableConfigurationProperties注入的PrintProperties
    public PrintService printService(PrintProperties properties)
    {
        return new PrintService(properties.getName());
    }
}

模版代码

public class PrintService {

    private String name;

    public PrintService(String name) {
        this.name = name;
    }
    public void print(){
        System.out.println("Hello "+name);
    }
}

imports文件

org.xxx.myspringbootstarter.configuration.MyConfiguration //类的全限定名

依赖My-Print-Starter的项目(App)

项目目录

image.png

pom

<?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 https://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>3.3.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.xxx</groupId>
    <artifactId>StarterUser</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>StarterUser</name>
    <description>StarterUser</description>
    <url/>
    <licenses>
        <license/>
    </licenses>
    <developers>
        <developer/>
    </developers>
    <scm>
        <connection/>
        <developerConnection/>
        <tag/>
        <url/>
    </scm>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.xxx</groupId>
            <artifactId>MySpringBootStarter</artifactId>
            <version>0.0.3-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

启动类

// 组合注解,其中包含了EnableAutoConfiguration等注解
@SpringBootApplication
public class StarterUserApplication {

    public static void main(String[] args) {
        SpringApplication.run(StarterUserApplication.class, args);
    }

}

controller(依赖的使用类)

@RestController
public class PrintController {
    // 二方包中的类
    @Resource
    private PrintService printService;

    @GetMapping("/print")
    public void print() {
        printService.print();
    }
}

application.properties

print.name=StarterUser

核心注解的实现原理

EnableAutoConfiguration

核心代码

org.springframework.boot.context.annotation.ImportCandidates#load,查看路径 SpringBootApplication-> EnableAutoConfiguration -> AutoConfigurationImportSelector-> selectImports -> getAutoConfigurationEntry -> getCandidateConfigurations方法内调用了ImportCandidates#load

public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) {
    ClassLoader classLoaderToUse = decideClassloader(classLoader);
    //location = META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    String location = String.format(LOCATION, annotation.getName());
    // 读取类路径下所有上述文件的全路径名称
    // 例如:file:/opt/org/xxx/MySpringBootStarter/0.0.3-SNAPSHOT/MySpringBootStarter-0.0.3-SNAPSHOT.jar!/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
    Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location);
    List<String> importCandidates = new ArrayList<>();
    while (urls.hasMoreElements()) {
       URL url = urls.nextElement();
       // 逐行读取文件
       importCandidates.addAll(readCandidateConfigurations(url));
    }
    return new ImportCandidates(importCandidates);
}

EnableConfigurationProperties

@EnableConfigurationProperties 是一个组合注解,它本身包含了 @Import 注解,用于导入特定的配置类ConfigurationPropertiesRegistrationBean,ConfigurationPropertiesBeanRegistrar 实现了BeanDefinitionRegistryPostProcessor 接口,在 Spring 容器的后处理阶段,它会搜索所有的 @ConfigurationProperties 注解。找到带有 @ConfigurationProperties 注解的类后,ConfigurationPropertiesBeanRegistrar 会为这些类创建一个或多个 BeanDefinition,并注册到 Spring 容器中。