Spring 覆盖 bean 配置设置其"Primary"



使用Spring 3.X.X,我有两个用@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;
}
}

条形界面:

public interface Bar {
void doBar();
}

条形默认实现:

@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,而不是这里设置的Foo。

private Bar readOnlyBar;
@Autowired
@Qualifier("readOnlyBar")
public void setReadOnlyBar(Bar readOnlyBar) {
this.readOnlyBar = readOnlyBar;
}
...
readOnlyBar.doBar();

具有带有"fooDefaultMessage"的Foo bean。为什么?如何确保Spring使用我在@Configuration上设置的Bean?

提前感谢

EDIT:请注意,如果不是在@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;
}
}

如果我尝试使用构造函数依赖项注入,那么一切都会如我所希望/期望的那样工作,但不幸的是,我无法使用它。

@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的多个实例。。。

祝你今后一切顺利!

不能在实例变量上使用@Autowired,也不能像以前那样在@Bean方法中设置它。春季生命周期的一部分是这样的

扫描的Bean定义=>创建的Bean实例=>调用的后处理器=>。。。。。

在您的示例中,

@Bean
@Qualifier("readOnlyBar")
Bar readOnlyBar() {
BarImpl bar = new BarImpl();
bar.setFoo(readOnlyFoo());
return bar;
}

readOnlyBarbean被创建,并且因为readOnlyFoo()是从该方法中调用的,所以readOnlyFoobean也被实例化。到目前为止,readOnlyBar的实例变量中有readOnlyFoo。一旦bean被实例化,就会调用AutowiredAnnotationBeanPostProcessor,它会扫描bean的类(在本例中为BarImpl),以查找实例变量或方法上的任何@Autowired注释。它找到它们,并尝试使用字段注入或setter注入(只要存在@Autowired注释)将bean注入到相应的变量中。在这种情况下,由于我们有@Autowiredsetter,并且没有指定@Qualifier注释,spring注入了@Primarybean,即defaultFooMessagebean。

AFAIK,没有一种简单的方法可以在春季配置@Bean方法,使其优先于自动布线。

使用@Bean方法实例化所有bean将解决此问题。

通常最好使用@Bean("beanName"),而不是两个注释:Bean和Qualifier。我不确定Qualifier是否适用于命名bean。无论如何,你应该用一种风格编写代码。所以,如果您在@Service注释中使用value属性,那么您应该在@Bean注释中使用值属性。

创建字段@Autowired List<Foo> allFoos并在调试模式下查看它。你会看到你们所有的Foo豆,这样你就可以检查它们的名字。