[picocontainer-dev] Looking for advices on dynamic Component Providers
Fabio Kung <[email protected]> Mon, 6 Jul 2009 15:19:45 -0300
| Newsgroups | gmane.comp.java.picocontainer.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi all,
I'm involved in a project which needs to be independent from IoC vendors. We
ended building a thin anti-corruption layer which translates our needs to
several IoC providers and we already have implementations for Pico and
Spring, with Guice coming.
My problem appeared when trying to support what we call
"ComponentFactories". I believe Pico calls them "Providers". Here is a small
example:
@Component // causes it to be registered in the container
public class HibernateProvider implements ComponentFactory<Session> {
private SessionFactory factory;
public HibernateProvider(SessionFactory regularConstructorInjection) {
// this component is managed, then it may receive any available
dependency
}
public Session getInstance() {
// this is similar to a Pico's provide() method
return ...; // hibernate session for current scope
}
}
We are doing a work very similar to what JSR-330 (Dependency Injection for
Java) is trying to achieve, abstracting IoC providers. Hopefully, I also
believe that some of you guys are involved in this JSR and probably had (or
will have to) deal with the same problem, because the spec has a Provider<T>
interface.
My idea is to register a ProviderAdapter inside Pico, for each
ComponentFactory. The main issue is that Pico statically checks for the
Provider targetType, taking the return type of the provide() method. So, it
would require a new class for each ComponentFactory, as every associated
ProviderAdapter must have a provide() method with a different return type.
I know I could use ASM and build a new ProviderAdapter class at runtime for
each ComponentFactory, with a different provide() method. But I really
wanted to avoid dealing with ASM, bytecode manipulation and ClassLoaders if
it could be solved in an easier manner.
Then, my second thought was to create a "generic ProviderAdapter" for Pico,
that doesn't require target types to be specified as return types of
provide() methods. It simply receives the target type as a regular
dependency:
public class GenericProviderAdapter implements Injector, Adapter {
private static final Method provideMethod;
static {
try {
provideMethod =
ComponentFactoryAdapter.class.getMethod("provide", Container.class);
} catch (NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
}
private final Class<? extends ComponentFactory> factoryType;
private final Class<?> targetType;
public GenericProviderAdapter(Class<? extends ComponentFactory>
factoryType) {
this.factoryType = factoryType;
// not relevant and specific to my case, but I'm taking the
targetType from
// the generic type definition: XYZ implements
ComponentFactory<Session> => targetType = Session.class
this.targetType = new
ComponentFactoryIntrospector().targetTypeFor(factoryType);
}
public Object provide(Container container) {
// the ComponentFactory is managed, then I need the container to
retrieve an instance
// note: Container interface is implemented with Pico in this
example
return container.instanceFor(factoryType).getInstance();
}
public Object getComponentInstance(PicoContainer container, Type into)
throws PicoCompositionException {
return new Reinjector(container).reinject(targetType,
this.getClass(), this, Characteristics.NONE, new
MethodInjection(provideMethod));
}
// ...
}
My idea is that for every ComponentFactory in the application, one
ProviderAdapter is registered inside pico:
pico.addComponent(new GenericProviderAdapter(componentFactoryType));
The important pieces of the GenericProviderAdapter are:
- the return type of the provide() method is generic (Object), so that I
can use the same ProviderAdapter for every ComponentFactory;
- the getComponentInstance() method is based on the original code from
ProviderAdapter class, but it infers the targetType of the Provider from the
ComponentFactory class, instead of using the return type of the provide()
method.
It is almost working, despite the fact that Pico has a type check for
Provider classes in the AbstractAdapter#checkTypeCompatibility() method. The
type check throws an exception when the provide() method return type differs
from the actual targetType, that is my case.
protected void checkTypeCompatibility() {
// ...
if (Provider.class.isAssignableFrom(componentImplementation)) {
if (!componentType.isAssignableFrom(
ProviderAdapter.getProvideMethod(componentImplementation).getReturnType()))
{
throw newCCE(componentType);
}
}
// ...
}
Would you mind relaxing the type check? Or does someone have an alternative
idea/approach?
By now, I'm just building custom implementations of all Injectors involved
in the process (ReInjector, MethodInjection, MethodInjector and
MethodInjector$ByReflectionMethod), overriding the checkTypeCompatibility()
method. I had to override all injectors, because the type check is being
called at _every_ injector instantiation (as the call is in the
AbstractAdapter constructor).
It would be much easier if you could just drop (or change) the type check
inside the AbstractAdapter class. Do you think it is reasonable?
I think it would also be useful to people implementing JSR-330 (which has
the Provider<T> interface), JSR-299 (which has Producer<T> interface) and
others, on top of Pico. All of them could be easily mapped one-to-one to a
Pico GenericProducerAdapter instance, without requiring class generation at
runtime.
Thanks!
Cheers,
--
Fabio Kung
http://www.fabiokung.com
Caelum - Ensino e Inovação
http://www.caelum.com.br