Java annotations


Questo progetto consiste nello studio delle annotazioni in Java (disponibili nella tecnologia J2SE 5.0): viene fornita una relazione su cosa sono le annotazioni e come si usano, viene spiegato come intercettarle con gli strumenti forniti da Java, come trattarle utilizzando l'ATP (Annotation Processing Tool) da riga di comando e, infine, viene spiegato come realizzare un ATP come plugin per Eclipse.

  1. Introduzione
  2. I metadati - (Annotazioni)
  3. Come dichiarare una nuova annotazione
  4. Come intercettare l'annotazione con gli strumenti forniti da java
  5. Accenno all'APT (Annotation Processing Tool) da riga di comando
  6. Annotation, un caso concreto: realizzazione di un APT come plugin per Eclipse

    1.  Introduzione

Con oltre un centinaio di nuove funzionalità introdotte, la tecnologia J2SE 5.0 consente agli sviluppatori di essere più produttivi, permette agli amministratori di sistema di monitorare e gestire le applicazioni più efficacemente ed assicura agli utenti finali di PC desktop un maggior numero di funzionalità d'utilizzo.
Gli aggiornamenti al linguaggio introdotti nella nuova versione consentono agli sviluppatori di essere più efficienti e produttivi grazie alla drastica riduzione della stesura di codice ripetitivo.  Tra le nuove funzionalità figurano tipi di dato generic, enumerated, metadata e autoboxing.  I miglioramenti prestazionali che contraddistinguono la nuova versione riguardano l'abbreviazione del tempo di startup, un minor consumo di memoria e l'ottimizzazione automatica delle prestazioni a seconda del tipo di computer.


    2.  I metadati (Annotazioni)

L'annotazione dei metadati è stata standardizzata secondo JSR 175 e fa parte di J2SE 5.0.
La piattaforma Java ha sempre avuto vari meccanismi di annotazioni creati "ad hoc".  Per esempio il transient modifier è una annotazione "ad hoc" ed indica che il campo "annotato" dovrebbe essere ignorato durante il processo di "serialization subsystem".  Altro esempio è il tag @deprecated: indica che il metodo annotato non dovrebbe più essere usato. Così dalla release 5.0, la piattaforma Java implementa le Annotazioni (conosciute come metadata) general purpose permettendo al programmatore di crearne di nuove senza troppa fatica. Le agevolazioni introdotte consistono in una sintassi per dichiarare i tipi di annotazioni, una sintassi per usarle, delle API per leggerle, una classe che rappresenta l'annotazione stessa e un annotation processing tool.
Molte API Java richiedono la realizzazione di una grossa quantità di codice ad hoc che, nonostante segua un preciso standard descritto da rigorose specifiche, deve per forza essere adattato ad ogni particolare caso anche se con minime varianti.  Le classi d'appoggio create per questi scopi potrebbero essere ricavate automaticamente a partire dalla classe principale se solo questa contenesse le informazioni necessarie.  Le annotazioni possono permettere di risolvere anche questo problema senza influire sulla semantica del programma modificata solamente a run time.
Così è ora possibile espandere la gamma dei modificatori Java con delle proprie “annotazioni”.  Non sono particolarmente difficili da usare, ma per loro natura sono riservate a pochi casi concreti d'utilizzo.


    3.  Come dichiarare una nuova annotazione

Dichiarare un nuovo tipo di annotazione è come implementare una normale dichiarazione di interfaccia:

Ecco un esempio di annotazione:

 package annotation;
	  public @interface Persona {
	   String nome();
	   String cognome();
	   int età();
 	   String indirizzo() default "unassigned";
	  } 

La dichiarazione deve essere salvata in un file con lo stesso nome ed estensione .java.
Verrà poi compilata in un comune file .class (eventualmente in un particolare package, usando la direttiva package come si farebbe in una qualsiasi classe).
Una volta definita la potremo inserire prima di un metodo nel nostro codice riferendoci ad essa con il simbolo @ seguito dal nome ed eventuali parametri.

Ecco un esempio di utilizzo:

@Persona(nome="Mario", cognome="Rossi", età=35)

I due tipi principali di meta-annotazioni sono:

  1. @Retention: indica a quale istante di vita del codice l'annotazione deve essere applicata.
    I valori possibili sono:
    - RetentionPolicy.SOURCE // no JVM e compiler
    - RetentionPolicy.CLASS // solo compiler
    - RetentionPolicy.RUNTIME // solo JVM

  2. @Target: indica a quale porzione di codice una annotazione si riferisce.
    I valori possibili sono:
    - ElementType.TYPE // class, interface, enum
    - ElementType.FIELD
    - ElementType.METHOD
    - ElementType.PARAMETER
    - ElementType.CONSTRUCTOR
    - ElementType.LOCAL_VARIABLE
    - ElementType.ANNOTATION_TYPE
    - ElementType.PACKAGE

e possono essere inserite nella definizione dell'annotazione stessa:

package annotation;
  @Retention(RetentionPolicy.RUNTIME)
  @Target(ElementType.METHOD)
  public @interface Persona { 
    String nome(); 
    String cognome(); 
    int età();
    String indirizzo() default "unassigned"; 
  } 

    4.  Come intercettare l'annotazione con gli strumenti forniti da java

Per controllare se in un metodo è presente o meno un'annotazione si utilizza la funzione “isAnnotationPresent” (presente nella libreria “java.lang.reflect.AccessibleObject”), la cui sintassi è:

boolean isAnnotationPresent (annotationClass.class)

Per intercettare l'annotazione posso utilizzare le seguenti funzioni (tutte presenti nella libreria “java.lang.reflect.Method”):

public annotationClass getAnnotation (annotationClass.class)

Questa fuzione ritorna il contenuto dell'annotazione intercettata.

