Contents

Flips 简介

1. 概述

在本教程中,我们将了解Flips ,这是一个以强大注解的形式为 Spring Core、Spring MVC 和 Spring Boot 应用程序实现功能标志的库。

功能标志(或切换)是一种快速安全地交付新功能的模式。这些切换允许我们在不更改或部署新代码的情况下修改应用程序行为。

2. Maven依赖

在我们开始之前,我们需要将 Flips 库添加到我们的pom.xml 中:

<dependency>
    <groupId>com.github.feature-flip</groupId>
    <artifactId>flips-core</artifactId>
    <version>1.0.1</version>
</dependency>

Maven Central 有最新版本的库 ,Github 项目在这里

当然,我们还需要包含一个Spring:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.5.10.RELEASE</version>
</dependency>

由于 Flips 还不兼容 Spring 5.x 版本,我们将在 4.x 分支中使用最新版本 的 Spring Boot。

3. Flips 的简单 REST 服务

让我们构建一个简单的 Spring Boot 项目,用于添加和切换新功能和标志。

我们的 REST 应用程序将提供对Foo资源的访问:

public class Foo {
    private String name;
    private int id;
}

我们将简单地创建一个维护Foos列表的服务:

@Service
public class FlipService {
    private List<Foo> foos;
    public List<Foo> getAllFoos() {
        return foos;
    }
    public Foo getNewFoo() {
        return new Foo("New Foo!", 99);
    }
}

我们将参考其他服务方法,但这个片段应该足以说明FlipService在系统中的作用。

当然,我们需要创建一个控制器:

@RestController
public class FlipController {
    private FlipService flipService;
    // constructors
    @GetMapping("/foos")
    public List<Foo> getAllFoos() {
        return flipService.getAllFoos();
    }
}

4. 基于配置的控制特性

Flips 最基本的用途是根据配置启用或禁用功能。Flips 对此有几个注解。

4.1. 环境属性

假设我们为FlipService添加了一项新功能;通过他们的 id检索Foos

让我们将新请求添加到控制器:

@GetMapping("/foos/{id}")
@FlipOnEnvironmentProperty(
  property = "feature.foo.by.id", 
  expectedValue = "Y")
public Foo getFooById(@PathVariable int id) {
    return flipService.getFooById(id)
      .orElse(new Foo("Not Found", -1));
}

@FlipOnEnvironmentProperty控制此 API 是否可用。

简单来说,当feature.foo.by.idY时,我们可以通过 Id 来请求。如果它没有(或根本没有定义),Flips 将禁用 API 方法。

如果未启用某个功能,Flips 将抛出FeatureNotEnabledException,而 Spring 将向 REST 客户端返回“未实现”。

当我们调用 API 并将属性设置为N时,这就是我们看到的:

Status = 501
Headers = {Content-Type=[application/json;charset=UTF-8]}
Content type = application/json;charset=UTF-8
Body = {
    "errorMessage": "Feature not enabled, identified by method 
      public com.blogdemo.flips.model.Foo
      com.blogdemo.flips.controller.FlipController.getFooById(int)",
    "className":"com.blogdemo.flips.controller.FlipController",
    "featureName":"getFooById"
}

正如预期的那样,Spring 捕获了FeatureNotEnabledException并将状态 501 返回给客户端。

4.2. 活动资料

Spring 长期以来使我们能够将 bean 映射到不同的配置文件 ,例如devtestprod。将此功能扩展到将功能标志映射到活动配置文件具有直观意义。

让我们看看如何根据活动的Spring Profile 启用或禁用功能:

@RequestMapping(value = "/foos", method = RequestMethod.GET)
@FlipOnProfiles(activeProfiles = "dev")
public List getAllFoos() {
    return flipService.getAllFoos();
}

@FlipOnProfiles注解接受配置文件名称列表。如果活动配置文件在列表中,则可以访问 API。

4.3. Spring 表达式

Spring 的表达式语言 (SpEL) 是操纵运行时环境的强大机制。Flips 也为我们提供了一种切换功能的方法。

@FlipOnSpringExpression切换基于返回布尔值的 SpEL 表达式的方法。

让我们使用一个简单的表达式来控制一个新功能:

@FlipOnSpringExpression(expression = "(2 + 2) == 4")
@GetMapping("/foo/new")
public Foo getNewFoo() {
    return flipService.getNewFoo();
}

4.4. 停用

要完全禁用某个功能,请使用*@FlipOff*:

@GetMapping("/foo/first")
@FlipOff
public Foo getFirstFoo() {
    return flipService.getLastFoo();
}

在此示例中,*getFirstFoo()*是完全不可访问的。

正如我们将在下面看到的,我们可以组合 Flips 注解,从而可以使用*@FlipOff*根据环境或其他条件禁用功能。

5. 使用日期/时间控制功能

Flips 可以根据日期/时间或星期几切换功能。将新功能的可用性与日期或日期联系起来具有明显的优势。

5.1. 日期和时间

@FlipOnDateTime接受以ISO 8601 格式格式化的属性名称。

因此,让我们设置一个属性,指示将于 3 月 1 日启用的新功能:

first.active.after=2018-03-01T00:00:00Z

然后我们将编写一个 API 来检索第一个 Foo:

@GetMapping("/foo/first")
@FlipOnDateTime(cutoffDateTimeProperty = "first.active.after")
public Foo getFirstFoo() {
    return flipService.getLastFoo();
}

Flips 将检查命名的属性。如果该属性存在并且指定的日期/时间已过,则启用该功能。

5.2. 星期几

该库提供了*@FlipOnDaysOfWeek*,这对 A/B 测试等操作很有用:

@GetMapping("/foo/{id}")
@FlipOnDaysOfWeek(daysOfWeek={DayOfWeek.MONDAY, DayOfWeek.WEDNESDAY})
public Foo getFooByNewId(@PathVariable int id) {
    return flipService.getFooById(id).orElse(new Foo("Not Found", -1));
}

*getFooByNewId()*仅在星期一和星期三可用。

6. 替换一个Bean

打开和关闭方法很有用,但我们可能希望通过新对象引入新行为。@FlipBean指示 Flips 调用新 bean 中的方法。

Flips 注解可以在任何 Spring @Component 上工作。到目前为止,我们只修改了*@RestController*,让我们尝试修改我们的服务。

我们将创建一个行为与FlipService不同的新服务:

@Service
public class NewFlipService {
    public Foo getNewFoo() {
        return new Foo("Shiny New Foo!", 100);
    }
}

我们将用新版本替换旧服务的getNewFoo()

@FlipBean(with = NewFlipService.class)
public Foo getNewFoo() {
    return new Foo("New Foo!", 99);
}

Flips 会将对getNewThing() 的调用定向到NewFlipService@FlipBean是另一个与其他开关组合时最有用的开关。现在让我们看一下。

7. 组合切换

我们通过指定多个来组合切换。Flips 使用隐含的“AND”逻辑按顺序评估这些。因此,所有这些都必须为真才能打开该功能。

让我们结合之前的两个例子:

@FlipBean(
  with = NewFlipService.class)
@FlipOnEnvironmentProperty(
  property = "feature.foo.by.id", 
  expectedValue = "Y")
public Foo getNewFoo() {
    return new Foo("New Foo!", 99);
}

我们已经使用了可配置的新服务。