首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Spring Java Config:如何创建带有运行时参数的prototype作用域@Bean?

Spring Java Config:如何创建带有运行时参数的prototype作用域@Bean?
EN

Stack Overflow用户
提问于 2014-03-04 03:43:44
回答 8查看 105.5K关注 0票数 160

使用Spring的Java Config,我需要使用只能在运行时获得的构造函数参数来获取/实例化一个原型作用域的bean。考虑下面的代码示例(为简洁起见进行了简化):

代码语言:javascript
复制
@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类的定义如下:

代码语言:javascript
复制
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配置配合使用,例如:

代码语言:javascript
复制
<bean id="thing", class="com.whatever.Thing" scope="prototype">
    <!-- other post-instantiation properties omitted -->
</bean>

如何使用Java配置实现相同的功能?以下内容在使用Spring 3.x时不起作用:

代码语言:javascript
复制
@Bean
@Scope("prototype")
public Thing thing(String name) {
    return new Thing(name);
}

现在,我可以创建一个Factory,例如:

代码语言:javascript
复制
public interface ThingFactory {
    public Thing createThing(String name);
}

但是,这违背了使用Spring来取代ServiceLocator和工厂设计模式的全部意义,而这种设计模式对于这个用例来说是非常理想的。

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

为工厂implementation定义工厂implementation

  • writing测试工厂

对于Spring已经通过XML配置支持的微不足道的东西来说,这是一项繁重的工作(相对而言)。

EN

回答 8

Stack Overflow用户

回答已采纳

发布于 2014-03-05 05:59:33

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

代码语言:javascript
复制
@Bean
@Scope("prototype")
public Thing thing(String name) {
    return new Thing(name);
}

用于注册bean定义并提供用于创建bean的工厂。它定义的ApplicationContext仅在请求时使用参数实例化,这些参数是直接或通过扫描该bean确定的。

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

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

允许指定显式的构造函数参数/工厂方法参数,覆盖bean定义中指定的默认参数(如果有的话)。

参数:

使用静态工厂方法的显式参数创建原型时要使用的参数参数。在任何其他情况下,使用非空args值都是无效的。

换句话说,对于这个prototype作用域的bean,您将提供将要使用的参数,而不是在bean类的构造函数中,而是在@Bean方法调用中。(此方法具有非常弱的类型保证,因为它使用bean的名称查找。)

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

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

请注意,如果您不想从ApplicationContextBeanFactory开始检索bean,则可以注入ObjectProvider (从Spring4.3开始)。

是专门为注入点设计的ObjectFactory变体,允许编程可选性和宽松的非唯一处理。

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

返回此工厂管理的对象的实例(可能是共享的,也可能是独立的)。

允许按照BeanFactory.getBean(String, Object)的方式指定显式构造参数。

例如,

代码语言:javascript
复制
@Autowired
private ObjectProvider<Thing> things;

[...]
Thing newThing = things.getObject(name);
[...]
票数 110
EN

Stack Overflow用户

发布于 2017-04-25 02:08:24

使用Spring > 4.0和Java 8,您可以更安全地执行此操作:

代码语言:javascript
复制
@Configuration    
public class ServiceConfig {

    @Bean
    public Function<String, Thing> thingFactory() {
        return name -> thing(name); // or this::thing
    } 

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

}

用法:

代码语言:javascript
复制
@Autowired
private Function<String, Thing> thingFactory;

public void onRequest(Request request) {
    //request is already validated
    String name = request.getParameter("name");
    Thing thing = thingFactory.apply(name);

    // ...
}

因此,现在您可以在运行时获取bean。当然,这是一个工厂模式,但是您可以节省一些编写特定类(如ThingFactory )的时间(但是,您必须编写自定义的@FunctionalInterface来传递两个以上的参数)。

票数 67
EN

Stack Overflow用户

发布于 2018-08-26 07:13:40

从Spring 4.3开始,就有了新的方法来解决这个问题。

ObjectProvider -它允许您将其作为依赖项添加到“带参数的”原型作用域bean中,并使用参数对其进行实例化。

下面是一个如何使用它的简单示例:

代码语言:javascript
复制
@Configuration
public class MyConf {
    @Bean
    @Scope(BeanDefinition.SCOPE_PROTOTYPE)
    public MyPrototype createPrototype(String arg) {
        return new MyPrototype(arg);
    }
}

public class MyPrototype {
    private String arg;

    public MyPrototype(String arg) {
        this.arg = arg;
    }

    public void action() {
        System.out.println(arg);
    }
}


@Component
public class UsingMyPrototype {
    private ObjectProvider<MyPrototype> myPrototypeProvider;

    @Autowired
    public UsingMyPrototype(ObjectProvider<MyPrototype> myPrototypeProvider) {
        this.myPrototypeProvider = myPrototypeProvider;
    }

    public void usePrototype() {
        final MyPrototype myPrototype = myPrototypeProvider.getObject("hello");
        myPrototype.action();
    }
}

这当然会在调用usePrototype时打印hello字符串。

票数 29
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22155832

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档