
AOP toepassen
Aspect-georiënteerd programmeren is ontworpen om transversale taken uit te voeren, wat elke code kan zijn die vele malen kan worden herhaald door verschillende methoden, die niet volledig in een afzonderlijke module kunnen worden gestructureerd. Dienovereenkomstig laat AOP ons dit buiten de hoofdcode houden en verticaal declareren. Een voorbeeld is het gebruik van een beveiligingsbeleid in een applicatie. Doorgaans loopt de beveiliging door veel elementen van een applicatie. Bovendien moet het beveiligingsbeleid van de applicatie in gelijke mate worden toegepast op alle bestaande en nieuwe onderdelen van de applicatie. Tegelijkertijd kan een in gebruik zijnd beveiligingsbeleid zelf evolueren. Dit is de perfecte plaats om AOP te gebruiken . Een ander voorbeeld is loggen. Er zijn verschillende voordelen aan het gebruik van de AOP-benadering voor logboekregistratie in plaats van handmatig logboekfunctionaliteit toe te voegen:-
De code voor het loggen is eenvoudig toe te voegen en te verwijderen: u hoeft alleen maar een paar configuraties van een bepaald aspect toe te voegen of te verwijderen.
-
Alle broncode voor logboekregistratie wordt op één plaats bewaard, dus u hoeft niet handmatig alle plaatsen op te sporen waar deze wordt gebruikt.
-
Logging-code kan overal worden toegevoegd, of het nu gaat om methoden en klassen die al zijn geschreven of om nieuwe functionaliteit. Dit vermindert het aantal codeerfouten.
Ook wanneer u een aspect uit een ontwerpconfiguratie verwijdert, kunt u er zeker van zijn dat alle traceercode is verdwenen en dat er niets is gemist.
- Aspecten zijn aparte code die steeds weer kan worden verbeterd en gebruikt.

