Aspectul J.

De la Wikipedia, enciclopedia liberă.
Salt la navigare Salt la căutare
Aspectul J.
limbaj de programare
Autor Fundația Eclipse
Data de origine 2001
Ultima versiune 1.9.2 (23 octombrie 2018) și 1.9.3RC1 (7 martie 2019)
Utilizare extensie pentru Java
Paradigme programare orientată pe aspect
Tastare Static
Extensii comune aj
Influențată de LISP comun
Implementare referință
Licență Licența publică Eclipse
Site-ul web www.eclipse.org/aspectj/

AspectJ este o extensie Java pentru a adăuga așa-numitele aspecte Java în sine. AspectJ este instrumentul principal în Java pentru a profita de programarea orientată pe aspect .

Istorie

Extensia AspectJ a fost dezvoltată la sfârșitul anilor 1990 la laboratorul Xerox PARC , sub îndrumarea lui Gregor Kiczales , coautor în 1996 al primei lucrări care descrie programarea orientată pe aspect [1] . Prima versiune publică a extensiei datează din 2001 [2] .

Noțiuni de bază

Unire punct și punct

Un concept de bază al AspectJ este cel al unui punct de îmbinare. Un punct de asociere poate fi definit intuitiv ca un anumit moment în executarea programului ; orice apel către orice metodă a oricărui obiect ar putea fi un punct de îmbinare, precum și citirea valorii unei variabile , crearea unui obiect sau schimbarea valorii unei variabile.

Punctele, pe de altă parte, descriu situațiile particulare care apar în timpul executării programului. O punctă este aceea că este un fel de variabilă booleană a cărei valoare depinde de un anumit set de puncte de îmbinare, care la un moment dat al execuției programului pot fi adevărate sau false .

Este posibil să definiți un punct într-un mod extrem de detaliat; De exemplu, puteți defini o pointcut care este valabil atunci când orice metodă de clasa A este numit, sau defini o pointcut care este valabil atunci când o metodă a unui obiect de clasa A numește xyz (int a, int b) metoda a unui obiect din clasa B sau o punctă care este valabilă atunci când se execută metoda B.exec (String s), cu excepția cazului în care metoda menționată anterior este apelată de un membru din clasa F. Este, de asemenea, posibil să se definească o punctă care este valabil atunci când este apelat oricare dintr-un anumit set de metode specificate de programator.

O pointcut poate conține informații despre punctele de îmbinare care o compun, cum ar fi argumentele metodei apelate (dacă pointcut conține un punct de îmbinare definit de un apel de metodă) sau obiectul specific către care a fost direcționat apelul (atâta timp cât pointcut conține un punct de îmbinare constând în apelarea unei metode a unui obiect). Referindu-ne la metafora anterioară, tăieturile punctuale definesc când spectatorii pășesc pe scenă, adică momentul în care aspectele schimbă fluxul normal al programului.

Sfat

Sfaturile sunt seturi de instrucțiuni care sunt executate atunci când o anumită punctă devine validă. Este important să rețineți că un sfat poate conține în sine definiția punctului care îl declanșează. Un sfat în execuția sa poate accesa orice informații despre punctele de îmbinare care o compun pe care pointcut-ul le poate oferi. Din nou, cu referire la metafora teatrului, sfatul definește ce fac spectatorii pe scenă.

Aspect

Unitățile elementare ale POO sunt obiectele, grupate în clase; unitățile fundamentale ale POA sunt aspectele.

Un aspect conține de obicei una sau mai multe puncte și unul sau mai multe sfaturi, precum și metode și atribute, cum ar fi clasele normale. Un aspect modelează o anumită „problemă transversală” (adică comună mai multor obiecte) a problemei de rezolvat; de exemplu, să presupunem că trebuie să efectuăm aceeași verificare a valorilor de intrare pe mai multe metode aparținând unor clase diferite. Presupunând că aceste obiecte nu sunt legate între ele prin relații de moștenire, POO ar trebui să implementeze acest control în fiecare clasă; în AOP, pe de altă parte, este suficient să implementăm un singur aspect care are o punctă definită ca un SAU logic între un set de puncte de îmbinare care corespund executării metodelor care ne interesează, care conține în interiorul său un sfat care returnează o eroare dacă argumentele metodei depășesc valorile prestabilite.

