I have a PicoContainer which caches all components. Since it caches all components, I expect it to call start
, stop
and dispose
at the appropriate points in the container lifecycle.
However, I'm finding that if I construct a component using a FactoryInjector
, these methods don't get called at all, despite that component also being cached.
Take the following example:
import java.lang.reflect.Type;
import org.picocontainer.Characteristics;
import org.picocontainer.DefaultPicoContainer;
import org.picocontainer.Disposable;
import org.picocontainer.MutablePicoContainer;
import org.picocontainer.PicoContainer;
import org.picocontainer.Startable;
import org.picocontainer.injectors.FactoryInjector;
public class PicoContainerFactoryTest {
public static void main(String[] args) {
MutablePicoContainer container =
new DefaultPicoContainer().as(Characteristics.CACHE);
try {
System.out.println("Adding components...");
container.addComponent(InstanceService.class,
new InstanceServiceImpl());
container.addComponent(ConstructedService.class,
ConstructedServiceImpl.class);
container.addAdapter(
new FactoryConstructedServiceAdapter());
System.out.println("Starting...");
container.start();
// Even this doesn't trigger it. :(
//container.getComponent(FactoryConstructedService.class);
System.out.println("Stopping...");
container.stop();
}
finally
{
System.out.println("Disposing...");
container.dispose();
}
}
public interface InstanceService
extends Startable, Disposable {}
public interface ConstructedService
extends Startable, Disposable {}
public interface FactoryConstructedService
extends Startable, Disposable {}
private static class InstanceServiceImpl extends Impl
implements InstanceService {
public InstanceServiceImpl() {
super("InstanceServiceImpl");
}
}
public static class ConstructedServiceImpl extends Impl
implements ConstructedService {
public ConstructedServiceImpl() {
super("ConstructedServiceImpl");
}
}
private static class FactoryConstructedServiceAdapter
extends FactoryInjector<FactoryConstructedService> {
public FactoryConstructedServiceAdapter() {
super(FactoryConstructedService.class);
}
@Override
public FactoryConstructedService getComponentInstance(
PicoContainer picoContainer, Type type) {
return new FactoryConstructedServiceImpl();
}
private static class FactoryConstructedServiceImpl extends Impl
implements FactoryConstructedService {
public FactoryConstructedServiceImpl() {
super("FactoryConstructedServiceImpl");
}
}
}
public static class Impl implements Startable, Disposable {
private final String name;
public Impl(String name) {
this.name = name;
System.out.println(" " + name + "#<init>");
}
@Override
public void start() {
System.out.println(" " + name + "#start");
}
@Override
public void stop() {
System.out.println(" " + name + "#stop");
}
@Override
public void dispose() {
System.out.println(" " + name + "#dispose");
}
}
}
The output of running this is as follows:
Adding components...
InstanceServiceImpl#<init>
Starting...
ConstructedServiceImpl#<init>
InstanceServiceImpl#start
ConstructedServiceImpl#start
Stopping...
ConstructedServiceImpl#stop
InstanceServiceImpl#stop
Disposing...
ConstructedServiceImpl#dispose
InstanceServiceImpl#dispose
So on start()
, the component I created and injected as an instance is started. The component I injected via constructor injection gets constructed and then started. But nothing is seen from the component I injected via the factory.
As far as the documentation goes, the Javadoc for FactoryInjector
shows #start
, #stop
and #dispose
methods, which appear to be intended for the factory itself to do its own lifecycle stuff, not for the components the factory spins out.
A quick look at the source shows that the adapter implementing ComponentLifecycle
will have its methods called, but it isn't immediately clear how to hook it in. If I look at the other implementing classes, practically everything seems to delegate to something else, making it difficult to figure out what is really happening.
What is the proper way to do this? Is there even a proper way to do this?
FactoryConstructedServiceAdapter should implement LifecycleStrategy and have
@Override
public boolean hasLifecycle(Class<?> type) {
return true;
}
Basically, that's all, factory will be included in the standard lifecycle and can manage the components AND the actual instantiation for FactoryConstructedServiceImpl will be called (if you don't need lifecycle on a component provided by your factory and just wonder why it's not instantiated, keep in mind that factories are lazy and you don't see "FactoryConstructedServiceImpl#init" in log until you actually wire or request the component).
Take InstanceAdapter if you need a big example.