CodeGym/Java-blogg/Tilfeldig/Hva er AOP? Prinsipper for aspektorientert programmering
John Squirrels
Nivå
San Francisco

Hva er AOP? Prinsipper for aspektorientert programmering

Publisert i gruppen
Hei, gutter og jenter! Uten å forstå de grunnleggende konseptene er det ganske vanskelig å fordype seg i rammer og tilnærminger til å bygge funksjonalitet. Så i dag skal vi snakke om et slikt konsept - AOP, også kjent som aspektorientert programmering . Hva er AOP?  Prinsipper for aspektorientert programmering - 1Dette emnet er ikke lett og brukes sjelden direkte, men mange rammeverk og teknologier bruker det under panseret. Og selvfølgelig, noen ganger under intervjuer, kan du bli bedt om å beskrive i generelle termer hva slags beist dette er og hvor det kan brukes. Så la oss ta en titt på de grunnleggende konseptene og noen enkle eksempler på AOP i Java . Nå står AOP for aspektorientert programmering, som er et paradigme ment å øke modulariteten til de forskjellige delene av en applikasjon ved å skille tverrgående bekymringer. For å oppnå dette legges ytterligere virkemåte til den eksisterende koden uten å gjøre endringer i den opprinnelige koden. Med andre ord kan vi tenke på det som å henge tilleggsfunksjonalitet på toppen av metoder og klasser uten å endre den modifiserte koden. Hvorfor er dette nødvendig? Før eller siden konkluderer vi med at den typiske objektorienterte tilnærmingen ikke alltid kan løse visse problemer effektivt. Og når det øyeblikket kommer, kommer AOP til unnsetning og gir oss ytterligere verktøy for å bygge applikasjoner. Og ytterligere verktøy betyr økt fleksibilitet i programvareutvikling, noe som betyr flere muligheter for å løse et bestemt problem.

Bruker AOP

Aspektorientert programmering er designet for å utføre tverrgående oppgaver, som kan være en hvilken som helst kode som kan gjentas mange ganger ved forskjellige metoder, som ikke kan struktureres fullstendig i en egen modul. Følgelig lar AOP oss holde dette utenfor hovedkoden og erklære det vertikalt. Et eksempel er bruk av en sikkerhetspolicy i en applikasjon. Vanligvis går sikkerhet gjennom mange elementer i en applikasjon. Dessuten bør applikasjonens sikkerhetspolicy brukes likt på alle eksisterende og nye deler av applikasjonen. Samtidig kan en sikkerhetspolicy i bruk selv utvikle seg. Dette er det perfekte stedet å bruke AOP . Et annet eksempel er også logging. Det er flere fordeler ved å bruke AOP-tilnærmingen til logging i stedet for å legge til loggingsfunksjoner manuelt:
  1. Koden for logging er enkel å legge til og fjerne: alt du trenger å gjøre er å legge til eller fjerne et par konfigurasjoner av et eller annet aspekt.

  2. All kildekoden for logging holdes på ett sted, slik at du ikke trenger å lete manuelt opp alle stedene der den brukes.

  3. Loggkode kan legges til hvor som helst, enten i metoder og klasser som allerede er skrevet eller i ny funksjonalitet. Dette reduserer antallet kodefeil.

    Når du fjerner et aspekt fra en designkonfigurasjon, kan du også være sikker på at all sporingskoden er borte og at ingenting ble savnet.

  4. Aspekter er egen kode som kan forbedres og brukes igjen og igjen.
Hva er AOP?  Prinsipper for aspektorientert programmering - 2AOP brukes også til unntakshåndtering, bufring og utpakking av visse funksjoner for å gjøre den gjenbrukbar.

Grunnleggende prinsipper for AOP

For å komme videre i dette emnet, la oss først bli kjent med hovedkonseptene til AOP. Råd — Ytterligere logikk eller kode kalt fra et sammenføyningspunkt. Råd kan gis før, etter eller i stedet for et sammenføyningspunkt (mer om dem nedenfor). Mulige typer råd :
  1. Før — denne typen råd lanseres før målmetoder, dvs. sammenføyningspunkter, blir utført. Når vi bruker aspekter som klasser, bruker vi @Before- kommentaren for å markere rådene som kommer før. Når du bruker aspekter som .aj- filer, vil dette være before() -metoden.

  2. Etter — råd som utføres etter utførelse av metoder (sammenføyningspunkter) er fullført, både i normal utførelse så vel som når du kaster et unntak.

    Når du bruker aspekter som klasser, kan vi bruke @Etter -kommentaren for å indikere at dette er råd som kommer etter.

    Når du bruker aspekter som .aj- filer, er dette after()- metoden.

  3. Etter retur — dette rådet utføres bare når målmetoden fullfører normalt, uten feil.

    Når aspekter er representert som klasser, kan vi bruke @AfterReturning- kommentaren for å merke rådet som utført etter vellykket gjennomføring.

    Når du bruker aspekter som .aj- filer, vil dette være after()-returmetoden (Object obj) .

  4. Etter kasting — dette rådet er ment for tilfeller der en metode, det vil si et sammenføyningspunkt, gir et unntak. Vi kan bruke dette rådet til å håndtere visse typer mislykket utførelse (for eksempel for å rulle tilbake en hel transaksjon eller logg med det nødvendige sporingsnivået).

    For klasseaspekter brukes @AfterThrowing- kommentaren for å indikere at dette rådet brukes etter å ha kastet et unntak.

    Når du bruker aspekter som .aj- filer, vil dette være after() throwing-metoden (Unntak e) .

  5. Rundt — kanskje en av de viktigste typene råd. Den omgir en metode, det vil si et sammenføyningspunkt som vi kan bruke til for eksempel å velge om vi skal utføre en gitt sammenføyningspunktmetode eller ikke.

    Du kan skrive rådskode som kjører før og etter at join point-metoden er utført.

    Rundrådet er ansvarlig for å kalle join point-metoden og returverdiene hvis metoden returnerer noe. Med andre ord, i dette rådet kan du ganske enkelt simulere driften av en målmetode uten å kalle den, og returnere hva du vil som et returresultat.

    Gitt aspekter som klasser, bruker vi @Around -kommentaren for å lage råd som omslutter et sammenføyningspunkt. Når du bruker aspekter i form av .aj- filer, vil denne metoden være around()- metoden.

Join Point — punktet i et kjørende program (dvs. metodekall, objektoppretting, variabel tilgang) hvor rådene skal brukes. Dette er med andre ord et slags regulært uttrykk som brukes for å finne steder for kodeinjeksjon (steder der råd bør brukes). Pointcut — et sett med sammenføyningspunkter . En pointcut avgjør om gitt råd er aktuelt for et gitt sammenføyningspunkt. Aspekt – en modul eller klasse som implementerer tverrgående funksjonalitet. Aspekt endrer oppførselen til den gjenværende koden ved å bruke råd ved sammenføyningspunkter definert av noen punktutsnitt . Det er med andre ord en kombinasjon av råd og sammenføyningspunkter. Introduksjon— endre strukturen til en klasse og/eller endre arvehierarkiet for å legge til aspektets funksjonalitet til fremmed kode. Mål — objektet som rådet vil bli brukt på. Veving — prosessen med å koble aspekter til andre objekter for å lage anbefalte proxy-objekter. Dette kan gjøres ved kompileringstid, lastetid eller kjøretid. Det er tre typer veving:
  • Kompileringstidsveving — hvis du har aspektets kildekode og koden der du bruker aspektet, kan du kompilere kildekoden og aspektet direkte ved å bruke AspectJ-kompilatoren;

  • Post-kompiler veving (binær veving) — hvis du ikke kan eller vil bruke kildekodetransformasjoner for å veve aspekter inn i koden, kan du ta tidligere kompilerte klasser eller jar-filer og injisere aspekter i dem;

  • Veving i belastningstid — dette er bare binær veving som er forsinket til klasselasteren laster klassefilen og definerer klassen for JVM.

    En eller flere vevklasselastere kreves for å støtte dette. De er enten eksplisitt gitt av kjøretiden eller aktivert av en "veveagent."

AspectJ — En spesifikk implementering av AOP- paradigmet som implementerer evnen til å utføre tverrgående oppgaver. Dokumentasjonen finner du her .

Eksempler i Java

Deretter, for en bedre forståelse av AOP , vil vi se på små eksempler i "Hello World"-stil. Til høyre for flaggermusen vil jeg merke at eksemplene våre vil bruke kompileringstidsveving . Først må vi legge til følgende avhengighet i vår pom.xml- fil:
<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.9.5</version>
</dependency>
Som regel er den spesielle ajc- kompilatoren hvordan vi bruker aspekter. IntelliJ IDEA inkluderer det ikke som standard, så når du velger det som applikasjonskompilatoren, må du spesifisere banen til 5168 75 AspectJ -distribusjonen. Dette var den første måten. Den andre, som er den jeg brukte, er å registrere følgende plugin i pom.xml- filen:
<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>
Etter dette er det en god idé å reimportere fra Maven og kjøre mvn clean compile . La oss nå gå direkte til eksemplene.

Eksempel nr. 1

