PicoContainer can add behaviors to components automatically during instantiation.
Behaviors are deleivered by passing a BehaviorFactory reference into the PicoContainer instance on construction. For the most part BehaviorFactories are completely stateless, and the Behavior instances they make are not.
See below for details on Caching and Singletons, Implementation Hiding and Hot Swapping, as well as Other behaviors
DefaultPicoContainer can take a behavior factory instance in its constructor:
pico = new DefaultPicoContainer(new BehaviorClass()); pico.addComponent(Foo.class); // other components added Foo foo = pico.getcomponent(Foo.class) // Foo instance will be affected by an additonal behavior.
Or it can take behaviours that wrap each other:
pico = new DefaultPicoContainer(new BehaviorClass().wrap(new AnotherBehaviorClass())); pico.addComponent(Foo.class); // other components added Foo foo = pico.getcomponent(Foo.class) // Foo instance will be affected by two additonal behaviors.
Behaviors can be signalled by properties per component:
pico = new DefaultPicoContainer(); pico.as(SOME_BEHAVIOR).addComponent(Foo.class); // the behavior has a property marking it, and the default component facory understands that property // other components added Foo foo = pico.getcomponent(Foo.class) // Foo instance will be affected by an additonal behavior.
You can build a container with the applicable property:
pico = new PicoBuilder().withXXXBehavior().build(); pico.addComponent(Foo.class); // other components added Foo foo = pico.getcomponent(Foo.class) // Foo instance will be affected by an additonal behavior.
Behaviors can be chained together by PicoBuilder:
import static org.picocontainer.behaviors.Behaviors.xxxxxxx; import static org.picocontainer.behaviors.Behaviors.yyyyyyy; ... pico = new PicoBuilder().withBehaviors(xxxxxxx(), yyyyyyy()).build(); pico.addComponent(Foo.class); // other components added Foo foo = pico.getcomponent(Foo.class) // Foo instance will be affected by two additonal behaviors.
When components are created by two threads concurrently, with the intention of the instance being cached, it is possible in a small percentage of cases for the first instance into the cache to be replaced with a second instance. To prevent this, you may want to try one of two behaviors to make the operation thread safe.
Synchronizing wraps object creation in Java's classic synchronize feature:
pico = new DefaultPicoContainer(new Synchronizing().wrap(new Caching())); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
pico = new DefaultPicoContainer(); pico.as(SYNCHRONIZE, CACHE).addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
pico = new PicoBuilder().withSynchronizing().withCaching().build(); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
import static org.picocontainer.behaviors.Behaviors.synchronizing; import static org.picocontainer.behaviors.Behaviors.caching; ... pico = new PicoBuilder().withBehaviors(synchronizing(), caching()).build(); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
Locking wraps object creation in JDK 1.5's ReentrantLock facility. It is suggested that this is a more efficient alternative to the Synchronizing behaviour above:
pico = new DefaultPicoContainer(new Locking().wrap(new Caching())); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
pico = new DefaultPicoContainer(); pico.as(LOCK, CACHE).addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
pico = new PicoBuilder().withLocking().withCaching().build(); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
import static org.picocontainer.behaviors.Behaviors.locking; import static org.picocontainer.behaviors.Behaviors.caching; ... pico = new PicoBuilder().withBehaviors(synchronizing(), caching()).build(); pico.addComponent(Apple.class); Apple a1 = pico.getComponent(Apple.class); Apple a2 = pico.getComponent(Apple.class); // both the same instance
This is where there are a number of setters for a component that will be could be set after instantiation. A way of handing in some configuration if you like.
class Foo {
String message;
public void setMessage(String message) {
this.message = message;
}
public String toString() {
return message;
}
}
...
pico = new DefaultPicoContainer(new PropertyApplying());
pico.addComponent(Foo.class);
PropertyApplicator pa = (PropertyApplicator) pico.getComponentAdapter(Foo.class);
pa.setProperty("message", "hello");
System.out.println(pico.getComponent(Foo.class)); // prints hello
pico = new DefaultPicoContainer();
pico.as(APPLY_PROPERTIES).addComponent(Foo.class);
PropertyApplicator pa = (PropertyApplicator) pico.getComponentAdapter(Foo.class);
pa.setProperty("message", "hello");
System.out.println(pico.getComponent(Foo.class)); // prints hello
pico = new PicoBuilder().withProperties().build();
pico.addComponent(Foo.class);
PropertyApplicator pa = (PropertyApplicator) pico.getComponentAdapter(Foo.class);
pa.setProperty("message", "hello");
System.out.println(pico.getComponent(Foo.class)); // prints hello
import static org.picocontainer.behaviors.Behaviors.propertyApplying;
...
pico = new PicoBuilder().withBehaviors(propertyApplying()).build();
pico.addComponent(Foo.class);
PropertyApplicator pa = (PropertyApplicator) pico.getComponentAdapter(Foo.class);
pa.setProperty("message", "hello");
System.out.println(pico.getComponent(Foo.class)); // prints hello
import static org.picocontainer.behaviors.Behaviors.caching;
import static org.picocontainer.behaviors.Behaviors.propertyApplying;
...
pico = new PicoBuilder().withBehaviors(caching(), propertyApplying()).build();
pico.addComponent(Foo.class);
Cached cached = (Cached) pico.getComponentAdapter(Foo.class);
PropertyApplicator pa = (PropertyApplicator) getDelegate(PropertyApplicator.class);
pa.setProperty("message", "hello");
System.out.println(pico.getComponent(Foo.class)); // prints hello
This is where a component is going to be instantiated regardless of whether:
It is most likely that you're doing this because the component in question is self contained and doing something once only. Alternatively, you're cutting a legacy codebase over from nest-of-singletons to dependency injection in stages.
class Foo {
public Foo() {
System.out.println("Foo was instantiated");
}
}
...
pico = new DefaultPicoContainer(new Automatic());
pico.addComponent(Foo.class);
pico.addComponent("bar", String.class);
pico.getComponent("bar"); // Foo instantiated too.
pico = new DefaultPicoContainer();
pico.as(AUTOMATIC).addComponent(Foo.class);
pico.addComponent("bar", String.class);
pico.getComponent("bar"); // Foo instantiated too.
pico = new PicoBuilder().withAutomatic().build();
pico.addComponent(Foo.class);
pico.addComponent("bar", String.class);
pico.getComponent("bar"); // Foo instantiated too.
import static org.picocontainer.behaviors.Behaviors.automatic;
...
pico = new PicoBuilder().withBehaviors(automatic()).build();
pico.addComponent(Foo.class);
pico.addComponent("bar", String.class);
pico.getComponent("bar"); // Foo instantiated too.