I. Introduction▲
Dans ce billet, nous verrons comment configurer en Java le contexte Spring d'une application basée sur Spring MVC, Spring Security, Spring Data JPA et Hibernate, et cela sans utiliser la moindre ligne de XML. Personnellement, je n'ai rien contre la syntaxe XML à laquelle j'étais habituée. D'autant plus que la verbosité de la configuration avait considérablement diminué grâce à l'introduction des namespaces XML et des annotations. Avant d'utiliser la syntaxe Java sur une application d'entreprise, j'étais même sceptique quant aux gains qu'elle pouvait apporter. Aujourd'hui, je comprends mieux son intérêt et pourquoi les projets du portfolio Spring tels Spring Integration 4.0, Spring Web Service 2.2 ou bien Spring Security 3.2 proposent dans leur dernière version un niveau de configuration Java iso-fonctionnel avec leur équivalent XML. Sans compter que le support de la configuration Java leur ouvre la porte d'une intégration plus poussée à Spring Boot, le nouveau fer de lance de Pivotal.
II. Préambule▲
Avant de faire partie intégrante du framework Spring, la configuration Java était proposée aux développeurs Spring dans un projet externe : Spring JavaConfig. Depuis la version 3.0 du framework Spring, des fonctionnalités équivalentes ont été introduites au fil des versions. La version 4.0 voit l'aboutissement de ce travail.
Tous les extraits de code suivants sont issus d'une application web disponible sur Github : spring-javaconfig-sample.
Les dépendances maven requises sont déclarées dans le pom.xml. Les toutes dernières versions des frameworks ont été exploitées :
- Spring Framework 4.0 ;
- Spring Data JPA 1.6 ;
- Spring Security 3.2 ;
- Hibernate 4.3.
Afin que cette application puisse être déployée dans un conteneur de Servlet 2.5, l'initialisation de la configuration Spring repose intégralement sur les déclarations réalisées en XML dans le web.xml. À cet effet, lors de l'exécution de la commande mvn clean install, un serveur Jetty 6 compatible servlet 2.5 démarre puis arrête l'application.
Dans un conteneur de Servlet 3.x, la déclaration du « DispatcherServlet » pourrait être réalisée en Java via la classe AbstractAnnotationConfigDispatcherServletInitializer introduite dans Spring 3.2.
Ce billet n'a pas pour vocation de se substituer au manuel de référence du framework Spring. Il ne vous montrera pas non plus l'équivalent XML de la configuration Java. Son objectif est de vous donner un exemple de configuration afin que vous puissiez monter rapidement une application utilisant la syntaxe Java.
III. Contextes applicatifs Spring▲
L'application web donnée en exemple reprend l'architecture type d'une application Spring MVC dans laquelle un WebApplicationContext parent (ou root) est chargé avec le listener ContextLoaderListener et un WebApplicationContext enfant est chargé via la DispatcherServlet.
Le contexte parent met à disposition les beans Spring de la couche service métier et de la couche de persistance (infrastructure compris). La sécurité est également définie au niveau du contexte parent, car les services métiers peuvent être sécurisés (@Secured). Chacune de ces couches étant configurée dans une classe qui lui est dédiée, la classe MainConfig a pour rôle de toutes les référencer :
@Configuration
@Import
(
value =
{
DataSourceConfig.class
,
InfrastructureConfig.class
,
RepositoryConfig.class
,
ServiceConfig.class
,
SecurityConfig.class
}
)
public
class
MainConfig {
L'annotation @Configuration joue un rôle central. Les classes où elle est apposée se substituent en effet aux traditionnels fichiers de configuration XML. Nous y retrouvons la déclaration de beans Spring, l'import de fichiers ou de classes de configuration, la détection automatique de beans annotés par analyse de classpath ou bien encore l'activation de fonctionnalités avancées et des annotations associées (@Transactional, @Cacheable, @Scheduled, @Async …).
Remarque : la déclaration de beans Spring peut se faire en dehors d'une classe de @Configuration. C'est ce qu'on appelle le mode lite Beans. Non recommandé, le manuel de référence de Spring explique quels en sont les limitations et les dangers.
La classe MainConfig est également annotée avec @Import permettant d'importer d'autres classes de configuration. Via l'annotation @ImportResource, Spring offre la possibilité d'importer des fichiers de configuration XML. Très pratique lorsqu'on dépend de librairies tierces n'offrant qu'une configuration XML.
Pour Spring, les classes annotées avec @Configuration sont des beans Spring :
08:02:13.246 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating shared instance of singleton bean 'mainConfig'
08:02:13.246 [main] DEBUG o.s.b.f.s.DefaultListableBeanFactory - Creating instance of bean 'mainConfig'
Cette spécificité infère aux beans de configuration la possibilité d'utiliser l'injection de dépendance via les annotations @Autowired et @Inject. Leur cycle de vie permet également d'utiliser les annotations @PostConstruct et @PreDestroy. La classe MainConfig exploite cette possibilité pour injecter l'Environment modélisant l'environnement d'exécution de l'application. Cette interface permet notamment d'accéder aux profils Spring activés. Lors de l'initialisation de la configuration spécifiée par MainConfig, la méthode initApp() annotée avec @PostConstruct génère une trace listant les profils actifs.
private
static
final
Logger LOG =
LoggerFactory.getLogger
(
MainConfig.class
);
@Autowired
private
Environment env;
/**
* Application custom initialization code.
*
<
p/
>
* Spring profiles can be configured with a system property
* -Dspring.profiles.active=javaee
*
<
p/
>
*/
@PostConstruct
public
void
initApp
(
) {
LOG.debug
(
"Looking for Spring profiles..."
);
if
(
env.getActiveProfiles
(
).length ==
0
) {
LOG.info
(
"No Spring profile configured, running with default configuration."
);
}
else
{
for
(
String profile : env.getActiveProfiles
(
)) {
LOG.info
(
"Detected Spring profile: {}"
, profile);
}
}
}
Le contexte enfant est quant à lui défini au travers d'une seule classe WebMvcConfig que nous détaillerons par la suite. Cette hiérarchie de contexte permet aux beans de type @Service déclarés dans le contexte parent d'être visibles par les beans de type @Controller, l'inverse n'étant pas vrai.
IV. Tester la configuration Spring▲
De par le fait qu'elle est vérifiée à la compilation, la configuration codée en Java permet d'éviter de facto les erreurs que l'on retrouvait couramment en XML : fautes de frappe, déplacement de classes, JAR non présent dans le classpath…
Ce premier garde-fou n'empêche pas d'autres erreurs. Par exemple, l'annotation @EnableCaching génère une exception lorsqu'aucun bean de type CacheManager n'a été déclaré :
Caused by: java.lang.IllegalStateException: No bean of type CacheManager could be found. Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.
Pour se prémunir de ce genre d'exceptions découvertes au démarrage de votre application, le module Spring Test propose tout un jeu d'annotations : @WebAppConfiguration, @ContextConfiguration, @ActiveProfiles ou bien encore @ContextHierarchy. Introduite avec la version 3.2.2 de Spring, cette dernière permet de reproduire une hiérarchie de contextes Spring et de tester ainsi le chargement de contextes en conditions réelles.
La classe SpringConfigTest donne un exemple de test d'intégration de configuration Spring :
@RunWith
(
SpringJUnit4ClassRunner.class
)
@WebAppConfiguration
@ContextHierarchy
({
@ContextConfiguration
(
classes =
MainConfig.class
),
@ContextConfiguration
(
classes =
WebMvcConfig.class
) }
)
@ActiveProfiles
(
"test"
)
public
class
SpringConfigTest {
@Autowired
private
WebApplicationContext wac;
@Test
public
void
springConfiguration
(
) {
assertNotNull
(
wac);
}
}
V. DataSource▲
La couche de persistance reposant sur JPA, une connexion à une base de données relationnelle est nécessaire. Selon le contexte dans lequel s'exécute l'application, cette DataSource peut être récupérée de deux manières :
- Par lookup JNDI lorsque l'application est déployée dans un serveur d'application JavaEE ou dans un conteneur web gérant ses propres ressources ;
- Initialisationau démarrage de l'application par création d'une base de données en mémoire. Utile pour les tests d'intégration ou pour faciliter le déploiement de l'application.
Déclaré dans le bean de configuration DataSourceConfig, le bean dataSource sera par la suite injecté dans le bean de configuration InfrastructureConfig.
L'instanciation, la configuration et l'initialisation d'un bean Spring sont réalisées dans une méthode annotée par @Bean. Par défaut, le nom du bean Spring est déduit du nom de sa méthode. Voici un exemple de déclaration de beans :
@Configuration
@PropertySource
({
"classpath:com/javaetmoi/sample/config/datasource.properties"
}
)
public
class
DataSourceConfig {
@Autowired
private
Environment env;
@Bean
@Profile
(
"javaee"
)
public
JndiObjectFactoryBean dataSource
(
) throws
IllegalArgumentException {
JndiObjectFactoryBean dataSource =
new
JndiObjectFactoryBean
(
);
dataSource.setExpectedType
(
DataSource.class
);
dataSource.setJndiName
(
env.getProperty
(
"jdbc.jndiDataSource"
));
return
dataSource;
}
@Bean
@Profile
(
"test"
)
public
DataSource testDataSource
(
) {
return
new
EmbeddedDatabaseBuilder
(
).setType
(
EmbeddedDatabaseType.H2).build
(
);
}
}
On retrouve une méthode de déclaration de DataSource pour chacun des deux contextes présentés ci-dessus. Chaque méthode est annotée avec un @Profile Spring différent : javaee et test.
Le profile javaee est activé dans le web.xml :
<context-param>
<param-name>
spring.profiles.active</param-name>
<param-value>
javaee</param-value>
</context-param>
Le profil test est quant à lui activé par l'annotation @ActiveProfiles(“test”) apposée sur la classe de test SpringConfigTest.
La méthode dataSource() utilise la fabrique de beans JndiObjectFactoryBean pour récupérer la DataSource à partir de son nom JNDI.
Le bean Environment est de nouveau utilisé. Cette fois-ci, pour récupérer la valeur de la propriété “jdbc.jndiDataSource” définie dans le fichier datasource.properties chargé à l'aide de l'annotation @PropertySource.
À noter que le type de retour de cette méthode est un JndiObjectFactoryBean. Pour retourner directement un DataSource, il aurait été nécessaire de se substituer au conteneur Spring en invoquant la méthode afterPropertiesSet() de la fabrique de beans :
dataSource.afterPropertiesSet
(
);
return
(
DataSource) dataSource.getObject
(
);
En effet, appelée pendant la phase d'initialisation du bean, afterPropertiesSet() effectue le lookup JNDI. Sans cet appel, la dataSource serait nulle :
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public javax.persistence.EntityManagerFactory com.javaetmoi.sample.config.InfrastructureConfig.entityManagerFactoryBean()] threw exception; nested exception is java.lang.IllegalArgumentException: DataSource must not be null
VI. Couche de persistance▲
Déjà amorcée par la déclaration de la source de données, la mise en œuvre de la couche de persistance est complétée par deux autres beans de configuration :
- InfrastructureConfig : infrastructure JPA composée d'une fabrique du gestionnaire d'entités JPA et du gestionnaire de transactions associé ;
- RepositoryConfig : activation de Spring Data JPA et détection des beans de type Repository (DAOs).
L'annotation @EnableTransactionManagement portée par le bean InfrastructureConfig permet d'activer l'utilisation des annotations @Transactional chargées de délimiter les transactions base de données :
@Configuration
@EnableTransactionManagement
@PropertySource
({
"classpath:com/javaetmoi/sample/config/infrastructure.properties"
}
)
public
class
InfrastructureConfig {
@Autowired
Environment env;
@Autowired
private
DataSource dataSource;
Récupérée par JNDI ou instanciée en mémoire, la DataSource est injectée sur le même modèle que n'importe quel bean Spring.
Vient ensuite la déclaration des beans transactionManager et transactionTemplate :
@Bean
public
JpaTransactionManager transactionManager
(
) {
JpaTransactionManager jpaTransactionManager =
new
JpaTransactionManager
(
);
jpaTransactionManager.setEntityManagerFactory
(
entityManagerFactory
(
));
return
jpaTransactionManager;
}
@Bean
public
TransactionTemplate transactionTemplate
(
) {
TransactionTemplate transactionTemplate =
new
TransactionTemplate
(
);
transactionTemplate.setTransactionManager
(
transactionManager
(
));
return
transactionTemplate;
}
Mettons de côté la méthode entityManagerFactory() sur laquelle nous reviendrons plus loin. Cette configuration montre comment mettre en relations deux beans Spring : le bean transactionTemplate utilise en effet le bean transactionManager. En XML, cette mise en relation est habituellement réalisée à l'aide de la balise ref et de l'identifiant du bean. En Java, l'injection d'un bean dans un autre se fait en utilisant la méthode de déclaration du bean à injecter, en l'occurrence ici transactionManager(). Ce qui ressemble à un appel de méthode est trompeur. En effet, Spring interprète ce type d'appel afin de gérer le cycle de vie des Beans. Par exemple, lorsqu'un bean est de portée singleton, la méthode de création du bean n'est invoquée qu'une seule et unique fois, même si le bean est injecté dans plusieurs beans. Techniquement, Spring instrumente les classes annotées avec @Configuration au démarrage du contexte applicatif.
Le bean entityManagerFactory est déclaré de la façon suivante :
@Bean
public
EntityManagerFactory entityManagerFactory
(
) {
LocalContainerEntityManagerFactoryBean em =
new
LocalContainerEntityManagerFactoryBean
(
);
em.setDataSource
(
dataSource);
em.setPersistenceUnitName
(
"javaconfigSamplePersistenceUnit"
);
em.setPackagesToScan
(
"com.javaetmoi.sample.domain"
);
em.setJpaVendorAdapter
(
jpaVendorAdaper
(
));
em.setJpaPropertyMap
(
additionalProperties
(
));
em.afterPropertiesSet
(
);
return
em.getObject
(
);
}
Il utilise le bean dataSource injecté plus haut dans la classe de configuration. Derrière la méthode jpaVendorAdaper() se cache un autre bean. Quant à additionalProperties(), il s'agit d'un vrai appel de méthode. De la même manière que pour la DataSource, l'appel aux méthodes afterPropertiesSet() et getObject() est nécessaire pour retourner un bean de type EntityManagerFactory.
Une autre possibilité aurait consisté à renvoyer un LocalContainerEntityManagerFactoryBean puis à utiliser l'annotation @Autowired sur la méthode transactionManager. Charge à Spring de demander à la fabrique de beans de lui retourner un EntityManagerFactory :
@Bean
public
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean
(
) {
LocalContainerEntityManagerFactoryBean em =
new
LocalContainerEntityManagerFactoryBean
(
);
// …
return
em;
}
@Bean
@Autowired
public
JpaTransactionManager transactionManager
(
EntityManagerFactory entityManagerFactory) {
JpaTransactionManager jpaTransactionManager =
new
JpaTransactionManager
(
);
jpaTransactionManager.setEntityManagerFactory
(
entityManagerFactory);
return
jpaTransactionManager;
}
L'injection du bean dataSource dans le LocalContainerEntityManagerFactoryBean aurait pu être réalisée d'une autre manière. En effet, en tant que beans Spring, les beans de configuration peuvent être injectés dans d'autres beans. Ainsi, le bean DataSourceConfig aurait pu être injecté dans InfrastructureConfig. Il devient alors possible d'appeler dataSourceConfig.dataSource() pour récupérer la dataSource :
@Autowired
private
DataSourceConfig dataSourceConfig;
…
@Bean
public
EntityManagerFactory entityManagerFactory
(
) {
LocalContainerEntityManagerFactoryBean em =
new
LocalContainerEntityManagerFactoryBean
(
);
em.setDataSource
(
dataSourceConfig.dataSource
(
));
…
}
Bien que facilitant la navigation dans la configuration Spring, cette technique induit un couplage fort entre les deux beans de configuration.
Une fois l'infrastructure Hibernate/JPA mis en place, le bean de configuration RepositoryConfig initie la couche d'accès aux données. Par commodité, son implémentation est réalisée à l'aide du projet Spring Data JPA. L'annotation @EnableJpaRepositories active ce dernier : toutes les interfaces contenues dans un package donné et étendant l'interface Repository de Spring Data sont détectées puis interprétées. Par défaut, les requêtes JPA sont générées à partir du nom des méthodes exposées dans ces interfaces. Les implémentations personnalisées des Repository sont recherchées à l'aide du suffixe Impl.
@Configuration
@EnableJpaRepositories
(
"com.javaetmoi.sample.repository"
)
public
class
RepositoryConfig {
//
}
La couche de persistance est sans doute celle nécessitant le plus de configuration. À côté, la couche services métiers est particulièrement simple.
VII. Couche services▲
L'objectif premier de ServiceConfig consiste à détecter tous les beans Spring annotés par @Service. L'annotation @ComponentScan joue ce rôle en analysant récursivement le package Java dédié aux services métiers, dans notre exemple le package com.javaetmoi.sample.service :
@Configuration
@EnableAsync
@EnableScheduling
@EnableAspectJAutoProxy
@EnableCaching
@ComponentScan
(
basePackages =
{
"com.javaetmoi.sample.service"
}
)
public
class
ServiceConfig implements
AsyncConfigurer {
]
C'est dans cette couche que je vous propose d'activer des fonctionnalités avancées de Spring :
- @EnableAsync : appel de méthode asynchrone via l'annotation @Async ;
- @EnableScheduling : planification d'appel de méthode via l'annotation @Scheduled ;
- @EnableAspectJAutoProxy : proxyfication de beans Spring ne possédant pas d'interface ;
- @EnableCaching : résultat de l'appel de méthode mis en cache avec @Cacheable, évincé avec @CacheEvict ou bien rafraichi avec @CachePut. Si vous n'êtes pas encore passés à Java 8, l'annotation @Caching permet d'utiliser plusieurs fois l'une des trois annotations précédentes sur la même méthode.
Comme indiqué plus haut, l'annotation @EnableCaching nécessite un CacheManager. La définition d'un bean cacheManager et d'un defaultCache est donnée ici à titre indicatif :
@Bean
public
CacheManager cacheManager
(
) {
SimpleCacheManager cacheManager =
new
SimpleCacheManager
(
);
List<
Cache>
caches =
new
ArrayList<
Cache>(
);
// to customize
caches.add
(
defaultCache
(
));
cacheManager.setCaches
(
caches);
return
cacheManager;
}
@Bean
public
Cache defaultCache
(
) {
ConcurrentMapCacheFactoryBean cacheFactoryBean =
new
ConcurrentMapCacheFactoryBean
(
);
cacheFactoryBean.setName
(
"default"
);
cacheFactoryBean.afterPropertiesSet
(
);
return
cacheFactoryBean.getObject
(
);
}
Afin de pouvoir personnaliser le pool de threads utilisés lors de traitements asynchrones, l'annotation @EnableAsync encourage à définir son propre pool en implémentant l'interface AsyncConfigurer et sa méthode public Executor getAsyncExecutor() :
@Override
public
Executor getAsyncExecutor
(
) {
log.debug
(
"Creating Async Task Executor"
);
ThreadPoolTaskExecutor executor =
new
ThreadPoolTaskExecutor
(
);
// to customize with your requirements
executor.setCorePoolSize
(
5
);
executor.setMaxPoolSize
(
40
);
executor.setQueueCapacity
(
100
);
executor.setThreadNamePrefix
(
"MyExecutor-"
);
executor.initialize
(
);
return
executor;
}
Avant de passer à la configuration de la couche présentation, terminons l'initialisation du contexte racine par la mise en place de la sécurité.
VIII. Sécurité▲
Reposant sur le projet Spring Security, la configuration de la sécurité applicative est centralisée dans le fichier SecurityConfig. L'annotation @EnableWebMvcSecurity s'utilise de pair avec la classe abstraite WebSecurityConfigurerAdapter. Cette dernière permet d'activer la configuration par défaut de Spring Security. La redéfinition de certaines méthodes permet de personnaliser la configuration via les classes AuthenticationManagerBuilder, HttpSecurity et WebSecurity.
@Configuration
@EnableWebMvcSecurity
public
class
SecurityConfig extends
WebSecurityConfigurerAdapter {
Publié sur le blog de Spring, le billet Spring Security Java Config Preview : Web Security explique comment utiliser la syntaxe Java.
Dans notre exemple, la déclaration du filtre springSecurityFilterChain dans le web.xml permet de rester compatible avec les conteneurs de Servlet 2.5.
L'un des apports du fichier SecurityConfig réside dans la mise à disposition du bean authenticatedUserDetails. Implémentant l'interface UserDetails de Spring Security, ce bean permet d'accéder aux données de l'utilisateur authentifié (ex : login, nom, habilitations). De portée session (spécifiée avec l'annotation @Scope), ce bean peut être injecté dans des beans de scopes différents, par exemple dans les contrôleurs ou les services métiers de portée singleton. Très pratique pour les logs et/ou les données de traçabilité.
@Bean
@Scope
(
value =
"session"
, proxyMode =
ScopedProxyMode.TARGET_CLASS)
public
UserDetails authenticatedUserDetails
(
) {
SecurityContextHolder.getContext
(
).getAuthentication
(
);
Authentication authentication =
SecurityContextHolder.getContext
(
).getAuthentication
(
);
if
(
authentication !=
null
) {
if
(
authentication instanceof
UsernamePasswordAuthenticationToken) {
return
(
UserDetails) ((
UsernamePasswordAuthenticationToken) authentication).getPrincipal
(
);
}
if
(
authentication instanceof
RememberMeAuthenticationToken) {
return
(
UserDetails) ((
RememberMeAuthenticationToken) authentication).getPrincipal
(
);
}
}
return
null
;
}
Remarque : le système d'authentification sur lequel est branchée votre application peut renvoyer des données supplémentaires. Il est alors fréquent d'avoir à spécialiser la classe User implémentant UserDetails. Si vous ne définissez pas d'interface sur votre classe MyUser, l'utilisation du ScopedProxyMode.TARGET_CLASS est requise par Spring pour proxyfier votre classe.
IX. Couche présentation▲
La configuration Java de Spring MVC ressemble à celle de Spring Security. L'annotation @EnableWebMvc s'utilise conjointement à la classe abstraite WebMvcConfigurerAdapter. L'application bénéficie d'une configuration par défaut qu'il est possible de personnaliser par redéfinition de méthodes. La classe WebMvcConfig joue ce rôle :
@Configuration
@EnableWebMvc
@ComponentScan
(
basePackages =
{
"com.javaetmoi.sample.web"
}
)
public
class
WebMvcConfig extends
WebMvcConfigurerAdapter {
private
static
final
int
CACHE_PERIOD =
31556926
; // one year
@Autowired
private
RequestMappingHandlerAdapter requestMappingHandlerAdapter;
@PostConstruct
public
void
init
(
) {
requestMappingHandlerAdapter.setIgnoreDefaultModelOnRedirect
(
true
);
}
@Bean
public
ViewResolver viewResolver
(
) {
// Example: the 'info' view logical name is mapped to the file '/WEB-INF/jsp/info.jsp'
InternalResourceViewResolver bean =
new
InternalResourceViewResolver
(
);
bean.setViewClass
(
JstlView.class
);
bean.setPrefix
(
"/WEB-INF/jsp/"
);
bean.setSuffix
(
".jsp"
);
return
bean;
}
@Bean
(
name =
"messageSource"
)
public
ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource
(
) {
ReloadableResourceBundleMessageSource messageSource =
new
ReloadableResourceBundleMessageSource
(
);
messageSource.setBasenames
(
"classpath:com/javaetmoi/sample/web/messages"
);
messageSource.setDefaultEncoding
(
"UTF-8"
);
return
messageSource;
}
@Override
public
void
addResourceHandlers
(
ResourceHandlerRegistry registry) {
// Static ressources from both WEB-INF and webjars
registry
.addResourceHandler
(
"/resources/**"
)
.addResourceLocations
(
"/resources/"
)
.setCachePeriod
(
CACHE_PERIOD);
registry
.addResourceHandler
(
"/webjars/**"
)
.addResourceLocations
(
"classpath:/META-INF/resources/webjars/"
)
.setCachePeriod
(
CACHE_PERIOD);
}
@Override
public
void
addViewControllers
(
ViewControllerRegistry registry) {
registry.addViewController
(
"/about"
); // the about page did not required any controller
}
@Override
public
void
configureDefaultServletHandling
(
DefaultServletHandlerConfigurer configurer) {
// Serving static files using the Servlet container's default Servlet.
configurer.enable
(
);
}
@Override
public
void
addFormatters
(
FormatterRegistry formatterRegistry) {
// add your custom formatters
}
}
En complément de la redéfinition de méthode, il est possible de configurer Spring MVC en paramétrant les beans créés automatiquement par l'annotation @EnableWebMvc et la classe DelegatingWebMvcConfiguration qu'elle référence.
C'est précisément le cas du bean RequestMappingHandlerAdapter paramétré dans la fonction init() qui est appelée au chargement du contexte applicatif Spring.
Pour les nouvelles applications Spring MVC, il est recommandé par Spring de forcer à true le flag ignoreDefaultModelOnRedirect. Lors de redirection, l'utilisation de l'interface RedirectAttributes devient alors nécessaire pour spécifier quelle donnée doit être passée à la RedirectView.
La configuration donnée en exemple ne donne qu'un aperçu de l'éventail des possibilités de configuration offertes par Spring MVC : choix de la technologie de rendu (ici JSP), accès aux ressources statiques, définition du cache, internationalisation, déclaration des formateurs globaux…
X. Conclusion▲
En guise de conclusion, voici un tableau récapitulant les avantages et les inconvénients qu'a un développeur Java à passer sur une syntaxe full Java :
Avantages |
Inconvénients |
---|---|
|
|
XI. Références▲
- Manuel de référence Spring Framework 4 (Pivotal)
- Spring Java Based Configuration (Tutorials Point)
- Spring Data, une autre façon d'accéder aux données (So@t)
- Spring Security Java Config Preview : Web Security (Pivotal)
- Spring Java Config 101 (DZone)
- Spring Cache (Zenika)
XII. Remerciements▲
Cet article a été publié avec l'aimable autorisation d'Antoine Rey.
Nous tenons à remercier milkoseck pour sa relecture orthographique attentive de cet article et Régis Pouiller pour la mise au gabarit.