Spring Boot 2中的功能切换

无论您是否喜欢,软件开发都是一项协作活动。 整合工作一直被妖魔化,并被视为必不可少的邪恶。 有几种方法可以解决有效集成的挑战。 功能切换开关属于该组。 在本文中,您将在实践中看到如何在Spring Boot应用程序中使用功能切换(也称为功能标志)。

1.什么是功能切换?

简而言之, 功能切换是允许根据其当前值在应用程序中执行替代路径的变量。 通过保持不同的执行方案,您可以在不更改代码的情况下修改应用程序的行为。

根据您的需求,可以在启动应用程序之前设置切换开关的值,也可以在运行时对其进行调整。 在后一种情况下,值的更改可以保留或仅影响应用程序的当前执行。

通常,您会读到有关功能标志的信息, 以作为功能源代码分支的替代方法 ,但是,实际上,两种技术可以一起使用。 例如,您可以使用功能分支在应用程序中开发新的用户故事,同时可以使用功能切换来控制对不同环境(例如,具有不同要求的客户端)上功能的访问。

尽管有许多用途,功能切换也有其缺点。 最大的问题是复杂性 。 如果没有适当的策略,他们可能会Swift失控,成为维护的噩梦。 幸运的是,如果您遵循几种良好的做法并围绕features来组织应用程序 ,则使用Feature标志应该更加简单。

2.使用功能切换选择豆

在Spring Boot应用程序中使用功能切换的最常见情况是基于功能切换的当前值激活某些接口的不同实现。 我们来看一个示例来说明所描述的情况。

2.1依赖抽象

假设您有一个Web端点,该端点返回从数据库存储库中获取的产品列表。 您的目标是创建一个功能切换,该功能切换允许将存储库实现切换为使用Web服务作为数据源的实现。

如果要允许要素切换的类直接在其他类中使用,则您要做的第一件事是使用接口抽象依赖关系。

下面的代码片段提供了一个示例Product REST端点,该端点依赖于ProductRepository接口。

@RestController
@RequestMapping("/products")
class ProductController {private final ProductRepository productRepository;ProductController(ProductRepository productRepository) {this.productRepository = productRepository;}@GetMappingCollection<Product> getAll() {return productRepository.findAll();}}

目前,我们只有一个接口实现。 不久,我们将添加另一个,您将通过功能切换激活它。

@Repository
class DbProductRepository implements ProductRepository {//...
}

2.2 application.properties中的功能切换

由于application.properties文件用于配置Spring Boot应用程序,因此是放置功能切换标志的好地方。

feature.toggles.productsFromWebService=true

在提交代码之前,将标志设置为false。 这样,默认情况下,您的队友将禁用新功能。 如果有人要激活该功能,则他们可以在本地开发环境中将标志值更改为true。

2.3有条件的Bean创建

下一步是创建要通过功能切换激活的接口的替代实现。 为了根据创建的属性的值实例化bean,可以使用Spring Boot注释@ConditionalOnProperty 。 设置切换属性的名称和应激活它的值。 该值应与放在application.properties文件中的值相同。

@Repository
@ConditionalOnProperty(name = "feature.toggles.productsFromWebService",havingValue = "true"
)
class WebServiceProductRepository implements ProductRepository {//...
}

在启动应用程序之前,必须禁用数据库存储库,否则,您将获得有关接口的多个活动实现的异常。 返回第一个实现并应用以下更改:

@Repository
@ConditionalOnProperty(name = "feature.toggles.productsFromWebService",havingValue = "false",matchIfMissing = true
)
class DbProductRepository implements ProductRepository {

我们使用与以前相同的功能切换名称,只是其值已更改。 设置matchIfMissing属性是可选的。 这样,如果您从application.properties文件中删除功能切换,即使缺少该值,也将创建该bean。

3.如何通过功能切换禁用控制器

您可以应用相同的策略有条件地激活整个Spring Web控制器。 您不需要创建其他接口,因为您只想通过功能切换控制一个实现。

@RestController
@RequestMapping("/coupons")
@ConditionalOnProperty(name = "feature.toggles.coupons", havingValue = "true")
class CouponController {//...
}

application.properties应该包含以下行。

feature.toggles.coupons=true

当您不将值设置为true时,Spring不会实例化控制器。 客户端将仅收到404 HTTP状态代码。

不幸的是, @ ConditionalOnProperty批注不能在单个@RequestMapping方法上使用。 解决方法是,可以将所需的映射移动到单独的控制器Bean。 或者,可以简单地插入功能切换的值并在映射方法的主体中创建if语句。 但是,您应谨慎使用此解决方案。 如果您有兴趣,为什么在下一段中找到答案。

private final boolean couponsToggled;CouponController(@Value("${feature.toggles.coupons}") boolean couponsToggled) {this.couponsToggled = couponsToggled;
}@GetMapping
List<String> listCouponNames() {if (!couponsToggled) {throw new NotSupportedException();}//...
}

4.多功能切换管理

正如您可以阅读Martin Fowler的bliki上的功能切换一样 , 功能标志倾向于在整个代码库中扩展,并且很快就会变得难以管理 。 即使您的应用程序中只有几个功能切换,最好还是从使用标记的决策点抽象出标记的存储。

4.1避免特征标记耦合

上一段中的最后一个代码示例使用直接从application.properties文件注入的标志值,因此不会抽象存储。 如果要在应用程序的不同部分中使用相同的标志,则必须重复注入。

相反,您可以做的是将所有功能切换值放在一个类中,这将作为单个true来源 。 使用单独的类可为您提供更大的灵活性。 例如,您可以用数据库替换标志的存储,或者实现一种允许在运行时切换标志的机制。

4.2在Spring Boot中提取功能切换决策

一旦具有用于功能切换的单独的bean,就可以使用@ConfigurationProperties批注轻松地从application.properties文件中注入所有标志。 在这里,您可以看到一个示例实现:

@Component
@Component
@ConfigurationProperties("feature")
public class FeatureDecisions {private Map<String, Boolean> toggles = new HashMap<>();public Map<String, Boolean> getToggles() {return toggles;}public boolean couponEnabled() {return toggles.getOrDefault("coupons", false);}}

上面的类将获取所有以feature.toggles开头的属性,并将它们放入切换图。 如您所见,有一个名为couponEnabled()的方法,可用于从决策背后的逻辑中提取功能决策点。

此外,您还需要一个额外的依赖项才能启用对@ConfigurationProperties的处理。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId>
</dependency>

5.用于功能切换的执行器端点

由于您已经将所有功能切换都放在一个位置,因此现在要做的就是使用自定义的Actuator端点公开列表。 以下示例将向您展示如何进行。

@Component
@Endpoint(id = "feature-toggles")
class FeatureToggleInfoEndpoint {private final FeatureDecisions featureDecisions;FeatureToggleInfoEndpoint(FeatureDecisions featureDecisions) {this.featureDecisions = featureDecisions;}@ReadOperationpublic Map<String, Boolean> featureToggles() {return featureDecisions.getToggles();}}

如果您使用默认的Spring Boot 2 Actuator设置,则不会通过HTTP公开端点 。 为了在浏览器中对其进行测试,您必须通过将其标识符添加到application.properties文件中的Web include过滤器来启用Actuator端点。

management.endpoints.web.exposure.include=health,info,feature-toggles

运行应用程序后,请转到http:// localhost:8080 / actuator / feature-toggles查看端点返回的结果:

根据您的需求,您还可以在创建的端点上使用@WriteOperation实现在运行时切换功能切换的可能性。 本示例仅涵盖输出部分。

结论

在本文中,您可以了解Spring Boot应用程序中功能切换的实际示例。 我们从一个非常基本的示例开始,该示例涵盖了框架的所有需求。 之后,我们编写一些自定义代码来完成更多的自定义功能切换要求。 我们完成了有用的Actuator端点,以显示应用程序中所有功能标志的状态。

您可以在Github存储库中找到工作示例应用程序 。 如果您喜欢该帖子并认为它有用,请与您的关注者分享。 我也期待您在本文下面的问题和评论。

翻译自: https://www.javacodegeeks.com/2018/03/feature-toggle-in-spring-boot-2.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/348186.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java学习 第三章 数组(三)排序算法

** Java学习 第三章 数组&#xff08;三&#xff09;排序算法 ** 主要内容&#xff1a;排序算法、排序算法横向比较、Arrays工具类的使用、数组常见异常 1.数组中涉及到的常见算法&#xff1a;排序算法 1.1 排序算法分类&#xff1a;内部排序和外部排序 1.2 十大内部排序算…

xmx java_为什么我的Java进程比Xmx消耗更多的内存?

xmx java你们有些人去过那里。 您已经在启动脚本中添加了-Xmx选项&#xff0c;并放松了下来&#xff0c;因为您知道Java进程将不会消耗比经过微调的选项所允许的更多的内存。 然后&#xff0c;您感到非常讨厌。 要么自己检查开发/测试框中的过程表&#xff0c;要么事情真的变坏…

卫星通信系统概述

卫星通信系统指通过在轨人造卫星作为中继站对无线电信号进行转发&#xff0c;实现地面及空间等用户之间信息传输的系统。卫星通信系统组成包括空间段及地面段&#xff0c;系统组成如图所示。其中空间段主要指在轨卫星、对在轨卫星进行操控的地面站&#xff0c;这些地面站主要实…

Opnet入门

一、opnet快速入门 1.系统界面&文件菜单说明 2.常用文件名后缀及描述 3.Opnet建模层次 用户只有一种节点域模型 三、 OPNET Modeler网络仿真机制 1.事件的属性 每次点击next会出现以下界面&#xff1a; 2.事件的执行 调度型&#xff1a;按照正常程序调度事件 强制性&…

【强化学习】Policy Gradient原理

1.Policy Gradient和DQN系列强化算法最大的区别在于&#xff1a; DQN系列基于Value&#xff0c;也就是说执行完所有的动作并保存所得到的价值&#xff0c;根据这些价值计算出最优价值函数&#xff0c;并以此选择动作&#xff0c;最终获得一个特定的策略。 Policy Gradient基于策…

ajax的url怎么将后缀补上_蜂蜜杏仁怎么做?杏仁和蜂蜜腌制方法

蜂蜜杏仁怎么做?杏仁和蜂蜜腌制方法蜂蜜杏仁是一种非常好吃的小零食&#xff0c;很多小可爱都喜欢吃蜂蜜杏仁。不过有些时候忘记补货&#xff0c;就会断粮&#xff0c;于是大家都想要在家里自制蜂蜜杏仁。不过你知道蜂蜜杏仁应该怎么做吗?蜂蜜杏仁的做法其实并不难&#xff0…

【强化学习】Actor Critic原理

PG算法是一种只基于policy的一种方法&#xff0c;存在的问题就是该算法需要完整的状态序列&#xff0c;且单独对策略函数进行迭代更新&#xff0c;不太容易收敛。 Actor-critic方法呢是一种将 策略(Policy Based)和价值(Value Based)相结合的方法。下面继续来理一下AC方法的思路…

【强化学习】A3C原理

先解释一下什么叫异步、什么叫并发&#xff1a; **异步&#xff1a;**和同步相对&#xff0c;同步是顺序执行&#xff0c;而异步是彼此独立&#xff0c;在等待某个事件的过程中继续做自己的事&#xff0c;不要等待这一事件完成后再工作。线程是实现异步的一个方式&#xff0c;异…

jboss eap 7.0_创建委托登录模块(用于JBoss EAP 6.1)

jboss eap 7.0[如果只想查看代码&#xff0c;请向下滚动] 动机 在RHQ中&#xff0c;我们需要一个安全域&#xff0c;该域可用于通过容器管理的安全性来保护REST-api及其Web应用程序。 过去&#xff0c;我只是使用经典的DatabaseServerLoginModule对DatabaseServerLoginModule进…

【强化学习】可视化学习tensorboard

tensorflow定义了一个图结构&#xff1a; 代码&#xff1a; a tf.constant(3.0, name"a")b tf.constant(4.0, name"b")c tf.add(a, b, name"add")var tf.Variable(tf.random_normal([2, 3], mean0.0, stddev1.0), name"variable"…

面向初学者的JSF 2.0教程

1.什么是JSF&#xff1f; JSF是Java Server Faces的首字母缩写。 它是一种服务器端处理技术&#xff0c;它允许将服务器端代码嵌入到网页中。 由于可以将服务器端处理和呈现代码嵌入网页本身&#xff0c;因此使项目的整体编码更加简单。 减少总体数量以及文件大小。 JSF包含2个…

华为nova7保密柜_华为发布nova8系列新品 轻松拍出Vlog黄金脸占比

2020年12月23日&#xff0c;华为nova8系列手机正式发布。作为深受年轻人喜爱的手机品牌之一&#xff0c;华为nova8系列此次延续其强大的前置视觉实力、强悍的设计性能以及独特的设计美学&#xff0c;带来业内首款专为Vlog拍摄设计的5G手机。随着短视频和Vlog的崛起&#xff0c;…

26.0.0-alpha1_WildFly 8.0.0.Alpha1的发布和一些历史

26.0.0-alpha1自从我们发布WildFly 8.0.0.Alpha1版本以来&#xff0c; 已经过去了大约2周。 该下载位于WildFly下载页面上 。 我敢肯定&#xff0c;你们中的许多人可能会想知道WildFly是什么&#xff0c;而其中一些知道它是什么的人可能不会知道已经发布了。 我将尝试回答其中的…

单机最大负载_分布式高可靠之负载均衡,今天看了你肯定会

到目前为止&#xff0c;我已经为你介绍了分布式起源、分布式协调与同步、分布式资源管理与负载调度、分布式计算技术、分布式通信技术和分布式数据存储。可以说&#xff0c;掌握了这些内容&#xff0c;基本上就掌握了分布式的关键技术。然而&#xff0c;只有可靠的分布式系统才…

【强化学习】一文带你理清强化学习

整理不易&#xff0c;希望留个赞再走哦&#xff01;&#xff01; 学习路线 这个图描述的比较清晰&#xff0c;蓝框里是整个强化学习的一些概念基础了&#xff0c;橙色是一些学习方法&#xff0c;可以针对性的选择一些&#xff0c;废话不多说&#xff0c;接下来就按照这个路线图…

了解Java缓冲池

了解Java缓冲池 缓冲池空间位于垃圾收集器管理的内存之外。 这是分配本地堆外内存的一种方法。 使用缓冲池有什么好处&#xff1f; 为了回答这个问题&#xff0c;让我们首先了解什么是字节缓冲区。 字节缓冲区 非直接缓冲区 ByteBuffer类附带了java.nio包。 它允许我们分配直接…

java 循环拼接字符串用分号隔开_Java 8中字符串拼接新姿势:StringJoiner

在为什么阿里巴巴不建议在for循环中使用””进行字符串拼接一文中&#xff0c;我们介绍了几种Java中字符串拼接的方式&#xff0c;以及优缺点。其中还有一个重要的拼接方式我没有介绍&#xff0c;那就是Java 8中提供的StringJoiner &#xff0c;本文就来介绍一下这个字符串拼接…

doctrine find的对象转换成数组_「ES6基础」Array数组的新方法(上)

在日常工作中我们经常会与数组打交道&#xff0c;因此需要熟练掌握数组操作的相关方法&#xff0c;ES6中关于数组的操作&#xff0c;又给我们带来了哪些惊喜呢&#xff0c;Array数组操作又添加了哪些新方法&#xff1f;本篇文章将从以下几个方面进行介绍&#xff1a;Array.from…

No module named ‘__main__.common‘; ‘__main__‘ is not a package

今天在调通代码时遇到一个难缠的bug,源代码如下&#xff1a; from .common import _FLOATX, _EPSILON结果报错&#xff1a; 问题原因&#xff1a; 也就是说&#xff0c;这是相对导入&#xff0c;只有在父模块在当前运行环境中被导入过才能用。 这揭示了报错的缘由&#xff0…

ValueError( Shape(4, ?, 1, 20) and () are incompatible

报错&#xff1a; 解决&#xff1a; 将文件中的return tf.concat(axis, tensors) 改为&#xff1a;return tf.concat(tensors, axis)问题完美解决