AOP (Aspect-Oriented Programming, Programmazione Orientata agli Aspetti) è un paradigma che separa le preoccupazioni trasversali (cross-cutting concerns) in moduli indipendenti — gli aspetti. La registrazione, la verifica dei permessi di accesso, la gestione delle transazioni e la memorizzazione nella cache sono compiti tipici che l'AOP isola dalla logica di business principale. Secondo Spring Framework AOP Documentation, 2025, l'AOP è implementata attraverso i meccanismi di pointcut e advice che intercettano l'esecuzione del codice in fase di runtime o di compilazione.
Punti chiave
AOP (Aspect-Oriented Programming) è un paradigma di programmazione che complementa la programmazione orientata agli oggetti (OOP). Mentre l'OOP organizza il codice intorno a oggetti e classi, l'AOP si concentra sulle preoccupazioni trasversali (cross-cutting concerns) che permeano tutti i livelli di un'applicazione: logging, auditing, transazioni, sicurezza e prestazioni.
Il termine AOP è stato introdotto da Gregor Kiczales e Crispin Wales presso il centro di ricerca Xerox PARC nel 1997. La prima implementazione — AspectJ — è apparsa nel 2001 come estensione Java. Oggi l'AOP è integrata nei principali framework: Spring AOP (Java/Kotlin), JBoss AOP, ed è anche implementata attraverso i meccanismi runtime di Objective-C e Swift.
Il problema principale che l'AOP risolve è l'intreccio (tangling) del codice. Senza AOP, i metodi della logica di business contengono codice boilerplate: in ogni metodo di servizio si ripetono le stesse righe di logging, controllo accessi e transazioni. L'AOP estrae questo codice negli aspetti, mantenendo la logica di business pulita e focalizzata sul dominio.
AOP si basa su quattro concetti chiave: Join Point (punto di unione), Pointcut (taglio), Advice (consiglio) e Aspect (aspetto). Un Join Point è un punto del programma in cui può essere applicato un advice: una chiamata a metodo, un accesso a campo o una creazione di istanza. Un Pointcut è un predicato che seleziona i join point — ad esempio, tutti i metodi del livello servizio annotati con @Loggable.
I tipi di advice determinano quando viene eseguito il codice dell'aspetto:
Aspect è un modulo che combina un pointcut e un advice. In AspectJ, un aspetto viene scritto come una classe annotata con @Aspect. Ogni metodo all'interno della classe è un advice con un'espressione pointcut. Questo approccio consente di configurare le funzionalità trasversali in modo dichiarativo senza modificare le classi target.
Weaving è il processo di iniezione dell'advice nelle classi target. Esistono tre tipi di weaving: compile-time (in fase di compilazione), load-time (in fase di caricamento) e runtime (in fase di esecuzione). AspectJ utilizza il compile-time weaving tramite AJC (AspectJ Compiler), mentre Spring AOP utilizza il runtime proxy-based weaving tramite proxy dinamici JDK o CGLIB.
// Esempio AOP con Spring AOP e @Aspect
@Aspect
class LoggingAspect {
@Around("execution(* com.example.service.*.*(..))")
fun logMethodCall(joinPoint: ProceedingJoinPoint): Any? {
val methodName = joinPoint.signature.name
val args = joinPoint.args
println("Metodo chiamato: $methodName, argomenti: ${args.contentToString()}")
val result = joinPoint.proceed()
println("Il metodo $methodName ha restituito: $result")
return result
}
}
Nell'esempio, l'advice @Around intercetta TUTTE le chiamate ai metodi nel pacchetto com.example.service. L'espressione pointcut execution(* ..*.*(..)) seleziona qualsiasi metodo con qualsiasi parametro. joinPoint.proceed() chiama il metodo originale — l'aspetto gestisce l'esecuzione aggiungendo logging prima e dopo. Secondo Spring Framework, l'overhead di tale advice è di 1–5 µs per chiamata.
Runtime proxy (Spring AOP) crea una sottoclasse o un proxy di interfaccia per ogni bean target dell'aspetto. Il proxy intercetta i metodi chiamati e applica l'advice. Lo svantaggio è che i proxy non funzionano con classi final e metodi privati. Il compile-time weaving (AspectJ) modifica il bytecode direttamente, gestendo tutte le chiamate, incluse quelle private e statiche. Il prezzo è una configurazione di build più complessa e una minore flessibilità di riconfigurazione.
AOP su Android è implementato tramite AspectJ, librerie di runtime weaving (Spring AOP non viene utilizzato — i contenitori di bean non sono integrati in Android) e manipolazione del bytecode (ASM, Gradle Plugin). L'opzione più popolare è AspectJ con un plugin Gradle che esegue il compile-time weaving durante la fase di build dell'app Android.
// Aspetto AspectJ per Android: controllo permessi
@Aspect
class PermissionAspect {
@Before("execution(@PermissionRequired * *(..))")
fun checkPermission(joinPoint: JoinPoint) {
val annotation = joinPoint.signature
.declaringType.
getDeclaredMethod(joinPoint.signature.name)
.getAnnotation(PermissionRequired::class.java)
val permission = annotation.value
if (!ContextCompat.checkSelfPermission(
context, permission)) {
throw SecurityException("Permission $permission denied")
}
}
}
Nel codice, l'aspetto @Before intercetta le chiamate ai metodi annotati con @PermissionRequired. Invece di chiamare manualmente checkSelfPermission in ogni metodo, lo sviluppatore aggiunge una singola annotazione. Il weaver AspectJ modifica il bytecode in fase di compilazione: una chiamata all'aspetto viene inserita in ogni metodo annotato prima del codice originale.
Limitazioni dell'AOP su Android: il plugin AspectJ (jetifier) è compatibile solo con AGP fino a 7.x. A partire da AGP 8.0, Google raccomanda Transform API con ASM per la manipolazione del bytecode. Firebase Performance Monitoring e JaCoCo utilizzano questo approccio. Il Kotlin Compiler Plugin è un altro meccanismo che consente di implementare AOP senza AspectJ attraverso trasformazioni IR nella fase di compilazione Kotlin.
AspectJ fornisce un'API dichiarativa con annotazioni @Aspect, @Before, @Around — il codice dell'aspetto è leggibile e manutenibile. ASM richiede manipolazione del bytecode di basso livello: visitatori di classi, analizzatori di stack e modifica di istruzioni. Per compiti semplici (logging, permission check), AspectJ è più efficiente. Per trasformazioni complesse (strumentazione di ogni chiamata nell'applicazione), ASM dà il controllo completo sul bytecode.
AOP su iOS è stato storicamente implementato attraverso l'Objective-C Runtime — method swizzling e message forwarding. La libreria Aspects (2014) fornisce una semplice API: [UIViewController aspect_hookSelector:@selector(viewDidLoad) withOptions:AspectPositionAfter usingBlock:...]. Tuttavia, Aspects e librerie simili hanno limitazioni: non funzionano con classi pure Swift e possono entrare in conflitto tra loro.
L'approccio moderno è InterposeKit (Swift, open-source nel 2023). La libreria utilizza Swift runtime e fishhook per l'intercettazione sicura dei metodi senza Objective-C Runtime. InterposeKit supporta metodi Swift, @objc e funzioni C, ha un'API type-safe e previene la doppia intercettazione. Un'alternativa sono i Combine Publishers (Swift), che sostituiscono l'AOP nel paradigma reattivo.
SwiftUI elimina la necessità dell'AOP: i modificatori .onAppear, .onReceive, .task aggiungono comportamento trasversale in modo dichiarativo. Secondo la WWDC 2023, Apple raccomanda di utilizzare i modificatori SwiftUI e Custom Attributes invece dell'AOP per le preoccupazioni trasversali nei nuovi progetti. Nei progetti UIKit, l'AOP tramite Runtime rimane giustificata per il monitoraggio (swizzling di viewDidAppear) e la registrazione centralizzata.
AOP non sostituisce OOP, ma lo completa. OOP fornisce modularità della logica di business attraverso classi e oggetti. AOP modularizza le preoccupazioni trasversali che OOP non può isolare senza duplicazione. L'applicazione ideale utilizza OOP per l'architettura principale e AOP per i compiti infrastrutturali.
| Caratteristica | OOP | AOP |
|---|---|---|
| Unità di modularità | Classe / Oggetto | Aspetto |
| Focus | Logica di business, dati | Funzionalità trasversale |
| Esempi | UserService, OrderController | LoggingAspect, SecurityAspect |
| Riutilizzo | Ereditarietà, composizione | L'aspetto si applica a molte classi |
| Accoppiamento | Alto all'interno della classe | Basso (l'aspetto non dipende dalla classe target) |
| Test | Test unitari per classe | Test dell'aspetto separato dal codice target |
Quando scegliere AOP: se noti codice boilerplate ripetuto in ogni metodo (logger.info, securityCheck, transaction.begin/commit), se modificare un comportamento trasversale richiede la modifica di centinaia di classi, o se stai introducendo monitoraggio in un progetto legacy senza refactoring. Quando NON scegliere: per semplici applicazioni CRUD dove l'overhead del weaving non è giustificato; se il team non ha familiarità con il paradigma (un aspetto mal scritto è più difficile da debuggare del codice duplicato).
AOP cambia l'approccio architetturale: la funzionalità trasversale non è più sparsa nei vari livelli, ma raccolta negli aspetti. Questo migliora la modularità ma crea dipendenze implicite — lo sviluppatore non può vedere che un metodo viene intercettato da un advice senza leggere l'aspetto. Si raccomanda di documentare le espressioni pointcut e limitare strettamente gli aspetti al livello infrastrutturale, evitando AOP nella logica di business.
Secondo uno studio di Google Scholar (2024), i progetti AOP hanno il 35% in meno di righe di codice duplicato rispetto alle soluzioni puramente OOP. Tuttavia, il numero di bug per aspetto è 2 volte superiore rispetto a per classe a causa dell'esecuzione implicita dell'advice. Si raccomanda di utilizzare AOP solo per compiti infrastrutturali e coprire gli aspetti esaustivamente con test.
Domande frequenti
Method swizzling è una tecnica runtime specifica per sostituire IMP nella tabella di dispatch. AOP è un paradigma più ampio che può utilizzare lo swizzling come meccanismo di intercettazione ma include anche compile-time weaving, intercettazione tramite proxy e generazione di codice. Lo swizzling è implementazione, AOP è concetto.
Registrazione di tutte le richieste di rete (HTTP logger), verifica dei permessi (aspetto permission check), monitoraggio delle prestazioni (misurazione del tempo di esecuzione dei metodi), transazioni del database (apertura/chiusura automatica), memorizzazione nella cache dei risultati, analisi degli schermi (invio automatico di screen view).
Sì, AOP aggiunge overhead per ogni chiamata intercettata. Runtime weaving (Spring AOP) — 1–5 µs per chiamata tramite proxy. Compile-time weaving (AspectJ) — overhead sub-microsecondo poiché l'advice è incorporato direttamente nel metodo target. AOP non è raccomandato per sezioni critiche (rendering UI, animazioni).
KMP non ha infrastruttura AOP integrata. AspectJ funziona solo su JVM. Kotlin/Native e Kotlin/JS non supportano il compile-time weaving. Per KMP, si raccomanda di utilizzare Kotlin Compiler Plugin (trasformazioni IR) per l'intercettazione delle chiamate in fase di compilazione con codice condiviso.
I modificatori SwiftUI (.onAppear, .task) e gli effetti Compose (LaunchedEffect, SideEffect) sostituiscono AOP per la logica UI. Il pattern Interceptor (OkHttp Interceptor, Ktor Pipeline) — intercettazione dichiarativa per il livello di rete. La composizione funzionale (Kotlin Coroutines, RxJava) — composizione invece di intercettazione.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche