After you start using CDI, sooner or later, you will get an error of
type: "Ambiguous
dependencies for type...". Without too many words, this errors
means that CDI should inject a bean into another bean, but it cannot decide
what bean to inject.
In CDI, a bean can have multiple beans types (user-defined class or
interface) - is important to keep this in mind. For example:
public class Foo
extends Buzz implements Bizz {
...
}
The bean types are: Foo,
Buzz, Bizz
and java.lang.Object.
If you never saw this error
before then probably you are in one of the two most common scenarios from
below:
·
a bean type doesn't provide enough information
to the container to point out that it should be injected
·
you have multiple beans that "share"
bean types, so the container cannot determine the bean type that you are referring
Multiple implementations of the
same interface
One of the most common mistakes that result in an ambiguity case
consist in injecting an interface that has multiple implementations. For
example, let's suppose that you have an interface named FooI:
public
interface FooI {
public void fooify();
}
Furthermore, we have a simple implementation of this interface named Foo1:
public class
Foo1 implements FooI {
@Override
public void fooify() {
...
}
}
Now, we can easily inject the Foo1 bean into another bean like this:
@Inject
private FooI
foo;
Further, let's suppose that we write another implementation of FooI named, Foo2:
public class
Foo2 implements FooI {
@Override
public void fooify() {
...
}
}
At this point, you will see an "Ambiguous dependencies for type..."
error. This is happening because the container cannot determine if you want to
inject the Foo1
bean or the Foo2
bean.
You can easily solve this problem (and similar problems) via CDI
qualifiers. Basically, a qualifier is a user-defined annotation that is itself
annotated with @Qualifier.
For example, we can write a qualifier like below:
@Qualifier
@Retention(RUNTIME)
@Target({METHOD,
FIELD, PARAMETER, TYPE})
public
@interface F1 {
}
The next thing to do is to annotate the desired bean with this qualifier. For
example, let's annotate the Foo1
bean:
@F1
public class
Foo1 implements FooI {
...
}
Once we have done this the application works again, but pay attention
that the container will inject an instance of Foo2 bean, NOT Foo1 bean. This is happening because we did
not used our qualifier at the injection point! So, if we want to inject an
instance of Foo1,
we need to do this:
@Inject @F1
private FooI
foo;
Now, we can easily inject an instance of Foo1 and Foo2 in the same
bean:
@Inject @F1
private FooI
foo1;
@Inject
private FooI
foo2;
Done! The complete application is available here.
Ambiguous dependencies caused by
extension
Furthermore, let's see another common case that causes an ambiguous
dependency error. For example, let's suppose that we have a simple bean Foo:
public class
Foo {
...
}
Now, we know that this can be easily injected as:
@Inject
private Foo
foo;
Later, in development stage, you extend this bean like this:
public class
Buzz extends Foo {
...
}
Suddenly the application stop working! Again, you can notice an "Ambiguous dependencies for
type..." error. The problem is that our Foo bean has the bean types Foo and java.lang.Object, while the Buzz bean has the Foo, Buzz and java.lang.Object bean types. Even if we can successfully
use @Inject Buzz buzz,
we cannot use @Inject
Foo foo. In first case (@Inject
Buzz buzz) the container can uniquely identify the Buzz bean, while in
the second case (@Inject
Foo foo) the container observe that the Foo and Buzz beans have in common the bean type Foo, and it doesn't
know what to inject, a Foo
or the Buzz
instance. And this is the cause of the ambiguity.
We can solve this problem using a qualifier as in the previous case, or
we can use the @Typed
annotation. This annotation allows us to restrict the bean types to an explicit
set of classes. For example, we can instruct the container that the Buzz bean has the bean
type Buzz, BUT
not Foo:
@Typed(Buzz.class)
public class
Buzz extends Foo {
...
}
Now, the container knows that you want to inject an instance of Foo bean.
The complete application is available here.
Niciun comentariu :
Trimiteți un comentariu