SpringCore完整学习教程7,入门级别

简介: 如何创建Spring Boot的自定义自动配置,包括理解自动配置类、定位自动配置候选、使用条件注解、测试自动配置以及创建自定义启动器的完整教程。

本章可以说是完结,下一章可能讲kotlin+springboot

本章从第九章开始:

9. Creating Your Own Auto-configuration

如果您在开发共享库的公司工作,或者您在开发开源或商业库,那么您可能希望开发自己的自动配置。自动配置类可以捆绑在外部jar中,并且仍然由Spring Boot拾取。

自动配置可以关联到一个“启动器”,该启动器提供自动配置代码以及您将与之一起使用的典型库。我们首先介绍构建您自己的自动配置所需了解的内容,然后继续介绍创建自定义启动器所需的典型步骤。

9.1. Understanding Auto-configured Beans

实现自动配置的类用@AutoConfiguration注释。这个注释本身用@Configuration进行元注释,使自动配置成为标准的@Configuration类。附加的@Conditional注释用于约束何时应用自动配置。通常,自动配置类使用@ConditionalOnClass和@ConditionalOnMissingBean注释。这确保了自动配置仅在找到相关类并且没有声明自己的@Configuration时应用。

9.2. Locating Auto-configuration Candidates

Spring Boot检查是否存在META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件在已发布的jar中。该文件应该列出你的配置类,每行一个类名,如下例所示:

com.mycorp.libx.autoconfigure.LibXAutoConfiguration
com.mycorp.libx.autoconfigure.LibXWebAutoConfiguration

您可以使用#字符向导入文件添加注释。

自动配置只能通过在导入文件中命名来加载。确保它们是在特定的包空间中定义的,并且它们永远不是组件扫描的目标。此外,自动配置类不应该启用组件扫描来查找其他组件。应该使用特定的@Imports。

9.3. Condition Annotations

您几乎总是希望在自动配置类中包含一个或多个@Conditional注释。@ConditionalOnMissingBean注释是一个常见的例子,如果开发人员对默认值不满意,可以使用它重写自动配置

Spring Boot包含许多@Conditional注释,您可以通过注释@Configuration类或单个@Bean方法在自己的代码中重用这些注释。这些注释包括:

9.3.1. Class Conditions

@ConditionalOnClass和@ConditionalOnMissingClass注释允许根据特定类的存在与否来包含@Configuration类。由于注释元数据是通过使用ASM进行解析的,因此您可以使用value属性来引用实际的类,即使该类实际上可能不会出现在运行的应用程序类路径中。如果希望使用String值指定类名,也可以使用name属性。

这种机制不适用于@Bean方法,其中返回类型通常是条件的目标:在方法上的条件应用之前,JVM将加载类并可能处理方法引用,如果类不存在,则方法引用将失败。

为了处理这种情况,可以使用一个单独的@Configuration类来隔离条件,如下面的例子所示:

引入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>3.0.12-SNAPSHOT</version>
        </dependency>
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@AutoConfiguration
// Some conditions ...
public class MyAutoConfiguration {

    // Auto-configured beans ...

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(SomeService.class)
    public static class SomeServiceConfiguration {

        @Bean
        @ConditionalOnMissingBean
        public SomeService someService() {
            return new SomeService();
        }

    }

}

我来解释一下,

@ConditionalOnClass(SomeService.class)是当someService.class存在的时候

@Bean

@ConditionalOnMissingBean

是当没有这个bean的时候,注册一个bean

如果您使用@ConditionalOnClass或@ConditionalOnMissingClass作为元注释的一部分来组成您自己的组合注释,则必须使用name作为类的引用,在这种情况下不处理。

9.3.2. Bean Conditions
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;

@AutoConfiguration
public class MyAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean
    public SomeService someService() {
        return new SomeService();
    }

}
9.3.3. Property Conditions

