Confronto tra Spring AOP e AspectJ

1. Introduzione

Oggi sono disponibili più biblioteche AOP e queste devono essere in grado di rispondere a una serie di domande:

  • È compatibile con la mia applicazione esistente o nuova?
  • Dove posso implementare l'AOP?
  • Quanto velocemente si integrerà con la mia applicazione?
  • Qual è il sovraccarico delle prestazioni?

In questo articolo, esamineremo come rispondere a queste domande e introdurremo Spring AOP e AspectJ, i due framework AOP più popolari per Java.

2. Concetti AOP

Prima di iniziare, eseguiamo una rapida revisione di alto livello di termini e concetti fondamentali:

  • Aspetto: un codice / funzionalità standard distribuito in più punti dell'applicazione ed è in genere diverso dall'effettiva logica di business (ad esempio, gestione delle transazioni). Ogni aspetto si concentra su una specifica funzionalità trasversale
  • Joinpoint: è un punto particolare durante l'esecuzione di programmi come l'esecuzione di metodi, la chiamata al costruttore o l'assegnazione di campi
  • Consiglio: l'azione intrapresa dall'aspetto in uno specifico punto di unione
  • Pointcut: un'espressione regolare che corrisponde a un punto di unione. Ogni volta che un punto di unione corrisponde a un taglio di punti, viene eseguito un consiglio specifico associato a quel taglio di punti
  • Tessitura: il processo di collegamento di aspetti con oggetti mirati per creare un oggetto consigliato

3. Spring AOP e AspectJ

Ora, discutiamo di Spring AOP e AspectJ su una serie di assi, come capacità, obiettivi, tessitura, struttura interna, punti di unione e semplicità.

3.1. Capacità e obiettivi

In poche parole, Spring AOP e AspectJ hanno obiettivi diversi.

Spring AOP mira a fornire una semplice implementazione AOP in Spring IoC per risolvere i problemi più comuni che i programmatori devono affrontare. Non è inteso come una soluzione AOP completa : può essere applicato solo ai bean gestiti da un contenitore Spring.

D'altra parte, AspectJ è la tecnologia AOP originale che mira a fornire una soluzione AOP completa. È più robusto ma anche molto più complicato dello Spring AOP. Vale anche la pena notare che AspectJ può essere applicato a tutti gli oggetti di dominio.

3.2. Tessitura

Sia AspectJ che Spring AOP utilizzano il diverso tipo di tessitura che influisce sul loro comportamento in termini di prestazioni e facilità d'uso.

AspectJ utilizza tre diversi tipi di tessitura:

  1. Tessitura in fase di compilazione: il compilatore AspectJ prende come input sia il codice sorgente del nostro aspetto che della nostra applicazione e produce un file di classe intrecciato come output
  2. Tessitura post-compilazione : è nota anche come tessitura binaria. Viene utilizzato per intrecciare file di classe e file JAR esistenti con i nostri aspetti
  3. Tessitura a tempo di caricamento : è esattamente come la tessitura binaria precedente, con la differenza che la tessitura viene posticipata finché un caricatore di classi non carica i file di classe nella JVM

Per informazioni più approfondite su AspectJ stesso, vai a questo articolo.

Poiché AspectJ utilizza il tempo di compilazione e la tessitura del tempo di carico di classe, Spring AOP utilizza la tessitura runtime .

Con la tessitura runtime, gli aspetti vengono intrecciati durante l'esecuzione dell'applicazione utilizzando i proxy dell'oggetto mirato, utilizzando il proxy dinamico JDK o il proxy CGLIB (che sono discussi nel punto successivo):

3.3. Struttura interna e applicazione

Spring AOP è un framework AOP basato su proxy. Ciò significa che per implementare aspetti agli oggetti di destinazione, creerà proxy di quell'oggetto. Ciò si ottiene utilizzando uno dei due modi:

  1. Proxy dinamico JDK: il modo preferito per Spring AOP. Ogni volta che l'oggetto di destinazione implementa anche un'interfaccia, verrà utilizzato il proxy dinamico JDK
  2. Proxy CGLIB: se l'oggetto di destinazione non implementa un'interfaccia, è possibile utilizzare il proxy CGLIB

Possiamo saperne di più sui meccanismi di proxy di Spring AOP dai documenti ufficiali.

AspectJ, d'altra parte, non fa nulla in fase di esecuzione poiché le classi vengono compilate direttamente con gli aspetti.

E quindi, a differenza di Spring AOP, non richiede alcun motivo di progettazione. Per intrecciare gli aspetti al codice, introduce il suo compilatore noto come AspectJ compiler (ajc), attraverso il quale compiliamo il nostro programma e poi lo eseguiamo fornendo una piccola libreria runtime (<100K).

3.4. Joinpoints

Nella sezione 3.3, abbiamo mostrato che Spring AOP si basa su modelli proxy. Per questo motivo, è necessario creare una sottoclasse della classe Java di destinazione e applicare di conseguenza le preoccupazioni trasversali.

Ma ha una limitazione. Non possiamo applicare preoccupazioni trasversali (o aspetti) tra classi che sono "finali" perché non possono essere sovrascritte e quindi si tradurrebbe in un'eccezione di runtime.

Lo stesso vale per i metodi statici e finali. Gli aspetti primaverili non possono essere applicati perché non possono essere sovrascritti. Quindi Spring AOP a causa di queste limitazioni, supporta solo i punti di unione di esecuzione del metodo.

Tuttavia, AspectJ intreccia le preoccupazioni trasversali direttamente nel codice effettivo prima del runtime. A differenza di Spring AOP, non richiede la sottoclasse dell'oggetto mirato e quindi supporta anche molti altri joinpoint. Di seguito è riportato il riepilogo dei punti di unione supportati:

