Introduzione
Nell’articolo Spring JavaConfig (parte 1) abbiamo introdotto i concetti base della componente javaConfig di Spring e, per quanto possibile, li abbiamo messi in relazione gli analoghi aspetti presenti nella configurazione via XML. In questo articolo affrontiamo aspetti più avanzati.
Modularizzazione
Una best practice abbastanza comune è quella di suddividere la configurazione in diversi classi, soprattutto in progetti complessi dove è auspicabile avere bean di configurazione specifici per componenti o layer diversi. Ad esempio possiamo avere un bean di configurazione per lo strato di accesso ai dati, in cui è definito il datasource e la gestione delle transazioni, un bean per lo strato dei service, e così via.
La suddivisione in più bean di configurazione si realizza semplicemente annotando tutti i bean con @Configuration
ed elencandoli in fase di bootstrap del contesto di Spring. Ad esempio dati due classi di configurazione, AppConfigA
e AppConfigB
, definite nel modo seguente:
1 2 3 4 5 6 7 |
@Configuration public class AppConfigA { @Bean public ServiceBeanA serviceBeanA() { ... } } |
1 2 3 4 5 6 7 |
@Configuration public class AppConfigB { @Bean public ServiceBeanB serviceBeanB() { ... } } |
1 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( AppConfigA.class, AppConfigB.class ); |
In alternativa è possibile utilizzare l’annotazione @Inport su una classe di configurazione principale ed instaziare il contesto utilizzando solamente tale classe:
1 2 3 4 |
@Configuration @Import( {AppConfigA.class, AppConfigB.class} ) public class BaseConfig { } |
1 |
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( BaseConfig.class ); |
Una ulteriore possibilità è l’utilizzo dell’annotazione @ConponentScan con qui indicare al framework di ricercare tutte le classi di configurazione nell’elenco dei package indicato.
1 2 3 4 |
@Configuration @ComponentScan( basePackages = {"it.javaboss.config"} ) public class BaseConfig { } |
Se omessa la proprietà basePackages il framework considererà il package corrente come quello da ispezionare.
Bean Referencing
I bean registrati un una classe di configurazione diverse possono essere importati un un’altra classe semplicemente utilizzando l’autowiring. Ad esempio dato i due bean ServiceBeanA
e ServiceBeanB
così definiti:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ServiceBeanA { .... } public class ServiceBeanB { private ServiceBeanA serviceBeanA; public ServiceBeanB( ServiceBeanA serviceBeanA ) { this.serviceBeanA = serviceBeanA; } public ServiceBeanA getServiceBeanA() { return serviceBeanA; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@Configuration public class AppConfigA { @Bean public ServiceBeanA serviceBeanA() { return new ServiceBeanA(); } } @Configuration public class AppConfigB { @Autowired ServiceBeanA serviceBeanA; @Bean public ServiceBeanB serviceBeanB() { return new ServiceBeanB(serviceBeanA); } } |
Poiché gli stessi bean di configurazione sono essi stessi dei bean gestiti nel contesto di Spring anche essi possono essere iniettati. La classe AppConfigB
può quindi essere riscritta nel seguente modo:
1 2 3 4 5 6 7 8 9 10 |
@Configuration public class AppConfigB { @Autowired AppConfigA appConfigA; @Bean public ServiceBeanB serviceBeanB() { return new ServiceBeanB(appConfigA.serviceBeanA()); } } |
Tale approccio, denominato fully-qualified bean reference, ha il significativo vantaggio di ridurre la complessità del codice e, soprattutto, di permettere una più agevole navigazione dello stesso.
Esternalizzazione dei Valori
Nella configurazione di Spring con file XML la classe PropertyOverrideConfigurer
e il più recente tag <context:property-placeholder/>
consentono di leggere proprietà definite in propetries
file. L’analogo in JavaConfig si può ottenere con l’annotazione @PropertySource
che consente di aggiungere proprietà custom alle variabili di ambiente di Spring ed accessibili per mezzo dell’interfaccia Environment
.
Supponiamo ad esempio che esista un file a.properties
(nella cartella resources) che contiene parametri di configurazione utilizzati dalla classe AppConfigA. L’accesso a tali parametri avviene nel seguente modo:
1 2 3 4 5 6 7 8 9 10 11 |
@Configuration @PropertySource("classpath:a.properties") public class AppConfigA { @Autowired Environment env; @Bean public ServiceBeanA serviceBeanA() { return new ServiceBeanA( env.getProperty( "service.b.param" ) ); } } |
Nel caso di più file di configurazione è possibile utilizzare l’annotazione @PropertySources
nel modo seguente:
1 |
@PropertySources({@PropertySource("classpath:a.properties"),@PropertySource("classpath:b.properties")}) |
Una alternativa all’interfaccia Environment è l’utilizzo dell’annotazione @Value che, attraverso placeholder, consente di recuperare direttamente il valore di uno specifico parametro:
1 2 3 4 5 6 7 8 9 10 11 |
@Configuration @PropertySource("classpath:a.properties") public class AppConfigA { @Value("${service.b.param}") String param; @Bean public ServiceBeanA serviceBeanA() { return new ServiceBeanA( param ); } } |
Component Scan
Le annotazione @ComponentScan
e @ComponentScans
sono l’equivalente del tag XML <context:component-scan/>
e consentono di indicare i package in cui ricercare tutti i bean annotati con @Component
o altre annotazioni derivare tipo @Repository
, @Service
, etc.
Nel caso in cui non è indicato alcun package Spring eseguirà lo scanning dal package corrente. Si noti che anche @Configuration
è meta-annotata con @Component
, quindi se, ad esempio, inseriamo l’annotazione nella classe BaseConfig
degli esempi precedenti, anche tutte le altre classi di configurazione saranno analizzate, indipendentemente dalla presenza del tag @Import
.
1 2 3 |
@Configuration @ComponentScan public class BaseConfig {} |
Codice Sorgente
Il codice sorgente completo di tutti gli esempi presentati è scaricabile qui javaconfig parte 2.