Usage of PicoContainer

PicoContainer is a mature dependency injector framework similar to spring-core or Guice. It presents the advantage to be quite small (~260Ko) and yet very complete.

The advantages of dependency injection are numerous

  • Unloads your code from managing instance creation. You don't need to use the new keywords in your code, simply ask for the dependency.
  • Minimize coupling between classes

Specifically to IzPack, PicoContainer allows

  • No more singletons.
  • Better testing. Integration testings were possibles thanks to dependency injection.
  • Better support for dynamic construction. Panels and listener were build using reflexivity with fixed constructor. Now, they are instantiated with picoContainer thus you don't need a fixed constructor anymore. In fact, you can now obtain any of IzPack component by "asking" it in the constructor.

How to inject dependencies

Dependencies definitions

With pico, you create and manage your own container. Once created, you can

  • Add component to the container
  • Get component from the container
CompilerContainer.java
pico = new PicoBuilder().withConstructorInjection().withCaching().build();
pico.addComponent(CliAnalyzer.class);
CliAnalyzer cliAnalyzer = pico.getComponent(CliAnalyzer.class);

Here you create a new container, add CliAnalyzer as a dependency and get the instance of CliAnalizer.
The container is configured to "cache" dependencies. Meaning, it insures that there is only one instance of this component created. It is the same as a Singleton in Guice.

Expect some particular cases, you don't need to lookup for your component using the container. Exceptions are when component are constructed dynamically like panels. In this case, we add the panel and get the instance from the compiler.

In most case, you should use injection to get dependencies.

Injection methods

Two injections methods have been used primary used.

  1. Injection by constructor.
  2. Injection by provider
Injection by constructor :

(see http://picocontainer.com/constructor-injection.html)
PicoContainer supports many types of injections but the preferred way is to pass dependencies in the constructor.

For example, given the following class :

AClass.java
public class AClass {
 public AClass(CliAnalyzer cliAnalyzer){
  // Use cliAnalyzer
 }
}
CompilerContainer.java
pico = new PicoBuilder().withConstructorInjection().withCaching().build();
pico.addComponent(CliAnalyzer.class);
pico.addComponent(AClass.class);
AClass aClass = pico.getComponent(AClass.class);

The instance of AClass will be constructed using the instance of CliAnalyzer.

Injection by provider :

(see http://picocontainer.com/providers.html)
Provider gives more flexibility to construct the component instance and it is useful to do some works prior to instantiation.

It is used to create component which need informations from xml (like AutomatedInstallData). All providers are available in packages *.container.provider

Injection of primitives :

(see http://picocontainer.com/component-configuration.html)
PicoContainer gives the possibilities to inject primitives (String, Integer, ...). They are resolved by name and it is used to inject the install file name in components.

How to add a component in IzPack

If you create a component, you can add it to the container so that it will be avaible as a dependency.

On the compiler, the container is configured by the class com.izforge.izpack.compiler.container.CompilerContainer

On the installer, there are several containers : 

  • com.izforge.izpack.installer.container.impl.InstallerContainer : Container for installer.
  • com.izforge.izpack.installer.container.impl.CustomDataContainer :Specific container for customData. Child of InstallerContainer