Corso di Java Spring Framework #7 | Bean loading
Proseguiamo il corso su Spring occupandoci delle modalità di caricamento dei bean, della loro inizializzazione all'interno delle nostre applicazioni. Vedremo quale approccio viene adottato di default e come può essere opportunamente modificato.
Per caricamento (loading) intendiamo la procedura con cui vengono istanziati i bean definiti attraverso le annotazioni @Component
(e le altre @Service
, @Repository
, ecc.) o all'interno di una classe di configurazione mediante le annotazioni @Bean
.
Di fatto questi oggetti saranno disponibili all'interno dell'IoC container.
Tuttavia tale approccio presenta due problematiche principali:
-
il tempo di startup dell'applicazione per espletare tutte le procedure di creazione, configurazione e inizializzazione cresce in funzione del numero di bean
-
inoltre alcuni bean potrebbero essere utilizzati solo raramente o addirittura mai durante l'esecuzione dell'applicazione, occupando inutilmente delle prezione e costose risorse.
Una soluzione consiste nel differire il caricamento di un bean al momento in cui viene effettivamente richiesto.
Si parla di approccio lazy, un termine che si trova spesso in vari contesti come ad esempio in JPA quando si parla di entità e database.
Naturalmente dobbiamo informare Spring delle nostre intenzioni.
Vediamo come fare con un esempio pratico.
Introduciamo una classe di configurazione annotata con @Configuration
all'interno della quale sono presenti altre due classi di cui una sempre necessaria e l'altra no. (Il codice completo è su GitHub, trovate il link nella lista in basso)
@Configuration
public class Config {
@Bean
public AlwaysUsedBean alwaysUsedBean() {
return new AlwaysUsedBean();
}
@Bean
public NotAlwaysUsedBean notAlwaysUsedBean() {
return new NotAlwaysUsedBean();
}
}
In questa modalità entrambi i bean saranno creati e resi disponibili all'avvio dell'applicazione indipendentemente dal loro reale utilizzo.
Nel video associato vediamo in pratica il momento preciso in cui ciascuna classe viene istanziata.
Per fare questo andiamo ad interagire con l'Application Context facendo una piccola modifica alla classe principale annotata con @SpringBootApplication
.
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(BeanLazyLoadingApplication.class, args);
}
In sostanza il metodo SpringApplication.run()
restituisce proprio un'istanza del context che possiamo salvare in un'opportuna variabile per poi richiamare altri metodi specifici.
Visualizzando i log dell'applicazione è possibile ricostruire tutte le fasi dell'inizializzazione dei bean, di entrambi i bean.
Volendo differire l'inizializzazione di notAlwaysUsedBean
dobbiamo aggiungere l'annotazione @Lazy
per cui il codice assumerà questa forma
@Configuration
public class Config {
@Bean
public AlwaysUsedBean alwaysUsedBean() {
return new AlwaysUsedBean();
}
@Bean
@Lazy
public NotAlwaysUsedBean notAlwaysUsedBean() {
return new NotAlwaysUsedBean();
}
}
In questo modo diciamo a Spring di non inizializzare il bean all'avvio dell'applicazione ma solo quando verrà effettivamente richiesto.
Riavviando l'applicazione vedremo che il bean non viene più creato.
Per simulare la richiesta si può agire sul context richiamando il metodo getBean()
a cui passare la relativa classe.
Il tutto sfruttando la modifica vista in precedenza.
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(BeanLazyLoadingApplication.class, args);
ctx.getBean(NotAlwaysUsedBean.class);
}
Naturalmente i momenti di inizializzazione dei due bean saranno molto differenti rispetto all'esempio da cui siamo partiti.
Anche in questo caso i log sono preziosi per un'analisi più dettagliata (come mostrato nel video).
Lo svantaggio di questo approccio è che i tempi di inizializzazione del bean (che potrebbero essere anche molto lunghi in un contesto realistico) si spostano dall'avvio dell'applicazione al momento della richiesta e potrebbero incidere sulla reattività della stessa applicazione.
Quindi andranno valutati di volta in volta i pro e i contro di questa scelta tenendo in considerazione le risorse impiegate e i tempi di risposta necessari.
Inoltre il lazy loading può essere spostato a livello di classe di configurazione e in questo caso ciascun bean sarà inizializzato solo quando effettivamente richiesto.
Il comportamento di default, che prevede l'inizializzazione di tutti i bean all'avvio, può essere modificato impostando di base il lazy loading
attraverso la proprietà spring.main.lazy-initialization=true
da inserire in application.properties (oppure in application.yml nel formato YAML).
In questo caso nessun bean sarà creato all'avvio ad eccezione di quelli a cui è associata l'annotazione @Lazy(false)
.
Nel prossimo articolo cominceremo a parlare di Dependency Injection.
[VIDEO YOUTUBE]
[LINKS]
`