
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:-
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.
-
All kildekoden for logging holdes på ett sted, slik at du ikke trenger å lete manuelt opp alle stedene der den brukes.
-
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.
- Aspekter er egen kode som kan forbedres og brukes igjen og igjen.

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 :-
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.
- 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.
-
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) .
-
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) .
-
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.
-
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."
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:
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:
@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.
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:
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:
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:
- Når metoden har en returverdi — if (returningValue! = Null) {
- Når det ikke er noen returverdi – else {

GO TO FULL VERSION