web-dev-qa-db-fra.com

Le haricot autowired Spring pour l'aspect @Aspect est null

J'ai la configuration de ressort suivante:

<context:component-scan base-package="uk.co.mysite.googlecontactsync.aop"/>

<bean name="simpleEmailSender" class="uk.co.mysite.util.email.simple.SimpleEmailSenderImplementation"/>

<aop:aspectj-autoproxy/>

Ensuite, j'ai un aspect:

@Aspect
public class SyncLoggingAspect {
    @Autowired
    private SimpleEmailSender simpleEmailSender

    @AfterReturning(value="execution(* uk.co.mysite.datasync.polling.Poller+.doPoll())", returning="pusher")
    public void afterPoll(Pusher pusher) {      
        simpleEmailSender.send(new PusherEmail(pusher));
    }
}

Cet aspect fonctionne (je peux atteindre un point d'arrêt sur afterPoll) mais simpleEmailSender est null. Malheureusement, je ne trouve pas de documentation claire expliquant pourquoi. (Pour mémoire, mon bean simpleEmailSender existe et est correctement câblé dans d'autres classes) Les choses suivantes me confondent:

  1. Le contexte: composant-scan est-il supposé prendre @Aspect? Si c'est le cas, alors ce serait sûrement un haricot géré par un ressort, le câblage automatique devrait donc fonctionner?
  2. Si context: composant-scan n'est pas pour la création d'aspects, comment mon aspect est-il créé? Je pensais que aop: aspectj-autoproxy créait simplement un beanPostProcessor pour envoyer un proxy à ma classe @Aspect? Comment ferait-il cela s'il ne s'agit pas d'un haricot à gestion printanière?

Évidemment, vous pouvez dire que je ne comprends pas comment les choses devraient fonctionner à partir de la base.

30
mogronalol

L'aspect est un objet singleton et est créé en dehors du conteneur Spring. Une solution avec la configuration XML consiste à utiliser la méthode d'usine de Spring pour récupérer l'aspect.

<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect" 
     factory-method="aspectOf" />

Avec cette configuration, l'aspect sera traité comme n'importe quel autre bean Spring et le câblage automatique fonctionnera normalement.

Vous devez également utiliser la méthode factory sur les objets Enum et les autres objets sans constructeur ni avec les objets créés en dehors du conteneur Spring.

33
Espen

Une autre option consiste à ajouter @Configurable à votre classe d'aspect au lieu de jouer avec XML.

18
tobiasbayer

Pour que Spring Boot utilise @Autowired avec AspectJ, j'ai trouvé la méthode suivante . Dans la classe de configuration, ajoutez votre aspect:

@Configuration
@ComponentScan("com.kirillch.eqrul")
public class AspectConfig {

    @Bean
    public EmailAspect theAspect() {
        EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
        return aspect;
    }

}

Ensuite, vous pouvez réussir le transfert automatique de vos services dans votre classe d’aspect:

@Aspect
public class EmailAspect {

    @Autowired
    EmailService emailService;
8
Kirill Ch

Je n'ai pas 50 rep pour commenter une question alors voici une autre réponse relative à @ Jitendra Vispute answer . Le document officiel du printemps mentionne:

Vous pouvez enregistrer les classes d'aspect comme beans ordinaires dans votre configuration XML Spring ou les détecter automatiquement via l'analyse de chemin d'accès aux classes, comme tout autre bean géré par Spring. Toutefois, notez que l'annotation @Aspect n'est pas suffisante pour la détection automatique dans le chemin d'accès aux classes: pour cela, vous devez ajouter une annotation @Component distincte (ou une annotation stéréotypée personnalisée qui remplit les conditions requises, conformément aux règles du scanner de composants de Spring). Source: Spring '4.1.7.Release' documentation .

Cela signifierait que l'ajout d'une annotation @Component et de l'ajout du @ComponentScan sur votre configuration rendrait l'exemple de @Jitendra Vispute opérationnel. Cela a fonctionné pour l’échantillonnage Spring Boot aop, même si je n’ai pas négligé l’actualisation du contexte. Échantillon Spring Boot aop :

Application:

package sample.aop;
@SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {
    // Simple example shows how an application can spy on itself with AOP
    @Autowired
    private HelloWorldService helloWorldService;
    @Override
    public void run(String... args) {
        System.out.println(this.helloWorldService.getHelloMessage());
    }
    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleAopApplication.class, args);
    }
}

