Spring重写bean配置设置其“Primary”

时间:2017-11-03 15:00:02

标签: java spring spring-bean

使用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依赖注入,一切都按照我想要/期望的方式工作,但遗憾的是我无法使用它。

3 个答案:

答案 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,所以你可以检查他们的名字。