AspectJ în detaliu

Alăturați puncte

Un punct de asociere poate fi, de exemplu, un apel de metodă; în AspectJ, un punct de asociere care este declanșat apelând metoda void do something (int a), este scris ca:

 apel (nul face ceva (int))

sau, de asemenea, ca:

 executare (nul face ceva (int))

Diferența dintre un punct de asociere de tip execuție și un punct de asociere tip apel este foarte simplă: un punct asociere tip apel corespunde apelului unei metode printr-o altă metodă, în timp ce un punct asociere tip execuție corespunde execuției corpului metodei . În mod normal, un punct de asociere de apel este aproape echivalent cu un punct de asociere de execuție; uneori, însă, nu este cazul. Luați de exemplu cazul clasei care conține metoda principală: în java, prin convenție, fiecare program trebuie să includă o clasă care implementează o metodă care are următoarea semnătură: public static void main (String [] args). Această metodă este prima care este efectuată de VM Java. Un punct de asociere definit ca apel (public static void main (String [] args)) nu va fi atins niciodată în timpul executării unui program Java, spre deosebire de punctul de asociere definit de execuție (public static void main (String [] args)). Acest lucru se datorează faptului că, deși metoda principală este sigură că va fi executată, nu este apelată niciodată dintr-o altă metodă, deoarece va fi întotdeauna prima metodă care va fi executată. Există, de asemenea, multe alte modalități de a găsi puncte de asociere în fluxul de programe:

 handler (IOException)

indică execuția oricărui bloc de captură (IOException e) {..} care gestionează o excepție de tip IOException.

 get (int A.number)

set (octet B. valoare)

sunt puncte de unire care denotă, respectiv, citirea atributului întreg „numărul unui obiect aparținând clasei A și scrierea unui atribut de tip octet„ valoarea ”unui obiect aparținând inițializării tipului clasei B. (), pe de altă parte mână, corespunde creației unui obiect.

 inițializare (B.new ())

corespunde creării unui obiect din clasa B printr-un constructor care nu acceptă parametrii, în timp ce, de exemplu,

 inițializare (C.new (String, int))

corespunde creării unui obiect din clasa C printr-un constructor care are ca parametri un șir și un număr întreg. Un alt tip de punct de asociere definit de aspectj este asocierea corespunzătoare executării corpului oricărui sfat. Acest punct de asociere este scris ca

 aviz executare ()

Există, de asemenea, alte tipuri de puncte de îmbinare în aspectj, dar din moment ce limbajul este încă în evoluție, aici ne-am limitat să descriem doar cele mai frecvent utilizate, care vor fi, de asemenea, prezente în viitoarele versiuni ale aspectj, ulterior versiunii 1.1 care se referă autorul. În aspectj există și expresii care vă permit să definiți seturi de puncte de îmbinare; expresia

 apel (void conta (..))

corespunde apelului oricărei metode care are numele „count” și care nu returnează o valoare; adică definește un set ai cărui membri sunt punctele de îmbinare corespunzătoare apelului metodelor de vid care diferă între ele numai prin numărul și tipul de argumente. Expresia

 call (public * doSomething (..))

în schimb, definește un set de puncte de îmbinare corespunzătoare apelului metodelor numite doSomething care au fost declarate publice, care diferă între ele prin numărul și tipul de argumente; în plus, metodele de mai sus sunt, de asemenea, diferite pentru tipul de date pe care le returnează, care pot fi, de asemenea, nule (exemplu: void doSomething (..)).
Un alt exemplu de expresie care codifică o proprietate care definește un set de puncte de îmbinare este următorul:

 apel (nul A. * (..))

Această expresie indică setul de puncte de îmbinare generate prin apelarea oricărei metode de vid aparținând clasei A.

Pointcut

