javamonkey79
javamonkey79

Reputation: 17775

spring Unsatisfied dependency expressed...sometimes

When I try to declare this simple bean:

<bean id="file" class="java.io.File" c:child="foo.txt" c:parent="\tmp" />

I get:

Unsatisfied dependency expressed through constructor argument with index 0 of type [java.io.File]: Ambiguous constructor argument types - did you specify the correct bean references as constructor arguments?

Ok, ok... I get it. I can try to use indexes on my constructor arg. I see that as the de facto answer. What I don't get, is if I have a similar class (for demonstration):

public class MultipleConstructors {
    private String a;
    private String b;
    private int c;
    private Object d;

    public MultipleConstructors(String a, String b) {
        this(a, b, -1, null);
    }

    public MultipleConstructors(String a, String b, int c) {
        this(a, b, c, null);
    }

    public MultipleConstructors(String a, String b, int c, Object d) {
        this.a = a;
        this.b = b;
        this.c = c;
        this.d = d;
    }
}

I can in fact declare it in the same way as the File declaration and Spring let's me. Both of these work... but why?

<bean
id="multipleConstructors1"
class="sandbox.spring.MultipleConstructors"
c:a="testing"
c:b="testing, hello"
c:c="123"/>

    <bean
id="multipleConstructors2"
class="sandbox.spring.MultipleConstructors"
c:a="testing"
c:b="testing, hello"
/>

Is it because the File class is not instrumented somehow? e.g. MultipleConstructors is a local source file, vs. the other is in the jdk?

Upvotes: 0

Views: 1565

Answers (1)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279950

Is it because the File class is not instrumented somehow

Yes. The JDK was probably compiled in a way that did not preserve the parameter names. You can confirm this by running, for example,

System.out.println(Arrays.toString(File.class.getConstructor(String.class, String.class).getParameters()));

If this prints

[java.lang.String arg0, java.lang.String arg1]

then the parameter names are not present in the .class file used to run your program.

The parameter names are therefore not available to Spring and so these two

c:child="foo.txt" c:parent="\tmp" 

can't be mapped reliably to the underlying parameters. Spring can convert from a String value to a File object, so two File constructors are applicable. Spring can't choose which one to use.

This is not the case with your own class, which you're probably compiling in a way that preserves the parameter names. Or if you're not, then the arguments you've provided deterministically map to a constructor.

For example, only one constructor can be applied to

c:a="testing"
c:b="testing, hello"
c:c="123"

this one

public MultipleConstructors(String a, String b, int c) {

and only one can be applied to

c:a="testing"
c:b="testing, hello"

this one

public MultipleConstructors(String a, String b) {

Upvotes: 2

Related Questions