AOP avec Spring AOP
Introduction
D’après wikipedia, l’AOP c’est :
La programmation orientée aspect (POA, en anglais aspect-oriented programming – AOP) est un paradigme de programmation qui permet de séparer les préoccupations transverses ((en) en:Cross-cutting concern). Les préoccupations transverses sont des préoccupations qui sont difficiles à séparer et regrouper (encapsuler) et qui ont tendance à contaminer le code. Un exemple classique de ce phénomène est la journalisation. Ces préoccupations relèvent souvent de la technique (aspect en anglais) et contaminent les préoccupations métiers dans une application.
Donc en gros, ça sert à enlever du code métier tout ce qui le pollue et qui n’est pas en rapport avec lui.
Pour tester l’AOP avec spring (c’est la première fois que je l’utilise), je vais reprendre le projet que j’avais utilisé pour créer un web service REST sécurisé.
Je vais montrer un exemple simple, à savoir la journalisation, comme l’indique wikipedia.
En général, on aime bien logger un maximum d’informations dans une appli web. C’est pratique pour le débuggage, car ça permet de savoir quelles méthodes reçoivent quels arguments, et ce qu’elles renvoient. C’est aussi pratique en production pour savoir ce que font les utilisateurs (on l’utilise particulièrement pour le support).
Le problème, c’est que les loggers prennent beaucoup de place dans le code*, que c’est du code totalement inutile à la compréhension du fonctionnement métier et qu’il pollue donc le code métier. L’AOP est donc la solution pour pallier le problème.
*Rien que pour logger une information simple, il faut utiliser 3 lignes de code (le if n’est pas forcément nécessaire ici, mais il est fortement recommandé dès qu’on fait un peu de traitement) :
if (LOG.isInfoEnabled()) { LOG.info("Mon Info"); }
Configurer maven
Pour utiliser l’AOP avec spring dans un projet Maven, il suffit d’ajouter dans le pom :
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${org.springframework.version}</version> </dependency>
Configurer Spring
Il faut ensuite ajouter une configuration dans spring, pour configurer l’AOP :
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <aop:config> <aop:pointcut id="servicePointcut" expression="execution(* fr.mael.jfreenote.service.base.impl.ServiceBaseImpl.*(..)) or execution(* fr.mael.jfreenote.service.impl.*.*(..))"/> <aop:aspect id="loggingAspect" ref="serviceBaseAop"> <aop:before pointcut-ref="servicePointcut" method="beforeMethod" /> <aop:after-returning pointcut-ref="servicePointcut" method="afterMethod" returning="result" /> <aop:after-throwing pointcut-ref="servicePointcut" method="throwsMethod" throwing="exception"/> </aop:aspect> </aop:config> <bean id="serviceBaseAop" class="fr.mael.jfreenote.service.aop.ServiceAopLogger"/> </beans>
Le premier point important de cette configuration est le « pointcut », qui permet de définir quelles méthodes de quels objets seront interceptées. Ici, on indique que toutes les méthodes de la classe ServiceBaseImpl ou de toutes les classes du package fr.mael.jfreenote.service.impl seront interceptées.
On peut trouver plus d’informations sur la syntaxe des pointCuts ici : lien.
On déclare ensuite notre aspect, auquel on indique des advices. Selon Wikipedia, les advices sont :
les programmes qui seront activés avant, autour de ou après les points d’action définis.
Il s’agit donc, dans notre cas, de méthodes qui sont exécutées à un moment précis de l’exécution du programme (par exemple, avant l’exécution d’une méthode).
On peut voir que j’ai déclaré 3 advices :
- before : qui est appelé avant l’exécution d’une méthode
- after-returning : qui est appelé après la fin de la méthode
- after-throwing : qui est appelé quand une méthode lance une exception
Il existe d’autres advices. On peut les trouver dans la doc de spring.
L’attribut « method » des advices permet d’indiquer quelle méthode du bean serviceBaseAop sera exécutée.
Les autres attributs (throwing, returning) permettent d’indiquer des arguments de la méthode, ils sont spécifiques aux différents advices.
La classe de logging
Voici la classe contenant les méthodes :
public class ServiceAopLogger { public static final org.apache.commons.logging.Log LOG = org.apache.commons.logging.LogFactory.getLog(ServiceAopLogger.class); public void beforeMethod(JoinPoint joinPoint) { if (LOG.isInfoEnabled()) { Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); Object target = joinPoint.getTarget(); StringBuffer argsToPrint = new StringBuffer("\\n"); for (int i = 0; i < args.length; i++) { argsToPrint.append("\\t" + i + " => " + args[i] + "\\n"); } LOG.info(signature.toShortString() + " has been called on " + target.getClass().getCanonicalName() + " with arguments :"); } } public void afterMethod(StaticPart staticPart, Object result) { if (LOG.isInfoEnabled()) { LOG.info(staticPart.getSignature().toShortString() + " returned " + result); } } public void throwsMethod(JoinPoint joinPoint, Exception exception) { if (LOG.isInfoEnabled()) { Object target = joinPoint.getTarget(); Signature signature = joinPoint.getSignature(); LOG.info("method " + signature.toShortString() + " called on " + target.getClass().getCanonicalName() + "threw exception " + exception.getClass().getSimpleName()); } } }
On voit bien que la méthode throwsMethod ne sert pas à grand-chose, puisqu’elle ne fait que logger qu’une exception a été lancée, ce qu’on voit déjà dans l’application. La méthode beforeMethod, qui est donc appelée avant l’exécution des méthodes des différents services, affiche quelle méthode a été appelée sur quelle classe, et avec quels arguments. Et enfin, la méthode afterMethod indique ce que renvoie la méthode.
Conclusion
A travers un exemple simple, on a pu voir que l’AOP avec spring (ou sans spring) permet de dépolluer facilement le code métier pour le clarifier. Un autre avantage est que le code d’un aspect est réutilisable parce que justement, il est indépendant du code métier.
La question qui doit se poser au développeur est de savoir quel code on doit inclure dans les services métiers, et au contraire, quel code on doit gérer par AOP. Je pense qu’on peut être tenté assez facilement de déporter du code qui a sa place dans les services métiers. Par exemple, pour l’application qui me sert d’exemple, j’ai été tenté de déporter l’attribution de l’utilisateur aux objets créés (par exemple, à la création d’une note, on doit attribuer à la note l’utilisateur courant). Mais cette attribution doit être faite dans les services métiers (à moins que je me trompe), parce qu’il s’agit complètement de logique métier.
Cet article contient peut-être quelques erreurs, parce que je débute avec l’AOP, je les corrigerai si j’en trouve.
La totalité du projet (commencé ici) est disponible ici.