La oss lage en hovedklasse . I den vil vi ha et inngangspunkt og en metode som skriver ut et bestått navn på konsollen:
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);
  }
}
Det er ikke noe komplisert her. Vi ga et navn og viser det på konsollen. Hvis vi kjører programmet nå, ser vi følgende på konsollen:
Tanner Victor Sasha
Nå er det på tide å dra nytte av kraften til AOP. Nå må vi lage en aspektfil . De er av to typer: den første har filtypen .aj . Den andre er en vanlig klasse som bruker merknader for å implementere AOP- funksjoner. La oss først se på filen med filtypen .aj :
public aspect GreetingAspect {

  pointcut greeting() : execution(* Main.printName(..));

  before() : greeting() {
     System.out.print("Hi, ");
  }
}
Denne filen er litt som en klasse. La oss se hva som skjer her: pointcut er et sett med sammenføyningspunkter; hilsen() er navnet på dette punktkuttet; : execution indikerer å bruke den under kjøringen av alle ( * ) kall av Main.printName(...)- metoden. Deretter kommer et spesifikt råd — før() — som utføres før målmetoden kalles. : greeting() er skjæringspunktet som dette rådet svarer på. Vel, og nedenfor ser vi selve metoden, som er skrevet på Java-språket, som vi forstår. Når vi kjører main med dette aspektet til stede, får vi denne konsollutgangen:
Hei, Tanner Hei, Victor Hei, Sasha
Vi kan se at hvert kall til printName -metoden har blitt endret takket være et aspekt. La oss nå ta en titt på hvordan aspektet vil se ut som en Java-klasse med merknader:
@Aspect
public class GreetingAspect{

  @Pointcut("execution(* Main.printName(String))")
  public void greeting() {
  }

  @Before("greeting()")
  public void beforeAdvice() {
     System.out.print("Hi, ");
  }
}
Etter .aj aspekt-filen blir alt mer åpenbart her:
  • @Aspect indikerer at denne klassen er et aspekt;
  • @Pointcut("execution(* Main.printName(String))") er cutpointet som utløses for alle kall til Main.printName med et input-argument hvis type er String ;
  • @Before("greeting()") er råd som brukes før du kaller opp koden spesifisert i greeting() cutpoint.
Å kjøre main med dette aspektet endrer ikke konsollutgangen:
Hei, Tanner Hei, Victor Hei, Sasha

Eksempel nr. 2

Anta at vi har en metode som utfører noen operasjoner for klienter, og vi kaller denne metoden fra hoved :
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);
  }
}
La oss bruke @Around- kommentaren for å lage en "pseudotransaksjon":
@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...");
     }
  }
  }
Med fortsett -metoden til ProceedingJoinPoint- objektet kaller vi innpakningsmetoden for å bestemme plasseringen i rådet. Derfor blir koden i metoden ovenfor joinPoint.proceed(); er Før , mens koden under er Etter . Hvis vi kjører main , får vi dette i konsollen:
Åpner en transaksjon... Utfører noen operasjoner for Client Tanner Avslutter en transaksjon...
Men hvis vi kaster og unntak i metoden vår (for å simulere en mislykket operasjon):
public static void performSomeOperation(String clientName) throws Exception {
  System.out.println("Performing some operations for Client " + clientName);
  throw new Exception();
}
Så får vi denne konsollutgangen:
Åpner en transaksjon... Utfører noen operasjoner for Client Tanner Operasjonen mislyktes. Tilbakestiller transaksjonen...
Så det vi endte opp med her er en slags feilhåndteringsevne.

Eksempel nr. 3

I vårt neste eksempel, la oss gjøre noe som å logge på konsollen. Ta først en titt på Main , hvor vi har lagt til litt pseudo-forretningslogikk:
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();
     }
  }
}
I main bruker vi setValue for å tilordne en verdi til verdiforekomstvariabelen . Så bruker vi getValue for å få verdien, og så kaller vi checkValue for å se om den er lengre enn 10 tegn. I så fall vil et unntak bli kastet. La oss nå se på aspektet vi skal bruke for å logge arbeidet med metodene:
@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);
  }
}
Hva foregår her? @Pointcut("execution(* *(..))") vil slå sammen alle anrop av alle metoder. @AfterReturning(value = "methodExecuting()", returning = "returningValue") er råd som vil bli utført etter vellykket utførelse av målmetoden. Vi har to tilfeller her:
  1. Når metoden har en returverdi — if (returningValue! = Null) {
  2. Når det ikke er noen returverdi – else {
@AfterThrowing(value = "methodExecuting()", throwing = "unntak") er råd som vil utløses i tilfelle en feil, det vil si når metoden kaster et unntak. Og følgelig, ved å kjøre main , vil vi få en slags konsollbasert logging:
Vellykket kjøring: metode — setValue, klasse — Hoved Vellykket kjøring: metode — getValue, klasse — Hoved, returverdi — <noen verdi> Unntak kastet: metode — checkValue, klasse — Hovedunntak — java.lang.Exception Exception thrown: method — hoved, klasse — Hoved, unntak — java.lang.Unntak
Og siden vi ikke håndterte unntakene, får vi likevel et stackspor: Hva er AOP?  Prinsipper for aspektorientert programmering - 3Du kan lese om unntak og unntakshåndtering i disse artiklene: Unntak i Java og Unntak: fangst og håndtering . Det var alt for meg i dag. I dag ble vi kjent med AOP , og du kunne se at dette beistet ikke er så skummelt som noen gjør det til. Ha det bra alle sammen!
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå