知識庫 / Spring / Spring Boot RSS 訂閱

Spring Boot 和 Togglz 方面增強

Spring Boot
HongKong
6
02:34 PM · Dec 06 ,2025

1. 概述

在本教程中,我們將探討如何使用 Togglz 庫與 Spring Boot 應用程序一起使用。

2. Togglz

Togglz

Feature Toggles 設計模式的實現。該模式是指在應用程序運行時,通過一個機制來確定某個功能是否啓用,這取決於一個開關的狀態。

在運行時禁用功能可能在各種情況下都很有用,例如在尚未完成的新功能開發中、僅允許部分用户訪問某個功能或進行 A/B 測試。

在後面的部分,我們將創建一個攔截帶有註解的方法的方面,該註解提供功能名稱,並根據功能是否啓用來決定是否繼續執行方法。

3. Maven 依賴項

除了 Spring Boot 依賴項之外,Togglz 庫還提供一個 Spring Boot Starter jar。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.2</version>
</parent>

<dependency>
    <groupId>org.togglz</groupId>
    <artifactId>togglz-spring-boot-starter</artifactId>
    <version>2.4.1</version>
<dependency>
    <groupId>org.togglz</groupId>
    <artifactId>togglz-spring-security</artifactId>
    <version>2.4.1</version>
</dependency>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId> 
    <artifactId>spring-boot-starter-test</artifactId> 
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.1.214</version>
</dependency>

最新版本的 togglz-spring-boot-starter, togglz-spring-security, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-test, h2 可從 Maven Central 下載。

4. Togglz 配置

togglz-spring-boot-starter 庫包含用於創建必要的 Bean(如 FeatureManager)的自動配置。我們唯一需要提供的 Bean 是 featureProvider Bean。

首先,讓我們創建一個枚舉,該枚舉實現 Feature 接口,幷包含一個 feature 名稱列表:

public enum MyFeatures implements Feature {

    @Label("Employee Management Feature")
    EMPLOYEE_MANAGEMENT_FEATURE;

    public boolean isActive() {
        return FeatureContext.getFeatureManager().isActive(this);
    }
}

枚舉還定義了一個名為 isActive() 的方法,用於驗證某個功能是否已啓用。

然後,可以在 Spring Boot 配置類中定義一個類型為 EnumBasedFeatureProvider 的 Bean:

@Configuration
public class ToggleConfiguration {

    @Bean
    public FeatureProvider featureProvider() {
        return new EnumBasedFeatureProvider(MyFeatures.class);
    }
}

5. 創建方面 (Aspect)接下來,我們將創建一個攔截自定義 AssociatedFeature 註解並檢查註解參數中提供的特徵是否激活的方面:

@Aspect
@Component
public class FeaturesAspect {

    private static final Logger LOG = Logger.getLogger(FeaturesAspect.class);

    @Around(
      "@within(featureAssociation) || @annotation(featureAssociation)"
    )
    public Object checkAspect(ProceedingJoinPoint joinPoint, 
      FeatureAssociation featureAssociation) throws Throwable {
 
        if (featureAssociation.value().isActive()) {
            return joinPoint.proceed();
        } else {
            LOG.info(
              "Feature " + featureAssociation.value().name() + " is not enabled!");
            return null;
        }
    }
}

讓我們也定義自定義標註 FeatureAssociation,它將具有 value() 參數,類型為 MyFeatures 枚舉:

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface FeatureAssociation {
    MyFeatures value();
}

如果該功能已啓用,則該方面將繼續執行方法;如果未啓用,則會記錄一條消息而不運行方法代碼。

6. 特性激活

Togglz 中,一個特性可以是激活狀態或非激活狀態。這種行為由一個 enabled 標誌以及可選的激活策略控制。

要將 enabled 標誌設置為 true,我們可以使用在枚舉值定義上添加的 @EnabledByDefault 註解。

Togglz 庫還提供了多種激活策略,可用於根據特定條件確定特性是否已啓用。

在我們的示例中,讓我們為 EMPLOYEE_MANAGEMENT_FEATURE 使用 SystemPropertyActivationStrategy,該策略根據 System 屬性的值評估特性的狀態。可以使用 @ActivationParameter 註解指定所需的屬性名稱和值。

public enum MyFeatures implements Feature {