Un punct este un punct de asociere, sau o intersecție sau o uniune între punctele de asociere, sau mai informal, o anumită situație care apare în timpul executării programului. O definiție în definiția sa include unul sau mai multe puncte de asociere: un exemplu simplu de asociere definită pornind de la un singur punct de asociere este următorul:

 pointcut pc (): call (void doSomething (int))

Pointcuts-urile pot face, de asemenea, accesibile codului prezent în sfatul referitor la acesta unele date preluate din punctele de îmbinare care fac parte din definiția lor: de exemplu,

 pointcut pc (int i): call (void doSomething (int)) && args (i)

oferă consilierului o variabilă int i care va fi inițializată la valoarea parametrului întreg trecut metodei void doSomething (int n). Este demn de remarcat faptul că punctul tăiat definit mai sus este formal o intersecție, adică o logică și între două seturi de puncte de unire definite de cele două expresii call (void doSomething (int)) și args (int i): ultima expresie indică toate join puncte care iau un singur parametru de tip int ca intrare. Referindu-se la punctele care în definițiile lor au, printre altele, și puncte de tip tip apel, este posibil ca acestea să indice consilierului obiectul care face apelul și cel care îl primește. Să luăm, de exemplu, pointcut wait, definită după cum urmează:

 pointcut wait (A1 ac, B2 bd): call (void wait ()) && this (ac) && target (bd)

corespunde unui obiect aparținând clasei A1 care, în codul său, efectuează un apel către o metodă void wait () a unui obiect aparținând clasei B2; obiectul respectiv este identificat cu bd; ac, pe de altă parte, se referă la obiectul care efectuează apelul.
Este, de asemenea, posibil să definiți un punct de punctare ca o uniune de două puncte de unire: această poincut va deveni „validă” atunci când execuția programului atinge oricare (în definiție se folosește OR logică) dintre punctele de unire specificate. De exemplu:

 pointcut pt (): call (void compute ()) || executie (int se multiplica (int, int))

Această punctare coincide atât cu apelul metodei void compute (), cât și cu execuția corpului metodei int multiply (int a, int b)

de asemenea, este posibil să se utilizeze negația în definiția unei puncte:

 pointcut wait2 (A1 ac, B2 bd): call (void wait ()) &&! this (ac) && target (bd)

acest punct va fi activat printr-un apel al metodei B2.wait (), cu condiția ca apelul menționat anterior să nu fie făcut de un obiect aparținând clasei A1.

În general, o tăiere în puncte generează puncte de unire. Gândiți-vă doar că în corpul oricărei metode, poate apărea crearea unui obiect, apelul unei alte metode, gestionarea unei excepții. Același lucru este valabil și pentru gestionarea unei excepții sau pentru crearea unui obiect. Prin urmare, în aspectj există cuvinte cheie specifice pentru a grupa punctele de îmbinare în seturi pe baza punctului care le-a creat. Acestea sunt: ​​cflow și cflow de mai jos. Pentru a arăta diferența dintre cele două, să luăm de exemplu următorul fragment de cod:

 pointcut ct (): call (void sum ());
pointcut fl (): cflow (ct);

pointcut fl identifică setul de puncte de îmbinare generate în executarea metodei sumelor nule, în plus față de punctul de îmbinare generat de apelul metodei sum (). Punctul fl, conceput ca un set de puncte de îmbinare, conține poincut ct în interior. În schimb, dacă definim punctul fl2 ca:

 pointcut fl2 (): cflow de mai jos (ct);

acest pointcut fl2 va corespunde setului de puncte de unire generate în executarea sumei nule, dar nu și punctului de unire generat de apelul la suma nula. pointcut fl2 nu conține pointcut ct în interior. cflow și cflowbelow sunt două cuvinte cheie utilizate frecvent pentru a defini puncte care sunt intersecții ale altor puncte, adică puncte care folosesc ȘI logice în definiția lor. De exemplu:

 pointcut restart (): cflowbelow (executare (void wait ()))) && call (void resume (int));

