Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice assisted inject with several factory methods and null parameters

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)
like image 849
gontard Avatar asked Sep 03 '14 10:09

gontard


People also ask

What is assisted injection in Guice?

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:

How does assistedinject work in factorymodulebuilder?

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.

How do I visit an assistedinject binding in Guice?

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.

Can @assistedinject be injected directly into a constructor?

@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.


Video Answer


2 Answers

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);
}
like image 162
gontard Avatar answered Oct 31 '22 15:10

gontard


@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 Paths.

like image 42
Vladimir Matveev Avatar answered Oct 31 '22 15:10

Vladimir Matveev