Generic factory method in Spring DI

I wonder why the following bean definition works in Spring DI (I use a bean instance with the static factory method and Guava 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 is not:

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

It throws the following exception:

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

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

The second definition will be more stringent with respect to spaces, but, oddly enough, it does not work (it probably does not cope with the generic type, despite the fact that the type is specified in the type attribute).

Can I make Spring ignore spaces in the <value> tag somehow?

Or am I using <constructor-arg type="java.lang.String" value="someReallyLongValue" /> ? Or should I point out the problem because it is a Spring bug?

I would prefer not to make any assumptions about the string (i.e. use string.trim() here).

+6
source share
3 answers

The two configurations do not include the same execution path in Spring as per the help documentation on constructor resolution . The error arises from the generics used for this instanceOf method , which declares Object as an argument to the method.

Spring should perform the following tasks:

  • find the correct method according to the arguments
  • convert XML literal values ​​to Java objects either through an explicit type declaration, or based on the type of the argument of the method when index used

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

The second configuration scenario uses indexed detection and rejects the value because the required String type does not match exactly with Object . This test runs with ClassUtils.matchesTypeName , which does not validate the type hierarchy.

In the first configuration scenario, the value holder is ready with a String object, and the general argument mechanism is consistent with the value being assigned to the argument type of the detected method.

Theoretically, the following expression should work because a type is provided to generate an object from a value, and a pointer is provided to avoid guessing, even if there is only one method.

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

Unlucky, it doesn’t improve anything, the same execution path is still in use. I really think Spring improvement is required. Can you create a JIRA ticket .

+6
source

I would recommend using a property file and then using a property in the definition

But this does not explain your observed behavior.

0
source

Not 100% sure, but try

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

Not sure what your code looks like, but the arrangement order of the constructor arguments is not taken into account unless you specify a location. If you have multiple constructors, this can ruin things.

0
source

Source: https://habr.com/ru/post/911399/


All Articles