Spring : Définition d’un bean parent avec @nnotation

Un ancien collègue rencontré chez un client me contacte en fin de journée pour discuter sur Spring.

Son objectif était de définir un bean ayant comme parent un autre bean du contexte Spring…
Jusqu’ici tout va bien, je savais que cela était possible :)

En effet, la balise <bean> possède un attribut parent qui permet de référencer un bean du contexte.

Exemple:

‹bean id="myBean" parent="myParentBean" class="..." /›

Cependant, son souhait était de définir ce bean parent à l’aide d’annotation.
Je pensais que cette annotation devait sûrement existait et comme par hasard…
Il me réplique que non.

Je lui conseille alors d’attendre une correction ou faire la demande du côté de chez Spring !
Et la encore, il m’informe qu’il y a déjà une issue déclarée chez Spring et qu’à l’heure aucune solution n’a pu être apportée.

Trève de plaisanterie, je me suis dit qu’on peut bidouiller quelque chose pour arriver à notre fin.
Voici la démarche entreprise pour arriver à notre son but !

On y va !

Tout d’abord, on crée l’annotation souhaitée qui va nous permettre de déclarer un bean parent par annotation.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Parent {
    String value();
}

Jusqu’ici, rien de bien sorcier, on a déclaré tout simplement une annotation nommée Parent qui prend une valeur obligatoire.

On va ensuite annoté notre bean “fils” à l’aide de cette nouvelle annotation.

@Parent("myParentBean")
@Component
public class MyBean {
    /* ... */
}

On s’approche, on s’approche !

Il ne reste plus qu’à définir comment doit être traité cette annotation.
L’idée serait de redéfinir les propriétés de certains beans si ceux-ci sont annotés @Parent(…), en l’occurrence la classe MyBean.

Pour cela, Spring met à disposition une interface BeanFactoryPostProcessor avec sa méthode postProcessBeanFactory(ConfigurableListableBeanFactory).

A quoi sert cette jolie interface ? => Direction la doc.
On apprend que cette interface va nous permettre de surcharger ou ajouter des propriétés aux beans du contexte.
Génial, c’est ce que l’on cherche :D !

Note : Cette méthode intervient après le chargement de tous les beans et avant leur initialisation.

Alors on s’y attaque et on écrit une méthode qui ressemble à ceci :

public class ParentBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    /*...*/

    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        BeanDefinition beanDefinition;
        Class clazz;
        Parent parent;
        for (String bean : configurableListableBeanFactory.getBeanDefinitionNames()) {
            beanDefinition = configurableListableBeanFactory.getBeanDefinition(bean);
            try {
                clazz = Class.forName(beanDefinition.getBeanClassName());
                parent = AnnotationUtils.findAnnotation(clazz, Parent.class);
                if (parent != null &amp;&amp; !parent.value().equals(StringUtils.EMPTY))
                    beanDefinition.setParentName(parent.value());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}

Que fait cette méthode ?
Elle va parcourir les beans du contexte Spring et récupérer leur classe.
Une fois la classe récupérée, on vérifie si celle-ci est annotée de notre nouvelle annotation @Parent.
Enfin si c’est le cas, et qu’une valeur a bien été spécifiée, alors on défini le parent de ce bean à l’aide de la méthode setParentName.

On touche au but !

Il nous reste plus qu’une étape : déclarer le nouveau BeanFactoryPostProcessor dans le contexte.

Ainsi, dans un de nos fichiers de contexte Spring, on déclare :

‹bean class="com.excilys.labs.ParentBeanFactoryPostProcessor" /›

Et voila, le tour est joué ;) !

Je ne sais pas s’il s’agit d’une solution totalement appropriée à la situation. Seules vos remarques me le diront ;)

VN:R_U [1.9.22_1171]
Rating: 0 (from 0 votes)
Share
Ce contenu a été publié dans Spring, Trucs & astuces. Vous pouvez le mettre en favoris avec ce permalien.

Laisser un commentaire