Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic factory method in Spring DI

I wonder why in Spring DI the following bean definition works (I use bean instantiation with a static factory method and Guava's Suppliers.ofInstance):

<bean id="keySupplier" class="com.google.common.base.Suppliers"
    factory-method="ofInstance">
  <constructor-arg>
    <value type="java.lang.String">someReallyLongValue <!-- note line break here -->
    </value>
  </constructor-arg>
</bean>

but this one doesn't:

<bean id="keySupplier" class="com.google.common.base.Suppliers"
    factory-method="ofInstance">
  <constructor-arg type="java.lang.String" value="someReallyLongValue" />
</bean>

It throws following exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepo' defined in class path resource:
(...)
Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.Object]:
Ambiguous factory method argument types - did you specify the correct bean references as factory method arguments?

The problem is, in my case, when I use first bean definition with really long string as a value my editor breaks line after last character of string, which causes that string is passed with additional whitespace to Suppliers.ofInstance and in result it breaks my code.

The second definition would be more strict about whitespace but, suprisingly, it doesn't work (it probably doesn't cope with generic type, despite the type is specified in type attribute).

Can I force Spring to ignore whitespace in <value> tag somehow?

Or do I use <constructor-arg type="java.lang.String" value="someReallyLongValue" /> properly? Or should I file an issue because it's a Spring bug?

I'd rather not make any assumptions about the string (i.e. use string.trim() here).

like image 517
Xaerxess Avatar asked Mar 22 '12 11:03

Xaerxess


1 Answers

The two configurations do not involve the same execution path in Spring according to reference documentation about constructor resolution. The failure comes from generics used for this instanceOf method which declares Object as method argument.

Spring has to do the following tasks to succeed:

  • find the right method according to arguments
  • convert XML literal values into Java objects either thanks to explicit type declaration or based on method argument types when index is used

The logic involved is in ConstructorArgumentValues holder in the method getArgumentValue and in both getIndexedArgumentValue and getGenericArgumentValue. Both methods use tests to reject a ValueHolder based on information available.

In the second configuration scenario, the indexed detection is used and rejects the value because the required type String does not match exactly with Object. This test is done with ClassUtils.matchesTypeName which does not check the type hierarchy.

In the first configuration scenario, the value holder is ready with a String object and the generic argument mechanism agrees because the value is assignable to the detected method argument type.

Theoretically speaking, the following expression should work because type is provided to generate the object from the value and index is provided to avoid any guessing, even if there is only one method matching.

   <constructor-arg index="0" type="java.lang.String" value="queueName" />

Bad luck, it does not improve anything, the same execution path is still used. I really think a Spring improvement is required. May you create a JIRA ticket.

like image 114
Yves Martin Avatar answered Oct 14 '22 07:10

Yves Martin