@ConditionalOnProperty注释允许基于Spring环境属性来包含配置。使用前缀和名称属性来指定应该检查的属性。默认情况下,匹配存在且不等于false的任何属性。您还可以通过使用havingValue和matchIfMissing属性创建更高级的检查。

public interface NotificationSender {
    String send(String message);
}
public class EmailNotification implements NotificationSender {
    @Override
    public String send(String message) {
        return "Email Notification: " + message;
    }
}
@Bean(name = "emailNotification")
@ConditionalOnProperty(prefix = "notification", name = "service")
public NotificationSender notificationSender() {
    return new EmailNotification();
}
notification.service=email
9.3.4. Resource Conditions

@ConditionalOnResource注释允许只在存在特定资源时才包含配置。可以使用Spring的常规约定来指定资源,示例如下:file:/home/user/test.dat。

9.3.5. Web Application Conditions

@ConditionalOnWebApplication和@ConditionalOnNotWebApplication注释允许根据应用程序是否是web应用程序来包含配置。基于servlet的web应用程序是任何使用Spring WebApplicationContext、定义会话范围或具有可配置web环境的应用程序。响应式web应用程序是任何使用ReactiveWebApplicationContext或具有configurableereactivewebenvironment的应用程序。

@ConditionalOnWarDeployment和@ conditionalonnotardeployment注释允许根据应用程序是否是部署到servlet容器的传统WAR应用程序来包含配置。此条件不适用于使用嵌入式web服务器运行的应用程序。

9.3.6. SpEL Expression Conditions

@ConditionalOnExpression注释允许基于SpEL表达式的结果包含配置。

在表达式中引用bean将导致在上下文刷新处理中很早就初始化该bean。因此,bean将无法进行后处理(例如配置属性绑定),并且其状态可能是不完整的。

9.4. Testing your Auto-configuration

自动配置可能受到许多因素的影响:用户配置(@Bean定义和环境定制)、条件评估(特定库的存在)以及其他因素。具体地说,每个测试都应该创建一个定义良好的ApplicationContext,它代表了这些定制的组合。ApplicationContextRunner提供了一个很好的实现方法。

ApplicationContextRunner通常被定义为测试类的一个字段,用于收集基本的公共配置。下面的例子确保MyServiceAutoConfiguration总是被调用:

private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
    .withConfiguration(AutoConfigurations.of(MyServiceAutoConfiguration.class));

如果必须定义多个自动配置,则不需要对它们的声明进行排序,因为调用它们的顺序与运行应用程序时的顺序完全相同。

每个测试都可以使用运行器来表示一个特定的用例。例如,下面的示例调用一个用户配置(UserConfiguration),并检查自动配置是否正确退出。调用run提供了一个可与AssertJ一起使用的回调上下文。

@Test
void defaultServiceBacksOff() {
    this.contextRunner.withUserConfiguration(UserConfiguration.class).run((context) -> {
        assertThat(context).hasSingleBean(MyService.class);
        assertThat(context).getBean("myCustomService").isSameAs(context.getBean(MyService.class));
    });
}

@Configuration(proxyBeanMethods = false)
static class UserConfiguration {

    @Bean
    MyService myCustomService() {
        return new MyService("mine");
    }

}

运行器也可以用来显示ConditionEvaluationReport。报告可以打印为INFO或DEBUG级别。下面的示例显示如何使用ConditionEvaluationReportLoggingListener在自动配置测试中打印报告。

import org.junit.jupiter.api.Test;

import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;

class MyConditionEvaluationReportingTests {

    @Test
    void autoConfigTest() {
        new ApplicationContextRunner()
            .withInitializer(new ConditionEvaluationReportLoggingListener(LogLevel.INFO))
            .run((context) -> {
                    // Test something...
            });
    }

}
9.4.1. Simulating a Web Context

如果你需要测试一个只在servlet或响应式web应用上下文中运行的自动配置,请分别使用WebApplicationContextRunner或ReactiveWebApplicationContextRunner

