Générer des aspects avec un AnnotationProcessor
par
, 23/09/2015 à 22h14 (717 Affichages)
Donc j'ai utilisé Aspectj pour écrire un aspect me permettant d'ajouter des accesseurs à un attribut d'une classe java. Le problème, c'est que mon aspect n'est pas générique et que je ne peux pas le réutiliser.
Moi, ce que je voudrais, c'est pouvoir générer les accesseurs pour n'importe quel attribut. D'où l'idée de générer les aspects à partir d'un template. J'utiliser Xtend pour écrire mon template à partir de l'aspect, ce qui est assez rapide :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 class GetterSetterTpl { def static generate(String beanPackage, String beanName, String propertyName, String propertyType) ''' package «beanPackage»; privileged aspect «beanName»_«propertyName»ITD { «propertyType» around(«beanName» instance) : get(«propertyType» «beanName».«propertyName») && target(instance) && ! withincode(«propertyType» «beanName».get«propertyName.toFirstUpper»()) { return instance.get«propertyName.toFirstUpper»(); } void around(«beanName» instance, «propertyType» value) : set(«propertyType» «beanName».«propertyName») && args(value) && target(instance) && ! withincode(void «beanName».set«propertyName.toFirstUpper»(*)){ instance.set«propertyName.toFirstUpper»(value); } public «propertyType» «beanName».get«propertyName.toFirstUpper»() { System.out.println("«beanName».get«propertyName.toFirstUpper»()"); return this.«propertyName»; } public void «beanName».set«propertyName.toFirstUpper»(«propertyType» value) { System.out.println("«beanName».set«propertyName.toFirstUpper»(value)"); this.«propertyName» = value; } } ''' }
Pour piloter la génération de code elle-même, je vais utiliser un AnnotationProcessor qui me semble bien adapté pour ce genre de cas.
Je commence par créer une annotation @Property à positionner sur les attributs :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 public class SampleBean { @Property public String property; }
Je peut alors créer un AnnotationProcessor qui parcours la attributs annotés :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 @SupportedAnnotationTypes(value = { "annotations.Property" }) public class PropertyProcessor extends AbstractProcessor { public boolean process(Set elements, RoundEnvironment env) { for (Element element : env.getElementsAnnotatedWith(Property.class)) { generateAspect(element); } return true; } }
La méthode generateAspect() se charge de générer l'Aspect associé à l'attribut annoté.
Il faut commencer par récupérer les infos de l'attribut en passant par l'API java.lang.model qui n'est pas toujours intuitive :
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13 protected void generateAspect(Element element) { TypeElement beanElt = (TypeElement) element.getEnclosingElement(); PackageElement packageElt = (PackageElement) beanElt.getEnclosingElement(); String beanPackage = packageElt.getQualifiedName().toString(); String beanName = beanElt.getSimpleName().toString(); String propertyName = element.getSimpleName().toString(); TypeMirror fieldType = element.asType(); String propertyType = fieldType.toString();
Il ne reste plus qu'à instancier le template et générer le fichier.
Code java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 String aspectText = GetterSetterTpl.generate(packageName, beanName, propertyName, propertyType).toString(); try { final FileObject file = processingEnv.getFiler() .createResource(StandardLocation.SOURCE_OUTPUT, beanPackage, beanName + "_" + propertyName + "ITD.aj"); file.openWriter().append(aspectText).close(); System.out .println("Generated aspect to advise " + propertyName); } catch (IOException ioe) { } }
Et voilà, mission accomplie.
Il ne faut jamais négliger tout ce que peuvent apporter les AnnotationProcessors.