Spring et les profils

Une des fonctionnalités apportées par la version 3.1 de Spring est la gestion des profils.

Récemment, pour une application web, j’ai eu besoin de définir des comportements spécifiques dans mon environnement de développement pour ne pas interroger des services externes, inaccessible depuis mon poste.

Le besoin est donc simple, charger des implémentations de service différentes selon l’environnement d’exécution de l’application. Et c’est là qu’entre en jeux les profils ! Ceux ci permettent de charger des beans différents dans le contexte Spring selon le(s) profil(s) configurés.

Configuration XML

Un fichier par profil

Il est possible de spécifier un profil dès la balise racine, celà implique que tout le contenu du fichier ne sera chargé que si le profil correspondant est activé.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"
       profile="a">

    <bean class="com.github.jcgay.example.spring.BeanA" />

</beans>

Le bean n’est chargé que si le profile “a” est actif. Les beans définis en dehors de ce fichier de configuration seront accessibles dans tous les cas.

Blocs au sein d’un même fichier

On peut également définir des blocs de profils au sein d’un même fichier de configuration.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <bean class="com.github.jcgay.example.spring.BeanA" />

    <beans profile="b">
        <bean class="com.github.jcgay.example.spring.BeanB" />
    </beans>

</beans>

Le bean A est chargé dans tous les cas, le B seulement quand le profil “b” est actif.

Mutualisation de configuration pour plusieurs profils

Pour ne pas répéter la configuration de beans dans plusieurs profils, on peut déclarer des blocs valides pour ces différents profils.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <beans profile="a,b">
        <bean class="com.github.jcgay.example.spring.BeanA" />
        <bean class="com.github.jcgay.example.spring.BeanB" />
    </beans>

    <beans profile="a">
        <bean class="com.github.jcgay.example.spring.BeanC" />
    </beans>

    <beans profile="b">
        <bean class="com.github.jcgay.example.spring.BeanD" />
    </beans>

</beans>

Le profil “a” contient les beans A,B et C tandis que le profil “b” comprends les beans A,B et D. La déclaration de A et B est mutualisée.

Configuration par annotation

On peut également définir des profils via les annotations Spring. L’annotation @Profile peut être utilisé sur les différents stéréotypes disponibles (@Service, @Component, @Configuration, etc).

Au niveau @Configuration

@Configuration
@Profile("a")
public class ProfileA {

    @Bean
    public BeanA beanA() {
        return new BeanA();
    }
}

Tous les beans définis dans cette classe sont accessibles quand le profil “a” correspondant est actif.

Comme avec le modèle XML, il est possible de mutualiser la définition des profils.

@Configuration
@Profile({"a", "b"})
public class MultipleProfiles {

    @Bean
    public BeanA beanA() {
        return new BeanA();
    }

    @Bean
    public BeanB beanB() {
        return new BeanB();
    }
}

On peut annoter directement un composant Spring sans passer par des @Configuration.

@Component
@Profile("e")
public class BeanE {
}

Et enfin on peut utiliser @Profile comme meta-annotation et donc créer ses propres annotations au lieu de répeter le nom du profil un peu partout.

@Profile("a")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface CustomProfile {
}

Et voilà on peut utiliser @CustomProfile pour construire le profil “a”.

Activation d’un profil

L’option -Dspring.profiles.active=a en ligne de commande active le profil “a”. Pour activer plusieurs profils il faut les séparer par des virgules.

Ou alors, en variable d’environnement : export spring.profiles.active=a

Dans le cadre d’une application web, le choix du profil peut être passé en paramètre de la servlet :

<servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>spring.profiles.active</param-name>
        <param-value>a</param-value>
    </init-param>
</servlet>

ou en paramètre du listener :

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext*.xml</param-value>
</context-param>
<context-param>
    <param-name>spring.profiles.active</param-name>
    <param-value>a</param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

Définir un profil par défaut

La clef spring.profiles.default permet de choisir un profil qui sera activé automatiquement si aucun autre n’a été défini. Pratique si par exemple on veut que l’application charge une configuration type “production” si aucun profil n’est spécifié.

Pour finir, pour les utilisateurs IntelliJ IDEA, il y a une fonctionnalité plutôt sympa qui permet de filtrer la configuration Spring selon le profil actif : http://blogs.jetbrains.com/idea/2011/04/new-in-105-spring-31-bean-definition-profiles/

comments powered by Disqus