9.4.2. Overriding the Classpath

还可以测试在运行时不存在特定类和/或包时发生的情况。Spring Boot附带了一个FilteredClassLoader,运行器可以很容易地使用它。在下面的例子中,我们断言如果MyService不存在,自动配置被正确禁用:

@Test
void serviceIsIgnoredIfLibraryIsNotPresent() {
    this.contextRunner.withClassLoader(new FilteredClassLoader(MyService.class))
        .run((context) -> assertThat(context).doesNotHaveBean("myService"));
}

9.5. Creating Your Own Starter

典型的Spring Boot启动器包含用于自动配置和定制给定技术的基础结构的代码,我们称之为“acme”。为了使其易于扩展,可以向环境公开专用名称空间中的许多配置键。最后,提供了一个单独的“启动器”依赖项,以帮助用户尽可能轻松地开始。

具体来说,自定义启动器可以包含以下内容:

  • 包含"acme"的自动配置代码的autoconfigure模块。

  • starter模块,它提供了autoconfigure模块的依赖项,以及“acme”和其他通常有用的依赖项。简而言之,添加启动器应该提供开始使用该库所需的一切。

在两个模块中进行这种分离是没有必要的。如果“acme”有几种风格、选项或可选特性,那么最好将自动配置分开,因为您可以清楚地表达某些特性是可选的事实。此外,您还可以创建一个启动器,提供关于这些可选依赖项的意见。与此同时,其他人只能依赖于autoconfigure模块,并使用不同的意见制作自己的启动器。

如果自动配置相对简单,并且没有可选特性,那么在启动器中合并这两个模块绝对是一种选择。

9.5.1. Naming

您应该确保为启动器提供适当的名称空间。不要以spring-boot作为模块名的开头,即使使用不同的Maven groupId也是如此。我们可能会在将来为您的自动配置提供官方支持。
根据经验,应该以启动器的名字命名组合模块。例如,假设您正在为“acme”创建一个启动器,并将自动配置模块命名为acme-spring-boot,将启动器命名为acme-spring-boot-starter。如果只有一个模块结合了这两者,那么将其命名为acme-spring-boot-starter。

9.5.2. Configuration keys

如果启动器提供了配置键,请为它们使用唯一的名称空间。特别是,不要在Spring Boot使用的名称空间(如server、management、Spring等)中包含键。如果您使用相同的命名空间,我们可能会在将来以破坏您的模块的方式修改这些命名空间。根据经验,使用您拥有的名称空间作为所有密钥的前缀(例如acme)。
确保通过为每个属性添加字段javadoc来记录配置键,如下例所示:

import java.time.Duration;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("acme")
public class AcmeProperties {

    /**
     * Whether to check the location of acme resources.
     */
    private boolean checkLocation = true;

    /**
     * Timeout for establishing a connection to the acme server.
     */
    private Duration loginTimeout = Duration.ofSeconds(3);

    public boolean isCheckLocation() {
        return this.checkLocation;
    }

    public void setCheckLocation(boolean checkLocation) {
        this.checkLocation = checkLocation;
    }

    public Duration getLoginTimeout() {
        return this.loginTimeout;
    }

    public void setLoginTimeout(Duration loginTimeout) {
        this.loginTimeout = loginTimeout;
    }

}

您应该只使用纯文本的@ConfigurationProperties字段Javadoc,因为它们在添加到JSON之前不会被处理。

以下是我们内部遵循的一些规则,以确保描述的一致性:

  • 不要以“the”或“A”开头。

  • 对于布尔类型,以“是否”或“启用”开始描述。

  • 对于基于集合的类型,以“逗号分隔的列表”开始描述。

  • 使用java.time.Duration而不是long,并描述默认单位,如果它与毫秒不同,例如“如果未指定持续时间后缀,将使用秒”。

  • 不要在描述中提供默认值,除非它必须在运行时确定。

