CodeGym/Java blog/Véletlen/Mi az AOP? Az aspektusorientált programozás elvei
John Squirrels
Szint
San Francisco

Mi az AOP? Az aspektusorientált programozás elvei

Megjelent a csoportban
Sziasztok srácok és lányok! Az alapfogalmak megértése nélkül meglehetősen nehéz elmélyülni a funkcionalitásépítés keretrendszereiben és megközelítéseiben. Tehát ma egy ilyen koncepcióról fogunk beszélni – az AOP-ról, más néven aspektus-orientált programozásról . Mi az AOP?  Az aspektusorientált programozás alapelvei - 1Ez a téma nem könnyű, és ritkán használják közvetlenül, de sok keretrendszer és technológia ezt használja. És persze néha az interjúk során megkérhetik, hogy írja le általánosságban, hogy milyen vadállat ez, és hol lehet alkalmazni. Tehát vessünk egy pillantást a Java AOP alapfogalmaira és néhány egyszerű példájára . Most akkor az AOP az aspektusorientált programozást jelenti, amely egy olyan paradigma, amely az alkalmazás különböző részeinek modularitását hivatott növelni azáltal, hogy elkülöníti a több területet érintő szempontokat. Ennek eléréséhez további viselkedést adunk a meglévő kódhoz anélkül, hogy az eredeti kódon módosítanának. Más szóval, úgy is felfoghatjuk, hogy további funkciókat akasztunk a metódusok és osztályok tetejére anélkül, hogy a módosított kódon változtatnánk. Miért van erre szükség? Előbb-utóbb arra a következtetésre jutunk, hogy a tipikus objektum-orientált megközelítés nem mindig képes hatékonyan megoldani bizonyos problémákat. És amikor elérkezik ez a pillanat, az AOP segít, és további eszközöket ad az alkalmazások építéséhez. A további eszközök pedig nagyobb rugalmasságot jelentenek a szoftverfejlesztésben, ami több lehetőséget jelent egy adott probléma megoldására.

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:
  1. 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.

  2. 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.

  3. 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.

  4. Az aspektusok külön kódok, amelyek javíthatók és újra és újra felhasználhatók.
Mi az AOP?  Az aspektusorientált programozás alapelvei - 2Az AOP kivételkezelésre, gyorsítótárazásra és bizonyos funkciók kibontására is használható, hogy újrafelhasználható legyen.

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 :
  1. 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 .

  2. 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.

  3. 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 .

  4. 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 .

  5. 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 .

Join Point – az a pont egy futó programban (pl. metódushívás, objektum létrehozása, változó hozzáférés), ahol a tanácsot alkalmazni kell. Más szóval, ez egyfajta reguláris kifejezés, amelyet arra használnak, hogy megtalálják a kódbeszúrás helyeit (olyan helyeket, ahol tanácsokat kell alkalmazni). Pointcut – csatlakozási pontok halmaza . A pontvágás határozza meg, hogy az adott tanács alkalmazható-e egy adott csatlakozási pontra. Aspect – olyan modul vagy osztály, amely átfogó funkcionalitást valósít meg. Az Aspect megváltoztatja a fennmaradó kód viselkedését azáltal, hogy tanácsot ad valamilyen pointcut által meghatározott csatlakozási pontokon . Más szóval, ez tanácsok és csatlakozási pontok kombinációja. Bevezetés— egy osztály szerkezetének megváltoztatása és/vagy az öröklődési hierarchia megváltoztatása, hogy az aspektus funkcionalitása idegen kódhoz kerüljön. Cél – az az objektum, amelyre a tanácsot alkalmazni fogják. Szövés – a szempontok más objektumokhoz való kapcsolásának folyamata javasolt proxy objektumok létrehozása érdekében. Ez megtehető fordítási, betöltési vagy futási időben. Háromféle szövés létezik:
  • 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.

AspectJ – Az AOP paradigma sajátos megvalósítása , amely megvalósítja a több területet érintő feladatok végrehajtásának képességét. A dokumentáció itt található .

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:
Tanner Victor Sasha
Most akkor itt az ideje, hogy kihasználja az AOP erejét. Most létre kell hoznunk egy aspektusfájlt . Kétféle: az első .aj kiterjesztésű. A második egy közönséges osztály, amely annotációkat használ az AOP képességek megvalósításához . Először nézzük meg az .aj kiterjesztésű fájlt:
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:
Szia Tanner Szia Victor Szia Sasha
Láthatjuk, hogy a printName metódus minden hívása módosult egy szempontnak köszönhetően. Most pedig nézzük meg, hogyan nézne ki az aspektus annotációkkal ellátott Java osztályként:
@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.
A main futtatása ezzel a szemponttal nem változtatja meg a konzol kimenetét:
Szia Tanner Szia Victor Szia Sasha

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:
Tranzakció megnyitása... Néhány művelet végrehajtása a Client Tanner számára Tranzakció lezárása...
De ha a metódusunkban dobunk és kivételt teszünk (a sikertelen művelet szimulálására):
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:
Tranzakció megnyitása... Néhány művelet végrehajtása a Client Tanner számára A művelet meghiúsult. A tranzakció visszaállítása...
Tehát amit itt kötöttünk ki, az egyfajta hibakezelési képesség.

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:
  1. Ha a metódusnak visszatérési értéke van — if (returningValue! = Null) {
  2. Ha nincs visszatérési érték — else {
Az @AfterThrowing(érték = "methodExecuting()", dobás = "kivétel") egy olyan tanács, amely hiba esetén, vagyis amikor a metódus kivételt dob. Ennek megfelelően a main futtatásával egyfajta konzol alapú naplózást kapunk:
Sikeres végrehajtás: metódus — setValue, osztály — Main Sikeres végrehajtás: metódus — getValue, osztály — Main, visszatérési érték — <valamilyen érték> Kivétel: metódus — checkValue, osztály — Fő kivétel — java.lang.Exception Kivétel dobott: metódus — main, class — Main, kivétel — java.lang.Exception
És mivel nem kezeltük a kivételeket, továbbra is kapunk egy veremnyomot: A Mi az AOP?  Az aspektusorientált programozás alapelvei - 3kivételekről és a kivételkezelésről ezekben a cikkekben olvashat: Exceptions in Java and Exceptions: catching and handling . Nekem mára ennyi. Ma megismerkedtünk az AOP- val , és láthattátok, hogy ez a vadállat nem is olyan ijesztő, mint ahogy azt egyesek állítják. Viszlát mindenki!
Hozzászólások
  • Népszerű
  • Új
  • Régi
Hozzászólás írásához be kell jelentkeznie
Ennek az oldalnak még nincsenek megjegyzései