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
}

Usage

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

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

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

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 idiomatic 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
}

Usage

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

If you want to use an prefix other than 'set'...

pico = new DefaultPicoContainer(new SetterInjection("mySynonymForSet"));
pico.addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

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.

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
}

Usage

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

With an custom annotation instead of PicoContainer's @Inject

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

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 annotation-field injection for components.

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

Injecting into Typed Fields  

This is where a component has an empty constructor with dependencies indicated by a on a per component basis with an array of field-types to be injected into. These will be provided automatically by the container after instantiation.

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

Usage

import static org.picocontainer.injectors.TypedFieldInjection.injectionFieldTypes;
	pico = new DefaultPicoContainer(new TypedFieldInjection();
pico.as(injectionFieldTypes(Orange.class, Pear.class, Banana.class)).addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

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 TypedFieldInjection. It only handles typed-field injection for components.

Injecting into Named Fields  

This is where a component has an empty constructor with dependencies indicated by a on a per component basis with an array of field-names to be injected into. These will be provided automatically by the container after instantiation.

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

Usage

import static org.picocontainer.injectors.NamedFieldInjection.injectionFieldNames;
	pico = new DefaultPicoContainer(new NamedFieldInjection();
pico.as(injectionFieldNames("orange", "pear", "banana")).addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

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 TypedFieldInjection. It only handles typed-field injection for components.

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
}

Usage

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

With an custom annotation instead of PicoContainer's @Inject

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

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 the default component factory AdaptiveInjection can also handle method-annotation injection types, if the @Inject annotation from PicoContainer's code-base is used as the marker for injection. AdaptiveInjection will also fall through to constructor injection if there is no recognized annotation.

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
}

Usage

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

Custom injection method prefix:

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

Via the default AdaptiveInjection Method injection, via a characteristic:

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

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 the default 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 inject(Pear pear) {
 this.pear = pear;
 }
 // other methods
}

Usage

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

 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.

Composite Dependency Injection

This is also where a component has is a blend of one or more of Constructor, Setter, Method, Annotated Field. The difference is that you know precisely what your injection design is and want to tune specifically for that, or you are some way off the defaults supported by MultiInjection

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

Usage

pico = new DefaultPicoContainer(new CompositeInjection(
	new ConstructorInjection(), new AnnotatedFieldInjection(FruitNeeded.class), 
	new MethodInjection("fruitNeeded")));
pico.addComponent(Apple.class);
// etc
Apple apple = pico.getComponent(Apple.class);

 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 CompositeInjection.

Factory Injection

This allows an instance to be injected via a factory that is aware of the thing it is injecting into and can make a custom instance just for that injectee. This type of injection is only possible if you add an Adapter for it directly that subclasses FactoryInjection<T>

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

 // methods
}

Usage

public class LogInjector extends FactoryInjector<Log> {
 public Log getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException {
 return LogFactory.getLog((Class) into);
 }
}
...
pico = new DefaultPicoContainer(new ConstructorInjection());
pico.addComponent(Apple.class);
pico.addAdapter(new LogInjector());
// etc
Apple apple = pico.getComponent(Apple.class);

We have implementations in this style for Log4j, Commons-Logging, Java-Logging and SLF4J. They are in the org.picocontainer.gems.injectors package.

Where Next?

The Modifying Behaviors page outlines the adding of behaviors to components