Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring and passing parameters to factory-method in runtime

The documentation of method context.getBean(name, user) says

Allows for specifying explicit constructor arguments / factory method arguments

but no matter what I do (tried everything), with the most logical setting I get this when the beans are being loaded up during initialization:

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

The commentary says you can do it, but if you specify constructor arguments in xml definiton of that bean or not, it fails.

like image 258
lisak Avatar asked Jul 22 '11 02:07

lisak


People also ask

Is it possible to initiate a non-static factory method to Spring?

Spring provides an option to inject dependency using factory-method along with factory-bean attributes in case of non-static factory methods.

How do I use BeanFactory in Spring?

BeanFactory uses Beans and their dependencies metadata to create and configure them at run-time. BeanFactory loads the bean definitions and dependency amongst the beans based on a configuration file(XML) or the beans can be directly returned when required using Java Configuration.

What is instance factory method instantiation in Spring?

The IoC container will invoke a non-static method of an Existing Bean to Instantiate an Object. In our previous example we saw how Instantiation of Bean using Constructor args and Static-Factory Method with an Example.

What is factory-bean and factory method in Spring?

factory-method: represents the factory method that will be invoked to inject the bean. factory-bean: represents the reference of the bean by which factory method will be invoked. It is used if factory method is non-static.


2 Answers

The javadoc says:

args - arguments to use if creating a prototype using explicit arguments to a static factory method.

So the bean definition must be a prototype-scoped bean, i.e.

<bean id="fileValidator" 
      scope="prototype" 
      class="cz.instance.transl.validation.file.FileValidator" 
      factory-method="createInstance" />
like image 63
skaffman Avatar answered Nov 04 '22 12:11

skaffman


Reading across 20 posts, I found that it was not apparent how to get a custom factory method to take parameters at run-time, especially since we are forced to use the constructor-arg tags and refer to an existing bean in the context as setup below and the class in question acting as a static factory method.

<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>

I got it working by fetching an instance of the bean used in the constructor-arg out of the context and then populating it with the values that you are working with at run-time. This bean will then be used as the parameter when you get your factory-generated bean.

public class X {

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

Note this doesn't solve the problem asked of using context.getBean(arg1, arg2) as that method isn't relevant in this scenario. The reason it isn't is because all these beans are singleton and at this point the constructor isn't invoked. Not a problem and not anything to care about if you are working in a single-user system as you only have 1 User bean in your context at any time anyhow!

However, For a multi-user system you will need to make sure that you have a unique User bean for each real user and that you use the correct User bean in the factory method invocation.

In order to do this in a multi-user system, you will need to change the bean types to be prototype AND you should create a bean of your FileValidator that represents the factory (if you plan to dependency injection into the factory) and another bean FileValidator that represents your new instance. They will both be of the same class type but you must give each one a unique name. See below:

<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>

and in the class where you would like to get this new FileValidator bean from the factory, you can use the technique below:

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

    FileValidator fileValidator = 
               (FileValidator)context.getBean("fileValidatorBean",
                                              user);
    ...
}
like image 26
gannable Avatar answered Nov 04 '22 12:11

gannable