简体   繁体   中英

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 (ie use string.trim() here).

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 .

I would recommend to use a properties file and then use the property in the definition

But this does not explain your observed behaviour.

Not 100% sure but try

  <value index="0" [...]

Not sure what your code looks like but the order that you place your constructor args is not considered unless you specify the location. If you have multiple constructors this can mess things up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM