I have the following Guice module:
class MyModule extends AbstractModule {
@Override
protected void configure() {
bindListener(Matchers.any(), new TypeListener() {...});
}
@Provides
SomeClass createSomeClass(final Parameter param) {
log(param.getValue()); <-- this gets logged
...
}
}
What I found strange is that my TypeListener
doesn't get notified about the Parameter
type. Even though the provider is beign called and returns SomeClass
. I also see the log statement so clearly the Parameter
was injected by Guice.
@Override
protected void configure() {
bind(Parameter.class);
bindListener(Matchers.any(), new TypeListener() {...});
}
I'm aware of Untargetted bindings and the statement:
An untargetted binding informs the injector about a type, so it may prepare dependencies eagerly.
I would still expect Guice to call the TypeListener
for any type which is either explicitly binded or injected for the first time.
So do I need to make untargetted binding for such classes as a rule of thumb?
NOTE: marking the Parameter
constructor with @Inject
doesn't solve the problem.
EDIT:
The complete example (hope I din't leave too much garbage) is as follows:
public class TestGuice {
public static void main(String[] args) {
Injector parentInjector = Guice.createInjector(new ParentModule());
Injector childInjector = parentInjector.createChildInjector(new SubModule());
childInjector.getInstance(Runnable.class).run();
}
static class ParentModule extends AbstractModule {
@Override
protected void configure() {
}
}
static class SubModule extends AbstractModule {
@Override
protected void configure() {
bind(SampleInjectedClass.class); // <-- Comment/uncomment here
final TypeListener typeListener = new TypeListener() {
public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
System.out.println("Type: " + type.getRawType());
}
};
bindListener(Matchers.any(), typeListener);
}
@Provides
Runnable createRunnable(final SampleInjectedClass sampleClass) {
return new Runnable() {
@Override
public void run() {
sampleClass.test();
}
};
}
}
static class SampleInjectedClass {
public void test() {
System.out.println("Test");
}
}
}
If the line is present the output is:
Type: class com.barcap.test.TestGuice$SampleInjectedClass
Type: class com.google.inject.internal.ProviderMethod
Test
If I remove the line I get:
Type: class com.google.inject.internal.ProviderMethod
Test
I noticed that if the injector wasn't created via the createChildInjector
code the bind(...)
is not necessary.
Note that the only Guice-specific code in the above is the @Inject annotation. This annotation marks an injection point. Guice will attempt to reconcile the dependencies implied by the annotated constructor, method, or field.
The implementation is very easy to understand. We need to create Injector object using Guice class createInjector() method where we pass our injector class implementation object. Then we use injector to initialize our consumer class. If we run above class, it will produce following output.
Guice comes with a built-in binding annotation @Named that takes a string: public class RealBillingService implements BillingService { @Inject public RealBillingService(@Named("Checkout") CreditCardProcessor processor, TransactionLog transactionLog) { ... }
Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in. If it has dependencies, it will resolve those dependencies, pass them into it's constructor, then pass the resulting object into your object.
Just-in-time bindings created for child injectors will be created in an ancestor injector whenever possible. This means that, without the bind(SampleInjectedClass.class);
line, the binding for SampleInjectedClass
is created in the parent injector. Since the parent injector doesn't have your TypeListener
, it won't be triggered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With