java - Spring Java 配置 : how do you create a prototype-scoped @Bean with runtime arguments?

标签 java spring scope prototype spring-java-config

使用 Spring 的 Java Config,我需要使用只能在运行时获得的构造函数参数来获取/实例化一个原型(prototype)范围的 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;
    }
}

注意 namefinal:它只能通过构造函数提供,并保证不变性。其他依赖项是 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 和工厂设计模式的观点,这对于这个用例来说是理想的。

如果 Spring Java Config 可以做到这一点,我就可以避免:

  • 定义工厂接口(interface)
  • 定义工厂实现
  • 为工厂实现编写测试

对于 Spring 已经通过 XML 配置支持的微不足道的事情,这是大量的工作(相对而言)。

最佳答案

@Configuration 类中,像这样的 @Bean 方法

@Bean
@Scope("prototype")
public Thing thing(String name) {
    return new Thing(name);
}

用于注册一个bean定义并提供创建bean的工厂。它定义的 bean 仅根据请求使用直接或通过扫描 ApplicationContext 确定的参数进行实例化。

对于 prototype bean,每次都会创建一个新对象,因此也会执行相应的 @Bean 方法。

您可以通过 ApplicationContextBeanFactory#getBean(String name, Object... args) 检索 bean声明的方法

Allows for specifying explicit constructor arguments / factory method arguments, overriding the specified default arguments (if any) in the bean definition.

Parameters:

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 的名称查找。)

或者,您可以使用键入的 BeanFactory#getBean(Class requiredType, Object... args)按类型查找 bean 的方法。

至少对于 Spring 4+ 版本是这样。

请注意,如果您不想从 ApplicationContextBeanFactory 开始进行 bean 检索,您可以注入(inject) ObjectProvider (自 Spring 4.3 起)。

A variant of ObjectFactory designed specifically for injection points, allowing for programmatic optionality and lenient not-unique handling.

并使用它的getObject(Object... args)方法

Return an instance (possibly shared or independent) of the object managed by this factory.

Allows for specifying explicit construction arguments, along the lines of BeanFactory.getBean(String, Object).

例如,

@Autowired
private ObjectProvider<Thing> things;

[...]
Thing newThing = things.getObject(name);
[...]

关于java - Spring Java 配置 : how do you create a prototype-scoped @Bean with runtime arguments?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22155832/

相关文章:

c - 关于 C 指针范围

java - Swingx组件使用方法 : jXSearchPanel with jTable

java - 这个演示中的 JVM 内存分配和释放解释?

java - 获取maven中两个版本之间更改文件的列表

java - Spring Boot - JNDI 值查找

ruby - 您可以在方法内部访问具有外部作用域的 Ruby 变量吗?

java - 使用了哪些组件和技术来创建 Bitdefender 的 GUI?

spring - GORM无法从JAR引导

java - Struts 相对路径在 7.0.78 中变平,但在 7.0.79 中没有变平,打破了 isNormalized

angularjs - 多个指令 [myPopup、myDraggable] 请求新的/隔离的范围