Spring并在运行时将参数传递给factory-method

lis*_*sak 9 java spring factory-method

方法context.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?
Run Code Online (Sandbox Code Playgroud)
<bean id="fileValidator" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />
Run Code Online (Sandbox Code Playgroud)
private FileValidator(User user) {
    this.user = user;
}

public static FileValidator createInstance(User user) {
    return new FileValidator(user);
}
Run Code Online (Sandbox Code Playgroud)

评论说你可以这样做,但如果你在该bean的xml定义中指定构造函数参数,它就会失败.

ska*_*man 16

javadoc中说:

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

所以bean定义必须是原型范围的bean,即

<bean id="fileValidator" 
      scope="prototype" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />
Run Code Online (Sandbox Code Playgroud)

  • 它必须是3.1.0.M2中的一个错误...在那里找到原因是相当复杂的,如果我找到它,我会尝试提高票价.谢谢 (3认同)
  • 你能解释一下为什么它必须是原型吗? (2认同)

小智 11

阅读20篇文章,我发现如何获取自定义工厂方法在运行时获取参数并不明显,特别是因为我们被迫使用构造函数-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>
Run Code Online (Sandbox Code Playgroud)

我通过从上下文中获取构造函数-arg中使用的bean实例,然后使用您在运行时使用的值填充它来实现它.当您获得工厂生成的bean时,此bean将用作参数.

public class X {

   public void callFactoryAndGetNewInstance() {
      User user = context.getBean("user");
      user.setSomethingUsefull(...);
      FileValidator validator = (FileValidator)context.getBean("fileValidator");
      ...
   }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这并不能解决使用context.getBean(arg1,arg2)时遇到的问题,因为该方法与此方案无关.它不是因为所有这些bean都是单例,并且此时不调用构造函数.如果您在单用户系统中工作,这不是问题,也不需要关心,因为无论如何您在上下文中只有1个用户bean!

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

为了在多用户系统中执行此操作,您需要将bean类型更改为原型,并且您应该创建一个表示工厂的FileValidator的bean(如果您计划将依赖项注入工厂)和另一个bean FileValidator代表您的新实例.它们都属于同一类类型,但您必须为每个类别指定一个唯一的名称.见下文:

<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>
Run Code Online (Sandbox Code Playgroud)

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

public void someMethod() {
    ...
    User user = context.getBean("user");
    user.setSomethingUsefull(...);

    FileValidator fileValidator = 
               (FileValidator)context.getBean("fileValidatorBean",
                                              user);
    ...
}
Run Code Online (Sandbox Code Playgroud)


ale*_*wen 7

为了调用工厂方法,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>
Run Code Online (Sandbox Code Playgroud)

  • 我说我需要在运行时传递参数 - context.getBean(name,user). (2认同)

yer*_*ero 6

您也可以使用抽象工厂设置factory-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>
Run Code Online (Sandbox Code Playgroud)

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

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