AspectJ: differenze tra le versioni
Contenuto cancellato Contenuto aggiunto
mNessun oggetto della modifica |
m refuso |
||
(31 versioni intermedie di 23 utenti non mostrate) | |||
Riga 1:
{{F|linguaggi di programmazione|aprile 2012}}
{{Linguaggio di programmazione
|autore = Eclipse Foundation
|data = 2001
|utilizzo = estensione per Java
|paradigmi = programmazione orientata agli aspetti
|tipizzazione = Statica
}}
'''AspectJ''' è un'estensione di [[Java (linguaggio di programmazione)|Java]] per aggiungere a Java stesso i cosiddetti ''aspetti''. AspectJ rappresenta lo strumento primario, in ambito Java, per avvalersi della [[programmazione orientata agli aspetti]].
== Storia ==
L'estensione AspectJ venne sviluppata a partire dalla fine degli anni '90 presso il laboratorio [[Xerox Palo Alto Research Center|Xerox PARC]], dietro la guida di [[Gregor Kiczales]], co-autore nel 1996 del primo paper che descrive la programmazione orientata agli aspetti<ref>{{Cita pubblicazione|nome=G.|cognome=Kiczales|data=1996-12-01|titolo=Aspect-oriented programming|rivista=ACM Computing Surveys (CSUR)|volume=28|numero=4es|p=154|accesso=2018-10-21|doi=10.1145/242224.242420|url=https://dl.acm.org/citation.cfm?id=242224.242420}}</ref>. La prima release pubblica dell'estensione risale al 2001<ref>{{Cita pubblicazione|nome=Gregor|cognome=Kiczales|data=2001-06-18|titolo=An Overview of AspectJ|editore=Springer-Verlag|pp=327-353|accesso=2018-10-21|url=https://dl.acm.org/citation.cfm?id=646158.680006|nome2=Erik|cognome2=Hilsdale|nome3=Jim|cognome3=Hugunin}}</ref>.
==Concetti base==
===Join point e pointcut===
Un concetto basilare di '''AspectJ''' è quello di join point. Un join point può essere intuitivamente definito come un certo istante nell'esecuzione del [[Programmazione (informatica)|programma]]; ogni chiamata a un qualsiasi metodo di un oggetto qualunque potrebbe essere un join point, così come leggere il valore di una [[Variabile (informatica)|variabile]], creare un oggetto o modificare il valore di una variabile.
I ''pointcut'' invece descrivono le situazioni particolari che si vengono a creare durante l'esecuzione del programma. Un pointcut è cioè una sorta di [[variabile booleana]] il cui valore dipende da
È possibile definire un pointcut in maniera estremamente dettagliata; si può ad esempio definire un pointcut che sia valido quando un qualsiasi metodo della [[Classe (informatica)|classe]] A viene chiamato, o ancora, definire un pointcut che è valido quando
Un pointcut può contenere informazioni sui join point che lo compongono, quali ad esempio gli argomenti del metodo chiamato (se il pointcut contiene un join point definito dalla chiamata di un metodo) o lo specifico oggetto cui era diretta la chiamata (sempre che il pointcut contenga un join point consistente nella chiamata di un metodo di un oggetto). Facendo riferimento alla [[metafora]] precedente, i pointcut definiscono quando gli
===Advice===
Gli advice sono degli insiemi di istruzioni che vengono eseguite quando un certo pointcut diventa valido.
===Aspect===
Le unità elementari dell'OOP sono gli oggetti, raggruppati in classi; le unità fondamentali dell'AOP sono gli aspetti.
Un aspetto normalmente contiene uno o più pointcut e uno o più advice, oltre a metodi e attributi, come le normali classi. Un aspetto modella una certa 'problematica trasversale' (cioè comune a più oggetti) del problema da risolvere; per esempio, poniamo di dover effettuare uno stesso controllo sui valori di input su più
{{Organizzare|Sezione inutilmente prolissa.|informatica|marzo 2012}}
===Join points===
Un join point può essere, ad esempio, la chiamata di un metodo; in AspectJ, un join point che viene attivato dalla chiamata del metodo void faiqualcosa(int a), si scrive come:
Riga 28 ⟶ 39:
o anche come:
execution(void faiqualcosa(int))
La differenza tra un join point di tipo execution e uno di tipo call è molto semplice: un join point di tipo call corrisponde alla chiamata di un metodo da parte di un altro metodo, mentre un join point di tipo execution corrisponde all'esecuzione del corpo del metodo. Normalmente un join point di tipo call è pressoché equivalente a un join point di tipo execution; a volte però non è così. Prendiamo ad esempio il caso della classe contenente il metodo main: in java, per convenzione, ogni programma deve comprendere una classe che implementi un metodo che abbia la seguente firma: public static void main(String[] args). Tale metodo è il primo ad essere eseguito dalla java VM. Un join point definito come call (public static void main(String[] args)) non verrà mai raggiunto durante l'esecuzione di un programma java, a differenza del join point definito da execution (public static void main(String[] args)). Questo accade perché, anche se il metodo main verrà sicuramente eseguito, esso non viene mai chiamato da un altro metodo, in quanto sarà sempre il primo metodo ad essere eseguito.
Esistono inoltre molti altri modi per individuare dei join point nel flusso del programma:
handler(IOException)
indica l'esecuzione di un qualsiasi blocco catch(IOException e) { .. } che gestisce un'eccezione di tipo IOException.
get ( int A.numero )
sono dei join point che denotano, rispettivamente, la lettura dell'attributo di tipo intero 'numero di un oggetto appartenente alla classe A e la scrittura di un attributo di tipo byte 'valore' di un oggetto appartenente alla classe B. Un join point di tipo initialization(), invece, corrisponde alla creazione di un oggetto.
initialization(B.new())
corrisponde alla creazione di un oggetto di classe B tramite un costruttore che non accetta parametri, mentre, ad esempio,
initialization(C.new(String,int))
corrisponde alla creazione di un oggetto di classe C tramite un costruttore che ha come parametri una stringa e un intero. Un altro tipo di join point definito da aspectj è il join corrispondente all'esecuzione del corpo di un qualsiasi advice.
Tale join point si scrive come
adviceexecution()
Ci sono anche altri tipi di join point in aspectj, ma visto che il linguaggio è ancora in evoluzione, qui ci si è limitati a descrivere solo quelli più frequentemente usati, che probabilmente saranno presenti anche nelle future versioni di aspectj, successive alla versione 1.1 cui l'autore fa riferimento.
In aspectj esistono inoltre delle espressioni che permettono di definire degli insiemi di join point;
l'espressione
call(void conta(..))
corrisponde alla chiamata di un qualsiasi metodo che abbia nome 'conta' e che non restituisca un valore; definisce cioè un insieme i cui membri sono i join point corrispondenti alla chiamata di metodi void che differiscono tra loro solo per il numero e il tipo degli argomenti.
L'espressione
call(public * doSomething(..))
definisce invece un insieme di join point corrispondenti alla chiamata dei metodi di nome doSomething che siano stati dichiarati come pubblici, che differiscono tra loro per il numero e il tipo degli argomenti;
Un altro esempio di espressione che codifica una proprietà definente un insieme di join point è il seguente:
call(void A.*(..))
Riga 73 ⟶ 83:
Questa espressione indica l'insieme dei join point generati dalla chiamata di un qualsiasi metodo void appartenente alla classe A.
Un pointcut è un join point, oppure una intersezione o una unione tra join point, o più informalmente, una certa situazione che si viene a creare durante l'esecuzione del programma. Un pointcut nella sua definizione comprende uno o più join point: un semplice esempio di pointcut definito a partire da un unico join point è il seguente:
pointcut pc() : call(void doSomething(int))
I pointcut possono inoltre rendere accessibili al codice presente negli advice che vi fanno riferimento alcuni dati presi dai join point che fanno parte della loro definizione: per esempio,
pointcut pc(int i): call (void doSomething(int)) && args(i)
mette a disposizione dell'advice una variabile int i che sarà inizializzata al valore del parametro intero passato al metodo void doSomething(int n). Vale la pena notare che il pointcut definito qui sopra è formalmente una intersezione, ossia un and logico tra due insiemi di join point definiti dalle due espressioni call (void doSomething(int)) e args( int i): quest'ultima espressione indica tutti i join point
pointcut wait(A1 ac, B2 bd) : call(void wait()) && this(ac) && target(bd)
esso corrisponde a un oggetto appartenente alla classe A1 che, nel suo codice, effettui una chiamata di un metodo void wait() di un oggetto appartenente alla classe B2; detto oggetto viene identificato con bd; ac, invece, si riferisce all'oggetto che effettua la chiamata.
pointcut pt() : call(void calcola()) || execution(int moltiplica(int,int))
Tale pointcut coincide sia con la chiamata del metodo void calcola(), sia con l'esecuzione del corpo del metodo int moltiplica(int a, int b)
è anche possibile usare la negazione nella definizione di un pointcut:
pointcut wait2(A1 ac, B2 bd) : call(void wait()) && !this(ac) && target(bd)
questo pointcut verrà attivato da una chiamata del metodo B2.wait(), purché la suddetta chiamata non sia effettuata da parte di un oggetto appartenente alla classe A1.
In generale, un pointcut genera dei join point. Basti pensare che nel corpo di un qualsiasi metodo, si può verificare la creazione di un oggetto, la chiamata di un altro metodo, la gestione di un'eccezione. Idem dicasi per la gestione di un'eccezione, o per la creazione di un oggetto. In aspectj esistono pertanto delle apposite parole chiave per raggruppare i join point in insiemi in base al pointcut che li ha creati.
Esse sono: cflow e cflowbelow.
Per mostrare la differenza tra i due, prendiamo ad esempio il seguente frammento di codice
pointcut ct() : call(void sum()) ;
pointcut fl() : cflow(ct) ;
il
pointcut fl2() : cflowbelow(ct) ;
questo pointcut fl2 corrisponderà all'insieme dei join point generati nell'esecuzione di void sum, ma non al join point generato dalla chiamata di void sum. il pointcut fl2 non contiene al suo interno il pointcut ct. cflow e cflowbelow sono due parole chiave usate frequentemente per definire pointcut che sono intersezioni di altri pointcut, ossia pointcut che usano nella loro definizione l'AND logico.
Ad esempio:
pointcut restart() : cflowbelow(execution(void wait()))) && call(void resume(int)) ;
Riga 120 ⟶ 130:
pointcut pi() : within(java.sql.*);
denota l'insieme dei join point generati dal codice che è parte del package java.sql; analogamente, wthincode denota l'insieme dei join point generati da codice che è parte di un certo metodo o di un certo costruttore.
pointcut pi2() : withincode(void insert(Object o));
Gli advice sono degli insiemi di istruzioni che vengono eseguiti prima, dopo oppure al posto di alcuni pointcut. In questo testo vengono trattati i primi due casi, visto che la semantica del terzo (advice di tipo 'around') è ancora in corso di definizione. Gli advice che vengono eseguiti prima che un pointcut diventi valido sono definiti dalla parola chiave before: prendiamo ad esempio il seguente codice:
<pre>
Riga 133 ⟶ 142:
}
</pre>
questo frammento di codice fa sì che prima dell'esecuzione di un metodo di tipo void e di nome faiQualcosa sia stampato a video '''ciao'''.
Un advice può anche essere eseguito dopo un certo pointcut
after(): punto1() {System.out.println("arrivederci");
Riga 142 ⟶ 150:
l'effetto di questo advice è di far stampare come messaggio a video, prima che il metodo termini, la parola '''arrivederci'''.
Un advice può definire esso stesso il pointcut da cui dipende; in altre parole, la seguente [[sintassi]] in aspectj è corretta:
after(): execution (void faQualcosa(..)) {
}
e tale codice è equivalente a quello definito precedentemente.
Il codice di un advice hanno inoltre accesso alle variabili che il pointcut mette a loro disposizione; con riferimento al pointcut wait definito in precedenza come
pointcut wait(A1 ac, B2 bd) : call(void wait()) && this(ac) && target(bd)
si può scrivere il seguente advice:
before (A1 ac , B2 bd ) : wait(ac,bd) {
// codice che
// agli oggetti ac e bd
}
Bisogna ricordare che in java un metodo può terminare regolarmente oppure lanciando un'eccezione: aspectj permette di distinguere questi due casi.
Si [[veda]] il seguente codice:
after() returning punto1() : { System.out.println("tutto regolare");
Riga 170 ⟶ 178:
}
quest'altro verrà eseguito solo se il metodo faiQualcosa ha lanciato un'eccezione.
Inoltre un advice del tipo after (..) returning può avere a disposizione il dato che il metodo contenuto nel poincut restituisce, ad esempio è possibile scrivere
after (int a) returning : call(int moltiplica(int,int)) {
// qui andrebbe il codice da eseguire ..
// che ha accesso alla variabile int a
// contenente il valore restituito da int moltiplica
}
analogamente, nel caso di un advice del tipo after(..) throwing è possibile avere a disposizione nel corpo dell'advice l'eccezione che il metodo ha lanciato. Ad esempio, è possibile scrivere:
after (Exception e ) throwing : call(int moltiplica(int,int)) {
Riga 185 ⟶ 193:
}
Un aspetto modella una problematica trasversale dell'applicazione. Può contenere al suo interno definizioni di pointcut, advice, attributi e metodi, implementare un'interfaccia od estendere una classe; il viceversa ovviamente non vale.
Esistono aspetti astratti e aspetti concreti, esattamente così come esistono classi astratte e classi concrete; però, a differenza dell'OOP, nell'AOP un aspetto concreto non può estendere un altro aspetto concreto, ma solo un aspetto astratto.
Un esempio è il seguente:
<syntaxhighlight lang="java" line="1">
final byte[] acapo = { 0x0a
};
File ris;
try {
ris = new File("RisulTato.txt");
ris.delete();
ris.createNewFile();
fl = new RandomAccessFile(ris, "rw");
} start1 = System.currentTimeMillis() stop1 = System.currentTimeMillis() fl.write(nome.getBytes()) } start2 = System.currentTimeMillis() after(): punto2() {
stop2 = System.currentTimeMillis();
long temp = stop2 - start2;
Long t = new Long(temp);
String data = t.toString();
String nome = "void stampa(String)";
try {
fl.write(nome.getBytes());
String st = " : tempo di esecuzione (ms) : ";
fl.write(st.getBytes());
fl.write(data.getBytes());
fl.write(acapo);
} catch (IOException eccez) {}
}
}
</syntaxhighlight>
alla riga 2 c'è la dichiarazione dell'aspetto. Gli aspetti possono essere privati o pubblici, esattamente come le classi, ma possono anche essere privilegiati ('privileged') oppure no: un normale aspetto rispetta il principio dell'information hiding, mentre uno privilegiato no. In parole povere, solo gli aspetti dichiarati come 'privileged' hanno la visibilità degli attributi e dei metodi privati di una classe, e di conseguenza solo gli aspetti privilegiati possono contenere al loro interno dei pointcut che nella loro definizione facciano riferimento a join point corrispondenti, per esempio, al get o al set di un attributo privato dell'oggetto o all'esecuzione di un metodo privato. L'aspetto in esame alla riga 2 viene dichiarato come un aspetto pubblico privilegiato.
Un aspetto, esattamente come qualsiasi altro oggetto, può
Nella riga 10 e 13 troviamo la definizione di due semplici pointcut, definiti tramite dei join point di tipo execution. Alla riga 16 e alla riga 18 ci sono due advice che vengono eseguiti, rispettivamente, prima e dopo il pointcut definito alla riga 10; nell'aspetto ci sono anche due advice (righe 29 e 31) che vengono eseguiti l'uno prima e l'altro dopo il pointcut definito alla riga 13. Come accade nelle classi, tutti questi advice hanno accesso alle variabili private dell'aspetto (definite nelle righe dalla 2 alla 15).
Si è prima accennato al fatto che gli aspetti hanno un costruttore, come gli oggetti. Tuttavia, a differenza degli oggetti, gli aspetti non vengono istanziati tramite delle apposite istruzioni. Normalmente esiste una istanza per ogni aspetto che viene creata automaticamente all'inizio del programma; tuttavia è possibile associare la creazione di aspetti a determinati eventi che si verificano durante l'esecuzione del programma. In precedenza, si era fornito un esempio di pointcut definito a partire da un join point di tipo call che specificava anche l'oggetto in esecuzione e l'oggetto cui era diretta la chiamata. Tale esempio era:
pointcut wait(A1 ac, B2 bd) : call(void wait()) && this(ac) && target(bd)
Riga 265 ⟶ 286:
dove this(TiPo) rappresenta l'insieme dei join point in cui l'oggetto attualmente in esecuzione è di tipo TiPo.
È anche possibile associare degli aspetti agli oggetti che ricevono la chiamata:
aspect nomeaspetto pertarget(wait(A1 ac, B2 bd)) { }
Riga 277 ⟶ 298:
creerà un aspetto ogni volta che il metodo stampa verrà chiamato.
Una caratteristica
declare precedence : Prestaz, Logging;
Riga 287 ⟶ 308:
avrà l'effetto opposto.
===''Inter-Type declarations''===
In aspectj un aspetto può alterare la struttura stessa degli oggetti aggiungendo ad essi metodi, attributi e costruttori oltre a quelli che già possiede. Bisogna però fare attenzione a non creare conflitti: i metodi, i costruttori e gli attributi che un aspetto aggiunge ad un oggetto non devono sovrapporsi a metodi, costruttori e attributi che l'oggetto già ha. In parole povere, un aspetto può aggiungere dei metodi, degli attributi o dei costruttori, purché non siano già definiti dall'oggetto stesso.
La sintassi delle inter-type declarations è molto semplice: la dichiarazione
public int Game.time;
aggiunge un attributo di tipo intero e di nome 'time' alla classe Game, mentre
private String Ship.name;
aggiunge un attributo di tipo stringa name alla classe Ship. È importante notare che questo attributo è stato dichiarato come privato dall'aspetto, e quindi
// codice che ritorna il punteggio del gioco...
}
Questa istruzione aggiunge alla classe Game un metodo score() pubblico.
public Game.new(String s ) {
Riga 306 ⟶ 328:
}
per far sì che Game abbia un costruttore che accetta come parametro
== Note ==
<references/>
== Altri progetti ==
{{interprogetto}}
== Collegamenti esterni ==
*
{{portale|informatica}}
[[Categoria:Linguaggi di programmazione]]
|