Authors: Paul Hammant, Jon Tirsen
Lifecycle is one third of Inversion of Control. Lifecycle concerns the post composition life of the component. Start, stop and dispose are the most commonly encountered lifecycle concepts.
For really simple PicoContainer compatible components, you would not bother with a lifecycle beyond instantiation and garbage collection - both of which are consequences of basic use of say DefaultPicoContainer. This is the vast majority of Pico components in the vast majority of applications.
public class Foo { public Foo() { // yippee, I am alive, active, going, steaming ahead, strutting my stuff… } }
It might be common to see a tree of components and containers in a advanced application. If one of the lifecycle methods is invoked on the root container, it is in turn invoked on all child containers and components where is is appropriately implemented. The starting of components is handled breadth The stopping and disposing of containers and components is handled depth first, and in reverse order to instatiation.
PicoContainer provides two very simple interfaces for lifecycle:
Startable and Disposable.
These interfaces honours the classic lifecycle concepts for components. These are start(), stop() and dispose(). If any of the components implements one of the applicable interfaces, the calling of the same method on the container will find that the call is percolated through to it. The container is forgiving, as it is just fine if some or none of the components contained are Startable etc.
public class Peach implements Startable, Disposable { public Peach() { // no wait, I'm fully composed but should wait for further instruction } public void start() {} public void stop() {} public void dispose() {} } public class Kiwi implements Startable, Stoppable { public void stop() {} }
Components can sometime implement start/stop, but not those mandated by an the PicoContainer lifecycle interface:
public interface Banana { public void banana(); } public class BananaImpl implements Banana { public void banana() {} public void start() {} public void stop() { } }
In this scenario, a simple extension can make the component honour the PicoContainer lifecycle interface:
public BananaExtender extends BananaImpl implements Startable, Stoppable {}
Delegation is also a neat way of adapting the component:
public class BananaDelegate implements Banana, Startable { private Banana realBanana; public BananaDelegate(Banana banana) { this.realBanana = realBanana; } public void banana() { realBanana.banana(); } public void start() { realBanana.start(); } public void stop() { realBanana.stop(); } }
The following functionality has been moved to NanoContainer-ProxytoysCustom lifecycle management is a common requirement for components. Probably because it is so crucial, there are many competing implementations. Each has its own passionate group of advocates.
PicoContaioner tries to avoid controversy by being partially agnostic about the lifecycle/lifecycles that it supports. As detailed previously, PicoContainer does have an implementation of a simple lifecycle and this is supported in the default container.
Instead of mandating PicoContainer's lifecycle interface, we provide a plug-in that should grant compatibility with other lifecycle concepts. Consider this custom interface:
public interface QuantumLeapable { void leap() }
PicoContainer pico = ... QuantumLeapable quantumLeapable = (QuantumLeapable) Multicaster.object(pico, true, new StandardProxyFactory()); // This will call leap() on all QuantumLeapable components inside pico. quantumLeapable.leap();