Reputation: 2274
I am new to Guice. I using Guice in conjunction with AWS SWF. My current structure in as below:
MainClass:
class MainClass {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new ClientModule(param1, param2));
injector = injector.createChildInjector(injector.getInstance(TestModule.class));
}
}
TestModule:
class TestModule extends AbstractModule {
@override
protected void configure() {
// create SWF service object
// Register Workflow worker and Activity worker
TempWorkflowClientExternalFactory fac = TempClientExternalFactoryImpl(swf_service, domain);
bind(TempWorkflowClientExternalFactory.class).annotatedWith(Names.named("ABC")).toInstance(fac);
}
}
Definition to class where I want to use it:
class Test {
@Inject
@Named("ABC")
TempWorkflowClientExternalFactoryImpl fac;
public void method() {
TempWorkflowClientExternal temp = fac.getClient("123");
temp.callWorkflowMethod() // This method is defined in SWF Workflow Activity class
}
}
However on execution I get the below error:
No implementation for <packageName>.TempWorkflowClientExternalFactoryImpl annotated with @com.google.inject.name.Named(value=ABC) was bound
I need the same object to be returned wherever TempWorkflowClientExternalFactory is injected. I tried looking up at Guice wiki/FAQ. Unfortunately I think I overlooking something or have misunderstood some concept.
If instead of binding, I use the below code in configure
method after creating the fac
object, my application works as expected.
TempWorkflowClientExternal temp = fac.getClient("123");
temp.callWorkflowMethod()
However when I try to bind, it fails.
Update:
If I perform the binding without the annotatedWith
, the program executes but I can observe that binding is not done correctly as the fac
object in Test
class gets instantiated with default constructor and not the object I want to associate with
Additional Detail: TempWorkflowClientExternalFactoryImpl, TempWorkflowClientExternalFactory, TempWorkflowClientExternal are also autogenerated classes by AWS-SWF. I cannot modify their constructors or add any annotations in those classes. Test.method() will be invoked by when certain business logic criteria is met.
Can someone please assist how to resolve the error?
Upvotes: 3
Views: 14357
Reputation: 95614
bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC"))
.toInstance(fac);
At this point, Guice is aware of one key: @Named("ABC") TempClientExternalFactory
. No Impl is bound.
@Inject
@Named("ABC")
TempClientExternalFactoryImpl fac;
Here, however, you don't request the Factory
, you request the FactoryImpl
. Guice doesn't know how to provide it, so it tells you:
No implementation for
<packageName>.TempWorkflowClientExternalFactoryImpl
annotated with@com.google.inject.name.Named(value=ABC)
was bound.
You have, as far as I can tell, 3 options:
Request the Factory, not the FactoryImpl. This is the best simple solution, because it helps you with the Guice/OOP best-practice of coding to the interface, not the implementation. You can replace the Factory in tests with any implementation, including one from a mocking framework.
@Inject
@Named("ABC")
TempClientExternalFactory fac;
Bind the @Named("ABC") FactoryImpl
to the instance, and then bind the @Named("ABC") Factory
to the @Named("ABC") FactoryImpl
. This is a good solution in the unlikely event that you want your classes to distinguish between requesting the interface and requesting the implementation even if they resolve to the same class right now. Otherwise, it may lead to confusing or inconsistent code, because both interface and impl will be available through the graph.
bind(TempClientExternalFactoryImpl.class).annotatedWith(Names.named("ABC"))
.toInstance(fac);
bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC"))
.to(Key.get(TempClientExternalFactoryImpl.class, Names.named("ABC"));
Abstract away the factories if the real dependency you need to provide is TempWorkflowClientExternal and the factory interface/impl is unlikely to change or be called directly. Dependency injection and factory patterns can have overlapping responsibilities, so rather than using Guice to return a factory, you can use Guice to return the right implementation.
None of that is to say that Guice shouldn't ever return a factory; if your parameter "123"
is likely to change then it's exactly right to have Guice return a factory. If that parameter is universal across your codebase, though, you can abstract it away into your Guice Provider.
bind(TempWorkflowClientExternal.class).toProvider(
new Provider<TempWorkflowClientExternal>() {
TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null);
return fac.getClient("123");
});
...or...
@Provides TempWorkflowClientExternal getExternalFactory() {
TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null);
return fac.getClient("123");
}
Upvotes: 3
Reputation: 52
PS: This is the first time I am using Guice.
I tried to reproduce the issue. But I couldn't. Here is my complete code. Let me know if I am doing something different.
TempClientExternalFactory
public interface TempClientExternalFactory {
public TempWorkflowClientExternal getClient(String in);
}
TempClientExternalFactoryImpl
public class TempClientExternalFactoryImpl implements TempClientExternalFactory {
public TempClientExternalFactoryImpl(String swf_service, String domain) {
}
public TempWorkflowClientExternal getClient(String in) {
return new TempWorkflowClientExternal();
}
}
TempWorkflowClientExternal
public class TempWorkflowClientExternal {
public void callWorkflowMethod() {
System.out.println("In callWorkflowMethod");
}
}
Test
import com.google.inject.Inject;
import com.google.inject.name.Named;
class Test {
@Inject
@Named("ABC")
TempClientExternalFactory fac;
public void method() {
TempWorkflowClientExternal temp = fac.getClient("123");
temp.callWorkflowMethod();
System.out.println("Success");
}
}
TestModule
import com.google.inject.AbstractModule;
import com.google.inject.name.Names;
public class TestModule extends AbstractModule {
@Override
protected void configure() {
TempClientExternalFactory fac = new TempClientExternalFactoryImpl(null, null);
bind(TempClientExternalFactory.class).annotatedWith(Names.named("ABC")).toInstance(fac);
}
}
App
import com.google.inject.Guice;
import com.google.inject.Injector;
public class App {
public static void main(String[] args) {
Injector injector = Guice.createInjector(new TestModule());
Test app = injector.getInstance(Test.class);
app.method();
}
}
Also, this stackoverflow question looks similar to what you have asked;
Error in binding: No implementation was bound - Guice
Upvotes: 0