使用Spring 3.X.X我有2个使用@Primary注释的服务,我创建了另一个配置类,我想使用其中一个服务的“自定义”版本。
由于某种原因,我在调试配置类时忽略了我看到它获得了正确的依赖关系但是当我想使用它时它设置了错误的依赖关系。
我发现使用代码更容易解释,这是一个例子:
界面Foo:
public interface Foo {
String getMessage();
}
使用硬编码的默认邮件进行主要实施:
@Primary
@Service("FooImpl")
public class FooImpl implements Foo {
private String message = "fooDefaultMessage";
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Bar界面:
public interface Bar {
void doBar();
}
Bar默认实现:
@Primary
@Service("BarImpl")
public class BarImpl implements Bar {
private Foo foo;
@Override
public void doBar() {
System.out.println(foo.getMessage());
}
public Foo getFoo() {
return foo;
}
@Autowired
public void setFoo(Foo foo) {
this.foo = foo;
}
}
到目前为止一切顺利,如果我想注入一个酒吧,我会按预期得到一切。事情是我有一个配置如下:
@Configuration
public class BeanConfiguration {
@Bean
@Qualifier("readOnlyFoo")
Foo readOnlyFoo() {
FooImpl foo = new FooImpl();
foo.setMessage("a read only message");
return foo;
}
@Bean
@Qualifier("readOnlyBar")
Bar readOnlyBar() {
BarImpl bar = new BarImpl();
bar.setFoo(readOnlyFoo());
return bar;
}
}
当我想注入一个readOnlyBar时,我得到了默认的Foo而不是这里设置的那个。
private Bar readOnlyBar;
@Autowired
@Qualifier("readOnlyBar")
public void setReadOnlyBar(Bar readOnlyBar) {
this.readOnlyBar = readOnlyBar;
}
...
readOnlyBar.doBar();
Foo bean的“fooDefaultMessage”。这是为什么?如何确保Spring使用我在@Configuration上设置的Bean?
提前致谢,
编辑:请注意,如果不是在@Configuration类中调用readOnly()方法,而是传递@Qualified参数,也会发生同样的情况。即:
@Configuration
public class BeanConfiguration {
@Bean
@Qualifier("readOnlyFoo")
Foo readOnlyFoo() {
FooImpl foo = new FooImpl();
foo.setMessage("a read only message");
return foo;
}
@Bean
@Qualifier("readOnlyBar")
Bar readOnlyBar(@Qualifier("readOnlyFoo") Foo readOnlyFoo) {
BarImpl bar = new BarImpl();
bar.setFoo(readOnlyFoo);
return bar;
}
}
如果我尝试使用Constructor依赖注入,一切都按照我想要/期望的方式工作,但遗憾的是我无法使用它。
答案 0 :(得分:3)
正如@yaswanth在他的回答中指出的那样,问题是foo
属性被bean创建后发生的属性注入所覆盖。
解决这个问题的一种方法是使用BarImpl
的构造函数注入而不是属性注入。这会让你的代码看起来像......
@Primary @Service("BarImpl")
class BarImpl implements Bar
{
private Foo foo;
@Autowired
public BarImpl(Foo foo) {
this.foo = foo;
}
}
,您的配置将是......
@Configuration
class BeanConfiguration
{
@Bean @Qualifier("readOnlyFoo")
Foo readOnlyFoo() {
FooImpl foo = new FooImpl();
foo.setMessage("a read only message");
return foo;
}
@Bean @Qualifier("readOnlyBar")
Bar readOnlyBar(@Qualifier("readOnlyFoo") Foo readOnlyFoo) {
return new BarImpl(readOnlyFoo);
}
}
作为侧轨;你还应该确保在工厂方法中使用依赖注入,而不是显式调用工厂方法,否则你最终会得到bean的多个实例...
祝你未来的事业顺利!
答案 1 :(得分:2)
您不能在实例变量上使用@Autowired
,并在@Bean
方法中设置它,就像您一样。春季生命周期的一部分是这样的
扫描的Bean定义=>创建的Bean实例=>后处理器称为=> .....
在您的示例中,
@Bean
@Qualifier("readOnlyBar")
Bar readOnlyBar() {
BarImpl bar = new BarImpl();
bar.setFoo(readOnlyFoo());
return bar;
}
创建了 readOnlyBar
bean,并且因为在此方法中调用了readOnlyFoo()
,所以readOnlyFoo
bean也会被实例化。至此,readOnlyBar
在其实例变量中有readOnlyFoo
。实例化bean之后,将调用AutowiredAnnotationBeanPostProcessor
来扫描bean的类(在本例中为BarImpl
),以获取实例变量或方法上的任何@Autowired
注释。它找到它们并尝试使用字段注入或setter注入(在@Autowired
注释存在的任何地方)将bean注入相应的变量。在这种情况下,因为我们有@Autowired
setter并且没有指定@Qualifier
注释,所以spring会注入@Primary
bean作为defaultFooMessage
bean。
AFAIK,没有简单的方法可以在春季配置@Bean
方法优先于自动装配。
使用@Bean
方法实例化所有bean将解决问题。
答案 2 :(得分:2)
通常最好使用@Bean(“beanName”)而不是两个注释:Bean和Qualifier。而且我不确定Qualifier是否适用于命名bean。无论如何,你应该用一种风格编写代码。因此,如果在@Service注释中使用value属性,则应在@Bean注释中使用value属性。
创建字段@Autowired List<Foo> allFoos
并在调试模式下查看它。你会看到你所有的Foo bean,所以你可以检查他们的名字。