Spring autowired bean для аспекта @ Aspect имеет значение null

у меня есть следующая весенняя конфигурация:

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

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

<aop:aspectj-autoproxy/>

тогда у меня есть аспект:

@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));
    }
}

этот аспект работает (я могу нажать точку останова на afterPoll), но simpleEmailSender равен null. К сожалению, я не могу найти четкую документацию о том, почему это так. (Для записи мой simpleemailsender bean существует и правильно подключен к другим классам) меня смущают следующие вещи:

  1. является ли context: component-scan должен собирать @Aspect? Если это тогда, конечно, это будет spring managed bean, таким образом, autowired должен работать?
  2. если context: component-scan не предназначен для создания аспектов, как создается мой аспект? Я думал, что aop:aspectj-autoproxy просто создает beanPostProcessor для прокси-класса @Aspect? Как бы это сделать, если это не spring managed bean?

очевидно, вы можете сказать, что у меня нет понимания, как нужно работать с нуля.

9 ответов


аспект является одноэлементным объектом и создается вне контейнера Spring. Решение с конфигурацией XML заключается в использовании Заводского метода Spring для извлечения аспекта.

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

С этой конфигурацией аспект будет рассматриваться как любой другой Spring bean, и autowiring будет работать как обычно.

вы должны использовать factory-method также для объектов перечисления и других объектов без конструктора или объектов, созданных вне Spring контейнер.


другой вариант-добавить @Configurable к вашему классу аспекта вместо возиться с XML.


для Spring Boot для использования @Autowired с AspectJ я нашел следующий метод. В классе конфигурации добавьте свой аспект:

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

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

}

после этого вы можете успешно autowire ваши обслуживания в вашем классе аспекта:

@Aspect
public class EmailAspect {

    @Autowired
    EmailService emailService;

у меня нет 50 rep, чтобы прокомментировать вопрос, поэтому вот еще один ответ, касающийся @ Джитендра Vispute ответ. Официальный весенний документ упоминает:

вы можете зарегистрировать классы аспектов как обычные бобы в конфигурации Spring XML или автоопределить их с помощью сканирования classpath - как и любой другой управляемый Spring Боб. Однако обратите внимание, что аннотации @Aspect недостаточно для автоопределения в пути к классам: для этого необходимо добавить отдельная аннотация @Component (или, альтернативно, пользовательская аннотация стереотипа, которая квалифицируется в соответствии с правилами сканера компонентов Spring).Источник: Spring ' 4.1.7.Release ' documentation.

это означало бы, что добавление аннотации @Component и добавление @ComponentScan в вашу конфигурацию заставит работать пример @Jitendra Vispute. Для образца spring boot aop он работал, хотя я не возился с обновлением контекста.Весна загрузите образец aop:

приложение:

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);
    }
}

приложение также должно работать как простое приложение Spring Framework со следующими аннотациями вместо @SpringBootApplication:

  • @Configuration
  • @EnableAspectJAutoProxy
  • @ComponentScan

и AnnotationConfigApplicationContext вместо SpringApplication.

сервис:

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;
    }
}

Монитор Аспект:

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);
    }
}

настройка @Autowired только с конфигурацией java (поэтому нет конфигурации на основе XML) требует немного дополнительной работы, чем просто добавление @Configuration классу, так как ему также нужен метод aspectOf.

что сработало для меня, так это создание нового класса:

@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;
    }
}

а затем использовать это в вас аспект в сочетании с использованием @DependsOn @настроено и @Autowired:

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

    @Autowired
    private SomeBean someBean;

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

@DependsOn необходим, потому что spring не может определить зависимость, потому что Боб используется статично.


этой блоге объясняет это очень хорошо. Из-за того, что аспект singleton создается вне контейнера spring, вам нужно будет использовать factory-method="aspectOf", который доступен только после того, как он вплетен AspectJ ( не Spring AOP ) :

уведомление factory-method= "aspectOf", который сообщает Spring использовать реальный AspectJ (не Spring AOP ) аспект для создания этого компонента. Так что после аспект сплетен в нем имеет "aspectOf" метод.

Так что :

не найден соответствующий заводской метод: заводской метод "aspectOf ()" - это это означало бы, что аспект не был соткан AspectJ weaver.

из моего опыта работы с spring 3.1, Если я не использую @Autowired, но традиционный сеттер для инъекции зависимостей, он вводится и работает, как ожидалось, без aspectJ weaver. Хотя я сталкиваюсь с проблемами с аспектом, являющимся синглтоном... Это приводит к "perthis" модель создания экземпляра. .


добавьте @Component в класс aspect, и ваши зависимости должны вводиться автоматически. и добавьте контекст: компонент-сканирование пакета, в котором ваш аспект находится в файле контекста spring.

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

используйте время компиляции, см. Пример плагина по адресу: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml

следующая комбинация аннотации и Spring config работает для меня благодаря Примечаниям выше Тобиаса / Вилли/Эрика:

класс:

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

XML:

<context:spring-configured />
<context:component-scan base-package="com.abc" />

@Configurable(autowire = Autowire.BY_TYPE)

добавить эту аннотацию в свой Aspectj класса. Затем он будет обработан весенним МОК.