Spring Boot und Togglz Aspekt
1. Überblick
In diesem Tutorial werden wir uns ansehen, wie dieTogglz-Bibliothek mit einer Spring Boot-Anwendung verwendet werden kann.
2. Togglz
DasTogglz library liefert eine Implementierung des Entwurfsmusters vonFeature Toggles. Dieses Muster bezieht sich auf einen Mechanismus, mit dem während der Laufzeit einer Anwendung festgestellt werden kann, ob eine bestimmte Funktion aktiviert ist oder nicht, basierend auf einem Umschalter.
Das Deaktivieren einer Funktion zur Laufzeit kann in einer Reihe von Situationen hilfreich sein, z. B. wenn Sie an einer neuen Funktion arbeiten, die noch nicht vollständig ist, den Zugriff auf eine Funktion nur einer Teilmenge von Benutzern erlauben oder A / B-Tests ausführen möchten.
In den folgenden Abschnitten werden wir einen Aspekt erstellen, der Methoden mit einer Anmerkung abfängt, die einen Feature-Namen enthält, und festlegen, ob die Methoden weiterhin ausgeführt werden sollen, je nachdem, ob das Feature aktiviert ist oder nicht.
3. Maven-Abhängigkeiten
Zusammen mit den Spring Boot-Abhängigkeiten bietet die BibliothekTogglzeine Spring Boot Starter-JAR:
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
Die neuesten Versionen vontogglz-spring-boot-starter,togglz-spring-security,spring-boot-starter-web,spring-boot-starter-data-jpa,spring-boot-starter-test,h2 können von Maven Central heruntergeladen werden.
4. Togglz-Konfiguration
Die Bibliothektogglz-spring-boot-starter enthält eine automatische Konfiguration zum Erstellen der erforderlichen Beans wieFeatureManager. Die einzige Bohne, die wir bereitstellen müssen, ist diefeatureProvider-Bohne.
Zunächst erstellen wir eine Aufzählung, die dieFeature-Schnittstelle implementiert und eine Liste von Feature-Namen enthält:
public enum MyFeatures implements Feature {
@Label("Employee Management Feature")
EMPLOYEE_MANAGEMENT_FEATURE;
public boolean isActive() {
return FeatureContext.getFeatureManager().isActive(this);
}
}
Die Aufzählung definiert auch eine Methode namensisActive(), die überprüft, ob eine bestimmte Funktion aktiviert ist.
Dann können wir eine Bean vom TypEnumBasedFeatureProvider in einer Spring Boot-Konfigurationsklasse definieren:
@Configuration
public class ToggleConfiguration {
@Bean
public FeatureProvider featureProvider() {
return new EnumBasedFeatureProvider(MyFeatures.class);
}
}
5. Aspekt erstellen
Als Nächstes erstellen wir einen Aspekt, der die Annotation eines benutzerdefiniertenAssociatedFeatureabfängt und die im Annotation-Parameter bereitgestellte Funktion überprüft, um festzustellen, ob sie aktiv ist oder nicht:
@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;
}
}
}
Definieren wir auch die benutzerdefinierte Annotation mit dem NamenFeatureAssociation, die einenvalue()-Parameter vom TypMyFeatures enum enthält:
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface FeatureAssociation {
MyFeatures value();
}
Wenn die Funktion aktiv ist, setzt der Aspekt die Ausführung der Methode fort. Andernfalls wird eine Nachricht protokolliert, ohne den Methodencode auszuführen.
6. Funktionsaktivierung
Eine Funktion inTogglz kann entweder aktiv oder inaktiv sein. Dieses Verhalten wird durch einenabled-Flag und optional eine Aktivierungsstrategie gesteuert.
Um das Flagenabled auf true zu setzen, können Sie die Annotation@EnabledByDefault für die Definition des Enum-Werts verwenden.
Die Bibliothek vonTogglzbietet auch eine Vielzahl von Aktivierungsstrategien, mit denen bestimmt werden kann, ob eine Funktion basierend auf einer bestimmten Bedingung aktiviert ist.
In unserem Beispiel verwenden wirSystemPropertyActivationStrategy für unsere EMPLOYEE_MANAGEMENT_FEATURE, die den Status des Features basierend auf dem Wert einer Systemeigenschaft bewertet. Der erforderliche Eigenschaftsname und -wert kann mithilfe der Annotation@ActivationParameterangegeben werden:
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;
//...
}
Wir haben unsere Funktion so eingestellt, dass sie nur aktiviert wird, wenn die Eigenschaftemployee.featureden Werttrue hat.
Andere Arten von Aktivierungsstrategien, die von derTogglz-Bibliothek bereitgestellt werden, sind:
-
UsernameActivationStrategy - Ermöglicht die Aktivierung der Funktion für eine bestimmte Benutzerliste
-
UserRoleActivationStrategy - Die Rolle des aktuellen Benutzers wird verwendet, um den Status eines Features zu bestimmen
-
ReleaseDateActivationStrategy - Aktiviert automatisch eine Funktion zu einem bestimmten Datum und einer bestimmten Uhrzeit
-
GradualActivationStrategy - Aktiviert eine Funktion für einen bestimmten Prozentsatz der Benutzer
-
ScriptEngineActivationStrategy - Ermöglicht die Verwendung eines benutzerdefinierten Skripts in einer Sprache, die vonScriptEngine der JVM unterstützt wird, um zu bestimmen, ob eine Funktion aktiv ist oder nicht
-
ServerIpActivationStrategy - Eine Funktion wird basierend auf den IP-Adressen des Servers aktiviert
7. Den Aspekt testen
7.1. Beispielanwendung
Um unseren Aspekt in Aktion zu sehen, erstellen wir ein einfaches Beispiel, das eine Funktion zum Verwalten der Mitarbeiter einer Organisation enthält.
Während diese Funktion entwickelt wird, können wir Methoden und Klassen hinzufügen, die mit der Annotation@AssociatedFeaturemit dem Wert EMPLOYEE_MANAGEMENT_FEATURE versehen sind. Dadurch wird sichergestellt, dass sie nur verfügbar sind, wenn die Funktion aktiv ist.
Definieren wir zunächst die Entitätsklasse und das Repository einesEmployeebasierend auf Spring Data:
@Entity
public class Employee {
@Id
private long id;
private double salary;
// standard constructor, getters, setters
}
public interface EmployeeRepository
extends CrudRepository{ }
Als nächstes fügen wir einEmployeeService hinzu, um das Gehalt eines Mitarbeiters zu erhöhen. Wir werden der Methode die Annotation@AssociatedFeature mit einem Parameter vonEMPLOYEE_MANAGEMENT_FEATURE hinzufügen:
@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);
}
}
Die Methode wird von einem/increaseSalary-Endpunkt aufgerufen, den wir zum Testen aufrufen werden:
@Controller
public class SalaryController {
@Autowired
SalaryService salaryService;
@PostMapping("/increaseSalary")
@ResponseBody
public void increaseSalary(@RequestParam long id) {
salaryService.increaseSalary(id);
}
}
7.2. JUnit-Test
Fügen wir zunächst einen Test hinzu, in dem wir unsere POST-Zuordnung aufrufen, nachdem wir die Eigenschaftemployee.featureauffalse gesetzt haben. In diesem Fall sollte die Funktion nicht aktiv sein und der Wert des Gehalts des Mitarbeiters sollte sich nicht ändern:
@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);
}
Als nächstes fügen wir einen Test hinzu, bei dem wir den Aufruf ausführen, nachdem wir die Eigenschaft auftrue gesetzt haben. In diesem Fall sollte der Wert des Gehalts erhöht werden:
@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. Schlussfolgerungen
In diesem Tutorial haben wir gezeigt, wie wir die Bibliothek vonTogglzmithilfe eines Aspekts in Spring Boot integrieren können.
Der vollständige Quellcode des Beispiels istover on GitHub.