Spring Java Config:如何使用运行时参数创建原型范围的@Bean? [英] Spring Java Config: how do you create a prototype-scoped @Bean with runtime arguments?
问题描述
使用 Spring 的 Java Config,我需要使用只能在运行时获得的构造函数参数获取/实例化原型范围的 bean.考虑以下代码示例(为简洁起见进行了简化):
Using Spring's Java Config, I need to acquire/instantiate a prototype-scoped bean with constructor arguments that are only obtainable at runtime. Consider the following code example (simplified for brevity):
@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 类的定义如下:
where the Thing class is defined as follows:
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
类的特定于实现的依赖项,不应为请求处理程序实现所知(紧密耦合).
Notice name
is final
: it can only be supplied via a constructor, and guarantees immutability. The other dependencies are implementation-specific dependencies of the Thing
class, and shouldn't be known to (tightly coupled to) the request handler implementation.
此代码与 Spring XML 配置完美配合,例如:
This code works perfectly well with Spring XML config, for example:
<bean id="thing", class="com.whatever.Thing" scope="prototype">
<!-- other post-instantiation properties omitted -->
</bean>
如何使用 Java 配置实现相同的功能?以下不适用于 Spring 3.x:
How do I achieve the same thing with Java config? The following does not work using Spring 3.x:
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
现在,我可以创建一个工厂,例如:
Now, I could create a Factory, e.g.:
public interface ThingFactory {
public Thing createThing(String name);
}
但这完全违背了使用 Spring 替换 ServiceLocator 和 Factory 设计模式的要点,这对于此用例来说是理想的.
But that defeats the entire point of using Spring to replace the ServiceLocator and Factory design pattern, which would be ideal for this use case.
如果 Spring Java Config 可以做到这一点,我就可以避免:
If Spring Java Config could do this, I would be able to avoid:
- 定义工厂接口
- 定义工厂实现
- 为 Factory 实现编写测试
对于 Spring 已经通过 XML 配置支持的微不足道的事情,这是大量的工作(相对而言).
That's a ton of work (relatively speaking) for something so trivial that Spring already supports via XML config.
推荐答案
在一个 @Configuration
类中,一个 @Bean
方法就像这样
In a @Configuration
class, a @Bean
method like so
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
用于注册bean定义并提供创建bean的工厂.它定义的 bean 仅根据请求使用直接或通过扫描 ApplicationContext
确定的参数实例化.
is used to register a bean definition and provide the factory for creating the bean. The bean that it defines is only instantiated upon request using arguments that are determined either directly or through scanning that ApplicationContext
.
对于prototype
bean,每次都会创建一个新对象,因此相应的@Bean
方法也会被执行.
In the case of a prototype
bean, a new object is created every time and therefore the corresponding @Bean
method is also executed.
您可以通过 BeanFactory#getBean(String name, Object... args)
声明的方法
You can retrieve a bean from the ApplicationContext
through its BeanFactory#getBean(String name, Object... args)
method which states
允许指定显式构造函数参数/工厂方法参数,覆盖指定的默认参数(如果有)bean定义.
Allows for specifying explicit constructor arguments / factory method arguments, overriding the specified default arguments (if any) in the bean definition.
参数:
args 使用显式参数创建原型时使用的参数到静态工厂方法.使用非空的 args 值是无效的在任何其他情况下.
args arguments to use if creating a prototype using explicit arguments to a static factory method. It is invalid to use a non-null args value in any other case.
换句话说,对于这个 prototype
作用域 bean,您提供将要使用的参数,而不是在 bean 类的构造函数中,而是在 @Bean
方法调用.(此方法具有非常弱的类型保证,因为它对 bean 使用名称查找.)
In other words, for this prototype
scoped bean, you are providing the arguments that will be used, not in the constructor of the bean class, but in the @Bean
method invocation. (This method has very weak type guarantees since it uses a name lookup for the bean.)
或者,您可以使用类型化的 BeanFactory#getBean(Class requiredType, Object... args)
看起来的方法按类型添加 bean.
Alternatively, you can use the typed BeanFactory#getBean(Class requiredType, Object... args)
method which looks up the bean by type.
至少对于 Spring 4+ 版本来说是这样.
This is at least true for Spring versions 4+.
请注意,如果您不想从 ApplicationContext
或 BeanFactory
开始进行 bean 检索,您可以注入一个 ObjectProvider
(从 Spring 4.3 开始).
Note that, if you don't want to start with the ApplicationContext
or BeanFactory
for your bean retrieval, you can inject an ObjectProvider
(since Spring 4.3).
专为注入点设计的 ObjectFactory
变体,允许程序化的可选性和宽松的非唯一处理.
A variant of
ObjectFactory
designed specifically for injection points, allowing for programmatic optionality and lenient not-unique handling.
并使用它的 getObject(Object...args)
方法
and use its getObject(Object... args)
method
返回对象的一个实例(可能是共享的或独立的)由这家工厂管理.
Return an instance (possibly shared or independent) of the object managed by this factory.
允许指定显式构造参数,沿着线BeanFactory.getBean(String, Object)
.
Allows for specifying explicit construction arguments, along the lines
of BeanFactory.getBean(String, Object)
.
例如
@Autowired
private ObjectProvider<Thing> things;
[...]
Thing newThing = things.getObject(name);
[...]
这篇关于Spring Java Config:如何使用运行时参数创建原型范围的@Bean?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!