
AOP alkalmazása
Az aspektusorientált programozás keresztmetszeti feladatok végrehajtására szolgál, ami lehet bármilyen, különböző módszerekkel sokszor ismétlődő kód, amely nem strukturálható teljesen egy külön modulba. Ennek megfelelően az AOP lehetővé teszi, hogy ezt a fő kódon kívül tartsuk, és függőlegesen deklaráljuk. Példa erre a biztonsági szabályzat alkalmazása egy alkalmazásban. A biztonság általában egy alkalmazás számos elemén keresztül fut. Sőt, az alkalmazás biztonsági politikáját egyformán alkalmazni kell az alkalmazás minden meglévő és új részére. Ugyanakkor a használt biztonsági politika maga is fejlődhet. Ez a tökéletes hely az AOP használatához . Egy másik példa a naplózás. Számos előnye van annak, ha az AOP-megközelítést használja a naplózáshoz, nem pedig a naplózási funkció manuális hozzáadása:-
A naplózási kód könnyen hozzáadható és eltávolítható: mindössze annyit kell tennie, hogy hozzáad vagy távolít el néhány konfigurációt valamilyen szempontból.
-
A naplózás összes forráskódja egy helyen van tárolva, így nem kell manuálisan levadásznia minden felhasználási helyet.
-
A naplózó kód bárhol hozzáadható, akár már megírt metódusokban és osztályokban, akár új funkcionalitásban. Ez csökkenti a kódolási hibák számát.
Ezenkívül, amikor eltávolít egy szempontot a tervezési konfigurációból, biztos lehet benne, hogy az összes nyomkövetési kód eltűnt, és semmi sem hiányzott.
- Az aspektusok külön kódok, amelyek javíthatók és újra és újra felhasználhatók.

