【发布时间】:2014-04-05 00:52:31
【问题描述】:
使用 Spring 的 Java Config,我需要使用只能在运行时获得的构造函数参数来获取/实例化一个原型范围的 bean。考虑以下代码示例(为简洁起见):
@Autowired
private ApplicationContext appCtx;
public void onRequest(Request request) {
//request is already validated
String name = request.getParameter("name");
Thing thing = appCtx.getBean(Thing.class, name);
//System.out.println(thing.getName()); //prints name
}
其中Thing类定义如下:
public class Thing {
private final String name;
@Autowired
private SomeComponent someComponent;
@Autowired
private AnotherComponent anotherComponent;
public Thing(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
注意name 是final:它只能通过构造函数提供,并保证不变性。其他依赖项是 Thing 类的特定于实现的依赖项,不应为请求处理程序实现所知(紧密耦合)。
此代码与 Spring XML 配置完美配合,例如:
<bean id="thing", class="com.whatever.Thing" scope="prototype">
<!-- other post-instantiation properties omitted -->
</bean>
如何使用 Java 配置实现相同的目标?以下不适用于 Spring 3.x:
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
现在,我可以创建一个工厂,例如:
public interface ThingFactory {
public Thing createThing(String name);
}
但这完全违背了使用 Spring 替换 ServiceLocator 和 Factory 设计模式的要点,这对于这个用例来说是理想的。
如果 Spring Java Config 可以做到这一点,我就可以避免:
- 定义工厂接口
- 定义工厂实现
- 为工厂实现编写测试
对于 Spring 已经通过 XML 配置支持的微不足道的事情,这是大量的工作(相对而言)。
【问题讨论】:
-
但是,您是否有理由不能自己实例化该类而必须从 Spring 中获取它?它是否依赖于其他bean?
-
@SotiriosDelimanolis 是的,
Thing实现实际上更复杂,并且确实依赖于其他 bean(为了简洁起见,我只是省略了它们)。因此,我不希望请求处理程序实现知道它们,因为这会将处理程序紧密耦合到它不需要的 API/bean。我会更新问题以反映您的(优秀)问题。 -
我不确定 Spring 是否允许在构造函数上这样做,但我知道您可以将
@Qualifier放在参数上,并将@Autowired放在设置器本身上。 -
在 Spring 4 中,您使用
@Bean的示例有效。使用您传递给getBean(..)的适当参数调用@Bean方法。 -
注入标有
@Autowired的字段或自己使用Spring类的setter并不难(几行代码),我已经使用了一段时间(如果我没记错的话,使用Spring 2.5 ) 不使用@Bean、@Scope("prototype")、@Configuration。我认为BeanFactory.getBean(String, Object[])不合理,因为它缺少编译时检查。当我想出我可以推荐的设计时,我可能会发布一个答案(我目前的设计有一些问题)。
标签: java spring scope prototype spring-java-config