
Aplicarea AOP
Programarea orientată pe aspecte este concepută pentru a îndeplini sarcini transversale, care poate fi orice cod care poate fi repetat de mai multe ori prin metode diferite, care nu poate fi structurat complet într-un modul separat. În consecință, AOP ne permite să păstrăm acest lucru în afara codului principal și să îl declarăm vertical. Un exemplu este utilizarea unei politici de securitate într-o aplicație. De obicei, securitatea rulează prin multe elemente ale unei aplicații. În plus, politica de securitate a aplicației ar trebui să fie aplicată în mod egal tuturor părților existente și noi ale aplicației. În același timp, o politică de securitate în uz poate evolua ea însăși. Acesta este locul perfect pentru a utiliza AOP . De asemenea, un alt exemplu este înregistrarea în jurnal. Există mai multe avantaje în utilizarea abordării AOP pentru înregistrare, mai degrabă decât adăugarea manuală a funcțiilor de înregistrare:-
Codul pentru înregistrare este ușor de adăugat și de eliminat: tot ce trebuie să faceți este să adăugați sau să eliminați câteva configurații cu un anumit aspect.
-
Tot codul sursă pentru înregistrare este păstrat într-un singur loc, astfel încât nu este nevoie să vânați manual toate locurile în care este utilizat.
-
Codul de înregistrare poate fi adăugat oriunde, fie în metode și clase care au fost deja scrise, fie în funcționalități noi. Acest lucru reduce numărul de erori de codare.
De asemenea, atunci când eliminați un aspect dintr-o configurație de design, puteți fi sigur că tot codul de urmărire a dispărut și că nimic nu a fost ratat.
- Aspectele sunt coduri separate care pot fi îmbunătățite și utilizate din nou și din nou.

Principiile de bază ale AOP
Pentru a merge mai departe în acest subiect, să cunoaștem mai întâi conceptele principale ale AOP. Sfat — Logică suplimentară sau cod apelat dintr-un punct de conectare. Sfatul poate fi efectuat înainte, după sau în loc de un punct de unire (mai multe despre ele mai jos). Tipuri posibile de sfaturi :-
Înainte — acest tip de consiliere este lansat înainte ca metodele țintă, adică punctele de unire, să fie executate. Când folosim aspecte ca clase, folosim adnotarea @Before pentru a marca sfatul ca fiind înainte. Când utilizați aspecte ca fișiere .aj , aceasta va fi metoda before() .
- După — sfatul care este executat după executarea metodelor (punctele de unire) este complet, atât în execuția normală, cât și atunci când se aruncă o excepție.
Când folosim aspecte ca clase, putem folosi adnotarea @After pentru a indica că acesta este un sfat care vine după.
Când utilizați aspecte ca fișiere .aj , aceasta este metoda after() .
-
După revenire — acest sfat este efectuat numai când metoda țintă se termină normal, fără erori.
Când aspectele sunt reprezentate ca clase, putem folosi adnotarea @AfterReturning pentru a marca sfatul ca fiind executat după finalizarea cu succes.
Când utilizați aspecte ca fișiere .aj , aceasta va fi metoda de returnare after() (Object obj) .
-
După aruncare — acest sfat este destinat cazurilor în care o metodă, adică un punct de unire, aruncă o excepție. Putem folosi acest sfat pentru a gestiona anumite tipuri de execuție eșuată (de exemplu, pentru a derula înapoi o întreagă tranzacție sau pentru a înregistra cu nivelul de urmărire necesar).
Pentru aspectele de clasă, adnotarea @AfterThrowing este folosită pentru a indica faptul că acest sfat este folosit după aruncarea unei excepții.
Când utilizați aspecte ca fișiere .aj , aceasta va fi metoda de aruncare after() (Excepția e) .
-
În jur - poate unul dintre cele mai importante tipuri de sfaturi. Acesta înconjoară o metodă, adică un punct de îmbinare pe care îl putem folosi, de exemplu, pentru a alege dacă să executăm sau nu o anumită metodă a punctului de unire.
Puteți scrie un cod de sfat care rulează înainte și după executarea metodei punctului de unire.
Sfatul around este responsabil pentru apelarea metodei punctului de unire și a valorilor returnate dacă metoda returnează ceva. Cu alte cuvinte, în acest sfat, puteți să simulați pur și simplu funcționarea unei metode țintă fără a o apela și să returnați ceea ce doriți ca rezultat returnat.
Având în vedere aspectele ca clase, folosim adnotarea @Around pentru a crea sfaturi care înglobează un punct de îmbinare. Când utilizați aspecte sub formă de fișiere .aj , această metodă va fi metoda around() .
-
Tesare în timp de compilare — dacă aveți codul sursă al aspectului și codul în care utilizați aspectul, atunci puteți compila codul sursă și aspectul direct folosind compilatorul AspectJ;
-
Țesătură post-compilare (împletire binară) — dacă nu puteți sau nu doriți să utilizați transformări de cod sursă pentru a țese aspecte în cod, puteți lua clase sau fișiere jar compilate anterior și puteți injecta aspecte în ele;
-
Încărcare în timp de țesere — aceasta este doar țesere binară care este întârziată până când încărcătorul de clasă încarcă fișierul de clasă și definește clasa pentru JVM.
Unul sau mai multe încărcătoare din clasa de țesut sunt necesare pentru a sprijini acest lucru. Acestea sunt fie furnizate în mod explicit de runtime, fie activate de un „agent de țesut”.
Exemple în Java
În continuare, pentru o mai bună înțelegere a AOP , ne vom uita la mici exemple în stil „Hello World”. În dreptul liliacului, voi reține că exemplele noastre vor folosi țeserea în timp de compilare . Mai întâi, trebuie să adăugăm următoarea dependență în fișierul nostru pom.xml :
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.5</version>
</dependency>
De regulă, compilatorul special ajc este modul în care folosim aspectele. IntelliJ IDEA nu îl include în mod implicit, așa că atunci când îl alegeți ca compilator al aplicației, trebuie să specificați calea către distribuția 5168 75 AspectJ . Aceasta a fost prima cale. Al doilea, care este cel pe care l-am folosit, este să înregistrez următorul plugin în fișierul pom.xml :
<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>
După aceasta, este o idee bună să reimportați din Maven și să rulați mvn clean compile . Acum să trecem direct la exemple.
Exemplul nr. 1
Să creăm o clasă principală . În el, vom avea un punct de intrare și o metodă care imprimă un nume transmis pe consolă:
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);
}
}
Nu e nimic complicat aici. Am dat un nume și l-am afișat pe consolă. Dacă rulăm programul acum, vom vedea următoarele pe consolă:
public aspect GreetingAspect {
pointcut greeting() : execution(* Main.printName(..));
before() : greeting() {
System.out.print("Hi, ");
}
}
Acest fișier este oarecum ca o clasă. Să vedem ce se întâmplă aici: pointcut este un set de puncte de unire; greeting() este numele acestui pointcut; : execuția indică aplicarea acestuia în timpul execuției tuturor apelurilor ( * ) ale metodei Main.printName(...) . Urmează un sfat specific — before() — care este executat înainte ca metoda țintă să fie apelată. : greeting() este punctul de tăiere la care răspunde acest sfat. Ei bine, și mai jos vedem corpul metodei în sine, care este scris în limbajul Java, pe care îl înțelegem. Când rulăm main cu acest aspect prezent, vom obține această ieșire din consolă:
@Aspect
public class GreetingAspect{
@Pointcut("execution(* Main.printName(String))")
public void greeting() {
}
@Before("greeting()")
public void beforeAdvice() {
System.out.print("Hi, ");
}
}
După fișierul aspect .aj , totul devine mai evident aici:
- @Aspect indică faptul că această clasă este un aspect;
- @Pointcut("execution(* Main.printName(String))") este punctul de tăiere care este declanșat pentru toate apelurile către Main.printName cu un argument de intrare al cărui tip este String ;
- @Before(„greeting()”) este un sfat care se aplică înainte de a apela codul specificat în punctul de tăiere greeting() .
Exemplul nr. 2
Să presupunem că avem o metodă care efectuează unele operații pentru clienți și numim această metodă din 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);
}
}
Să folosim adnotarea @Around pentru a crea o „pseudo-tranzacție”:
@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...");
}
}
}
Cu metoda de procedare a obiectului ProceedingJoinPoint , apelăm metoda de împachetare pentru a determina locația acesteia în sfat. Prin urmare, codul din metoda de mai sus joinPoint.proceed(); este Înainte , în timp ce codul de mai jos este După . Dacă rulăm main , obținem asta în consolă:
public static void performSomeOperation(String clientName) throws Exception {
System.out.println("Performing some operations for Client " + clientName);
throw new Exception();
}
Apoi obținem această ieșire de consolă:
Exemplul nr. 3
În exemplul următor, să facem ceva de genul logării la consolă. Mai întâi, aruncați o privire la Main , unde am adăugat o pseudo-logică de afaceri:
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();
}
}
}
În main , folosim setValue pentru a atribui o valoare variabilei de instanță valoare . Apoi folosim getValue pentru a obține valoarea și apoi apelăm la checkValue pentru a vedea dacă are mai mult de 10 caractere. Dacă da, atunci va fi aruncată o excepție. Acum să ne uităm la aspectul pe care îl vom folosi pentru a înregistra activitatea metodelor:
@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);
}
}
Ce se petrece aici? @Pointcut ("execuție(* *(..))") va uni toate apelurile tuturor metodelor. @AfterReturning(value = "methodExecuting()", returning = "returningValue") este un sfat care va fi executat după executarea cu succes a metodei țintă. Avem două cazuri aici:
- Când metoda are o valoare returnată — dacă (returningValue! = Null) {
- Când nu există o valoare returnată — else {

GO TO FULL VERSION