În acest caz, numai cele generate de CV-ul nul () sunt selectate din setul de puncte de unire generate de așteptarea nulă (); în practică, această reducere punctuală va deveni valabilă numai atunci când CV-ul void () va fi apelat din metoda void wait () sau va fi apelat dintr-un alt punct de îmbinare generat, direct sau indirect, de executarea void wait () . Alte două cuvinte cheie utilizate frecvent în definirea punctelor de acces sunt în și cu cod. În cadrul (ClassName) indicăm setul de puncte de asociere corespunzătoare executării codului care este, la nivelul sursei, parte a clasei ClassName. De exemplu:

 pointcut pi (): în cadrul (java.sql. *);

denotă setul de puncte de îmbinare generate de codul care face parte din pachetul java.sql; în mod similar, wthincode indică setul de puncte de îmbinare generate de cod care face parte dintr-o anumită metodă sau constructor. Apoi puteți defini un alt punctcut pi2 ca

 pointcut pi2 (): withincode (void insert (Object o));

Sfat

Sfaturile sunt seturi de instrucțiuni care sunt executate înainte, după sau în locul unor puncte. Acest text tratează primele două cazuri, având în vedere că semantica celui de-al treilea (sfatul „în jurul”) este încă în curs de definire. Sfaturile care sunt executate înainte ca un punct să devină valid sunt definite de cuvântul cheie înainte: să luăm de exemplu următorul cod:

 pointcut point1 (): executare (voS faSomething (..));
before (): dot1 () {System.out.println ("salut");
}

acest fragment de cod face ca înainte de a executa o metodă de tip void și numită doSomething să fie tipărită pe ecran salut . Un sfat poate fi, de asemenea, efectuat după o anumită reducere:

 after (): dot1 () {System.out.println ("goodbye");
}

efectul acestui sfat este ca cuvântul „ la revedere” să fie tipărit ca mesaj pe ecran, înainte ca metoda să se termine . Un sfat poate el însuși să definească punctul de care depinde; cu alte cuvinte, următoarea sintaxă în aspectj este corectă:

after (): execution (void faSomething (..)) {System.out.println ("goodbye"); }

iar acest cod este echivalent cu cel definit mai sus. Codul de consiliere are, de asemenea, acces la variabilele pe care pointcut le pune la dispoziție; cu referire la pointcut așteptați definită anterior ca

 pointcut wait (A1 ac, B2 bd): call (void wait ()) && this (ac) && target (bd)

puteți scrie următoarele sfaturi:

 înainte (A1 ac, B2 bd): așteptați (ac, bd) {
// cod care poate accesa
// la obiectele ac și bd
}

Trebuie să ne amintim că în java o metodă se poate termina în mod regulat sau aruncă o excepție: aspectj permite să distingem aceste două cazuri. Vezi următorul cod:

 after () returning point1 (): {System.out.println ("toate obișnuite");
}

acest sfat va fi executat numai dacă executarea s-a încheiat corect

 after () throwing: dot1 () {System.out.println ("a fost aruncată o excepție");
}

celălalt va fi executat numai dacă metoda doSomething a aruncat o excepție. În plus, un sfat de tip după (..) returnare poate avea datele pe care le returnează metoda conținută în poincut, de exemplu este posibil să scrieți

 după (int a) returnare: apel (int multiplică (int, int)) {
// aici ar merge codul de executat ..
// care are acces la variabila int a 
// care conține valoarea returnată de int multiply
}

în mod similar, în cazul sfaturilor de tip aruncare după (..), este posibil să existe excepția pe care metoda a aruncat-o în corpul sfatului. De exemplu, puteți scrie:

 după (Excepția e) aruncare: apel (int se înmulțește (int, int)) {
  System.out.println ("excepție:" + e.toString ());
}

Aștepta

Un aspect modelează o problemă transversală a aplicației. Poate conține definiții ale punctelor, sfaturi, atribute și metode, poate implementa o interfață sau poate extinde o clasă; viceversa evident nu se aplică. Există aspecte abstracte și aspecte concrete, la fel cum există clase abstracte și clase concrete; cu toate acestea, spre deosebire de POO, în POO un aspect concret nu poate extinde un alt aspect concret, ci doar un aspect abstract. Un exemplu este următorul:

 1. import java.io. *;

2. aspect public privilegiat Performanță {octet final [] acapo = {0x0a};
3. fișier Res;
4. RandomAccessFile fl;
5. Performanță publică () {try {ris = new File ("Result.txt");
6. ris.delete ();
7. ris.createNewFile ();
8. fl = new RandomAccessFile (ris, "rw");

9.} catch (excepție IOException) {}}

10. private pointcut point1 (): executare (void main (..));
11. privat lung start1;
12. oprire privată lungă1;
13. pointcut privat point2 (): executare (print nul (String));
14. privat lung start2;
15. oprire privată lungă2;
16. before (): point1 () {start1 = System.currentTimeMillis ();

17.}

18. after (): dot1 () {stop1 = System.currentTimeMillis ();
19. long temp = stop1-start1;
20. Long t = new Long (temp);
21. Date șir = t.toString ();
22. String name = "void main (..)";

23. încercați {fl.write (name.getBytes ());
24. Șir st = ": timpul de execuție (ms):";
25. fl.write (st.getBytes ());
26. fl.write (data.getBytes ());
27. fl.write (acapo);
28.} catch (excepție IOException) {}}

29. before (): point2 () {start2 = System.currentTimeMillis ();

30.}

31. after (): dot2 () {stop2 = System.currentTimeMillis ();
32. long temp = stop2-start2;
33. Long t = new Long (temp);
34. Date șir = t.toString ();
35. String name = "void print (String)";
36. încercați {fl.write (name.getBytes ());
37. String st = ": timp de execuție (ms):";
38. fl.write (st.getBytes ());
39. fl.write (data.getBytes ());
40. fl.write (acapo);
41.} catch (excepție IOException) {}}
42.}

pe linia 2 este declarația de înfățișare. Aspectele pot fi private sau publice, la fel ca cursurile, dar pot fi și „privilegiate” sau nu: un aspect normal respectă principiul ascunderii informațiilor, în timp ce unul privilegiat nu. Pur și simplu, doar aspectele declarate drept „privilegiate” au vizibilitatea atributelor și metodelor private ale unei clase și, în consecință, numai aspectele privilegiate pot conține în interiorul lor puncte care, în definiția lor, se referă la punctele de îmbinare corespunzătoare, de exemplu, pentru a obține sau setați un atribut privat al obiectului sau pentru a executa o metodă privată. Problema examinată în linia 2 este declarată ca un aspect public privilegiat.

O aparență, la fel ca orice alt obiect, poate avea unul sau mai mulți constructori: în acest caz, constructorul este implementat în liniile 5-9.

În liniile 10 și 13 găsim definiția a două puncte simple, definite prin puncte de tip tip de execuție. În linia 16 și linia 18 există două sfaturi care sunt efectuate, respectiv, înainte și după punctul definit în linia 10; în aspect există și două sfaturi (liniile 29 și 31) care sunt efectuate una înainte și cealaltă după punctul definit în linia 13. Așa cum se întâmplă în clase, toate aceste sfaturi au acces la variabilele private ale aspectului (definite în rândurile 2-15).

Am menționat mai devreme că aspectele au un constructor, cum ar fi obiectele. Cu toate acestea, spre deosebire de obiecte, aparențele nu sunt instanțiate prin instrucțiuni specifice. În mod normal, există o instanță pentru fiecare aspect care este creată automat la începutul programului; cu toate acestea, crearea aspectului poate fi asociată cu anumite evenimente care apar în timpul execuției programului. Anterior, am dat un exemplu de punctaj definit dintr-un punct de asociere de tip apel care specifica de asemenea obiectul care se execută și obiectul către care a fost direcționat apelul. Acest exemplu a fost:

 pointcut wait (A1 ac, B2 bd): call (void wait ()) && this (ac) && target (bd)

