Dependency Injection

See Martin Fowlers's Inversion of Control Containers and the Dependency Injection article from 2003 for a thorough description. Surely everyone has read this by now?

Very quickly: Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields.  Those components do not get their dependencies themselves, or instantiate them directly.  This is very much related to the encompassing design principle Inversion of Control.

Different types of Dependency Injection supported by PicoContainer

PicoContainer supports multiple ways to specify the injection of dependencies into components.  Constructor injection (listed first) is the recomended idiom for PicoContainer.  Other types leverage fields and methods.  Variations of the method types, can follow a naming convention or be marked with an annotation.  Dependencies for those could be populated one by one, or all in one method call. Indeed components could be populated with combinations of Constructor, Method and Field Injection.

Constructor Injection

This is where a component has a constructor, with arguments that are its dependencies:

public class Apple {
  private final Orange orange;
  private final Pear pear;
  private final Banana banana;
	
  public Apple(Orange orange, Pear pear, Banana banana) {
    this.orange = orange;
    this.pear = pear;
    this.banana = banana;
  }
		
  // methods
}

The PicoContainer team recommends Constructor Dependency Injection (CDI) over other types - the project was started to pioneer this approach. With PicoContainer is no need to mark up the constructor with an annotation. Having more than one constructor is OK too, as PicoContainer will try to use the one with the most arguments and fall back to ones with fewer if it cannot satisfy the longer ones.

The component factory for this is ConstructorInjection. It only handles constructor injection types of components. Factory AdaptiveInjection defaults to constructor injection, after checking first to see it the component in question is a Annotated Method or Field type (see below).

Constructor Injection is idomatic PicoContainer - choose this style over all others if you can - especially if you are starting out with Dependency Injection. 

Injecting into Setter Methods

This is where a component has an empty constructor with dependencies provided by setters after instantiation:

public class Apple {
  private Orange orange;
  private Pear pear;
  private Banana banana;	
  public setOrange(Orange orange) {
    this.orange = orange;
  }		
  public setPear(Pear pear) {
    this.pear = pear;
  }
  public setBanana(Banana banana) {
    this.banana = banana;
  }
  // other methods
}

Setter methods (those prefixed with 'set') may not be your preferred choice. You can force a different prefix to be choosable in PicoContainer, such as 'init' or 'inject'.

The component factory for this is SetterInjection. It only handles setter injection types of components.

Factory AdaptiveInjection can also handle setter injection types, though it requires that the component was registered with the property 'SDI' in order to activate the Setter Injection functionality. AdaptiveInjection will also fall through to constructor injection if there is no SDI property.

If you want to use an prefix other than 'set', then specify your preferred prexix in the non-default constructor for SetterInjection.

Injecting into Annotated Fields  

This is where a component has an empty constructor with dependencies indicated by a field annotation and provided automatically by the container after instantiation.

public class Apple {
  @Inject
  private Orange orange;
  @Inject
  private Pear pear;
  @Inject
  private Banana banana;
  // methods
}

Yes that's right, there's no constructor needed. It means that for a Unit Test, you cannot simply 'new' the class without some magic to populate the dependency fields.

The component factory for this is a class AnnotatedFieldInjection. It only handles field-annotation injection types of components.

Additionally component factory AdaptiveInjection can also handle field annotation types, if the @Inject annotation from PicoContainer's codebase is used as the marker for injection. AdaptiveInjection will also fall through to constructor injection if there is no recognised @Inject annotation.

If you want to use an @Inject annotation from another codebase, then specify the preferred annotation class in the constructor for AnnotatedFieldInjection.

Injecting into Annotated Methods 

This is where a component has an empty constructor and gets its dependencies injected into annotated methods after instantiation:

public class Apple {
  private Orange orange;
  private Pear pear;
  private Banana banana;

  @Inject
  public injectOrange(Orange orange) {
    this.orange = orange;
  }
  @Inject
  public setPear(Pear pear) {
    this.pear = pear;
  }
  @Inject
  public provideBanana(Banana banana) {
    this.banana = banana;
  }
  // other methods
}

The method (whatever its name) needs an @Inject annotation. That is from our codebase (org.picocontainer.Inject).

