下面的示例显示了使用spring java config显式地进行依赖关系的连接,这导致在对spring配置类使用and接口时,连接了另一个bean。

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

对这个问题有什么想法吗?我的猜测是语法“ this.iConfig.a()”所隐含的配置类之间没有真实名称间隔吗?是否可以将其视为错误(如果仅是为了不警告有关两个候选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接口,也会产生相同的结果。

最佳答案

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名称,并使用ApplicationContextBeanFactory解析bean。由于名为a的bean的bean定义已被覆盖,因此将使用新的bean定义。该bean定义使用ServiceConfig#a()方法作为其工厂方法。

在文档here中对此进行了描述。


  所有@Configuration类在启动时都使用CGLIB进行了子类化。
  在子类中,子方法首先检查容器中是否存在任何
  在调用父方法并创建一个
  新实例。





  可以将其视为错误[...]吗?


我不相信该行为已记录。

08-03 18:18
查看更多