I have this interface and simple implementation:
public interface Data {
}
import java.nio.file.Path;
import javax.annotation.Nullable;
import javax.inject.Inject;
import com.google.inject.assistedinject.Assisted;
public class SimpleData implements Data {
@Inject
public SimpleData(@Assisted @Nullable Path path) {
}
}
I want to generate a Factory
with different methods using guice.
import java.nio.file.Path;
import javax.annotation.Nullable;
public interface Factory {
Data create();
Data load(@Nullable Path path);
}
But the following module configuration:
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.assistedinject.FactoryModuleBuilder;
public class Main {
public static void main(String[] args) {
Injector injector = Guice.createInjector(
binder -> binder.install(
new FactoryModuleBuilder().implement(Data.class, SimpleData.class)
.build(Factory.class)));
Data data = injector.getInstance(Factory.class).create();
}
}
fails:
Exception in thread "main" com.google.inject.CreationException: Guice creation errors:
1) No implementation for java.nio.file.Path annotated with @com.google.inject.assistedinject.Assisted(value=) was bound.
while locating java.nio.file.Path annotated with @com.google.inject.assistedinject.Assisted(value=)
for parameter 0 at SimpleData.<init>(SimpleData.java:10)
at Factory.create(Factory.java:1)
at com.google.inject.assistedinject.FactoryProvider2.initialize(FactoryProvider2.java:539)
at com.google.inject.assistedinject.FactoryModuleBuilder$1.configure(FactoryModuleBuilder.java:335)
1 error
at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:435)
at com.google.inject.internal.InternalInjectorCreator.injectDynamically(InternalInjectorCreator.java:175)
at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:109)
at com.google.inject.Guice.createInjector(Guice.java:95)
at com.google.inject.Guice.createInjector(Guice.java:72)
at com.google.inject.Guice.createInjector(Guice.java:62)
at Main.main(Main.java:9)
Guice can eliminate a lot of that boilerplate by auto-generating Factory implementations from simple interfaces. This process is (possibly misleadingly) known as assisted injection. Sometimes a class gets some of its constructor parameters from the Guice Injector and others from the caller:
See the FactoryModuleBuilder javadoc for complete details. AssistedInject maps the create () method's parameters to the corresponding @Assisted parameters in the implementation class' constructor. For the other constructor arguments, it asks the regular Injector to provide values.
Inspecting AssistedInject Bindings (new in Guice 3.0) Visiting an assisted inject binding is useful for tests or debugging. AssistedInject uses the extensions SPI to let you learn more about the factory binding. You can visit bindings with an AssistedInjectTargetVisitor to see details about the binding.
@AssistedInject types cannot be injected directly, only the @AssistedFactory type can be injected. This is true even if the constructor does not contain any assisted parameters. @AssistedInject types cannot be scoped. If multiple @Assisted parameters have the same type, you must distinguish them by giving them an identifier.
I solved my problem using the annotation @AssistedInject
. Quote from the javadoc:
When used in tandem with
FactoryModuleBuilder
, constructors annotated with @AssistedInject indicate that multiple constructors can be injected, each with different parameters.
So i add the annotation and a constructor to the SimpleData
class:
public class SimpleData implements Data {
@AssistedInject
public SimpleData(@Assisted Path path) {
}
@AssistedInject
public SimpleData() {
}
}
i removed the @Nullable annotation from the factory:
import java.nio.file.Path;
public interface Factory {
Data create();
Data load(Path path);
}
@Nullable
does not mean that if you don't have a binding, then null
will be injected. It only allows writing bindings to null
. If you don't have a binding and there is no applicable JIT-binding, then injection will fail.
Your factory's create()
method requires Guice to find an @Assisted Path
binding, but it obviously can't find it since you've never created one, so it fails.
Honestly, I'm not sure if there is a clean way to implement such defaulting. Ideally you should mark Path
with some binding annotation and add a default binding to null for it, but @Assisted
already is a binding annotation, and it is not possible to have multiple binding annotations on a single injection point. You can try creating a binding for @Assisted Path
:
binder.bind(Path.class).annotatedWith(Assisted.class).toInstance(null);
However, I'm not sure if it would work because Assisted
can be special to Guice. And even if it will work, it is not very clean - there may be conflicts with other assisted factories accepting Path
s.
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