Joinpoint Spring AOP supportato AspectJ supportato
Metodo di chiamata No
Esecuzione del metodo
Chiamata del costruttore No
Esecuzione del costruttore No
Esecuzione di inizializzatori statici No
Inizializzazione di oggetti No
Riferimento di campo No
Assegnazione sul campo No
Esecuzione del gestore No
Esecuzione di consigli No

Vale anche la pena notare che in Spring AOP, gli aspetti non vengono applicati al metodo chiamato all'interno della stessa classe.

Questo è ovviamente perché quando chiamiamo un metodo all'interno della stessa classe, non stiamo chiamando il metodo del proxy fornito da Spring AOP. Se abbiamo bisogno di questa funzionalità, allora dobbiamo definire un metodo separato in bean differenti, o usare AspectJ.

3.5. Semplicità

Spring AOP è ovviamente più semplice perché non introduce alcun compilatore o tessitore aggiuntivo tra il nostro processo di compilazione. Utilizza la tessitura runtime e quindi si integra perfettamente con il nostro consueto processo di compilazione. Anche se sembra semplice, funziona solo con i bean gestiti da Spring.

Tuttavia, per usare AspectJ, dobbiamo introdurre il compilatore AspectJ (ajc) e ricompattare tutte le nostre librerie (a meno che non passiamo alla tessitura post-compilazione o al caricamento).

Questo è, ovviamente, più complicato del primo, perché introduce AspectJ Java Tools (che includono un compilatore (ajc), un debugger (ajdb), un generatore di documentazione (ajdoc), un browser della struttura del programma (ajbrowser)) che noi necessità di integrazione con il nostro IDE o lo strumento di compilazione.

3.6. Prestazione

Per quanto riguarda le prestazioni, la tessitura in fase di compilazione è molto più veloce della tessitura in runtime . Spring AOP è un framework basato su proxy, quindi c'è la creazione di proxy al momento dell'avvio dell'applicazione. Inoltre, ci sono alcune altre chiamate di metodi per aspetto, che influiscono negativamente sulle prestazioni.

D'altra parte, AspectJ intreccia gli aspetti nel codice principale prima che l'applicazione venga eseguita e quindi non c'è alcun sovraccarico di runtime aggiuntivo, a differenza di Spring AOP.

Per questi motivi, i benchmark suggeriscono che AspectJ è quasi da 8 a 35 volte più veloce di Spring AOP.

4. Riepilogo

Questa rapida tabella riassume le principali differenze tra Spring AOP e AspectJ:

Primavera AOP AspectJ
Implementato in puro Java Implementato utilizzando estensioni del linguaggio di programmazione Java
Non è necessario un processo di compilazione separato Richiede il compilatore AspectJ (ajc) a meno che LTW non sia impostato
È disponibile solo la tessitura runtime La tessitura runtime non è disponibile. Supporta la tessitura in fase di compilazione, post-compilazione e caricamento
Meno potente: supporta solo la tessitura a livello di metodo Più potente: può tessere campi, metodi, costruttori, inizializzatori statici, classi / metodi finali, ecc ...
Può essere implementato solo su bean gestiti da Spring container Può essere implementato su tutti gli oggetti del dominio
Supporta solo i pointcuts di esecuzione del metodo Supporta tutti i pointcuts
I proxy vengono creati di oggetti mirati e gli aspetti vengono applicati a questi proxy Gli aspetti vengono inseriti direttamente nel codice prima dell'esecuzione dell'applicazione (prima del runtime)
Molto più lento di AspectJ Migliori prestazioni
Facile da imparare e da applicare Comparativamente più complicato di Spring AOP

5. Scegliere la struttura giusta

Se analizziamo tutti gli argomenti fatti in questa sezione, inizieremo a capire che non è affatto che un framework sia migliore di un altro.

In poche parole, la scelta dipende fortemente dalle nostre esigenze:

  • Framework: se l'applicazione non utilizza il framework Spring, non abbiamo altra scelta che abbandonare l'idea di utilizzare Spring AOP perché non può gestire nulla che sia al di fuori della portata del contenitore Spring. Tuttavia, se la nostra applicazione viene creata interamente utilizzando il framework Spring, allora possiamo usare Spring AOP in quanto è semplice da imparare e applicare
  • Flessibilità: dato il supporto limitato di joinpoint, Spring AOP non è una soluzione AOP completa, ma risolve i problemi più comuni che i programmatori devono affrontare. Sebbene se vogliamo scavare più a fondo e sfruttare l'AOP al massimo delle sue capacità e desideriamo il supporto da un'ampia gamma di joinpoint disponibili, AspectJ è la scelta
  • Prestazioni: se utilizziamo aspetti limitati, ci sono differenze di prestazioni banali. Ma a volte ci sono casi in cui un'applicazione ha più di decine di migliaia di aspetti. Non vorremmo utilizzare la tessitura runtime in questi casi, quindi sarebbe meglio optare per AspectJ. AspectJ è noto per essere da 8 a 35 volte più veloce di Spring AOP
  • Il meglio di entrambi: entrambi questi framework sono completamente compatibili tra loro. Possiamo sempre trarre vantaggio da Spring AOP quando possibile e continuare a utilizzare AspectJ per ottenere supporto per i punti di unione che non sono supportati dal primo

6. Conclusione

In questo articolo, abbiamo analizzato sia Spring AOP che AspectJ, in diverse aree chiave.

Abbiamo confrontato i due approcci all'AOP sia sulla flessibilità che sulla facilità con cui si adatteranno alla nostra applicazione.