dimanche 25 janvier 2009
Spring - Injection de dépendance avec l'annotation @Configurable
Par Philippe De Oliveira, dimanche 25 janvier 2009 à 21:27 dans Java

Spring est devenu ces dernières années incontournable au sein de l'éco-système Java. Pourtant, il est au moins aussi controversé qu'il est adopté. Sa lourdeur, ses concepts complexes et le manque de "clarté" de ses objectifs n'ont en effet pas aidé à sa démocratisation. Mon opinion sur ce framework, ou plus exactement, sur le portfolio Spring, est très partagée. J'entends continuellement les reproches fait à Spring, je m'aperçois aussi de ses qualités. Toutefois, je me rends compte que bien souvent la méconnaissance de celui-ci est à la source de ce sentiment de rejet. Reste à déterminer à qui incombe la faute de sa méconnaissance...
En tout cas, pour qu'elle ne m'incombe pas, voici un premier article sur Spring et sur une fonctionnalité vraiment méconnue : @Configurable. Cette annotation permet l'injection de dépendances dans un bean sans que celui-ci soit demandé explicitement au conteneur Spring (typiquement le cas pour un bean métier, une servlet, etc.).
Cette fonctionnalité n'étant pas des plus connues, souvent lorsque l'on a besoin d'un bean déjà déclaré dans la configuration Spring mais que l'on se trouve "en dehors" de celui-ci, la technique utilisée est de faire un simple mais assez horrible :
SpringApplicationContext.getBean("mySpringBean");
Plutôt moche, car cette méthode a pour conséquence de coupler votre code avec le conteneur Spring et surtout avec le nom du bean que vous souhaitez atteindre.
Pour éviter ce genre de choses, ce qu'il faudrait, c'est qu'à chaque instanciation d'une classe (utilisation de new) Spring procède à l'injection des dépendances telles que définies dans sa configuration. Et il se trouve que Spring est capable de faire ce genre de manipulations en utilisant l'AOP (Aspect Oriented Programming). Pour cela, il va greffer un aspect au constructeur du bean afin d'être informé de sa construction, lui permettant ainsi d'y injecter les dépendances. La composante AOP est ici confiée à AspectJ
Pour l'exemple, je vais utiliser une Servlet. Cette dernière est instanciée par le conteneur de Servlet et donc non servie par le conteneur Spring :
Cette Servlet utilise un service pour l'authentification, ici matérialisé par l'interface IUserService. Ce service permet de vériifer le login/mdp d'un utilisateur en faisant appel à une implémentation de l'interface IUserDAO. Cette dernière est chargée d'interroger la source de données quelle qu'elle soit. Il faudrait donc dans l'idéal que lorsque la Servlet instancie son IUserService, celui-ci se voit injecter l'implémentation de DAO dont il a besoin. Voici une implémentation possible de ce service :public class CheckCredentialsServlet extends HttpServlet { private IUserService userService = null; @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { userService = new UserService(); String username = req.getParameter("username"); Boolean check = req.getParameter("password"); if (userService.checkCredentials(username, password)) { resp.sendRedirect("pages/ok.html"); } else { resp.sendRedirect("pages/ko.html"); } } }
Dans cette implémentation du service IUserService, on note la DAO encapsulée par des accesseurs, et surtout l'annotation @Configurable. Cette annotation va permettre à Spring de reconnaitre cette classe comme devant être "observée". Ainsi, si une injection de dépendances est requise pour les instances de cette classe, Spring se chargera de l'injection lors de l'instanciation. Voici la configuration Spring (applicationContext.xml) déclarant l'injection :@Configurable public class UserService implements IUserService { private IUserDAO userDAO; @Override public Boolean checkCredentials(String username, String pwd) { User user = userDAO.getUser(username, pwd); return user == null ? false : true; } public IUserDAO getUserDAO() { return userDAO; } public void setUserDAO(IUserDAO userDAO) { this.userDAO = userDAO; } }
Tout d'abord, l'implémentation du service IUserDAO est déclarée (LDAPUserDAO pour l'exemple). A noter qu'un alias est utilisé. Cette fonctionnalité de Spring est elle aussi méconnue alors qu'elle permet de ne changer le fichier qu'à un seul endroit lorsque l'instance de service à utiliser change (lors d'une phase d'intégration par exemple). Le bean DAO est ensuite injecté dans le UserService afin que ce dernier puisse vérifier le login/mdp d'un utilisateur. Les dernières lignes de la configuration Spring sont celles qui permettent la création de l'aspect et son tissage :<alias alias="userDAO" name="LDAPUserDAO"/> <bean id="LDAPUserDAO" class="dao.LDAPUserDAO" /> <bean class="services.UserService"> <property name="userDAO" ref="userDAO"/> </bean> <context:spring-configured/> <context:annotation-config/> <context:load-time-weaver/>
Cette déclaration indique à AspectJ de se baser sur la configuration Spring pour positionner ses aspects.<context:spring-configured/>
Cette déclaration indique à Spring de suivre les annotations pour sa configuration.<context:annotation-config/>
Enfin, la dernière déclaration instancie un tisseur d'aspect qui se chargera de greffer les aspects lors du chargement de l'application. Toutefois, pour que cela fonctionne, un java-agent doit être enregistré au lancement de la machine virtuelle Java. Il faut donc fournir l'agent en paramètre au démarrage de la VM :<context:load-time-weaver/>
En conclusion, avec un minimum d'annotations (dont l'abus est dangereux pour la santé) et d'XML, il est possible d'utiliser Spring pour injecter nos dépendances même lorsque celui-ci n'est pas à l'origine des instanciations. Toutefois, obtenir un découplage parfait nous a obligé à ajouter des aspects, pratique hautement discutable (Gavin si tu m'entends :-)). Ca ressemble en effet à une magnifique utilisation de "Golden Hammer" (concept à découvrir dans "Better, Faster, Lighter Java"). En termes de performances, c'est certes couteux, mais uniquement au démarrage de l'application car c'est là qu'est effectué le tissage d'aspects. Aucune différence sensible à l'exécution n'est à prévoir. L'application complète autour de la Servlet peut être téléchargée ici (sources inclues dans le WAR).-javaagent:/path/to/the/spring-agent.jar
Commentaires
1. Le mercredi 25 février 2009 à 18:39, par Romain
2. Le jeudi 5 novembre 2009 à 12:46, par Romain
3. Le mardi 26 juin 2012 à 15:29, par flash game
Ajouter un commentaire
Les commentaires pour ce billet sont fermés.