Spring Java Config的继承使用不同的bean

时间:2015-12-22 21:23:19

标签: java spring

以下示例显示了使用spring java config显式连接依赖关系,这会导致在使用spring接口和Spring接口类时连接不同的bean。

这似乎不应该发生或者至少给出正常警告,即有两个bean作为自动装配的候选者,并且它不知道选择哪个。

关于这个问题的任何想法?我的猜测是配置类之间没有真正的名称间距,正如语法" this.iConfig.a()"这可能被视为一个错误(如果只是因为没有警告2个候选bean)?

public class Main
{
    public static void main( final String[] args )
    {
        final ApplicationContext context = new AnnotationConfigApplicationContext( IConfigImpl.class, ServiceConfig.class );
        final Test test = context.getBean( Test.class );

        System.out.println( test );
    }
}

public class Test
{
    private final String string;

    public Test( final String param )
    {
        this.string = param;
    }

    public String toString()
    {
        return this.string;
    }
}

@Configuration
public interface IConfig
{
    @Bean
    public String a();
}

@Configuration
public class IConfigImpl implements IConfig
{
    @Bean
    public String a()
    {
        return "GOOD String";
    }
}

@Configuration
public class ServiceConfig
{
    @Autowired
    IConfig iConfig;

    @Bean
    Test test()
    {
        return new Test( this.iConfig.a() );
    }

    @Bean
    String a()
    {
        return "BAD String";
    }
}

在这种情况下,我希望有" GOOD String"要始终在Test对象中连接,但是翻转IConfigImpl.class的顺序,上下文加载器中的ServiceConfig.class会更改加载的字符串。

使用Spring 4.0.7进行测试

编辑:进一步的测试表明这与无意识的配置无关。如果删除IConfig接口,也会产生同样的结果。

2 个答案:

答案 0 :(得分:2)

我相信这是Spring的一种行为多年。

如果重新定义bean,那么最后加载的bean将获胜。

另一个问题是如何在使用java配置时控制bean加载的顺序。查看本文http://www.java-allandsundry.com/2013/04/spring-beans-with-same-name-and.html,其中介绍了如何使用其他Spring java配置的@Import进行排序。

  

解决方案实际上很简单 - 如果您需要覆盖以前的解决方案   定义bean(没有说自动装配的灵活性   不同的bean名称),要么都使用XML bean配置   被覆盖的bean和覆盖的bean或使用   @组态。 XML bean配置是这里的第一个例子   @Configuration的那个会是这样的:

@Configuration
public class Context1JavaConfig {
    @Bean
    public MemberService memberService() {
        return new MemberSvcImpl1();
    }
}

@Configuration
@Import(Context1JavaConfig.class)
public class Context2JavaConfig {
    @Bean
    public MemberService memberService() {
        return new MemberSvcImpl2();
    }
}

答案 1 :(得分:0)

Stepan提到了秩序问题。以下是关于答案的your comment

  

覆盖相同名称的bean是有道理的,但在这种情况下,我是   特别引用iConfig中指定的bean   组态。我希望得到那里指定的那个。

为了实现@Configuration和bean的缓存以便像

这样的调用
@Configuration
class Example {
    @Bean
    public UncaughtExceptionHandler uncaughtExceptionHandler() {
        return (thread, throwable) -> System.out.println(thread + " => " + throwable.getMessage());
    }

    @Bean
    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public Thread newThread() {
        Thread thread = new Thread();
        thread.setUncaughtExceptionHandler(uncaughtExceptionHandler()); // <<<<<< allowing this
        return thread;
    }
}

Spring实际上使用CGLIB来创建@Configuration带注释类的代理子类型。此代理维护对后备ApplicationContext的引用,并使用它来解析bean。

所以你的例子中的电话

return new Test(this.iConfig.a());

并未真正调用IConfigImpl#a()。它从代理拦截器调用this code(截至4.2)。代码使用相应的Method来确定目标bean名称,并使用ApplicationContext的{​​{1}}来解析bean。由于已经覆盖了名为BeanFactory的bean的bean定义,因此将使用该新bean定义。该bean定义使用a方法作为工厂方法。

文档here

中对此进行了描述
  

所有ServiceConfig#a()类在启动时都使用CGLIB进行子类化。   在子类中,子方法首先检查容器是否为any   缓存(作用域)bean在调用父方法并创建一个   新实例。

  

这可能被视为错误[...]吗?

我不相信。行为记录在案。