Basisprincipes van AOP
Om verder te gaan in dit onderwerp, laten we eerst de belangrijkste concepten van AOP leren kennen. Advies — Aanvullende logica of code aangeroepen vanaf een join-punt. Advies kan worden uitgevoerd vóór, na of in plaats van een verbindingspunt (meer hierover hieronder). Mogelijke soorten advies :-
Voor — dit type advies wordt gelanceerd voordat doelmethoden, dwz verbindingspunten, worden uitgevoerd. Wanneer we aspecten als klassen gebruiken, gebruiken we de @Before annotatie om het advies te markeren als komend ervoor. Wanneer aspecten als .aj- bestanden worden gebruikt, is dit de methode before() .
- After — advies dat wordt uitgevoerd nadat de uitvoering van methoden (join points) is voltooid, zowel bij normale uitvoering als bij het genereren van een uitzondering.
Wanneer we aspecten als klassen gebruiken, kunnen we de annotatie @After gebruiken om aan te geven dat dit een advies is dat erna komt.
Bij het gebruik van aspecten als .aj- bestanden is dit de methode after() .
-
Na terugkeer - dit advies wordt alleen uitgevoerd als de doelmethode normaal en zonder fouten wordt voltooid.
Wanneer aspecten worden weergegeven als klassen, kunnen we de annotatie @AfterReturning gebruiken om het advies na succesvolle voltooiing te markeren als uitvoerend.
Wanneer aspecten als .aj- bestanden worden gebruikt, is dit de methode after() die terugkeert (Object obj) .
-
After Throwing — dit advies is bedoeld voor gevallen waarin een methode, dat wil zeggen een join-punt, een exception genereert. We kunnen dit advies gebruiken om bepaalde soorten mislukte uitvoering af te handelen (bijvoorbeeld om een volledige transactie of log terug te draaien met het vereiste traceerniveau).
Voor klasse-aspecten wordt de annotatie @AfterThrowing gebruikt om aan te geven dat dit advies wordt gebruikt na het genereren van een uitzondering.
Wanneer aspecten als .aj- bestanden worden gebruikt, is dit de after() throwing-methode (uitzondering e) .
-
Rond - misschien wel een van de belangrijkste soorten advies. Het omringt een methode, dat wil zeggen een verbindingspunt dat we kunnen gebruiken om bijvoorbeeld te kiezen of we een bepaalde verbindingspuntmethode al dan niet willen uitvoeren.
U kunt adviescode schrijven die wordt uitgevoerd voordat en nadat de join-puntmethode wordt uitgevoerd.
Het advies rond is verantwoordelijk voor het aanroepen van de join-puntmethode en de retourwaarden als de methode iets retourneert. Met andere woorden, in dit advies kunt u eenvoudig de werking van een doelmethode simuleren zonder deze aan te roepen, en als resultaat retourneren wat u maar wilt.
Gegeven aspecten als klassen, gebruiken we de @Around- annotatie om advies te maken dat een join-punt omsluit. Bij gebruik van aspecten in de vorm van .aj- bestanden, is deze methode de around()- methode.
-
Compileer-time weaving — als je de broncode van het aspect hebt en de code waarin je het aspect gebruikt, dan kun je de broncode en het aspect rechtstreeks compileren met behulp van de AspectJ-compiler;
-
Post-compile weaving (binary weaving) — als je geen broncodetransformaties kunt of wilt gebruiken om aspecten in de code te weven, kun je eerder gecompileerde klassen of jar-bestanden nemen en aspecten erin injecteren;
-
Load-time weaving - dit is gewoon binair weven dat wordt uitgesteld totdat de classloader het klassenbestand laadt en de klasse voor de JVM definieert.
Om dit te ondersteunen zijn een of meer weefklasseladers vereist. Ze worden expliciet geleverd door de runtime of geactiveerd door een 'weefmiddel'.
Voorbeelden in Java
Vervolgens zullen we voor een beter begrip van AOP kijken naar kleine voorbeelden in "Hallo wereld"-stijl. Meteen merk ik op dat onze voorbeelden compile-time weaving zullen gebruiken . Eerst moeten we de volgende afhankelijkheid toevoegen aan ons pom.xml- bestand:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
In de regel is de speciale ajc- compiler hoe we aspecten gebruiken. IntelliJ IDEA bevat het niet standaard, dus wanneer u het kiest als de toepassingscompiler, moet u het pad naar de 5168 75 AspectJ- distributie specificeren. Dit was de eerste manier. De tweede, die ik heb gebruikt, is om de volgende plug-in te registreren in het pom.xml- bestand:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
<<verbose>true<verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Hierna is het een goed idee om opnieuw te importeren vanuit Maven en mvn clean compile uit te voeren . Laten we nu direct doorgaan naar de voorbeelden.
Voorbeeld nr. 1
Laten we een hoofdklasse maken . Daarin hebben we een toegangspunt en een methode die een doorgegeven naam op de console afdrukt:
public class Main {
public static void main(String[] args) {
printName("Tanner");
printName("Victor");
printName("Sasha");
}
public static void printName(String name) {
System.out.println(name);
}
}
Er is hier niets ingewikkelds. We hebben een naam doorgegeven en deze op de console weergegeven. Als we het programma nu uitvoeren, zien we het volgende op de console:
public aspect GreetingAspect {
pointcut greeting() : execution(* Main.printName(..));
before() : greeting() {
System.out.print("Hi, ");
}
}
Dit bestand is een soort klasse. Laten we eens kijken wat hier gebeurt: pointcut is een set join-punten; begroeting() is de naam van deze pointcut; : uitvoering geeft aan om het toe te passen tijdens de uitvoering van alle ( * ) aanroepen van de methode Main.printName(...) . Vervolgens komt er een specifiek advies — before() — dat wordt uitgevoerd voordat de doelmethode wordt aangeroepen. : begroeting() is het snijpunt waarop dit advies reageert. Nou, en hieronder zien we de body van de methode zelf, die is geschreven in de Java-taal, die we begrijpen. Wanneer we main uitvoeren met dit aspect aanwezig, krijgen we deze console-uitvoer:
@Aspect
public class GreetingAspect{
@Pointcut("execution(* Main.printName(String))")
public void greeting() {
}
@Before("greeting()")
public void beforeAdvice() {
System.out.print("Hi, ");
}
}
Na het .aj- aspectbestand wordt alles hier duidelijker:
- @Aspect geeft aan dat deze klasse een aspect is;
- @Pointcut("execution(* Main.printName(String))") is het cutpoint dat wordt geactiveerd voor alle oproepen naar Main.printName met een invoerargument waarvan het type String is ;
- @Before("greeting()") is een advies dat wordt toegepast voordat de code wordt aangeroepen die is opgegeven in het begroeting()- afkappunt.
Voorbeeld nr. 2
Stel dat we een methode hebben die bepaalde bewerkingen voor clients uitvoert, en we noemen deze methode vanuit main :
public class Main {
public static void main(String[] args) {
performSomeOperation("Tanner");
}
public static void performSomeOperation(String clientName) {
System.out.println("Performing some operations for Client " + clientName);
}
}
Laten we de @Around- annotatie gebruiken om een "pseudo-transactie" te maken:
@Aspect
public class TransactionAspect{
@Pointcut("execution(* Main.performSomeOperation(String))")
public void executeOperation() {
}
@Around(value = "executeOperation()")
public void beforeAdvice(ProceedingJoinPoint joinPoint) {
System.out.println("Opening a transaction...");
try {
joinPoint.proceed();
System.out.println("Closing a transaction...");
}
catch (Throwable throwable) {
System.out.println("The operation failed. Rolling back the transaction...");
}
}
}
Met de methode doorgaan van het object ProceedingJoinPoint roepen we de methode wrap aan om de locatie in het advies te bepalen. Daarom is de code in de bovenstaande methode joinPoint.proceed(); is Before , terwijl de onderstaande code After is . Als we main uitvoeren , krijgen we dit in de console:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
Dan krijgen we deze console-uitvoer:
Voorbeeld nr. 3
Laten we in ons volgende voorbeeld bijvoorbeeld inloggen op de console. Kijk eerst eens naar Main , waar we wat pseudo-bedrijfslogica hebben toegevoegd:
public class Main {
private String value;
public static void main(String[] args) throws Exception {
Main main = new Main();
main.setValue("<some value>");
String valueForCheck = main.getValue();
main.checkValue(valueForCheck);
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return this.value;
}
public void checkValue(String value) throws Exception {
if (value.length() > 10) {
throw new Exception();
}
}
}
In main gebruiken we setValue om een waarde toe te wijzen aan de waarde- instantievariabele. Vervolgens gebruiken we getValue om de waarde op te halen en vervolgens bellen we checkValue om te zien of deze langer is dan 10 tekens. Als dat het geval is, wordt er een uitzondering gegenereerd. Laten we nu eens kijken naar het aspect dat we zullen gebruiken om het werk van de methoden te loggen:
@Aspect
public class LogAspect {
@Pointcut("execution(* *(..))")
public void methodExecuting() {
}
@AfterReturning(value = "methodExecuting()", returning = "returningValue")
public void recordSuccessfulExecution(JoinPoint joinPoint, Object returningValue) {
if (returningValue != null) {
System.out.printf("Successful execution: method — %s method, class — %s class, return value — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName(),
returningValue);
}
else {
System.out.printf("Successful execution: method — %s, class — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName());
}
}
@AfterThrowing(value = "methodExecuting()", throwing = "exception")
public void recordFailedExecution(JoinPoint joinPoint, Exception exception) {
System.out.printf("Exception thrown: method — %s, class — %s, exception — %s\n",
joinPoint.getSignature().getName(),
joinPoint.getSourceLocation().getWithinType().getName(),
exception);
}
}
Wat is hier aan de hand? @Pointcut("execution(* *(..))") voegt zich bij alle aanroepen van alle methodes. @AfterReturning(value = "methodExecuting()", return = "returningValue") is een advies dat zal worden uitgevoerd na succesvolle uitvoering van de doelmethode. We hebben hier twee gevallen:
- Wanneer de methode een retourwaarde heeft — if (returningValue! = Null) {
- Als er geen retourwaarde is — else {

GO TO FULL VERSION