Spring autowired bean для аспекта @Aspect является нулевым
У меня есть следующая конфигурация spring:
<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 существует и правильно подключен к другим классам). Следующие вещи меня путают:
- Контекст: компонентное сканирование должно собирать @Aspect? Если это так, то это был бы spring управляемый bean, таким образом, autwired должен работать?
- Если контекст: компонент-сканирование не для создания аспектов, как создается мой аспект? Я думал, что aop: aspectj-autoproxy просто создает beanPostProcessor для прокси-сервера моего класса @Aspect? Как это сделать, если это не spring управляемый bean?
Очевидно, вы можете сказать, что у меня нет понимания того, как все должно работать с нуля.
Ответы
Ответ 1
Аспект - это одноэлементный объект и создается вне контейнера Spring. Решение с конфигурацией XML состоит в использовании метода Spring factory для извлечения аспекта.
<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect"
factory-method="aspectOf" />
В этой конфигурации аспект будет рассматриваться как любой другой Spring bean, и автоустановка будет работать как обычно.
Вы должны использовать метод factory также для объектов Enum и других объектов без конструктора или объектов, созданных вне контейнера Spring.
Ответ 2
Другой вариант - добавить @Configurable
в ваш класс аспект вместо того, чтобы возиться с XML.
Ответ 3
Конфигурирование @Autowired только с настройкой java (так что без конфигурации на основе XML) требуется немного дополнительной работы, чем просто добавление @Конфигурация для класса, так как ему также нужен метод 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 @Configured и @Autowired:
@DependsOn("springApplicationContextHolder")
@Configuration
@Aspect
public class SomeAspect {
@Autowired
private SomeBean someBean;
public static SomeAspect aspectOf() {
return SpringApplicationContextProvider.getApplicationContext().getBean(SomeAspect.class);
}
Требуется @DependsOn, потому что spring не может определить зависимость, поскольку bean используется статически.
Ответ 4
У меня нет 50 комментариев, чтобы прокомментировать вопрос, так что вот еще один ответ, относящийся к @
Ответ Jitendra Vispute.
Официальный Spring doc упоминает:
Вы можете регистрировать классы классов как обычные beans в вашей XML-конфигурации Spring, или автоматически определять их через сканирование классов - как и любой другой Spring -установленный bean. Однако обратите внимание, что аннотации @Aspect недостаточно для автоопределения в пути к классам. Для этой цели вам нужно добавить отдельную аннотацию @Component (или, альтернативно, пользовательскую аннотацию стереотипа, которая квалифицируется в соответствии с правилами компонента Spring s сканер). Источник: Spring 'Документация 4.1.7.Release.
Это означало бы, что добавление аннотации @Component и добавление @ComponentScan в вашу конфигурацию приведет к работе примера @Jitendra Vispute. Для Spring boot aop sample он работал, хотя я не возился с обновлением контекста. Spring boot aop sample:
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);
}
}
Приложение также должно работать как обычное приложение 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);
}
}
Ответ 5
Это сообщение в блоге объясняет это очень хорошо. Из-за того, что аспект singleton создается вне контейнера spring, вам нужно использовать factory -method = "aspectOf", который доступен только после того, как он соткан в AspectJ (не spring AOP):
Обратите внимание на factory -method = "aspectOf", который сообщает spring использовать реальный AspectJ (не spring AOP) для создания этого bean. Так что после аспект сплетен в нем имеет Метод aspectOf.
Итак, чтобы:
Не найдено соответствующего метода factory: factory method 'aspectOf()' - Это означало бы, что этот аспект не был сплетен ткачом AspectJ.
Из моего опыта работы с spring 3.1, если я не использую @Autowired, но традиционный установщик для инъекции зависимостей, он вводится и работает так, как ожидалось, без искателя. Хотя я сталкиваюсь с проблемами с одним аспектом аэродрома... Это приводит к модели создания "perthis".
.
Ответ 6
Добавьте @Component в класс аспект, и ваши зависимости должны вводиться автоматически.
и добавить контекст: компонент-сканирование для пакета, в котором ваш аспект находится в spring файле контекста.
@Component
@Aspect
public class SomeAspect {
/* following dependency should get injected */
@Autowired
SomeTask someTask;
/* rest of code */
}
Ответ 7
Использовать время компиляции, см. пример плагина: 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" />
Ответ 8
Для Spring Загрузка для использования @Autowired с AspectJ Я нашел следующий метод.
В классе конфигурации добавьте свой аспект:
@Configuration
@ComponentScan("com.kirillch.eqrul")
public class AspectConfig {
@Bean
public EmailAspect theAspect() {
EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
return aspect;
}
}
Затем вы можете успешно выполнить аудит ваших сервисов в своем классе аспектов:
@Aspect
public class EmailAspect {
@Autowired
EmailService emailService;