Spring并在运行时将参数传递给工厂方法



方法上下文的文档。getBean(name, user)表示

允许指定显式构造函数参数/工厂方法参数

但是无论我做什么(尝试了所有的方法),在初始化期间加载bean时,我得到了最合乎逻辑的设置:

org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fileValidator' defined in
PortletContext resource
[/WEB-INF/classes/context/customer-form-portlet.xml]: Unsatisfied
dependency expressed through constructor argument with index 0 of type
[com.liferay.portal.model.User]: Ambiguous factory method argument
types - did you specify the correct bean references as factory method
arguments?
    org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'fileValidator' defined in
PortletContext resource
[/WEB-INF/classes/context/customer-form-portlet.xml]: Unsatisfied
dependency expressed through constructor argument with index 0 of type
[com.liferay.portal.model.User]: Ambiguous factory method argument
types - did you specify the correct bean references as factory method
arguments?

<bean id="fileValidator" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />

private FileValidator(User user) {
    this.user = user;
}
public static FileValidator createInstance(User user) {
    return new FileValidator(user);
}

注释说您可以这样做,但是如果您在该bean的xml定义中指定构造函数参数,则会失败。

javadoc说:

args -使用静态工厂方法的显式参数创建原型时使用的参数。

所以bean定义必须是一个原型作用域的bean,即
<bean id="fileValidator" 
      scope="prototype" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />

阅读了20篇文章,我发现如何让自定义工厂方法在运行时接受参数并不明显,特别是因为我们被迫使用constructor-arg标记并在上下文中引用现有bean作为下面的设置,并且所讨论的类充当静态工厂方法。

<bean id="user" class="something.something.User" />
<bean id="fileValidator" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" >
      <constructor-args ref="user" />
</bean>

我通过从上下文中获取构造器参数中使用的bean的实例,然后用您在运行时使用的值填充它,从而使其工作。当您获得工厂生成的bean时,这个bean将被用作参数。

public class X {
   public void callFactoryAndGetNewInstance() {
      User user = context.getBean("user");
      user.setSomethingUsefull(...);
      FileValidator validator = (FileValidator)context.getBean("fileValidator");
      ...
   }
}

注意,这并不能解决使用上下文所要求的问题。getBean(arg1, arg2)作为该方法在此场景中不相关。之所以不是这样,是因为所有这些bean都是单例的,此时没有调用构造函数。如果你在一个单用户系统中工作,这不是一个问题,也没有什么值得关心的,因为无论如何,在任何时候你的上下文中只有一个User bean !

但是,对于多用户系统,您需要确保每个实际用户都有一个惟一的User bean,并且在工厂方法调用中使用正确的User bean。

为了在多用户系统中做到这一点,你需要将bean类型更改为prototype,并且你应该创建一个代表工厂的FileValidator bean(如果你计划将依赖注入到工厂)和另一个代表新实例的FileValidator bean。它们都是相同的类类型,但是必须给每个类一个唯一的名称。

<bean id="user" scope="prototype" class="something.something.User" />
<bean id="validatorFactory"
            class="cz.instance.transl.validation.file.FileValidator">
    <constructor-arg value="something" />
</bean>
<bean id="fileValidatorBean"
            class="cz.instance.transl.validation.file.FileValidator"
    scope="prototype"
    factory-method="createInstance" >
    <constructor-arg ref="user" />
</bean>

,在您希望从工厂获得这个新的FileValidator bean的类中,您可以使用以下技术:

public void someMethod() {
    ...
    User user = context.getBean("user");
    user.setSomethingUsefull(...);
    FileValidator fileValidator = 
               (FileValidator)context.getBean("fileValidatorBean",
                                              user);
    ...
}

为了调用工厂方法,Spring需要访问一个用户实例以传递给createInstance。在本例中,我只是创建了一个bean并传入:

<bean id="user" class="something.something.User">
</bean>
<bean id="validator" class="cz.instance.transl.validation.file.FileValidator" factory-method="createInstance">
    <constructor-arg ref="user"/>
</bean>

您也可以使用抽象工厂来设置工厂bean属性。这里我们有一个ActionFactory来创建动作。

<bean id="actions_factory" class="com.imagina.control.actions.impl.ActionFactoryImpl"/>
<bean id="load_person_action" class="com.imagina.control.actions.impl.LoadPersonAction" 
  factory-bean="actions_factory" factory-method="create">
  <constructor-arg value="load_person_action"/>      
</bean>

要使用此配置,您必须考虑以下几点:

  1. create方法不是静态的。现在属于实例
  2. constructor-arg是工厂方法
  3. 的参数。

最新更新