The component factory for this is AnnotatedMethodInjection. It only handles method-annotation injection types of components.

Additionally component factory AdaptiveInjection can also handle method-annotation injection types, if the @Inject annotation from PicoContainer's codebase is used as the marker for injection. AdaptiveInjection will also fall through to constructor injection if there is no recognised annotation.

If you want to use an @Inject annotation from another codebase, then specify the preferred annotation class in the constructor for AnnotatedMethodInjection.

Injecting into a Single Method 

This is where a component has an empty constructor and gets all its dependencies injected into single method after instantiation:

public class Apple {
  private Orange orange;
  private Pear pear;
  private Banana banana;
  public inject(Orange orange, Pear pear, Banana banana) {
    this.orange = orange;
    this.pear = pear;
    this.banana = banana;
  }
  // other methods
}

The method name needs be 'inject' unless overridden in the InjectionFactory.

The component factory for this is MethodInjection. It only handles method-injection types of components.

Additionally component factory AdaptiveInjection can also handle method-injection types, but only if the METHOD_INJECTION characteristic is specified.

Muli-type Dependency Injection

This is where a component has is a blend of one or more of Constructor, Setter, Method, Annotated Field etc:

public class Apple {
  private Orange orange;
  private Pear pear;
  @Inject
  private Banana banana;
  public inject(Orange orange) {
    this.orange = orange;
  public void injectPear(Pear pear) {
    this.pear = pear;
  }
  // other methods
}

 In the case above, Orange comes in through the constructor, Pear by method injection and Banana is via Annotated Field Injection.

The component factory for this is MultiInjection. It only handles method-injection types of components.

Additionally component factory AdaptiveInjection can also handle method-injection types, but only if the MULTI_INJECTION characteristic is specified.

Using PicoContainer with Injection Types

Explicit Constructor Injection:

pico = new DefaultPicoContainer(new ConstructorInjection());
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Constructor Injection, is a default too (via AdaptiveInjection):

pico = new DefaultPicoContainer();
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Setter Injection:

pico = new DefaultPicoContainer(new SetterInjection());
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Annotated Field injection:

pico = new DefaultPicoContainer(new AnnotatedFieldInjection();
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Annotated Field injection, with alternate Annotation:

pico = new DefaultPicoContainer(new AnnotatedFieldInjection(MyInjectAnnotaton.class);
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Annotated Method injection:

pico = new DefaultPicoContainer(new AnnotatedMethodInjection();
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Annotated Method injection, with alternate Annotation:

pico = new DefaultPicoContainer(new AnnotatedMethodInjection(MyInjectAnnotaton.class);
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Method injection, implicit injection method prefix ('inject'):

pico = new DefaultPicoContainer(new MethodInjection();
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Explicit Method injection, explicit injection method prefix:

pico = new DefaultPicoContainer(new MethodInjection("setDependencies");
pico.addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Method injection, via a characteristic:

pico = new DefaultPicoContainer();
pico.as(Characteristics.METHOD_INJECTION).addComponent(Apple.class);
// others
Apple apple = pico.getComponent(Apple.class);

Leveraging Parameter Names

This is where the type alone is not enough to indicate which dependency should be put one or more constructor args. The following component has a need for two of the same type, with some subtly different characteristics.

public class AppleProcessor {
  private Apple dessertApple;
  private Apple cookingApple;

  public inject(Apple dessertApple, Apple cookingApple) {
    this.dessertApple = dessertApple;
    this.cookingApple = cookingApple;
  }
  // other methods
}

pico.addComponent("dessertApple", GoldenDeliciousApple.class);
pico.addComponent("cookingApple", BramleyApple.class);
pico.as(Characteristics.USE_NAMES).addComponent(AppleProcessor.class);

AppleProcessor appleProcessor = pico.getComponent(AppleProcessor.class);

In this case the constructor to AppleProcessor has carefully named parameters - 'dessertApple' and 'cookingApple'. This is picked up on for the two named components GoldenDeliciousApple and BramleyApple also set up in the container.

There are two ways for this to work:

  • relying on the fact that your component classes have debug info compiled into them.
  • post-processing component classes with Paranamer before placing them in their jar