L'application doit également être exécutée comme une application Spring Framework simple avec les annotations suivantes au lieu de @SpringBootApplication:

  • @Configuration
  • @EnableAspectJAutoProxy
  • @ComponentScan

et un AnnotationConfigApplicationContext au lieu de SpringApplication.

Un service:

package sample.aop.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class HelloWorldService {
    @Value("${name:World}")
    private String name;
    public String getHelloMessage() {
        return "Hello " + this.name;
    }
}

Aspect du moniteur:

package sample.aop.monitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ServiceMonitor {
    @AfterReturning("execution(* sample..*Service.*(..))")
    public void logServiceAccess(JoinPoint joinPoint) {
        System.out.println("Completed: " + joinPoint);
    }
}
4
secure_paul

Configurer @Autowired avec la configuration Java uniquement (donc pas de configuration basée sur XML) nécessite un peu plus de travail que simplement ajouter @Configuration à la classe, car elle nécessite également la méthode aspectOf.

Ce qui a fonctionné pour moi a été de créer une nouvelle classe:

@Component
public class SpringApplicationContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
       this.applicationContext = applicationContext;
    }
}

Et utilisez ensuite cela dans votre aspect conjointement avec @DependsOn @Configured and @Autowired:

@DependsOn("springApplicationContextHolder")
@Configuration
@Aspect
public class SomeAspect {

    @Autowired
    private SomeBean someBean;

    public static SomeAspect aspectOf() {
        return SpringApplicationContextProvider.getApplicationContext().getBean(SomeAspect.class);
    }

@DependsOn est nécessaire car le printemps ne peut pas déterminer la dépendance car le bean est utilisé de manière statique.

4
Wouter

Ce article de blog explique cela très bien. Etant donné que aspect singleton est créé en dehors du conteneur spring, vous devez utiliser factory-method = "aspectOf", disponible uniquement après son tissage par AspectJ (et non par Spring AOP):

Notez que factory-method = ”aspectOf” indique à Spring d’utiliser un vrai AspectJ (pas Spring AOP) pour créer ce haricot. Donc, après l'aspect est tissé dedans il a un Méthode «aspectOf».

Pour que : 

Aucune méthode d'usine correspondante trouvée: méthode d'usine 'aspectOf ()' - That signifierait que l'aspect n'a pas été tissé par AspectJ Weaver.

D'après mon expérience avec Spring 3.1, si je n'utilise pas @Autowired, mais un setter traditionnel pour l'injection de dépendance, il est injecté et fonctionne comme prévu sans aucun aspect tisserand. Bien que je rencontre des problèmes avec l'aspect d'être singleton ... Cela aboutit au modèle d'instanciation de 'perthis' ..

2
lisak

Ajoutez @Component à la classe d'aspect et vos dépendances devraient être automatiquement injectées ..__ et ajouter context: composant-scan pour le package contenant votre aspect dans le fichier de contexte Spring.

@Component
@Aspect
public class SomeAspect {
    /* following dependency should get injected */
    @Autowired
    SomeTask someTask;
    /* rest of code */  
}
1
Jitendra Vispute

Utilisez le temps de compilation, voir un exemple de plug-in à l'adresse: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml

La combinaison suivante d'annotation et de configuration de Spring fonctionne pour moi grâce aux notes ci-dessus de Tobias/Willie/Eric:

Classe:

package com.abc
@Configurable
@Aspect
public class MyAspect {
   @Autowired
   protected SomeType someAutoWiredField;
}

XML:

<context:spring-configured />
<context:component-scan base-package="com.abc" />
0
Qiang Li
@Configurable(autowire = Autowire.BY_TYPE)

Ajoutez cette annotation à votre classe Aspectj. Ensuite, il sera traité par Spring CIO.

0
Alan_Jin