2017-10-02 8 views
1

Ce que je dois faire est assez simple en utilisant des intercepteurs mais j'espérais vraiment une solution plus élégante basée sur des annotations. Le truc c'est que ma "solution" ne marche pas vraiment et je ne sais pas pourquoi. Peut-être que ce n'est même pas possible.Annotation personnalisée pour la méthode dans le contrôleur qui étend un contrôleur abstrait

Ma pile de base est: chaussure de ressort 1.4.1:

  • ressort-boot-démarreur-bande
  • ressort-boot-démarreur-aop
  • ressort-boot-démarreur-JDBC
  • printemps-boot-démarreur-cache

printemps-haricots 4.3.4 et divers autres services publics et des pots d'essai.

J'ai plusieurs contrôleurs qui étendent un contrôleur abstrait. Ce contrôleur abstrait doit préparer une connexion, puis chaque contrôleur utilise sa propre implémentation spécifique placée dans la méthode acquire(). De temps en temps, certains emplois cron atteignent ces points limites. Nous voulons faire une vérification sur certains contrôleurs/emplois, mais pas nécessairement tous. Je pensais donc à ajouter une annotation personnalisée là où l'audit devrait être.

public abstract class ImportController { 
    @RequestMapping(value = "/checkout", method = RequestMethod.GET, produces = "application/json") 
    public String importEntities() { 
     //some code here .... 
     MyResult result = acquire(param); 
     //some code again .... 
    } 

    public abstract MyResult acquire(MyParam param) 
} 

mise en œuvre qui a besoin de vérification:

@RestController 
@RequestMapping(value = "/cars") 
public class CarsImportController extends ImportController { 

    @Override 
    @MyJobAudit // <--- this should add a pointcut used for Audit logging 
    public MyResult acquire(MyParam param) { 
      //cars specific code 
    } 
} 

La mise en œuvre qui n'a pas besoin d'audit

@RestController 
@RequestMapping(value = "/tomatoes") 
public class TomatoesImportController extends ImportController { 

    @Override 
    //no audit annotation 
    public MyResult acquire(MyParam param) { 
      //tomatoes specific code 
    } 
} 

Mon annotation JobAudit:

@Retention(RetentionPolicy.RUNTIME) 
    public @interface MyJobAudit { 
} 

et la classe aspects:J'ai essayé de mettre mon annotation sur différentes classes de service et cela fonctionne. Mais pas sur acquérir() méthode. Quelque chose ne va vraiment pas ici. Je ne peux pas comprendre ce que ...

+0

Pouvez-vous confirmer que votre méthode 'acquire()' est vraiment 'public'? Parce que Spring Aspects ne fonctionne que sur les méthodes publiques. –

+0

Oui, c'est public – Buzzo

Répondre

2

Le problème est sur l'invocation de votre code conseillé. Vous voyez, vous avez le code suivant défini:

@GetMappein(value = "/checkout") 
public String getCheckout() { 
    //some code here .... 
    MyResult result = acquire(param); //Uh oh!!! 
    //some code again .... 
} 

Mais le problème est que le code conseillé est défini dans un proxy qui Spring crée pour vous (et qui, en cas de contrôleurs que vous ne voyez jamais), mais votre appel de la méthode acquire(param) ci-dessus n'est pas fait dans le proxy Spring, mais directement sur votre classe concrète, en d'autres termes c'est équivalent à dire this.acquire(param), mais le code est conseillé dans le proxy de this, et pas seulement sur this (votre objet concret).

La façon de résoudre le problème est d'accéder à votre proxy actuel. Je l'ai résolu comme suit. Commencez par activer expose-proxy dans votre application.

@SpringBootApplication 
@EnableAspectJAutoProxy(exposeProxy = true) 
public class MyApplication { 

    public static void main(String[] args) { 
     SpringApplication.run(MyApplication, args); 
    } 
} 

Puis, dans la classe concrète, où vous avez l'intention de faire une invocation d'une méthode qui est censé être informé, vous procédez comme suit:

@RestController 
public class ConcreteController { 

    @GetMapping("/checkout") 
    public String getSomething() { 
     Object proxy = AopContext.currentProxy(); 
     return ((ConcreteController) proxy).acquire("Luke Skywalker"); 
    } 

    @Auditable 
    public String acquire(Object param) { 
     return "Hello World, " + param; 
    } 

} 

Le AopContext.currentProxy() vous donnera accès à le mandataire du contrôleur this où le conseil pour acquire(params) est effectivement défini. Et cela fonctionnera comme prévu. Je comprends que la seule solution pour cela serait d'utiliser le vrai AOP et pas seulement les procurations de Spring. Et si vous utilisez le vrai AOP alors vous devrez faire une forme de tissage de code qui conseillera le code pendant la compilation ou le chargement. De cette façon, le code serait conseillé directement sur la classe concrète et pas seulement sur les proxies du printemps muets. L'utilisation du véritable AOP this.acquire() serait conseillée via l'instrumentation au moment de la compilation ou du chargement. Mais si vous n'utilisez que des procurations de Spring, vous ne pouvez pas directement faire des choses comme des invocations de méthodes directes dans une classe conseillée, vous devez vous assurer de passer par le proxy chaque fois que vous faites cela.

+0

Merci beaucoup Edwin! Certainement doit en apprendre davantage sur les astuces internes et cachées que Spring fait et «proxies» est l'un d'entre eux. – Buzzo