Status: New
Owner: ----

New issue 740 by [email protected]: Not obvious behavior of JIT bindings creations by parent
http://code.google.com/p/google-guice/issues/detail?id=740

Hi guys,

Today I recognized easy one tricky part of JIT binding based on TypeListeners and ChildInjectors usage.
Let me explain step by step:

First
For every binding bind(Foo.class).to(FooImpl.class) one JIT binding will be created. Binding for FooImpl.class implemented by ConstructorBindingImpl.class. As soon as FooImpl.class is not explicitly bind, it'll be bind just-in-time. As it always happened with final implementations.

Second
Guice tried to create JIT binding in parent injector first. If it's not possible then in would be created in a child.

Let's review the example. We have some base classes for binding:

   class Dependency {} //just a dependency

    interface Foo {} //interface for binding

class FooImpl implements Foo {} //base implementation without dependencies

class EnhancedFooImpl implements Foo { //enhanced implementation with dependency

        @Inject
        EnhancedFooImpl(Dependency dependency) {
        }

    }

Next, let's make injectors hierarchy: bind nothing in parent and bind our classes in child.

      Injector parentInjector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                //nothing here
            }
        });

        parentInjector.createChildInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(Dependency.class);
bind(Foo.class).annotatedWith(Names.named("simple")).to(FooImpl.class); bind(Foo.class).annotatedWith(Names.named("enhanced")).to(EnhancedFooImpl.class);
            }
        });

At the child injector initialization time, two just-in-time bindings will be created: for FooImpl.class and EnhancedFooImpl.class. But due to "JIT parent first strategy" FooImpl.class bindings will be created by parent injector due to both classes have no dependencies at all. As well as EnhancedFooImpl.class will be created by child injector, due to dependency on binding from child injector space (Dependency.class).

That means, JIT binding will be created by injector which can resolve all dependencies by himself.

Tricky
It's tricky because we don't know, what injector actually will build implementation instance. Depends on implementation dependencies it can be one from all injectors hierarchy.
And more, what about type listeners defined by child injector?

Let's add type listener for all classes in child injection:

Injector childInjector = parentInjector.createChildInjector(new AbstractModule() {

            @Override
            protected void configure() {
                bind(Dependency.class);
bind(Foo.class).annotatedWith(Names.named("simple")).to(FooImpl.class); bind(Foo.class).annotatedWith(Names.named("enhanced")).to(EnhancedFooImpl.class);

                //bind listener for all injections in this injector
                bindListener(Matchers.any(), new TypeListener() {
                    @Override
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
                        encounter.register(new InjectionListener<I>() {
                            @Override
                            public void afterInjection(I injectee) {
//this call was never happened for FooImpl creation //but will successfully called for EnhancedFooImpl creation
                            }
                        });
                    }
                });
            }
});

Due to the fact FooImpl.class binding created by parent injector, injection listener defined in the child injector would never called. Parent injector just doesn't no about some injection listeners in child containers :(

So behavior is not obvious. If we have a hierarchy of injector as well as hierarchy of type listeners, we can't guarantee what of them will be processed and what not. Depends on what level dependencies is enough to create JIT binding for implementation class. I think it can create some floating issues.

In addition, to reproduce the same effect, try to inject Injector dependency in both implementation classes. For FooImpl.class it'll be parent injector instance. For EnhancedFooImpl.class - child injector instance.

I'm not sure, what happened if we will always create JIT binding in an injector where binding will be requested.

P.S. Test file attached.


Attachments:
        TrickyJIT.java  2.5 KB

--
You received this message because you are subscribed to the Google Groups 
"google-guice-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/google-guice-dev?hl=en.
For more options, visit https://groups.google.com/groups/opt_out.


Reply via email to