以下示例显示了使用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接口,也会产生同样的结果。答案 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在调用父方法并创建一个 新实例。
这可能被视为错误[...]吗?
我不相信。行为记录在案。