este posibil să asociați un aspect fiecărui obiect care generează o punctă „validă”. Adică, o instanță de apariție este creată de fiecare dată când fluxul de program atinge punctul de așteptare și obiectul care rulează (acesta, în Java) nu are deja o instanță de apariție asociată cu acesta. În AspectJ, acest lucru este scris ca:

 aspect aspectname perthis (așteptați (A1 ac, B2 bd)) {}

această afirmație asigură faptul că un aspect este asociat cu fiecare obiect de curent alternativ care generează o așteptare punctuală. Pentru a ne asigura că fiecare obiect de un anumit tip, să zicem TiPo, este asociat cu un aspect, ar trebui să scriem:

 aspect aspectname perthis (this (TiPo)) {}

unde acesta (TiPo) reprezintă setul de puncte de îmbinare în care obiectul care rulează în prezent este de tip TiPo.

De asemenea, este posibil să asociați aparițiile obiectelor care primesc apelul:

 aspect aspectname pentru țintă (așteptați (A1 ac, B2 bd)) {}

în acest fel, ori de câte ori punctul de așteptare devine valid, un aspect va fi asociat cu bd (obiectul țintă), dacă nu are deja unul.

O altă posibilitate oferită de AspectJ este instanțierea aspectelor de fiecare dată când o anumită punctă devine valabilă.

 aspect aspectname percflow (tipărire fără apel (șir)) {}

va crea o apariție de fiecare dată când se apelează metoda de imprimare.

O caracteristică foarte utilă a AspectJ este posibilitatea de a specifica ce aspecte au prioritate față de celelalte: să presupunem că un anumit punct de asociere face parte din două puncte diferite, definite în două aspecte diferite, care sunt numite, respectiv, Performanță și Logare. Fiecare dintre aceste două puncte este asociată cu un sfat înainte; care dintre cele două sfaturi va fi efectuat mai întâi? Pentru a avea un răspuns la această întrebare, este necesar să scrieți, într-unul din cele două aspecte, următoarea afirmație:

 declarați precedenta: Prestaz, Logging;

dacă doriți ca sfatul Prestaz să ruleze înainte de aspectul Logging; în schimb, afirmația

 declarați precedenta: Logging, Prestaz;

va avea efectul opus.

Declarații inter-tip

În aspectj, un aspect poate modifica însăși structura obiectelor prin adăugarea de metode, atribute și constructori la acestea, pe lângă cele pe care le are deja. Cu toate acestea, trebuie să fim atenți să nu creăm conflicte: metodele, constructorii și atributele pe care un aspect le adaugă unui obiect nu trebuie să se suprapună cu metodele, constructorii și atributele pe care le are deja obiectul. Pur și simplu, un aspect poate adăuga metode, atribute sau constructori, atâta timp cât nu sunt deja definiți de obiectul însuși.

Sintaxa declarațiilor inter-tip este foarte simplă: declarația

 public int Game.time;

adaugă un atribut întreg numit „timp” la clasa Joc, în timp ce

 private String Ship.name;

adaugă un nume de atribut șir la clasa Ship. Este important să rețineți că acest atribut a fost declarat privat de la apariție și, prin urmare, acest atribut va fi vizibil numai din metodele și sfaturile apariției, dar nu și din metodele clasei Nave.

 public int Game.score () { 
// cod care returnează scorul jocului ...
}

Această declarație adaugă o metodă de scor public () la clasa Joc. De asemenea, este posibil să adăugați constructori la obiecte, de exemplu puteți scrie

 public Game.new (șiruri) {
// cod producător
}

pentru a face ca Game să aibă un constructor care ia un șir ca parametru.

Notă

  1. ^ G. Kiczales, Programare orientată pe aspect , în ACM Computing Surveys (CSUR) , vol. 28, 4es, 1 decembrie 1996, p. 154, DOI : 10.1145 / 242224.242420 . Adus pe 21 octombrie 2018 .
  2. ^ Gregor Kiczales, Erik Hilsdale și Jim Hugunin, An Overview of AspectJ , Springer-Verlag, 18 iunie 2001, pp. 327-353. Adus pe 21 octombrie 2018 .

linkuri externe