L’idée de base dans la programmation par Aspect est d’extraire du flot normal d’exécution d’un code les parties très techniques.
Dans Spring on a classiquement la partie du code qui gère le Controleur de l’entité Todo via cette méthode :
@RequestMapping(value = "/todos", method = RequestMethod.GET)
public String listeTodos() {
System.out.println("listeTodos");
List<Todo> listTodo = todoDao.listTodo();
String json = listTodo.stream()
.map( todo -> todo.toJson() )
.collect( Collectors.joining( "," , "[" , "]") );
return json;
}
Pourtant, on pourrait vouloir faire un log de cet appel en ajoutant classiquement un logger au sein de cette méthode listeTodos() sauf que le principe de la programmation par Aspect est de sortir cette partie technique en proposant l’interception de l’appel de cette méthode listeTodos avec un code qui s’exécute avant et/ou après l’appel à listeTodos.
De manière concrète on a cet enchainement :
- Appel à une méthode technique avant listeTodos
- Appel à listeTodos
- Appel à une méthode technique après listeTodos (optionnel)
En premier lieu, il faut ajouter sur la méthode principal l’ajout des fonctionnaltiés de programmation par Aspect:
@SpringBootApplication
@Configuration
@EnableAspectJAutoProxy
public class DemoApplication {
}
Ensuite, il faut coder une classe qui va assurer l’interception avant et après dans cet exemple avec les critères d’interceptions :
@Component
@Aspect
public class RestLogAspect {
@Around("execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) && @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public Object restLog(ProceedingJoinPoint joinPoint) throws Throwable {
// Partie @Before
Object returnValue = null;
Throwable throwable = null;
try {
System.out.println("Appel à TodoControleur avant");
returnValue = joinPoint.proceed();
System.out.println("Appel à TodoControleur après");
} catch (Throwable throwable1) {
throwable = throwable1;
System.out.println(String.format("[RestLogAspect] : %s", throwable1.getMessage()));
}
//Partie @After
return returnValue;
}
}
L’annotation @Around indique que l’appel à cette méthode sera fait avant et après les méthodes ayant les 2 critères suivants (notation && entre les 2 critères) :
- execution(* fr.ema.ceris.todo.demo.TodoControleur..*(..)) : une classe se trouvant dans ce package avec ce nom de classe
- @annotation(org.springframework.web.bind.annotation.RequestMapping), une annotion de type RequestMapping
Messages de la console :
Appel à TodoControleur avant listeTodos Appel à TodoControleur après
Nous avons donc un affichage des messages dans la console avant et après l’appel à la méthode listeTodos ce qui permet d’instrumentaliser le code fonctionnel par un code technique.
La valeur de la variable returnValue est aussi le résultat de l’appel à la fonction listeTodos donc le contenu json de la variable que l’on pourrait donc formater ou compresser avant renvoi vers la partie front du site web.
On aurait aussi pu utiliser l’annotation @Before à la place de l’annotation @Around pour avoir une exécution avant la méthode listeTodos et/ou l’annoation @After pour avoir une exécution après la méthode listeTodos. L’annotation @Around est un excellent exemple car elle permet de disposer des effets avant et après l’appel.