Рубрики
Без рубрики

Пружинный ботинок и аспект переключения

Быстрый и практичный пример использования переключателя с пружинным загрузчиком.

Автор оригинала: baeldung.

1. Обзор

В этом уроке мы рассмотрим, как библиотека Togglz может использоваться с приложением Spring Boot.

2. Переключение

Библиотека Togglz /обеспечивает реализацию шаблона Переключения функций|/. Этот шаблон относится к наличию механизма, который позволяет определить во время выполнения приложения, включена ли определенная функция или нет на основе переключателя.

Отключение функции во время выполнения может быть полезно в различных ситуациях, таких как работа над новой функцией, которая еще не завершена, желание разрешить доступ к функции только подмножеству пользователей или выполнение A/B-тестирования.

В следующих разделах мы создадим аспект, который перехватывает методы с аннотацией, предоставляющей имя объекта, и определим, следует ли продолжать выполнение методов в зависимости от того, включена ли функция или нет.

3. Зависимости Maven

Наряду с зависимостями загрузки Spring библиотека Toggle предоставляет банку стартера загрузки Spring:


    org.springframework.boot
    spring-boot-starter-parent
    2.4.0



    org.togglz
    togglz-spring-boot-starter
    2.4.1

    org.togglz
    togglz-spring-security
    2.4.1



    org.springframework.boot
    spring-boot-starter-web


    org.springframework.boot
    spring-boot-starter-data-jpa


    org.springframework.boot 
    spring-boot-starter-test 


    com.h2database
    h2
    1.4.194

Последние версии toggle-spring-boot-starter , toggle-spring-security , spring-boot-starter-web , spring-boot-starter-data-jpa , spring-boot-starter-test , h2 можно загрузить с Maven Central.

4. Переключение конфигурации

Библиотека toggle-spring-boot-starter содержит автоматическую настройку для создания необходимых компонентов, таких как FeatureManager . Единственный компонент, который нам нужно предоставить, – это поставщик функций bean.

Во-первых, давайте создадим перечисление, которое реализует интерфейс Feature и содержит список имен объектов:

public enum MyFeatures implements Feature {

    @Label("Employee Management Feature")
    EMPLOYEE_MANAGEMENT_FEATURE;

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

Перечисление также определяет метод с именем is Active () , который проверяет, включена ли определенная функция.

Затем мы можем определить компонент типа Поставщик функций на основе перечисления в классе конфигурации загрузки Spring:

@Configuration
public class ToggleConfiguration {

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

5. Создание аспекта

Затем мы создадим аспект, который перехватывает пользовательский Связанный объект аннотация и проверяет объект, указанный в параметре аннотации, чтобы определить, активен он или нет:

@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;
        }
    }
}

Давайте также определим пользовательскую аннотацию, называемую Ассоциацией объектов , которая будет иметь значение() параметр типа MyFeatures enum:

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

Если функция активна, аспект продолжит выполнение метода; если нет, он зарегистрирует сообщение без запуска кода метода.

6. Активация функции

Функция в Togglz может быть активной или неактивной. Это поведение контролируется флагом enabled и, возможно, стратегией активации.

Чтобы установить флаг enabled в значение true, мы можем использовать аннотацию @EnabledByDefault в определении значения перечисления.

Библиотека Toggle также предоставляет различные стратегии активации, которые можно использовать для определения того, включена ли функция на основе определенного условия.

В нашем примере давайте используем стратегию SystemPropertyActivation для нашего EMPLOYEE_MANAGEMENT_FEATURE, которая оценивает состояние функции на основе значения системного свойства. Требуемое имя и значение свойства можно указать с помощью аннотации @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;
    //...
}

Мы установили, что наша функция будет включена только в том случае, если сотрудник.свойство feature имеет значение true .

Другими типами стратегий активации, предоставляемых библиотекой Toggle , являются:

  • UsernameActivationStrategy – позволяет функции быть активными для указанного списка пользователей
  • UserRoleActivationStrategy – роль текущего пользователя используется для определения состояния функции
  • ReleaseDateActivationStrategy – автоматически активирует функцию в определенную дату и время
  • GradualActivationStrategy – включает функцию для определенного процента пользователей
  • ScriptEngineActivationStrategy – позволяет использовать пользовательский сценарий, написанный на языке, поддерживаемом ScriptEngine JVM, для определения того, активна функция или нет
  • ServerIpActivationStrategy – функция включена на основе IP-адресов сервера

7. Тестирование аспекта

7.1. Пример применения

Чтобы увидеть наш аспект в действии, давайте создадим простой пример, содержащий функцию управления сотрудниками организации.

По мере развития этой функции мы можем добавлять методы и классы, аннотированные нашей аннотацией @AssociatedFeature со значением EMPLOYEE_MANAGEMENT_FEATURE. Это гарантирует, что они будут доступны только в том случае, если функция активна.

Во-первых, давайте определим класс Employee entity и репозиторий на основе данных Spring:

@Entity
public class Employee {

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

Далее, давайте добавим Employee Service с методом увеличения зарплаты сотрудника. Мы добавим аннотацию @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);
    }
}

Метод будет вызван из конечной точки /increase , которую мы вызовем для тестирования:

@Controller
public class SalaryController {

    @Autowired
    SalaryService salaryService;

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

7.2. Тест JUnit

Во-первых, давайте добавим тест, в котором мы вызываем наше сопоставление ЗАПИСЕЙ после установки employee.функция свойство false . В этом случае функция не должна быть активной, и значение заработной платы сотрудника не должно изменяться:

@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. Выводы

В этом уроке мы показали, как мы можем интегрировать библиотеку Toggle с Spring Boot с помощью аспекта.

Полный исходный код примера можно найти на GitHub .