PicoContainer Presented by: Jim O’Hara Ed Kausmeyer Jingming Zhang
Lightweight Containers Attempts to build alternatives to the mainstream J2EE technologies Attempts to build alternatives to the mainstream J2EE technologies A common obstacle: A common obstacle: How to wire together different elements How to wire together different elements How do you fit together this web controller architecture with that database interface backing when they were built by different teams with little knowledge of each other? How do you fit together this web controller architecture with that database interface backing when they were built by different teams with little knowledge of each other? Lightweight Containers Lightweight Containers Frameworks that have taken a stab at this problem and are branching out to provide a general capability to assemble components from different layers Frameworks that have taken a stab at this problem and are branching out to provide a general capability to assemble components from different layers PicoContainer, Spring PicoContainer, Spring
Overview of PicoContainer Lightweight and highly embeddable container for components that honor Dependency Injection Lightweight and highly embeddable container for components that honor Dependency Injection Small, simple container for arbitrary components/services Small, simple container for arbitrary components/services Originally implemented in Java Originally implemented in Java Now available for other platforms and languages also (including C# and Ruby) Now available for other platforms and languages also (including C# and Ruby) tainer.jsp tainer.jsp tainer.jsp tainer.jsp
Overview of PicoContainer Not a replacement for a J2EE container Not a replacement for a J2EE container Does not offer any infrastructure services out of the box Does not offer any infrastructure services out of the box Can use the monitor support of PicoContainer to react on internal events e.g. by logging Can use the monitor support of PicoContainer to react on internal events e.g. by logging
Benefits of PicoContainer Embeddable inside other applications Embeddable inside other applications 50k jar that has no external dependencies except JDK k jar that has no external dependencies except JDK 1.3 Compact in size Compact in size Non-intrusive Non-intrusive Components don't have to implement any funny APIs and can be POJOs Components don't have to implement any funny APIs and can be POJOs Very extensible design: Enables virtually any form of extensions to the core Very extensible design: Enables virtually any form of extensions to the core
Benefits of PicoContainer Modularize how dependencies between parts of an application are laced up Modularize how dependencies between parts of an application are laced up Common to have dependencies scattered all over Common to have dependencies scattered all over Can be valuable for large projects Can be valuable for large projects Improve how components are configured in an application Improve how components are configured in an application Improve the testability of code Improve the testability of code
Dependency Injection A way of instantiating components and lacing them together with other dependent components A way of instantiating components and lacing them together with other dependent components Main idea: Have a separate object, an assembler, that populates a field in a class to be created with an appropriate implementation for an interface that class needs, resulting in a dependency Main idea: Have a separate object, an assembler, that populates a field in a class to be created with an appropriate implementation for an interface that class needs, resulting in a dependency That is, a component user specifies the interfaces it needs, and the implementations of the required components are provided at creation time That is, a component user specifies the interfaces it needs, and the implementations of the required components are provided at creation time Decouple the caller of the component from the implementation Decouple the caller of the component from the implementation
Types of Dependency Injection Constructor Dependency Injection (CDI): an object gets all its dependencies via the constructor Constructor Dependency Injection (CDI): an object gets all its dependencies via the constructor Setter Dependency Injection (SDI): the container or embedder hands dependencies to a component via setter methods after instantiation Setter Dependency Injection (SDI): the container or embedder hands dependencies to a component via setter methods after instantiation
Dependency Injection In PicoContainer PicoContainer supports CDI and SDI PicoContainer supports CDI and SDI PicoContainer identifies dependencies by looking at the constructors of registered classes (CDI) PicoContainer identifies dependencies by looking at the constructors of registered classes (CDI) PicoContainer can be thought of as a generic factory that can be configured dynamically PicoContainer can be thought of as a generic factory that can be configured dynamically PicoContainer is able to instantiate a complex graph of several interdependent objects PicoContainer is able to instantiate a complex graph of several interdependent objects
Components In PicoContainer Components are implemented as ordinary Java classes and do not typically have to rely on any PicoContainer APIs Components are implemented as ordinary Java classes and do not typically have to rely on any PicoContainer APIs The components are assembled in a container using a simple Java API that is similar to an intelligent hash map utilizing the type of its values The components are assembled in a container using a simple Java API that is similar to an intelligent hash map utilizing the type of its values This allows PicoContainer to instantiate arbitrary objects This allows PicoContainer to instantiate arbitrary objects You can put java.lang.Class objects in and get object instances back You can put java.lang.Class objects in and get object instances back
Components In PicoContainer A component user specifies the interfaces it needs, and PicoContainer provides the implementations of the required components at creation time A component user specifies the interfaces it needs, and PicoContainer provides the implementations of the required components at creation time This is what Dependency Injection is all about This is what Dependency Injection is all about A component can be used in the PicoContainer without importing or extending any interfaces or defined in the PicoContainer assembly A component can be used in the PicoContainer without importing or extending any interfaces or defined in the PicoContainer assembly
Demonstration: Juicer Example Example taken from Example taken from
Container Hierarchies Containers provide a powerful alternative to the singleton antipattern Containers provide a powerful alternative to the singleton antipattern The singleton pattern is static and global; it won't allow more than one instance and is visible from anywhere The singleton pattern is static and global; it won't allow more than one instance and is visible from anywhere Containers serve as singleton-like objects that provide fine-grained control over the visibility scope of the instance Containers serve as singleton-like objects that provide fine-grained control over the visibility scope of the instance
Container Hierarchies A container (and its registered components) can get access to components registered in a parent container, but not vice-versa A container (and its registered components) can get access to components registered in a parent container, but not vice-versa
//Container Hierarchy Example // Create x hierarchy of containers MutablePicoContainer x = new DefaultPicoContainer(); MutablePicoContainer y = new DefaultPicoContainer(x); MutablePicoContainer z = new DefaultPicoContainer(x); // Assemble components x.registerComponentImplementation(Apple.class);y.registerComponentImplementation(Juicer.class);z.registerComponentImplementation(Peeler.class);
//Container Hierarchy Example Continued // Instantiate components Peeler peeler = (Peeler)z.getComponentInstance(Peeler.class); // WON'T WORK! peeler will be null peeler = (Peeler) x.getComponentInstance(Peeler.class); // WON'T WORK! This will throw an exception Juicer juicer = (Juicer)y.getComponentInstance(Juicer.class); First line will work fine; z will be able to resolve the dependencies for Peeler (which is Apple ) from the parent container First line will work fine; z will be able to resolve the dependencies for Peeler (which is Apple ) from the parent container Second line will return null, as x can't see Peeler Second line will return null, as x can't see Peeler Third line will throw an exception, since Juicer 's dependency to Peeler can't be satisfied ( z can't be seen by y ) Third line will throw an exception, since Juicer 's dependency to Peeler can't be satisfied ( z can't be seen by y )
Lifecycle One third of Inversion of Control One third of Inversion of Control Inversion of Control = dependency resolution + configuration + lifecycle Inversion of Control = dependency resolution + configuration + lifecycle Concerns the post composition life of a component Concerns the post composition life of a component Most commonly encountered lifecycle concepts: start, stop, dispose Most commonly encountered lifecycle concepts: start, stop, dispose
Lifecycle The lifecycle of components are easy to manage in PicoContainer The lifecycle of components are easy to manage in PicoContainer Lifecycle callbacks are supported by implementing the lifecycle interfaces Lifecycle callbacks are supported by implementing the lifecycle interfaces PicoContainer provides two simple interfaces for lifecycle: Startable and Disposable PicoContainer provides two simple interfaces for lifecycle: Startable and Disposable A lifecycle can be extended or totally customized A lifecycle can be extended or totally customized
Lifecycle If a set of classes implement Startable, the lifecycle of all the objects can be controlled with method calls on the container If a set of classes implement Startable, the lifecycle of all the objects can be controlled with method calls on the container The container will figure out the correct order of invocation of start() or stop() for all the objects it manages The container will figure out the correct order of invocation of start() or stop() for all the objects it manages
Lifecycle Calling start() on the container will call stop() on all container-managed objects in the order of their instantiation Calling start() on the container will call stop() on all container-managed objects in the order of their instantiation This means starting with the ones that have no dependencies, and ending with the ones that have dependencies on others This means starting with the ones that have no dependencies, and ending with the ones that have dependencies on others
Lifecycle Calling start() on a container with child containers will start all the containers in a breadth-first order, starting with itself Calling start() on a container with child containers will start all the containers in a breadth-first order, starting with itself Likewise, calling stop() will call stop() on all containers in the hierarchy in a depth-first order Likewise, calling stop() will call stop() on all containers in the hierarchy in a depth-first order
Lifecycle In order for a hierarchy-aware lifecycle to work, child containers must be registered as components in their parent container In order for a hierarchy-aware lifecycle to work, child containers must be registered as components in their parent container Just creating a container with another one as a parent will not cause the parent container to know about the child container Just creating a container with another one as a parent will not cause the parent container to know about the child container Calling lifecycle methods on a child container will not propagate the lifecycle to its parent container Calling lifecycle methods on a child container will not propagate the lifecycle to its parent container
Lifecycle Example: A Direct Approach MutablePicoContainer parent = new DefaultPicoContainer(); MutablePicoContainer child = new DefaultPicoContainer(parent); // We must let the parent container know // about the child container. parent.registerComponentInstance(child); // This will start the parent, which // will start the child. parent.start();
Lifecycle Example: An Indirect Approach MutablePicoContainer parent = new DefaultPicoContainer(); parent.registerComponentImplementation("child", DefaultPicoContainer); // This will instantiate the child container // passing the parent (itself) as parent // container. // No need to register the child in the parent // here. MutablePicoContainer child = (MutablePicoContainer) parent.getComponentInstance("child"); // This will start the parent, which will start // the child. parent.start();
NanoContainer, for expanding capabilities Builds on top of PicoContainer the support for several scripting metalanguages (XML, Groovy, Bsh, Javascript and Jython), AOP, Web frameworks (Struts and WebWork), SOAP, JMX, and much more Builds on top of PicoContainer the support for several scripting metalanguages (XML, Groovy, Bsh, Javascript and Jython), AOP, Web frameworks (Struts and WebWork), SOAP, JMX, and much more
Example
import org.picocontainer.*; import org.picocontainer.*; import org.picocontainer.defaults.*; import org.picocontainer.defaults.*; public class DITest{ public class DITest{ public static void main(String[] args){ public static void main(String[] args){ //Two ways to test Inversion of Control in pico container //Two ways to test Inversion of Control in pico container //Both use Dependency Injection Patten. //Both use Dependency Injection Patten. //Constructor Injection with PicoContainer indirectly //Constructor Injection with PicoContainer indirectly System.out.println("Constructor Injection indirectly:"); System.out.println("Constructor Injection indirectly:"); MyMovieFinder mmf = new MyMovieFinder(); MyMovieFinder mmf = new MyMovieFinder(); mmf.testWithPico(); mmf.testWithPico(); //Constructor Injection with PicoContainer directly //Constructor Injection with PicoContainer directly System.out.println("constructor Injection directly:"); System.out.println("constructor Injection directly:"); MutablePicoContainer pico = new DefaultPicoContainer(); MutablePicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation(MovieFinderImpl.class); pico.registerComponentImplementation(MovieFinderImpl.class); pico.registerComponentImplementation(MovieLister.class); pico.registerComponentImplementation(MovieLister.class); MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); MovieLister lister = (MovieLister) pico.getComponentInstance(MovieLister.class); lister.listFinder(); lister.listFinder(); //Constructing Object without using PicoContainer //Constructing Object without using PicoContainer System.out.println("Construct Object without PicoContainer:"); System.out.println("Construct Object without PicoContainer:"); //String title = "Once Upon a Time in the West"; //String title = "Once Upon a Time in the West"; MovieFinder finder = new MovieFinderImpl(); MovieFinder finder = new MovieFinderImpl(); lister = new MovieLister(finder); lister = new MovieLister(finder); lister.listFinder(); lister.listFinder(); System.exit(0); System.exit(0); } } }
Output of Executing DITest Class F:\SE510>java DITest Constructor Injection indirectly: MovieFinder was listed by constructor Injection directly: MovieFinder was listed by Construct Object without PicoContainer: MovieFinder was listed by
References Page with several links for information about PicoContainer Page with several links for information about PicoContainer One-minute description: provides an overview of PicoContainer and lists some of its benefits One-minute description: provides an overview of PicoContainer and lists some of its benefits Two minute tutorial: provides code snippets that serve as a introduction to PicoContainer Two minute tutorial: provides code snippets that serve as a introduction to PicoContainer Five minute introduction: provides code snippets that serve as a introduction to PicoContainer and explains the concepts of container hierarchies and lifecycle Five minute introduction: provides code snippets that serve as a introduction to PicoContainer and explains the concepts of container hierarchies and lifecycle Paper: Inversion of Control Containers and the Dependency Injection pattern Paper: Inversion of Control Containers and the Dependency Injection pattern Constructor Dependency Injection with PicoContainer, a post-J2EE Nirvana: Explains Inversion of Control (IoC) and Constructor Dependency Injection (CDI) in PicoContainer and NanoContainer Constructor Dependency Injection with PicoContainer, a post-J2EE Nirvana: Explains Inversion of Control (IoC) and Constructor Dependency Injection (CDI) in PicoContainer and NanoContainer