使用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);
}
现在,我可以创建一个Factory,例如:
public interface ThingFactory {
public Thing createThing(String name);
}
但是,这违背了使用Spring来取代ServiceLocator和工厂设计模式的全部意义,而这种设计模式对于这个用例来说是非常理想的。
如果Spring Java Config可以做到这一点,我将能够避免:
为工厂implementation定义工厂implementation
对于Spring已经通过XML配置支持的微不足道的东西来说,这是一项繁重的工作(相对而言)。
发布于 2014-03-05 05:59:33
在@Configuration
类中,像这样的@Bean
方法
@Bean
@Scope("prototype")
public Thing thing(String name) {
return new Thing(name);
}
用于注册bean定义并提供用于创建bean的工厂。它定义的ApplicationContext
仅在请求时使用参数实例化,这些参数是直接或通过扫描该bean确定的。
对于prototype
bean,每次都会创建一个新对象,因此也会执行相应的@Bean
方法。
可以通过ApplicationContext
的BeanFactory#getBean(String name, Object... args)
方法从bean中检索bean,该方法声明
允许指定显式的构造函数参数/工厂方法参数,覆盖bean定义中指定的默认参数(如果有的话)。
参数:
使用静态工厂方法的显式参数创建原型时要使用的参数参数。在任何其他情况下,使用非空args值都是无效的。
换句话说,对于这个prototype
作用域的bean,您将提供将要使用的参数,而不是在bean类的构造函数中,而是在@Bean
方法调用中。(此方法具有非常弱的类型保证,因为它使用bean的名称查找。)
或者,您可以使用类型化的BeanFactory#getBean(Class requiredType, Object... args)
方法,该方法按类型查找bean。
至少对于Spring版本的4+来说是这样的。
请注意,如果您不想从ApplicationContext
或BeanFactory
开始检索bean,则可以注入ObjectProvider
(从Spring4.3开始)。
是专门为注入点设计的
ObjectFactory
变体,允许编程可选性和宽松的非唯一处理。
并使用它的getObject(Object... args)
方法
返回此工厂管理的对象的实例(可能是共享的,也可能是独立的)。
允许按照BeanFactory.getBean(String, Object)
的方式指定显式构造参数。
例如,
@Autowired
private ObjectProvider<Thing> things;
[...]
Thing newThing = things.getObject(name);
[...]
发布于 2017-04-25 02:08:24
使用Spring > 4.0和Java 8,您可以更安全地执行此操作:
@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);
}
}
用法:
@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
来传递两个以上的参数)。
发布于 2018-08-26 07:13:40
从Spring 4.3开始,就有了新的方法来解决这个问题。
ObjectProvider -它允许您将其作为依赖项添加到“带参数的”原型作用域bean中,并使用参数对其进行实例化。
下面是一个如何使用它的简单示例:
@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字符串。
https://stackoverflow.com/questions/22155832
复制相似问题