org.picocontainer.injectors.AbstractInjector$AmbiguousComponentResolutionException: class < Something Here > needs a '< Something Here >' injected, but there are too many choices to inject. These:[class < Something Here >, class < Something Here >], refer http://picocontainer.org/ambiguous-injectable-help.html at org.picocontainer.parameters.BasicComponentParameter.getTargetAdapter(BasicComponentParameter.java:270) at org.picocontainer.parameters.BasicComponentParameter.resolveAdapter(BasicComponentParameter.java:205) at org.picocontainer.parameters.BasicComponentParameter.isResolvable(BasicComponentParameter.java:136) at org.picocontainer.parameters.ComponentParameter.isResolvable(ComponentParameter.java:132) at org.picocontainer.injectors.ConstructorInjector.getGreediestSatisfiableConstructor(ConstructorInjector.java:105) at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:155) at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:262) at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:192) at org.picocontainer.injectors.AbstractInjector.getComponentInstance(AbstractInjector.java:103) at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:559) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:526) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:538)
Or possibly a CyclicDependencyException like this:
org.picocontainer.injectors.AbstractInjector$CyclicDependencyException: Cyclic dependency: [class < Something Here >, class < Something Here >, class < Something Here >] at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:257) at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:192) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:65) at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:85) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:61) at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:559) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:526) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:516) at org.picocontainer.parameters.BasicComponentParameter.resolveInstance(BasicComponentParameter.java:146) at org.picocontainer.parameters.ComponentParameter.resolveInstance(ComponentParameter.java:119) at org.picocontainer.injectors.SingleMemberInjector.getMemberArguments(SingleMemberInjector.java:89) at org.picocontainer.injectors.ConstructorInjector.getMemberArguments(ConstructorInjector.java:199) at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:162) at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:262) at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:192) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:65) at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:85) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:61) at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:559) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:526) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:516) at org.picocontainer.parameters.BasicComponentParameter.resolveInstance(BasicComponentParameter.java:146) at org.picocontainer.parameters.ComponentParameter.resolveInstance(ComponentParameter.java:119) at org.picocontainer.injectors.SingleMemberInjector.getMemberArguments(SingleMemberInjector.java:89) at org.picocontainer.injectors.ConstructorInjector.getMemberArguments(ConstructorInjector.java:199) at org.picocontainer.injectors.ConstructorInjector$1.run(ConstructorInjector.java:162) at org.picocontainer.injectors.AbstractInjector$ThreadLocalCyclicDependencyGuard.observe(AbstractInjector.java:262) at org.picocontainer.injectors.ConstructorInjector.getComponentInstance(ConstructorInjector.java:192) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:65) at org.picocontainer.behaviors.Stored.getComponentInstance(Stored.java:85) at org.picocontainer.behaviors.AbstractBehavior.getComponentInstance(AbstractBehavior.java:61) at org.picocontainer.DefaultPicoContainer.getInstance(DefaultPicoContainer.java:559) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:526) at org.picocontainer.DefaultPicoContainer.getComponent(DefaultPicoContainer.java:538)
And you say to your self something like, "What the heck happened?!?!?!?"
The situation here is not as scary as it seems. We'll examine what causes the errors one at a time.
Here's a test case that shows what can cause a CyclicDependencyException:
@Test public void testCircular() { MutablePicoContainer mpc = new PicoBuilder().build(); mpc.addComponent(List.class, ArrayList.class); mpc.addComponent(Set.class, HashSet.class); List l = mpc.getComponent(List.class); //Throws a CircularDependencyException }
Upon first glance at the code, you might think "Yeah, so?". But the problem here is that PicoContainer tries to satisfy the greediest constructor; or in other words, it looks for the constructor with the most arguments first. In this case, there is a constructor ArrayList(Collection src), and since it has the most arguments, PicoContainer uses this one.
Unfortunately, this results in the following situation:
And so on until we either run out of stack space or this document increases to 2 Gb in size explaining the recursion. Fortunately, PicoContainer is smart enough to know where there is a recursive satisfaction like this and throws an exception rather than going into infinite recursion.
That is what causes the Cyclic Dependency Injection.
So how do you solve it? Well, lets back up see what causes the AmbiguousComponentResolutionException first and then we'll talk about how to fix it. (We promise! We won't let you down!)
Here's a test case that shows what can cause AmbiguousComponentResolutionException:
@Test public void testAmbiguous() { MutablePicoContainer mpc = new PicoBuilder().build(); mpc.addComponent(List.class, ArrayList.class); mpc.addComponent(Set.class, HashSet.class); mpc.addComponent(Collection.class, TreeSet.class); List l = mpc.getComponent(List.class); //Throws a AmbiguousComponentResolutionException }
The only difference between this example and the CyclicDependencyException example is the introduction of a new object that can satisfy the java.util.Collection requirement.
Review
Pico will automatically grab the argument that has the most arguments that can be satisfied.
So what happens here?
Rather than try to guess which one the developer intended to use. (And frankly, knowing us developers, Pico would guess wrong), we throw an AmbiguousComponentResolutionException instead.
So, what we really need is a way to specify that PicoContainer uses the exact constructor we want with the exact arguments. Making sure that PicoContainer understands which constructor to use in a situation like this is called "Disambiguation". PicoContainer provides several methods to disambiguate registrations.
Careful examination of MutablePicoContainer.addComponent() will show an optional last object that takes a variable number of org.picocontainer.Parameter objects. Without going into the specifics yet, the general rule is that if you specify three parameter objects in your addComponent() registration, then you will use the constructor that takes three parameters in your object. Use Parameter.ZERO to force the default constructor.
There are two main types of Parameter objects: (Others are possible... hey, this is an interface we're talking about here.)
With these options, it is time to revisit the AmbiguousComponentResolutionException example and modify it with our new found knowledge:
@Test public void testAmbiguous() { MutablePicoContainer mpc = new PicoBuilder().withCaching().build(); mpc.addComponent(List.class, ArrayList.class, Parameter.ZERO); //Force Default Constructor mpc.addComponent(Set.class, HashSet.class, new ConstantParameter(33)); //Equiv to new HashSet(33) mpc.addComponent(Collection.class, TreeSet.class, new ComponentParameter(List.class)); //Equiv to new TreeSet(mpc.getComponent(List.class)). Collection c = mpc.getComponent(TreeSet.class); }
So how do the Parameters modify the construction?
Note:
In this example, we turned on Caching. Because of this, the collection instances are shared. So the following code would work:
List myList = mpc.getComponent(List.class); myList.add("This is a test"); Collection c = mpc.getComponent(Collection.class); assertEquals("This is a test", c.iterator().next());Because of caching, the results of myList are shared with the TreeSet retrieved by mpc.getComponent(Collection). Also note, however that because the constructor HashSet(Collection) makes a copy of the passed in collection, the following code would NOT work:
List myList = mpc.getComponent(List.class); myList.add("This is a test"); Collection c = mpc.getComponent(Collection.class); myList.add(0, "Another test"); assertEquals("This is a test", c.iterator().next()); //FAILS. The result of the evaluation is "Another Test"
With PicoContainer 2.0 we are able to leverage the parameter names of constructors and methods in order to remove the ambiguity on dependencies.
public class Store { public Store(StockManager workingDayStockManager, StockManager afterHoursStockManager) { // etc } }
pico.as(Characteristics.USE_NAMES).addComponent(Store.class);
Access to parameter names was dropped from JDK 6.0 and it is uncertain whether it will be added as a feature in another release, so PicoContainer relies on another open source library called Paranamer, without requiring a dependency on its Jar. In other words, PicoContainer has the same classes from Paranamer in its jar.
This works the same as Guice. Namely you make an annotation that extends our 'Bind' annotation and mark it in your constructor or method's signature like so.
@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Bind public static @interface WorkingDayStockManager { } @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.PARAMETER}) @Bind public static @interface AfterHoursStockManager { } public class Store { public Store(@WorkingDayStockManager StockManager workingDayStockManager, @AfterHoursStockManager StockManager afterHoursStockManager) { // etc } }
Making PicoContainer leverage binding annotations if present is automatic.
Binding annotations can be specified for constructor parameters (as shown above), method injection parameters as well as field injection.