Spring Java Config:如何创建一个带有运行时参数的原型范围 @Bean?
Spring Java Config:如何创建一个带有运行时参数的原型范围 @Bean?
使用Spring的Java配置,我需要获取/实例化具有只在运行时可获取的构造函数参数的原型作用域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; } }
请注意,名称为final:它只能通过构造函数提供,并保证不可变性。其他依赖项是Thing类的实现特定依赖项,并且不应该被了解(紧密耦合)请求处理程序实现。
这个代码在Spring XML配置下表现得非常出色,例如:
如何使用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和Factory设计模式的整个目的,这将是这种用例的理想选择。
如果Spring Java Config可以做到这一点,我将能够避免:
定义工厂接口
定义工厂实现
编写工厂实现的测试
这是一项相对而言非常琐碎的工作,Spring已经通过XML配置支持了这个问题。
使用Spring> 4.0和Java 8,你可以更加类型安全地执行此操作:
@Configuration public class ServiceConfig { @Bean public FunctionthingFactory() { return name -> thing(name); // or this::thing } @Bean @Scope(value = "prototype") public Thing thing(String name) { return new Thing(name); } }
使用方法:
@Autowired private FunctionthingFactory; public void onRequest(Request request) { //request is already validated String name = request.getParameter("name"); Thing thing = thingFactory.apply(name); // ... }
现在你可以在运行时获取你的bean。当然,这是一种工厂模式,但是你可以节省编写特定类(例如ThingFactory
)的时间(但是,您将不得不编写自定义@FunctionalInterface
以传递超过两个参数)。
在一个@Configuration类中,像下面这样的一个@Bean方法:\n
@Bean @Scope("prototype") public Thing thing(String name) { return new Thing(name); }
\n用于注册一个bean定义并提供创建bean的工厂。它所定义的bean仅在使用直接或通过扫描ApplicationContext确定的参数请求时才实例化。\n在原型bean的情况下,每次都会创建一个新对象,因此相应的@Bean方法也会被执行。\n可以通过其BeanFactory#getBean(String name, Object... args)方法从ApplicationContext检索bean,其中明确指定了构造函数参数/工厂方法参数,覆盖了bean定义中指定的默认参数(如果有)。\n换句话说,对于这个作用域为原型的bean,你提供的是将在@Bean方法调用中使用的参数,而不是bean类的构造函数。(这个方法使用名称查找bean,具有非常弱的类型保证。)\n或者,你可以使用具有类型的BeanFactory#getBean(Class requiredType, Object... args)方法按类型查找bean。\n至少对于Spring版本4+来说是这样的。\n请注意,如果你不想从ApplicationContext或BeanFactory开始检索你的bean,你可以注入一个ObjectProvider(自Spring4.3起)。\n这是专门为依赖注入点设计的ObjectFactory变体,允许编程的可选性和宽松的非唯一处理。\n并使用它的getObject(Object... args)方法返回此工厂管理的对象的一个实例(可能是共享的或独立的),允许指定明确的构造参数,类似于BeanFactory.getBean(String, Object)。例如,\n
@Autowired private ObjectProviderthings; [...] Thing newThing = things.getObject(name); [...]