    @Label("Employee Management Feature") 
    @EnabledByDefault 
    @DefaultActivationStrategy(id = SystemPropertyActivationStrategy.ID, 
      parameters = { 
      @ActivationParameter(
        name = SystemPropertyActivationStrategy.PARAM_PROPERTY_NAME,
        value = "employee.feature"),
      @ActivationParameter(
        name = SystemPropertyActivationStrategy.PARAM_PROPERTY_VALUE,
        value = "true") }) 
    EMPLOYEE_MANAGEMENT_FEATURE;
    //...
}

我們已將該功能設置為僅在 employee.feature 屬性值為 true 時啓用。

Togglz 庫還提供了其他類型的激活策略,如下所示:

  • UsernameActivationStrategy – 允許功能針對指定的用户列表處於啓用狀態
  • UserRoleActivationStrategy – 當前用户的角色用於確定功能的狀態
  • ReleaseDateActivationStrategy – 自動在指定日期和時間激活功能
  • GradualActivationStrategy – 針對指定百分比的用户啓用功能
  • ScriptEngineActivationStrategy – 允許使用 JVM 支持的語言編寫的自定義腳本來確定功能是否啓用
  • ServerIpActivationStrategy – 功能基於服務器的 IP 地址進行啓用

7. 測試方面

7.1. 示例應用程序

為了查看我們的功能在實際應用中的效果,讓我們創建一個簡單的示例,其中包含管理組織員工的特性。

由於該特性將在開發過程中添加方法和類,這些類將使用我們的 <em @AssociatedFeature> 註解,並設置值為 EMPLOYEE_MANAGEMENT_FEATURE。 這確保了它們僅在特性處於活動狀態時才可訪問。

首先,讓我們定義一個 <em Employee> 實體類和基於 Spring Data 的倉庫:

@Entity
public class Employee {

    @Id
    private long id;
    private double salary;
    
    // standard constructor, getters, setters
}
public interface EmployeeRepository
  extends CrudRepository<Employee, Long>{ }

接下來,我們添加一個 EmployeeService 類,其中包含一個用於提高員工薪資的方法。我們將使用 @AssociatedFeature 註解,併為該方法參數指定 EMPLOYEE_MANAGEMENT_FEATURE

@Service
public class SalaryService {

    @Autowired
    EmployeeRepository employeeRepository;

    @FeatureAssociation(value = MyFeatures.EMPLOYEE_MANAGEMENT_FEATURE)
    public void increaseSalary(long id) {
        Employee employee = employeeRepository.findById(id).orElse(null);
        employee.setSalary(employee.getSalary() + 
          employee.getSalary() * 0.1);
        employeeRepository.save(employee);
    }
}

該方法將從一個 /increaseSalary 端點調用,用於測試:

@Controller
public class SalaryController {

    @Autowired
    SalaryService salaryService;

    @PostMapping("/increaseSalary")
    @ResponseBody
    public void increaseSalary(@RequestParam long id) {
        salaryService.increaseSalary(id);
    }
}

7.2. JUnit 測試

首先,我們添加一個測試,其中在將 employee.feature 屬性設置為 false 後,調用我們的 POST 映射。在這種情況下,該功能不應處於活動狀態,員工的工資不應發生變化:

@Test
public void givenFeaturePropertyFalse_whenIncreaseSalary_thenNoIncrease() 
  throws Exception {
    Employee emp = new Employee(1, 2000);
    employeeRepository.save(emp);
    
    System.setProperty("employee.feature", "false");

    mockMvc.perform(post("/increaseSalary")
      .param("id", emp.getId() + ""))
      .andExpect(status().is(200));

    emp = employeeRepository.findOne(1L);
    assertEquals("salary incorrect", 2000, emp.getSalary(), 0.5);
}

接下來,我們添加一個測試,其中我們在將屬性設置為 true 後執行調用。在這種情況下,工資值應增加:

@Test
public void givenFeaturePropertyTrue_whenIncreaseSalary_thenIncrease() 
  throws Exception {
    Employee emp = new Employee(1, 2000);
    employeeRepository.save(emp);
    System.setProperty("employee.feature", "true");

    mockMvc.perform(post("/increaseSalary")
      .param("id", emp.getId() + ""))
      .andExpect(status().is(200));

    emp = employeeRepository.findById(1L).orElse(null);
    assertEquals("salary incorrect", 2200, emp.getSalary(), 0.5);
}

8. 結論在本教程中,我們演示瞭如何通過使用切面將 Togglz 庫集成到 Spring Boot 中。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.