我使用的是Spring 4.1.5。用@Bean定义的bean很奇怪。基本上我遇到问题当依赖类型与@Bean方法签名中定义的不同时,自动装配这些bean。
例如,如果我用它的接口类型(MessageService)定义一个@Bean,那么我就不能在另一个具有其实现类型(MessageServiceImpl)的依赖bean中自动装配它(我没有在代码中添加代理)。甚至没有它碰巧实现的另一个接口的类型。当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;
}
}
依赖bean
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;
首先,我认为它总能奏效。至少与我运行它的次数一样多。
这是一个错误还是我错过了什么?
编辑:我在想,为了模拟xml和@Component的确切行为,你必须将@Bean方法的返回类型声明为实现类型。如果我错了,请告诉我,但看起来总是以这种方式宣布@Beans没有任何缺点。 @Bean仍然可以被另一个具有相同方法名称和不同返回类型的@Bean覆盖。
感谢并为这篇长篇文章感到抱歉。
答案 0 :(得分:1)
非常有趣的问题。事实上,我只会发布我所假设的内容,而不是了解弹簧自动装配的细节。
首先让我们理所当然地认为spring在注入依赖项之前会构建一个bean定义表。此表包含元数据,例如bean名称和类型。当表格完成后,DI就会发生。
在xml中定义bean时,可以设置目标bean的类名。这是实现类。类似地,当使用@Component时,您也可以在实现类声明中设置它。这是在表中为这些bean定义注册的类型。但是,当使用@Bean并返回接口类型时,spring会使用此接口类型注册bean。
在DI过程中,当遇到@Autowired时,spring尝试使用类型信息解析bean表。当@Autowire需要一个接口时,spring会愉快地匹配表中的接口或实现定义。但是,当@Autowire需要实现类型并且只有可用的接口类型时,由于表信息更抽象,因此无法解析依赖关系。
现在,在MessagePrinter类中,实际上是三次注入相同的bean。如果spring设法解析它一次,可能会使用实现类型更新bean定义类型元数据。所以它第二次知道的比第一次更多,接线成功。从您的观察中可以看出,DI的顺序确实是非确定性的,但声明顺序在大多数时候都是有利的。如果你说它从未与实现声明一起工作,我就不会这样说。
如果有人更了解春天的内部并且可以提供更实际的解释,我也很想知道。