Spring Autowired bean pour @Aspect aspect est null

J'ai la configuration de printemps 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/>

Alors 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 peux pas trouver de documentation claire sur les raisons de cela. (Pour mémoire, mon bean simpleEmailSender existe et est correctement câblé dans d'autres classes) les choses suivantes me confondent:

  1. contexte:component-scan est-il censé récupérer @ Aspect ? Si c'est le cas alors sûrement il le ferait soyez un haricot géré par printemps, donc autowired devrait fonctionner?
  2. Si context: component-scan n'est pas destiné à créer des aspects, Comment mon aspect est-il créé? Je pensais aop: aspectj-autoproxy crée juste un beanPostProcessor pour proxy ma classe @ Aspect ? Comment ferait-il cela si ce n'est pas un haricot géré par le printemps?

Évidemment, vous pouvez dire que je n'ai pas une compréhension de la façon dont les choses devraient fonctionner à partir de zéro.

26
demandé sur mogronalol 2012-03-09 16:10:32

9 réponses

L'aspect est un objet singleton et est créé en dehors du conteneur Spring. Une solution avec une configuration XML consiste à utiliser la méthode factory 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 l'autowiring fonctionnera normalement.

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

31
répondu Espen 2012-03-11 20:56:00

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

17
répondu tobiasbayer 2012-04-18 07:22:36

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 exécuter automatiquement vos services dans votre classe d'aspect:

@Aspect
public class EmailAspect {

    @Autowired
    EmailService emailService;
8
répondu Kirill Ch 2017-09-06 11:06:49

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

Vous pouvez enregistrer des classes d'aspect en tant que beans réguliers dans votre configuration XML Spring, ou les détecter automatiquement via l'analyse classpath - comme tout autre bean géré par Spring. Cependant, notez que l'annotation @ Aspect n'est pas suffisante pour la détection automatique dans le classpath: à cette fin, vous devez ajouter un @ Component annotation (ou alternativement une annotation stéréotype personnalisée qui se qualifie, selon les règles du scanner de composants de Spring). Source: Printemps ' 4.1.7.La libération de la documentation.

Cela signifierait que l'ajout d'une annotation @ Component et l'ajout du @ ComponentScan sur votre Configuration feraient fonctionner l'exemple de @Jitendra Vispute. Pour l'exemple AOP spring boot, cela a fonctionné, même si je n'ai pas joué avec le rafraîchissement du contexte.démarrage à ressort aop échantillon :

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 s'exécuter en tant qu'application Spring Framework simple avec les annotations suivantes au lieu de @SpringBootApplication:

  • @ Configuration
  • @EnableAspectJAutoProxy
  • @ ComponentScan

Et une AnnotationConfigApplicationContext au lieu de SpringApplication.

Le 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
répondu secure_paul 2017-05-23 12:09:41

Configurer @ Autowired avec java config uniquement (donc pas de configuration basée sur XML) nécessite un peu de travail supplémentaire que d'ajouter simplement @Configuration à la classe, car elle a également besoin de la méthode aspectOf.

Ce qui a fonctionné pour moi était 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;
    }
}

Puis utilisez-le dans votre aspect en conjonction avec @ DependsOn @ configuré et @ Autowired:

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

    @Autowired
    private SomeBean someBean;

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

Le @ DependsOn est nécessaire car spring ne peut pas déterminer la dépendance car le haricot est utilisé staticly.

4
répondu Wouter 2017-08-29 22:06:23

Cet article de blog l'explique très bien. En raison du fait que aspect singleton est créé en dehors du conteneur spring, vous devez utiliser factory-method= "aspectOf" qui n'est disponible qu'après avoir été tissé par AspectJ (pas Spring AOP):

Avis factory-method= "aspectOf" qui indique à Spring d'utiliser un vrai AspectJ (pas Spring AOP ) aspect pour créer ce bean. De sorte qu'après l'aspect est tissé en elle a un méthode" aspectOf".

De sorte que :

Aucune Méthode Factory correspondante n'a été trouvée: factory method '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 le setter traditionnel pour l'injection de dépendance, il est injecté et fonctionne comme prévu sans aspectJ weaver. Bien que je rencontre des problèmes avec l'aspect étant singleton... Il en résulte un modèle d'instanciation 'perthis'. .

2
répondu lisak 2012-07-16 11:14:44

Ajoutez @Component à la classe aspect et vos dépendances devraient être injectées automatiquement. et ajoutez context: component-scan pour le paquet où votre aspect est dans le fichier de contexte spring.

@Component
@Aspect
public class SomeAspect {
    /* following dependency should get injected */
    @Autowired
    SomeTask someTask;
    /* rest of code */  
}
1
répondu Jitendra Vispute 2014-07-03 01:09:53

Utilisez le tissage de compilation, voir l'exemple de plugin à: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml

La combinaison suivante d'annotation et de configuration de printemps fonctionne pour moi grâce aux notes ci-dessus par 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
répondu Qiang Li 2015-11-16 00:44:37
@Configurable(autowire = Autowire.BY_TYPE)

Ajoutez cette annotation à votre classe Aspectj. Ensuite, il sera géré par le printemps CIO.

0
répondu astronuaa 2018-09-29 11:02:16