Spring и передача параметров фабричному методу во время выполнения - PullRequest
9 голосов
/ 22 июля 2011

Документация по методу context.getBean (имя, пользователь) говорит:

Позволяет указывать явные аргументы конструктора / аргументы метода фабрики

, независимо от того, что я делаю(попробовал все), с наиболее логичной настройкой, которую я получаю, когда бины загружаются во время инициализации:

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);
}

В комментарии сказано, что вы можете сделатьэто, но если вы укажете аргументы конструктора в определении xml этого компонента или нет, он завершится неудачей.

Ответы [ 4 ]

16 голосов
/ 22 июля 2011

Javadoc говорит:

args - аргументы для использования при создании прототипа с использованием явных аргументов статического метода фабрики.

Таким образом, определение компонента должно быть компонентом с прототипом, т.е.

<bean id="fileValidator" 
      scope="prototype" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />
10 голосов
/ 06 ноября 2012

Читая 20 постов, я обнаружил, что неясно, как заставить собственный метод фабрики принимать параметры во время выполнения, тем более что мы вынуждены использовать теги constructor-arg и ссылаться на существующий компонент в контекст, как показано ниже, и рассматриваемый класс, действующий как статический фабричный метод.

<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-компонента, используемого в конструкторе-arg, из контекста, а затем заполняя его значениями, с которыми вы работаете во время выполнения. Этот bean-компонент будет использоваться в качестве параметра при получении фабрично-сгенерированного bean-компонента.

public class X {

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

Обратите внимание, что это не решает проблему использования context.getBean (arg1, arg2), так как этот метод не применим в этом сценарии. Причина этого заключается в том, что все эти bean-компоненты одноэлементны, и на этом этапе конструктор не вызывается. Не проблема и не о чем беспокоиться, если вы работаете в однопользовательской системе, так как в любом случае у вас в вашем контексте только 1 пользовательский компонент!

Однако для многопользовательской системы вам необходимо убедиться, что у вас есть уникальный пользовательский бин для каждого реального пользователя и что вы используете правильный пользовательский бин в вызове фабричного метода.

Для того, чтобы сделать это в многопользовательской системе, вам необходимо изменить типы bean-компонентов на прототип И вы должны создать bean-компонент вашего FileValidator, который представляет фабрику (если вы планируете внедрение зависимостей в фабрику) и еще один компонент 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>

и в классе, где вы хотели бы получить этот новый компонент FileValidator с завода, вы можете использовать следующую технику:

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

    FileValidator fileValidator = 
               (FileValidator)context.getBean("fileValidatorBean",
                                              user);
    ...
}
7 голосов
/ 22 июля 2011

Для вызова фабричного метода Spring необходим доступ к пользовательскому экземпляру для передачи в createInstance.В этом случае я просто создаю бин и передаю его:

<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>
6 голосов
/ 19 января 2012

Вы также можете использовать абстрактную фабрику, устанавливая атрибут фабричного компонента. Здесь у нас есть 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. метод создания не является статическим. Теперь принадлежит экземпляру
  2. constructor-arg - параметр фабричного метода
...