Az AOP alapelvei
A témában való továbblépéshez először ismerjük meg az AOP főbb fogalmait. Tanács – További logika vagy kód, amelyet egy csatlakozási pontból hívnak meg. A tanácsadás elvégezhető a csatlakozási pont előtt, után vagy helyett (erről bővebben lentebb). A tanácsok lehetséges típusai :-
Előtte — az ilyen típusú tanácsok a célmetódusok, azaz a csatlakozási pontok végrehajtása előtt indulnak el. Amikor a szempontokat osztályként használjuk, a @Before annotációt használjuk, hogy megjelöljük a tanácsot korábban érkezőnek. Ha a szempontokat .aj fájlként használja, ez a before() metódus lesz .
- Utána – a metódusok (csatlakozási pontok) végrehajtása után végrehajtott tanácsok teljesek, mind normál végrehajtáskor, mind kivétel dobásakor.
Ha szempontokat használunk osztályként, az @After annotációt használhatjuk annak jelzésére, hogy ez az utána következő tanács.
Ha a szempontokat .aj fájlként használja, ez az after() metódus.
-
Visszatérés után – ez a tanács csak akkor kerül végrehajtásra, ha a célmódszer rendesen, hiba nélkül befejeződik.
Ha a szempontok osztályokként vannak ábrázolva, az @AfterReturning annotációval megjelölhetjük a tanácsot a sikeres befejezés után végrehajtottként.
Ha a szempontokat .aj fájlként használja, ez az after() visszatérési (Object obj) metódus lesz .
-
Dobás után – ez a tanács azokra az esetekre vonatkozik, amikor egy metódus, azaz a csatlakozási pont kivételt dob. Ezt a tanácsot használhatjuk bizonyos típusú sikertelen végrehajtások kezelésére (például egy teljes tranzakció vagy napló visszagörgetése a szükséges nyomkövetési szinttel).
Az osztályszempontok esetében az @AfterThrowing megjegyzés jelzi, hogy ezt a tanácsot kivétel dobása után használják.
Ha szempontokat .aj fájlként használunk, ez az after() dobás (e kivétel) metódus lesz .
-
Körülbelül – talán az egyik legfontosabb tanácstípus. Körülvesz egy metódust, vagyis egy csatlakozási pontot, amivel például kiválaszthatjuk, hogy végrehajtsunk-e egy adott csatlakozási pont módszert vagy sem.
Tanácsadó kódot írhat, amely a csatlakozási pont metódusának végrehajtása előtt és után fut.
A körüli tanács felelős a csatlakozási pont metódusának meghívásáért és a visszatérési értékekért, ha a metódus visszaad valamit. Más szóval, ebben a tanácsban egyszerűen szimulálhatja a célmetódus működését anélkül, hogy meghívná, és bármit visszaadhat, amit csak akar.
Osztályként adott szempontok esetén az @Around annotációt használjuk olyan tanácsok létrehozására, amelyek becsomagolják a csatlakozási pontot. Ha szempontokat .aj fájlok formájában használunk , ez a metódus az around() metódus lesz .
-
Fordítási idejű szövés — ha rendelkezik az aspektus forráskódjával és a kóddal, ahol az aspektust használja, akkor közvetlenül az AspectJ fordítóval fordíthatja le a forráskódot és az aspektust;
-
Fordítás utáni szövés (bináris szövés) – ha nem tud vagy nem akar forráskód-transzformációt használni aspektusok beszövésére a kódba, használhat korábban lefordított osztályokat vagy jar fájlokat, és szempontokat illeszthet beléjük;
-
Betöltési idejű szövés – ez csak bináris szövés, amely addig késik, amíg az osztálybetöltő betölti az osztályfájlt, és meghatározza az osztályt a JVM számára.
Ennek támogatásához egy vagy több szövési osztályú rakodógép szükséges. Ezeket vagy kifejezetten a futási környezet biztosítja, vagy egy "szövő ügynök" aktiválja.
Példák Java nyelven
Ezután az AOP jobb megértése érdekében kis "Hello World" stílusú példákat tekintünk meg. Pontosan megjegyzem, hogy a példáink a fordítási idejű szövést fogják használni . Először is hozzá kell adnunk a következő függőséget a pom.xml fájlunkhoz:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
Általános szabály, hogy a speciális ajc fordító hogyan használjuk az aspektusokat. Az IntelliJ IDEA alapértelmezés szerint nem tartalmazza, ezért ha alkalmazásfordítóként választja, meg kell adnia az 5168 75 AspectJ disztribúció elérési útját. Ez volt az első út. A második, amit használtam, a következő bővítmény regisztrálása a pom.xml fájlban:
<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>
Ezt követően érdemes újraimportálni a Maven- ből , és futtatni az mvn clean compile -t . Most folytassuk közvetlenül a példákkal.
1. számú példa
Hozzunk létre egy főosztályt . Ebben lesz egy belépési pontunk és egy metódusunk, amely egy átadott nevet nyomtat a konzolon:
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);
}
}
Nincs itt semmi bonyolult. Adtunk egy nevet, és megjelenítettük a konzolon. Ha most futtatjuk a programot, akkor a következőket fogjuk látni a konzolon:
public aspect GreetingAspect {
pointcut greeting() : execution(* Main.printName(..));
before() : greeting() {
System.out.print("Hi, ");
}
}
Ez a fájl olyan, mint egy osztály. Lássuk, mi történik itt: a pointcut csatlakozási pontok halmaza; greeting() ennek a pointcutnak a neve; : a végrehajtás azt jelzi, hogy a Main.printName(...) metódus összes ( * ) hívása során alkalmazni kell . Ezután jön egy speciális tanács – before() –, amely a célmetódus meghívása előtt végrehajtódik. : a greeting() a vágási pont, amelyre ez a tanács reagál. Nos, alább pedig magának a metódusnak a törzsét látjuk, ami az általunk értett Java nyelven van megírva. Ha a Main-t ezzel az aspektussal futtatjuk , akkor ezt a konzol kimenetet kapjuk:
@Aspect
public class GreetingAspect{
@Pointcut("execution(* Main.printName(String))")
public void greeting() {
}
@Before("greeting()")
public void beforeAdvice() {
System.out.print("Hi, ");
}
}
Az .aj- aspect fájl után itt minden nyilvánvalóbbá válik:
- Az @Aspect azt jelzi, hogy ez az osztály szempont;
- A @Pointcut("végrehajtás(* Main.printName(String))") az a vágási pont, amely a Main.printName összes hívása esetén aktiválódik egy bemeneti argumentummal, amelynek típusa String ;
- A @Before("greeting()") egy tanács, amelyet a greeting() vágási pontban megadott kód meghívása előtt alkalmaznak.
2. példa
Tegyük fel, hogy van olyan módszerünk, amely bizonyos műveleteket hajt végre az ügyfelek számára, és ezt a metódust a főből hívjuk :
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);
}
}
Használjuk az @Around annotációt egy „áltranzakció” létrehozásához:
@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...");
}
}
}
A ProceedingJoinPoint objektum folytatási metódusával a burkolási módszert hívjuk meg, hogy meghatározzuk a tanácsban való elhelyezkedését. Ezért a fenti metódus kódja joinPoint.proceed(); az Előtte , míg az alatta lévő kód Utána . Ha a maint futtatjuk , ezt kapjuk a konzolban:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
Ezután ezt a konzol kimenetet kapjuk:
3. példa
Következő példánkban tegyünk olyasmit, mint a naplózás a konzolra. Először is vessen egy pillantást a Main oldalra , ahol hozzáadtunk néhány pszeudo üzleti logikát:
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();
}
}
}
A main -ban a setValue segítségével értéket rendelünk az értékpéldány változóhoz. Ezután a getValue segítségével kapjuk meg az értéket, majd meghívjuk a checkValue-t, hogy megnézzük, nem hosszabb-e 10 karakternél. Ha igen, akkor kivételt tesznek. Most nézzük meg, hogy milyen szempontot fogunk használni a metódusok munkájának naplózásához:
@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);
}
}
Mi folyik itt? A @Pointcut("végrehajtás(* *(..))") csatlakozik az összes metódus összes hívásához. Az @AfterReturning(érték = "methodExecuting()", returning = "returningValue") olyan tanács, amely a célmetódus sikeres végrehajtása után kerül végrehajtásra. Két esetünk van itt:
- Ha a metódusnak visszatérési értéke van — if (returningValue! = Null) {
- Ha nincs visszatérési érték — else {

GO TO FULL VERSION