Я использую Spring 4.1.5. Bean-компоненты, определенные с помощью @Bean, ведут себя странно. В основном у меня возникают проблемы с автоматическим подключением этих компонентов, когда тип зависимости отличается от того, что определено в сигнатуре метода @Bean.
Например, если я определяю @Bean с его типом интерфейса (MessageService), я не могу автоматически связать его с другим зависимым компонентом с его типом реализации (MessageServiceImpl) (я не помещаю прокси в картину). Даже с типом другого интерфейса, который он реализует. Эти сценарии работают должным образом, когда bean-компонент определен в xml или с @Component. Вот код:
Основной интерфейс
package hello.annotations;
public interface MessageService {
String getMessage();
}
Дополнительный интерфейс
package hello.annotations;
public interface AnotherInterface {
boolean anotherMethod();
}
Реализация
package hello.annotations;
public class MessageServiceImpl implements MessageService, AnotherInterface {
public String getMessage() {
return "my msg";
}
public boolean anotherMethod() {
return true;
}
}
Зависимый компонент
package hello.annotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
public class MessagePrinter {
@Autowired
private MessageServiceImpl service1;
@Autowired
private AnotherInterface service2;
@Autowired
private MessageService service;
public void printMessage() {
System.out.println(System.identityHashCode(service));
System.out.println(System.identityHashCode(service1));
System.out.println(System.identityHashCode(service2));
System.out.println(this.service.getMessage());
}
}
Приложение
package hello.annotations;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class Application {
@Bean
MessageService mockMessageService() {
return new MessageServiceImpl();
}
@Bean
MessagePrinter messagePrinter() {
return new MessagePrinter();
}
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(Application.class);
MessagePrinter printer = context.getBean(MessagePrinter.class);
printer.printMessage();
}
}
Итак, вы можете видеть, что в MessagePrinter я пытаюсь внедрить MessageServiceImpl различными способами: как интерфейс MessageService, как MessageServiceImpl и как AnotherInterface.
Я бы сказал, что этот код не работает, и он выдает эту ошибку:
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private hello.annotations.MessageServiceImpl hello.annotations.MessagePrinter.service1; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.annotations.MessageServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:561)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
... 12 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.annotations.MessageServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1301)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1047)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:942)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:533)
... 14 more{@org.springframework.beans.factory.annotation.Autowired(required=true)}
Но вот где это становится еще более странным: я только что заметил, что это не детерминировано. Иногда это работает. Запустите его 5-6 раз, и вы заметите, что иногда он работает. И еще одно наблюдение: если я изменю порядок полей зависимостей в MessagePrinter, поставив
@Autowired private MessageService service;
во-первых, то я думаю, что это всегда работает. По крайней мере, столько раз, сколько я запускал его.
Это ошибка или я что-то упускаю?
EDIT: я думаю, что для имитации точного поведения xml и @Component вы должны объявить возвращаемый тип метода @Bean типом реализации. Дайте мне знать, если я ошибаюсь, но, похоже, нет ничего плохого в том, чтобы всегда объявлять @Beans таким образом. @Bean по-прежнему может быть переопределен другим @Bean с тем же именем метода и другим типом возвращаемого значения.
Спасибо и извините за длинный пост.