I'm trying to make DI with Guice work, doing (as it seems to me) exactly what's in the manual.
I can't explain the problem because I don't really understand it - everything seems very logical and should work.. but it doesn't. So, I can only attach the code and the stacktrace:
public class Runner {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TestModule());
//next line throws the exception
JMeterComponent jMeterComponent =
injector.getInstance(JMeterComponent.class);
....
}
}
As you can see, I'm trying to instantiate object of JMeterComponent
class. It's constructor (as you'll see later) accepts 3 arguments: all of them should be also instantiated by IoC and injected.
And here's the TestModule
with configuration of these 3 arguments:
public class TestModule extends AbstractModule {
@Override
protected void configure() {
bind(Callable.class).annotatedWith(Names.named("JMeter"))
.to(JMeterTask.class);
bind(Processor.class).annotatedWith(Names.named("JMeter"))
.to(JMeterResultsProcessor.class);
bind(Renderer.class).annotatedWith(Names.named("JMeter"))
.to(JMeterResultsWikiRenderer.class);
}
}
Now, let's look at those concrete implementations - JMeterTask
, JMeterResultsProcessor
and JMeterResultsWikiRenderer
(all of them have fake bodies for the simplicity):
public class JMeterTask implements Callable<JMeterRawResults> {
public JMeterRawResults call() throws Exception {
return new JMeterRawResults();
}
}
public class JMeterResultsProcessor implements
Processor<JMeterRawResults, JMeterResults> {
public JMeterResults process(JMeterRawResults raw) {
return new JMeterResults();
}
}
public class JMeterResultsWikiRenderer implements Renderer<JMeterResults> {
public Map<String, String> render(JMeterResults jMeterResults) {
Map<String, String> results = Maps.newHashMap();
...
return results;
}
}
And now let's look at the JMeterComponent
class, which instance's construction is the aim of the whole DI-related stuff here:
public class JMeterComponent extends AbstractComponent<String, String> {
@Inject
public JMeterComponent(@Named("JMeter") Callable<String> task,
@Named("JMeter")Processor<String, String> processor,
@Named("JMeter")Renderer<String> renderer) {
super(task, processor, renderer);
}
}
And here's the stacktrace:
Exception in thread "main" com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for stat.domain.Processor<java.lang.String, java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter) was bound.
while locating stat.domain.Processor<java.lang.String, java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter)
for parameter 1 at stat.components.jmeter.JMeterComponent.<init>(JMeterComponent.java:18)
while locating cstat.components.jmeter.JMeterComponent
2) No implementation for stat.domain.Renderer<java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter) was bound.
while locating stat.domain.Renderer<java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter)
for parameter 2 at stat.components.jmeter.JMeterComponent.<init>(JMeterComponent.java:18)
while locating stat.components.jmeter.JMeterComponent
3) No implementation for java.util.concurrent.Callable<java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter) was bound.
while locating java.util.concurrent.Callable<java.lang.String> annotated with @com.google.inject.name.Named(value=JMeter)
for parameter 0 at stat.components.jmeter.JMeterComponent.<init>(JMeterComponent.java:18)
while locating stat.components.jmeter.JMeterComponent
Some additional facts:
featured
label)com.google.inject
package in any other class in the codeProcessor
and Renderer
are placed in one module, and their jmeter
-implementations (JMeterResultsProcessor
and other) and JMeterComponent
class are placed in another module.That's pretty much everything there's to say about it.
Sorry for such a long post and thanks for your patience to read it to the end.
Any ideas on why did the errors occur and how to fix it?
A binding is an object that corresponds to an entry in the Guice map. You add new entries into the Guice map by creating bindings.
Guice forbids null by default So if something tries to supply null for an object, Guice will refuse to inject it and throw a NULL_INJECTED_INTO_NON_NULLABLE ProvisionException error instead. If null is permissible by your class, you can annotate the field or parameter with @Nullable .
Interface Injector. Builds the graphs of objects that make up your application. The injector tracks the dependencies for each type and uses bindings to inject them. This is the core of Guice, although you rarely interact with it directly.
The Guice module helps you to inject Guice managed components into your play application. The injection points are defined by the upcoming @javax. inject. Inject annotation, which is bundled with play at the moment.
There are several problems I'm seeing here.
First, Callable
and Callable<String>
are different. If you want to inject a Callable<String>
(or Processor<String, String>
, etc.) in Guice, you have to bind something to Callable<String>
, not Callable
.
Second, you're binding Callable
to JMeterTask
which implements Callable<JMeterRawResults>
but you're injecting Callable<String>
in JMeterComponent
's constructor (same deal for Processor
and Renderer
). I'm going to assume that JMeterComponent
should have Callable<JMeterRawResults>
etc. injected.
Anyway, what you need to do is generic bindings using TypeLiteral
:
bind(new TypeLiteral<Callable<JMeterRawResults>>(){})
.annotatedWith(Names.named("JMeter"))
.to(JMeterTask.class);
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