SpringCore完整学习教程7,入门级别
本章可以说是完结,下一章可能讲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的核心特性将通过核心启动器的存在而得到尊重。
SpringCore完整学习教程7,入门级别的更多相关文章
- gulp入门学习教程(入门学习记录)
前言 最近在通过教学视频学习angularjs,其中有gulp的教学部分,对其的介绍为可以对文件进行合并,压缩,格式化,监听,测试,检查等操作时,看到前三种功能我的心理思想是,网上有很多在线压缩,在线 ...
- mysql部分学习心得(入门级别)
mysql中针对不同的数据选择对应的存储引擎 mysql中也会针对不同的数据处理选择对应的存储的引擎 mysql中也会针对不同的数据处理选择对应的存储的引擎 mysql中一些授权(grant)等的通常 ...
- Linux入门学习教程:虚拟机体验之KVM篇
本文中可以学习到的命令: 1. aptitude 是apt-get 不会产生垃圾的版本 2. dpkg -L virtualbox 显示属于该包的文件 lsmod | grep kvmfi ...
- MyBatis入门学习教程-使用MyBatis对表执行CRUD操作
上一篇MyBatis学习总结(一)--MyBatis快速入门中我们讲了如何使用Mybatis查询users表中的数据,算是对MyBatis有一个初步的入门了,今天讲解一下如何使用MyBatis对use ...
- 【入门必备】最佳的 Node.js 学习教程和资料书籍
Web 开发人员对 Node.js 的关注日益增多,更多的公司和开发者开始尝试使用 Node.js 来实现一些对实时性要求高,I/O密集型的业务.这篇文章中,我们整理了一批优秀的资源,你可以得到所有你 ...
- C#入门教程(二)–C#常用快捷键、变量、类型转换-打造C#学习教程
C#入门教程(一)–.Net平台技术介绍.C#语言及开发工具介绍-打造C#学习教程 上次教程主要介绍了.Net平台以及C#语言的相关介绍.以及经典程序案例,helloworld程序. 初来乍到,第一次 ...
- TensorFlow 中文资源全集,官方网站,安装教程,入门教程,实战项目,学习路径。
Awesome-TensorFlow-Chinese TensorFlow 中文资源全集,学习路径推荐: 官方网站,初步了解. 安装教程,安装之后跑起来. 入门教程,简单的模型学习和运行. 实战项目, ...
- Nginx 入门学习教程
昨天听一个前同事说他们公司老大让他去研究下关于Nginx 方面的知识,我想了下Nginx 在如今的开发技术栈中应该会很大可能会用到,所以写篇博文记录总结下官网学习教程吧. 1. 什么是Nginx? 我 ...
- 【halcon教程资料】全网汇总如何快速、高效率学习机器视觉从入门到精通
我以八年的视觉工程师开发的工作经验告诉你,你不要再因为学习halcon发愁了,我接触过很多学习halcon的小白,并不是不愿意学,而是不知道怎么快速.高效率的学习精通,一天天的过去了,对学习halco ...
- Tensorflow学习教程------读取数据、建立网络、训练模型,小巧而完整的代码示例
紧接上篇Tensorflow学习教程------tfrecords数据格式生成与读取,本篇将数据读取.建立网络以及模型训练整理成一个小样例,完整代码如下. #coding:utf-8 import t ...
随机推荐
- Deno 中使用 @typescript/vfs 生成 DTS 文件
背景 前段时间开源的 STC 工具,这是一个将 OpenApi 规范的 Swagger/Apifox 文档转换成代码的工具.可以在上一篇(<OpenApi(Swagger)快速转换成 TypeS ...
- 《SQL与数据库基础》04. SQL-DQL
目录 DQL 基础查询 条件查询 分组聚合 聚合函数 分组查询 结果排序 分页限制 总结 本文以 MySQL 为例 DQL 语法结构: SELECT 字段列表 FROM 表名列表 WHERE 条件列表 ...
- shiro框架基本概念介绍
什么是Shiro: Shiro 是一个强大灵活的开源安全框架,可以完全处理身份验证.授权.加密和会话管理 Shiro的核心功能包括: 身份验证(Authentication):验证用户的身份,确保用户 ...
- QA|20221002|SecureCRT中退格键变成了^H
原因:backspace键和delete键的键码映射问题 解决办法一:要使用回删键(backspace)时,同时按住ctrl键 解决办法二:重新设置码值映射关系.比如SecureCRT中,会话 ...
- Python 实现Word转PDF
通过将 Word 文档转换为 PDF,您可以确保文档在不同设备上呈现一致,并防止其他人对文档内容进行非授权修改.此外,在你需要打印文档时,转换为PDF还能确保打印输出的准确性.本文将介绍如何使用Pyt ...
- FX3U-3A-ADP模拟量和数字量之间转换
简单的例子: 0-10V对应0-8,4-20mA对应0-30 以下是对上面例子的详解: 电压: 电压(0-10V) 0-10V对应着数字量0-4000 数字量与变频器HZ量之间的关系是(4000-0) ...
- Xshell远程连接、MBR/BOOT和GRUB三者关系总结(系统启动过程)
远程连接 远程连接Linux服务器的常见工具有Xshell.SecureCRT.Putty等,这些客户端连接工具在Linux服务器对应着相同SSH服务进程sshd,即远程连接都是使用SSH协议,当然它 ...
- CF1338A
题目简化和分析: \(a_{i}\ge a_{i-1}\) 已经满足直接跳过 \(a_{i}<a_{i-1}\) 我们就要将其的差进行二进制的分解,使得 \(a_{i-1}=a_i\) 我也不知 ...
- Super Apps 超级应用们背后的道家哲学
众所周知,Elon Musk 想将 Twitter 重新设计定位成一款"超级应用 - X"的野心已经不再是秘密.伴随着应用商店中 Twitter 标志性的蓝鸟 Logo 被 X 取 ...
- Kubernetes网络
kubernetes-Service 1.service存在的意义 1.防止破的失联(服务发现) 2.定义一组pod的访问策略(提供负载均衡) 2.pod与service的关系 1.通过lablel- ...