Annotation[] getAnnotations ()

questa funzione ritorna tutte le annotazioni presenti nel relativo metodo annotato.

Nel seguente esempio si analizzano tutti i metodi presenti in una classe (classES.class) e, per ognuno, controllo se sono presenti 2 annotazioni (Pre.class e Post.class), nel caso in cui fossero presenti una o tutte e due le annotazioni ne stampo il contenuto.

for (Method met : Class.forName (classES.class).getMethods ()) {
  if (met.isAnnotationPresent (Pre.class)) {
    met.invoke (null);
    Pre ann_pre = met.getAnnotation (Pre.class);
    System.out.println (ann_pre.value ());
  }
  if (met.isAnnotationPresent (Post.class)) {
    met.invoke (null);
    Post ann_post = met.getAnnotation (Post.class);
    System.out.println (ann_post.value ());
  } 
}

Per chiarire questo concetto, si possono analizzare i file sorgenti presenti in:

JavaAnnotation.zip;

in essi viene usata una classe principale (test), che ha il compito di lanciare, per ogni sotto-classe, TestingTool.java, necessario per scandire il file alla ricerca delle annotazioni specificate.


    5.  Accenno all'APT (Annotation Processing Tool) da riga di comando

Il grande vantaggio che si riscontra nell'utilizzo di questo “tool”, è che invece di gestire manualmente la i file e le classi base con i loro derivati, si lascia questo compito all'“apt” che in automatico assolve il compito di processare file e classi, alla ricerca di annotazioni indicanti la modalità di manutenzione dei nuovi pezzi di codice da “auto-generare”.

Per sviluppare o comunque conoscere meglio l'“apt” occorrono quattro package principali:

Ecco qui di seguito riportati le istruzioni più importanti da riga di comando che permettono di usare al meglio l'”apt”. Prima di tutto và specificato che valgono tutte le opzioni e i comandi validi per “javac”, ed in più valgono i seguenti comandi specifici:

-s dir : specifica il percorso della directory in cui andranno salvati i file generati dal processore delle annotazioni;

-nocompile: non compila i file sorgente in .CLASS;

-print: si limita a stampare una rappresentazione testuale dei tipi specificati;

-A [ key [ = val ]]: opzioni varie da passare alle annotazioni. Non sono interpretate direttamente da “apt”, ma singolarmente da ogni processore specifico;

-factorypath path: dà le informazioni su dove trovare le specifiche per la creazione di un processore di annotazioni;

-factory classname: è il nome dell' AnnotationProcessorFactory da usare di default.

E ora andiamo più nello specifico, spiegando come è stata realizzata e come usare, la nostra libreria, per essere eseguita da riga di comando.

Prima di tutto c'è da dire che lo scheletro principale è lo stesso che è stato dato per il plugin di Eclipse, solo che, naturalmente, è stato dovuto aggiustare per permetterne l'utilizzo da riga di comando, senza la veste grafica tipica dell'edito Eclipse.

I cinque componenti principali di questa parte di progetto sono: ConditionStatement.java, che riveste lo stesso ruolo della parte di progetto per Eclipse; MyAnnotation.java, che include solamente la definizione per l'interfaccia dell'annotazione; MyAnnotationProcessorFactory.java, che definisce le specifiche per poter processare correttamente l'annotazione MyAnnotation; MyAnnotationProcessor.java, che è il fulcro centrale per lo svolgimento “ad hoc” delle operazione lanciate dall'“apt”; ed infine il pacchetto com, necessari per la corretta compilazione.

Andiamo ora a descrivere per passi, cosa è stato dovuto fare per creare la libreria “myAnnotation.jar”, e come fare per usarla da riga di comando.

Inanzitutto è stato necessario compilare il file ConditionStatement.java, perchè viene utilizzato nel passo successivo per compilare gli altri file sorgente:

apt (o javac) ConditionStatement.java

successivamente, nella stessa directory dove vi è il file già compilato, ConditionStatement.class, vanno compilati i restanti file necessari per la lbreria, ovvero:

apt MyAnnotation.java MyAnnotationProcessor.java MyAnnotationProcessorFactory.java

a questo punto sono stati creati tutti i file .class, che saranno necessari per creare la nostra libreria. Quindi si crea, col comando jar cvf, il file myAnnotation.jar, contenente tutti i .class, nonché i pacchetti com e sun e una cartella MET-INF, con il file MANIFEST e la cartella services oppurtunamente personalizzati e modificati, al fine di evitare all'utente di digitare per intero tutte le opzioni di compilazione da utilizzare.

A questo punto resta solo da svolgere il lavoro di testing.

Il file .jar, và messo in una directory chiamata lib, all'interno della directory principale contenente i sorgenti da processare con l'”apt”.

Quindi il comando unico da dare sarà:

apt -cp ./lib/myAnnotation.jar FileClassName.java

Il programma darà due messaggi: il primo che avvertirà quando l'”apt” stà generando i file .java e .class, creati specificatamente in base ai metodi e alle annotazioni presenti nel file FileClassName.java; il secondo che avvertirà del corretto funzionamento del programma e indicherà il path dove trovare i file auto-generati.

Per il resto il funzionamento e la logica del programma è la stessa che è stata utilizzata nella realizzazione del plugin per Eclipse, ovvero le operazioni e i processi svolti sui file sono gli stessi, l'unica cosa che è cambiata è il modo di lanciare l'esecuzione di questi processi, che in questo caso sono innescati in automatico da riga di comando con l'”apt”.

Per scaricare quest'esempio, semplice, ma esauriente, cliccare sul link qui sotto:

MyAnnotation.zip ;

esso svolge le operazioni descritte sopra.


    6.  Annotation, un caso concreto: realizzazione di un APT come plugin per Eclipse