部分自动连接Spring原型bean,运行时确定构造函数参数

时间:2017-03-23 21:32:38

标签: java spring dependency-injection constructor autowired

ConstructorResolver.autowireConstructor(...)的javadoc说

  

如果指定了显式构造函数参数值,则还应用,将所有剩余参数与bean工厂中的bean匹配。

但我无法让它发挥作用。我得到了BeanCreationException

  

无法解析匹配的构造函数(提示:为简单参数指定索引/类型/名称参数以避免类型歧义)

在这个例子中,我有一个带有构造函数的bean,它接受Spring bean以及只在运行时才知道的Stringint

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class BeanWithRuntimeDependencies {

    public final DependencyA dependencyA;
    public final DependencyB dependencyB;
    public final String myString;
    public final int myInt;

    public BeanWithRuntimeDependencies(
            DependencyA dependencyA, DependencyB dependencyB, 
            String myString, int myInt) {
        this.dependencyA = dependencyA;
        this.dependencyB = dependencyB;
        this.myString = myString;
        this.myInt = myInt;
    }

}

@Component
public class DependencyA { /* ... */ }

@Component
public class DependencyB { /* ... */ }

和我的测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PrototypeBeanConstructorsApplicationTests {

    @Autowired private ApplicationContext context;

    @Autowired private DependencyA dependencyA;

    @Autowired private DependencyB dependencyB;

    @Test
    public void getBeanFromContext() {
        BeanWithRuntimeDependencies bean = 
            context.getBean(BeanWithRuntimeDependencies.class, "runtime string", 10);
        assertNotNull(bean);
        assertEquals(dependencyA, bean.dependencyA);
        assertEquals(dependencyB, bean.dependencyB);
        assertEquals("runtime string", bean.myString);
        assertEquals(10, bean.myInt);
    }

}

ConstructorResolver.autowireConstructor(...)的源代码有一条评论:

// Explicit arguments given -> arguments length must match exactly.

似乎与其javadoc相矛盾。

有可能这样做吗?我做错了什么?

1 个答案:

答案 0 :(得分:4)

看起来不可能按照你的方式去做。

实际上这是奇怪的情况。根据{{​​1}}中的下一个片段(源代码行#207 ),Spring不会将您的构造函数视为调用的候选者:

ConstructorResolver.autowireConstructor(...)

正如您所正确指出的那样,它与javadoc声明完全相反:

  

...将所有剩余参数与bean工厂中的bean匹配

但是无论如何实现意味着默认情况下Spring不能解析构造函数来实例化这样的bean。您必须手动创建工厂方法。类似的东西:

...    
// Explicit arguments given -> arguments length must match exactly.
if (paramTypes.length != explicitArgs.length) {
    continue;
}

然后你可以根据需要从上下文中获取bean:

@Configuration
public class Config{

    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public BeanWithRuntimeDependencies beanWithRuntimeDependencies(String myString, int myInt){
        return new BeanWithRuntimeDependencies(dependencyA(), dependencyB(), myString, myInt);
    }

    @Bean
    public DependencyA dependencyA(){
        return new dependencyA();
    }

    @Bean
    public DependencyB dependencyB(){
        return new dependencyB();
    }
}

如果您不想与配置类和工厂方法达成协议,您只需将所需的bean传递到BeanWithRuntimeDependencies bean = context.getBean(BeanWithRuntimeDependencies.class, "runtime string", 10); 即可。当然你必须从上下文中获取这些bean:

context.getBean()