Spring Boot et aspect Togglz

1. Vue d'ensemble

Dans ce didacticiel, nous allons examiner comment la bibliothèque Togglz peut être utilisée avec une application Spring Boot.

2. Togglz

La bibliothèque Togglz fournit une implémentation du modèle de conception Feature Toggles . Ce modèle fait référence à un mécanisme qui permet de déterminer pendant l'exécution d'une application si une certaine fonctionnalité est activée ou non en fonction d'une bascule.

La désactivation d'une fonctionnalité lors de l'exécution peut être utile dans diverses situations telles que travailler sur une nouvelle fonctionnalité qui n'est pas encore terminée, vouloir autoriser l'accès à une fonctionnalité uniquement à un sous-ensemble d'utilisateurs ou exécuter des tests A / B.

Dans les sections suivantes, nous allons créer un aspect qui intercepte les méthodes avec une annotation qui fournit un nom de fonctionnalité, et déterminer s'il faut continuer à exécuter les méthodes selon que la fonctionnalité est activée ou non.

3. Dépendances de Maven

Avec les dépendances Spring Boot, la bibliothèque Togglz fournit un 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 

Les dernières versions de togglz-spring-boot-starter, togglz-spring-security, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-test, h2 peuvent être téléchargées sur Maven Central.

4. Configuration Togglz

La bibliothèque togglz-spring-boot-starter contient une configuration automatique pour créer les beans nécessaires tels que FeatureManager . Le seul bean que nous devons fournir est le bean featureProvider .

Commençons par créer une énumération qui implémente l' interface des fonctionnalités et contient une liste de noms de fonctionnalités:

public enum MyFeatures implements Feature { @Label("Employee Management Feature") EMPLOYEE_MANAGEMENT_FEATURE; public boolean isActive() { return FeatureContext.getFeatureManager().isActive(this); } }

L'énumération définit également une méthode appelée isActive () qui vérifie si une certaine fonctionnalité est activée.

Ensuite, nous pouvons définir un bean de type EnumBasedFeatureProvider dans une classe de configuration Spring Boot:

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

5. Création de l'aspect

Ensuite, nous allons créer un aspect qui intercepte une annotation AssociatedFeature personnalisée et vérifie la fonctionnalité fournie dans le paramètre d'annotation pour déterminer si elle est active ou non:

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

Définissons également l'annotation personnalisée appelée FeatureAssociation qui aura un paramètre value () de type MyFeatures enum:

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

Si la fonctionnalité est active, l'aspect continuera l'exécution de la méthode; sinon, il enregistrera un message sans exécuter le code de la méthode.

6. Activation des fonctionnalités

Une fonctionnalité de Togglz peut être active ou inactive. Ce comportement est contrôlé par un indicateur activé et éventuellement une stratégie d'activation.

Pour définir l' indicateur enabled sur true, nous pouvons utiliser l' annotation @EnabledByDefault sur la définition de la valeur enum.

La bibliothèque Togglz fournit également une variété de stratégies d'activation qui peuvent être utilisées pour déterminer si une fonctionnalité est activée en fonction d'une certaine condition.

Dans notre exemple, utilisons la SystemPropertyActivationStrategy pour notre EMPLOYEE_MANAGEMENT_FEATURE qui évalue l'état de la fonctionnalité en fonction de la valeur d'une propriété System. Le nom et la valeur de propriété requis peuvent être spécifiés à l'aide de l' annotation @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; //... }

Nous avons défini notre fonctionnalité pour être activée uniquement si la propriété employee.feature a la valeur true .

Les autres types de stratégies d'activation fournis par la bibliothèque Togglz sont:

  • UsernameActivationStrategy - permet à la fonctionnalité d'être active pour une liste d'utilisateurs spécifiée
  • UserRoleActivationStrategy - le rôle de l'utilisateur actuel est utilisé pour déterminer l'état d'une fonctionnalité
  • ReleaseDateActivationStrategy - active automatiquement une fonctionnalité à une certaine date et heure
  • GradualActivationStrategy - active une fonctionnalité pour un pourcentage spécifié d'utilisateurs
  • ScriptEngineActivationStrategy - permet l'utilisation d'un script personnalisé écrit dans un langage pris en charge par le ScriptEngine de la JVM pour déterminer si une fonctionnalité est active ou non
  • ServerIpActivationStrategy - une fonctionnalité est activée en fonction des adresses IP du serveur

7. Test de l'aspect

7.1. Exemple d'application

Pour voir notre aspect en action, créons un exemple simple qui contient une fonctionnalité de gestion des employés d'une organisation.

Au fur et à mesure que cette fonctionnalité sera développée, nous pouvons ajouter des méthodes et des classes annotées avec notre annotation @AssociatedFeature avec une valeur de EMPLOYEE_MANAGEMENT_FEATURE. Cela garantit qu'ils ne seront accessibles que si la fonction est active.

Tout d'abord, définissons une classe d'entité Employee et un référentiel basés sur Spring Data:

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

Ensuite, ajoutons un EmployeeService avec une méthode pour augmenter le salaire d'un employé. Nous ajouterons l' annotation @AssociatedFeature à la méthode avec un paramètre de 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); } } 

La méthode sera appelée à partir d'un point de terminaison / augmenteSalary que nous appellerons pour le test:

@Controller public class SalaryController { @Autowired SalaryService salaryService; @PostMapping("/increaseSalary") @ResponseBody public void increaseSalary(@RequestParam long id) { salaryService.increaseSalary(id); } }

7.2. Test JUnit

Tout d'abord, ajoutons un test dans lequel nous appelons notre mappage POST après avoir défini la propriété employee.feature sur false . Dans ce cas, la fonctionnalité ne doit pas être active et la valeur du salaire de l'employé ne doit pas changer:

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

Ensuite, ajoutons un test où nous effectuons l'appel après avoir défini la propriété sur true . Dans ce cas, la valeur du salaire doit être augmentée:

@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. Conclusions

Dans ce tutoriel, nous avons montré comment nous pouvons intégrer la bibliothèque Togglz à Spring Boot en utilisant un aspect.

Le code source complet de l'exemple est disponible à l'adresse over sur GitHub.