Bota de Primavera e Aspecto Togglz

Bota de Primavera e Aspecto Togglz

1. Visão geral

Neste tutorial, vamos dar uma olhada em como a bibliotecaTogglz pode ser usada com um aplicativo Spring Boot.

2. Togglz

OTogglz library fornece uma implementação do padrão de designFeature Toggles. Esse padrão refere-se a um mecanismo que permite determinar durante o tempo de execução de um aplicativo se um determinado recurso está ativado ou não com base em uma alternância.

Desabilitar um recurso em tempo de execução pode ser útil em várias situações, como trabalhar em um novo recurso ainda não concluído, desejando permitir o acesso a um recurso apenas a um subconjunto de usuários ou executando testes A / B.

Nas seções a seguir, criaremos um aspecto que intercepta métodos com uma anotação que fornece um nome de recurso e determinamos se você continuará executando os métodos, dependendo se o recurso está ativado ou não.

3. Dependências do Maven

Junto com as dependências do Spring Boot, a bibliotecaTogglz fornece um jar Spring Boot Starter:


    org.springframework.boot
    spring-boot-starter-parent
    2.0.1.RELEASE



    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

4. Configuração Togglz

A bibliotecatogglz-spring-boot-starter contém configuração automática para criar os beans necessários, comoFeatureManager. O único bean que precisamos fornecer é o beanfeatureProvider.

Primeiro, vamos criar uma enumeração que implemente a interfaceFeature e contenha uma lista de nomes de recursos:

public enum MyFeatures implements Feature {

    @Label("Employee Management Feature")
    EMPLOYEE_MANAGEMENT_FEATURE;

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

A enumeração também define um método chamadoisActive() que verifica se um determinado recurso está habilitado.

Então podemos definir um bean do tipoEnumBasedFeatureProvider em uma classe de configuração Spring Boot:

@Configuration
public class ToggleConfiguration {

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

5. Criando o Aspecto

A seguir, criaremos um aspecto que intercepta uma anotaçãoAssociatedFeature personalizada e verifica o recurso fornecido no parâmetro de anotação para determinar se está ativo ou não:

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

Vamos também definir a anotação personalizada chamadaFeatureAssociation que terá um parâmetrovalue() do tipoMyFeatures enum:

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

Se o recurso estiver ativo, o aspecto continuará a execução do método; Caso contrário, ele registrará uma mensagem sem executar o código do método.

6. Ativação de recursos

Um recurso emTogglz pode estar ativo ou inativo. Este comportamento é controlado por um sinalizadorenabled e, opcionalmente, por uma estratégia de ativação.

Para definir o sinalizadorenabled como verdadeiro, podemos usar a anotação@EnabledByDefault na definição do valor enum.

A bibliotecaTogglz também fornece uma variedade de estratégias de ativação que podem ser usadas para determinar se um recurso está habilitado com base em uma determinada condição.

Em nosso exemplo, vamos usarSystemPropertyActivationStrategy para nosso EMPLOYEE_MANAGEMENT_FEATURE, que avalia o estado do recurso com base no valor de uma propriedade do sistema. O nome e o valor da propriedade necessários podem ser especificados usando a anotação@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;
    //...
}

Definimos nosso recurso para ser ativado apenas se a propriedadeemployee.feature tiver o valortrue.

Outros tipos de estratégias de ativação fornecidas pela bibliotecaTogglz são:

  • UsernameActivationStrategy - permite que o recurso esteja ativo para uma lista especificada de usuários

  • UserRoleActivationStrategy - a função do usuário atual é usada para determinar o estado de um recurso

  • ReleaseDateActivationStrategy - ativa automaticamente um recurso em uma determinada data e hora

  • GradualActivationStrategy - ativa um recurso para uma porcentagem especificada de usuários

  • ScriptEngineActivationStrategy - permite o uso de um script personalizado escrito em uma linguagem suportada porScriptEngine da JVM para determinar se um recurso está ativo ou não

  • ServerIpActivationStrategy - um recurso é habilitado com base nos endereços IP do servidor

7. Testando o Aspecto

7.1. Exemplo de aplicação

Para ver nosso aspecto em ação, vamos criar um exemplo simples que contém um recurso para gerenciar os funcionários de uma organização.

Como esse recurso será desenvolvido, podemos adicionar métodos e classes anotados com nossa anotação@AssociatedFeature com um valor de EMPLOYEE_MANAGEMENT_FEATURE. Isso garante que eles estarão acessíveis apenas se o recurso estiver ativo.

Primeiro, vamos definir uma classe de entidadeEmployee e um repositório com base em Spring Data:

@Entity
public class Employee {

    @Id
    private long id;
    private double salary;

    // standard constructor, getters, setters
}
public interface EmployeeRepository
  extends CrudRepository{ }

A seguir, vamos adicionar umEmployeeService com um método para aumentar o salário de um funcionário. Vamos adicionar a anotação@AssociatedFeature ao método com um parâmetro deEMPLOYEE_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);
    }
}

O método será chamado de um endpoint/increaseSalary que chamaremos para teste:

@Controller
public class SalaryController {

    @Autowired
    SalaryService salaryService;

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

7.2. Teste JUnit

Primeiro, vamos adicionar um teste no qual chamamos nosso mapeamento POST depois de definir a propriedadeemployee.feature parafalse. Nesse caso, o recurso não deve estar ativo e o valor do salário do funcionário não deve mudar:

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

A seguir, vamos adicionar um teste onde realizamos a chamada depois de definir a propriedade paratrue. Nesse caso, o valor do salário deve ser aumentado:

@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. Conclusões

Neste tutorial, mostramos como podemos integrar a bibliotecaTogglz com Spring Boot usando um aspecto.

O código-fonte completo do exemplo pode ser encontradoover on GitHub.