确保触发元数据生成,以便也为您的键提供IDE帮助。您可能需要查看生成的元数据(META-INF/spring-configuration-metadata.json),以确保正确地记录了密钥。在兼容的IDE中使用自己的启动器也是验证元数据质量的好方法。

9.5.3. The “autoconfigure” Module

autoconfigure模块包含了开始使用该库所需的所有内容。它还可能包含配置键定义(如@ConfigurationProperties)和任何可用于进一步定制组件初始化方式的回调接口。

您应该将库的依赖项标记为可选,以便您可以更轻松地在项目中包含autoconfigure模块。如果这样做,则不会提供库,并且在默认情况下,Spring Boot会退出。

Spring Boot使用注释处理器收集元数据文件(META-INF/ Spring -autoconfigure-metadata.properties)中自动配置的条件。如果该文件存在,它将用于主动过滤不匹配的自动配置,这将缩短启动时间。

当使用Maven构建时,建议在包含自动配置的模块中添加以下依赖项:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure-processor</artifactId>
    <optional>true</optional>
</dependency>

如果你在应用中直接定义了自动配置,请确保配置了spring-boot-maven-plugin,以防止重新打包目标将依赖项添加到fat jar中:

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-autoconfigure-processor</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

在Gradle中,依赖项应该在annotationProcessor配置中声明,如下例所示:

dependencies {
    annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor"
}
9.5.4. Starter Module

开胃菜实际上是一个空罐子。它的唯一目的是提供使用库所需的依赖项。你可以把它看作是一种对开始需要做什么的固执己见的看法。

不要对添加了启动器的项目做任何假设。如果自动配置的库通常需要其他启动器,也要提到它们。如果可选依赖项的数量很高,那么提供一组适当的默认依赖项可能会很困难,因为您应该避免包含对于库的典型使用来说不必要的依赖项。换句话说,您不应该包含可选的依赖项。

无论哪种方式,您的启动器都必须直接或间接地引用核心Spring Boot启动器(Spring - Boot -starter)(如果您的启动器依赖于另一个启动器,则不需要添加它)。如果一个项目是用您的自定义启动器创建的,Spring Boot的核心特性将通过核心启动器的存在而得到尊重。

目录
相关文章
|
23天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
15天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。
|
19天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2570 22
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
17天前
|
人工智能 IDE 程序员
期盼已久!通义灵码 AI 程序员开启邀测,全流程开发仅用几分钟
在云栖大会上,阿里云云原生应用平台负责人丁宇宣布,「通义灵码」完成全面升级,并正式发布 AI 程序员。
|
1天前
|
存储 人工智能 搜索推荐
数据治理,是时候打破刻板印象了
瓴羊智能数据建设与治理产品Datapin全面升级,可演进扩展的数据架构体系为企业数据治理预留发展空间,推出敏捷版用以解决企业数据量不大但需构建数据的场景问题,基于大模型打造的DataAgent更是为企业用好数据资产提供了便利。
152 2
|
19天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1566 16
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
2天前
|
JSON 自然语言处理 数据管理
阿里云百炼产品月刊【2024年9月】
阿里云百炼产品月刊【2024年9月】,涵盖本月产品和功能发布、活动,应用实践等内容,帮助您快速了解阿里云百炼产品的最新动态。
阿里云百炼产品月刊【2024年9月】
|
21天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
921 14
|
16天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
689 9
|
15天前
|
存储 监控 调度
云迁移中心CMH:助力企业高效上云实践全解析
随着云计算的发展,企业上云已成为创新发展的关键。然而,企业上云面临诸多挑战,如复杂的应用依赖梳理、成本效益分析等。阿里云推出的云迁移中心(CMH)旨在解决这些问题,提供自动化的系统调研、规划、迁移和割接等功能,简化上云过程。CMH通过评估、准备、迁移和割接四个阶段,帮助企业高效完成数字化转型。未来,CMH将继续提升智能化水平,支持更多行业和复杂环境,助力企业轻松上云。