Spring Boot e Togglz Aspect

1. Panoramica

In questo tutorial, daremo un'occhiata a come la libreria Togglz può essere utilizzata con un'applicazione Spring Boot.

2. Togglz

La libreria Togglz fornisce un'implementazione del modello di progettazione Feature Toggles . Questo modello si riferisce alla presenza di un meccanismo che consente di determinare durante il runtime di un'applicazione se una determinata funzionalità è abilitata o meno in base a un interruttore.

La disabilitazione di una funzionalità in fase di esecuzione può essere utile in una varietà di situazioni come lavorare su una nuova funzionalità non ancora completa, voler consentire l'accesso a una funzionalità solo a un sottoinsieme di utenti o eseguire test A / B.

Nelle sezioni seguenti, creeremo un aspetto che intercetta i metodi con un'annotazione che fornisce un nome di funzionalità e determineremo se continuare a eseguire i metodi a seconda che la funzionalità sia abilitata o meno.

3. Dipendenze di Maven

Insieme alle dipendenze Spring Boot, la libreria Togglz fornisce 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 

Le ultime versioni di togglz-spring-boot-starter, togglz-spring-security, spring-boot-starter-web, spring-boot-starter-data-jpa, spring-boot-starter-test, h2 possono essere scaricate da Maven Centrale.

4. Configurazione Togglz

La libreria togglz-spring-boot-starter contiene la configurazione automatica per la creazione dei bean necessari come FeatureManager . L'unico bean che dobbiamo fornire è il bean featureProvider .

Per prima cosa, creiamo un'enumerazione che implementa l' interfaccia Feature e contiene un elenco di nomi di funzionalità:

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

L'enumerazione definisce anche un metodo chiamato isActive () che verifica se una determinata funzionalità è abilitata.

Quindi possiamo definire un bean di tipo EnumBasedFeatureProvider in una classe di configurazione Spring Boot:

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

5. Creazione dell'aspetto

Successivamente, creeremo un aspetto che intercetta un'annotazione AssociatedFeature personalizzata e controlla la funzionalità fornita nel parametro di annotazione per determinare se è attiva o meno:

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

Definiamo anche l'annotazione personalizzata chiamata FeatureAssociation che avrà un parametro value () di tipo MyFeatures enum:

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

Se la funzionalità è attiva, l'aspetto continuerà l'esecuzione del metodo; in caso contrario, registrerà un messaggio senza eseguire il codice del metodo.

6. Attivazione delle funzionalità

Una funzionalità in Togglz può essere attiva o inattiva. Questo comportamento è controllato da un flag abilitato e facoltativamente da una strategia di attivazione.

Per impostare il flag enabled su true, possiamo usare l' annotazione @EnabledByDefault sulla definizione del valore enum.

La libreria Togglz fornisce anche una varietà di strategie di attivazione che possono essere utilizzate per determinare se una funzione è abilitata in base a una determinata condizione.

Nel nostro esempio, utilizziamo SystemPropertyActivationStrategy per il nostro EMPLOYEE_MANAGEMENT_FEATURE che valuta lo stato della funzionalità in base al valore di una proprietà di sistema. Il nome e il valore della proprietà richiesti possono essere specificati utilizzando l' annotazione @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; //... }

Abbiamo impostato la nostra funzione in modo che sia abilitata solo se la proprietà dipendente.feature ha il valore true .

Altri tipi di strategie di attivazione fornite dalla libreria Togglz sono:

  • UsernameActivationStrategy : consente alla funzione di essere attiva per un elenco di utenti specificato
  • UserRoleActivationStrategy: il ruolo dell'utente corrente viene utilizzato per determinare lo stato di una funzionalità
  • ReleaseDateActivationStrategy : attiva automaticamente una funzione in una determinata data e ora
  • GradualActivationStrategy : abilita una funzionalità per una percentuale di utenti specificata
  • ScriptEngineActivationStrategy : consente l'uso di uno script personalizzato scritto in un linguaggio supportato dallo ScriptEngine della JVM per determinare se una funzionalità è attiva o meno
  • ServerIpActivationStrategy : una funzione è abilitata in base agli indirizzi IP del server

7. Testare l'aspetto

7.1. Applicazione di esempio

Per vedere il nostro aspetto in azione, creiamo un semplice esempio che contenga una funzionalità per la gestione dei dipendenti di un'organizzazione.

Man mano che questa funzionalità verrà sviluppata, possiamo aggiungere metodi e classi annotati con la nostra annotazione @AssociatedFeature con un valore EMPLOYEE_MANAGEMENT_FEATURE. Ciò garantisce che saranno accessibili solo se la funzione è attiva.

Per prima cosa, definiamo una classe di entità Employee e un repository basato su Spring Data:

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

Successivamente, aggiungiamo un EmployeeService con un metodo per aumentare lo stipendio di un dipendente. Aggiungeremo l' annotazione @AssociatedFeature al metodo con un parametro 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); } } 

Il metodo verrà chiamato da un endpoint / aumentiSalario che chiameremo per il test:

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

7.2. JUnit Test

Innanzitutto, aggiungiamo un test in cui chiamiamo la nostra mappatura POST dopo aver impostato la proprietà employee.feature su false . In questo caso, la funzione non dovrebbe essere attiva e il valore dello stipendio del dipendente non dovrebbe cambiare:

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

Successivamente, aggiungiamo un test in cui eseguiamo la chiamata dopo aver impostato la proprietà su true . In questo caso, il valore dello stipendio dovrebbe essere aumentato:

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

In questo tutorial, abbiamo mostrato come possiamo integrare la libreria Togglz con Spring Boot utilizzando un aspetto.

Il codice sorgente completo dell'esempio può essere trovato su GitHub.