DarthVader
DarthVader

Reputation: 55132

Pass parameter to constructor with Guice

I have a factory as below,

public final class Application {

    private static IFoo foo;

    public static IFoo getFoo(String bar)
    {
        // i need to inject bar to the constructor of Foo
        // obvious i have to do something, not sure what
        Injector injector = Guice.createInjector();
        logger = injector.getInstance(Foo.class);
        return logger;              
    }

}

This is the Foo definition:

class Foo
{
   Foo(String bar)
   {

   }

}

OK. I m not sure how I can pass this parameter to Foo constructor with Guice?

Any ideas?

Upvotes: 42

Views: 61593

Answers (6)

Zyl
Zyl

Reputation: 3310

Here is what we ended up doing. It's a bad solution that should only be used when you aren't given time and just want to live to see another day.

  1. Remove all constructors, making the class default-constructible.
  2. Add a public method called init or such which takes dependencies as parameters and assigns them to fields.
  3. Use Guice's Injector.getInstance() to do the class discovery and construction for you, then call the init method with the fields you actually need.

Some forgiveness for doing this may be received by doing some aggressive null-checking that informs oblivious callers of the need to call init by throwing an appropriately formed IllegalStateException.

Upvotes: 0

Saran
Saran

Reputation: 1393

Although this isn't a direct answer to what you're asking, hope it helps. I was trying to understand where the constructor parameters are being passed earlier. If they are custom classes, they should be bind-ed in the module.

Class CustomHandler {

  private Config config;

  @Inject
  CustomHandler(Config config) {
    this.config = config;
  }

  public void handle() {
    // handle using config here
  }
}

Binding:

class Module extends AbstractModule {
  bind(Handler.class).to(CustomHandler.class);
  bind(Config.class).to(CustomConfig.class);
}

Injection:

CustomHandler handler = injector.getInstance(CustomHandler.class);
handler.handle();

Upvotes: 0

DougA
DougA

Reputation: 1255

All the "Guice Constructor Parameter" answers seem to be incomplete in some way. Here is a complete solution, including usage and a visual:

enter image description here

interface FooInterface {
    String getFooName();
}

// Annotate the constructor and assisted parameters on the implementation class

class Foo implements FooInterface {
    String bar;

    @Inject
    Foo(@Assisted String bar) {
        this.bar = bar;
    }

    // return the final name
    public String getFooName() {
        return this.bar;
    }

}

// Create a factory interface with a create() method that takes only the assisted parameters.

// FooFactory interface doesn't have an explicit implementation class (Guice Magic)

interface FooFactory {
    Foo create(String bar);
}

// Bind that factory to a provider created by AssistedInject

class BinderModule implements Module {

    public void configure(Binder binder) {
        binder.install(new FactoryModuleBuilder()
                .implement(FooInterface.class, Foo.class)
                .build(FooFactory.class));
    }
}

// Now use it:

class FooAction {
    @Inject private FooFactory fooFactory;

    public String doFoo() {
        // Send bar details through the Factory, not the "injector"
        Foo f = fooFactory.create("This foo is named bar. How lovely!");
        return f.getFooName(); // "This foo is named bar. How lovely!"
    }
}

Lots of helps here: https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/assistedinject/FactoryModuleBuilder.html

Upvotes: 70

JB Nizet
JB Nizet

Reputation: 692261

If this class is a factory, it should be a Guice-managed object, having a non static getFoo method, and the getFoo method would just use

new Foo(bar)

Not every class needs to be instantiated by Guice.

Also see AssistedInject, to avoid creating this factory yourself and let Guice create one for you.

Upvotes: 2

NejcT
NejcT

Reputation: 167

I know that this is old thread but I just hit the issue myself today. I only need two or maximum three different instances of 'Foo' and I really didn't want to write all the bolierplate code of Factory. With a little googling I found this Stubbisms – Tony’s Weblog I would suggest this solution which is perfect if you know exactly what instances you need.

In Guice module:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic A");
        }
    });
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic B");
        }
    });

Or in java 8:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(() -> new FooImpl("first"));
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(() -> new FooImpl("second"));

And in constructor of your service where you need Foo instances:

@Inject
public MyService (
    @Named("firstFoo") Foo firstFoo,
    @Named("secondFoo") Foo secondFoo) {
}

And Foo in my case:

public class FooImpl implements Foo {

    private String name;

    public FooImpl(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

Hope it helps someone.

Upvotes: 11

David H. Clements
David H. Clements

Reputation: 3638

What you are probably looking for is to use a Guice factory. Particularly easy with the AssistedInject functionality, but they have a manual example at the top of the page. The short of it for the manual example is that you get the factory under non-static getFoo method that you pass whatever parameters to that you need and build the object from there.

This won't work directly if you have method interception in Foo, but it will work in many other cases.

To use AssistedInject, which to me has somewhat cleaner semantics and means less manual wiring, you'll need the guice-assistedinject extension in the classpath, then when creating Foo (well, FooImpl, we should be using interfaces):

@Inject
public FooImpl(@Assisted String bar)
{
    this.baz = bar;
}

Then you create a FooFactory interface:

public interface FooFactory {
    public Foo create(String bar);
}

Then in your guice module:

install(new FactoryModuleBuilder()
    .implement(Foo.class, FooImpl.class)
    .build(FooFactory.class));

You can check out the javadoc for FactoryModuleBuilder for examples with more complex factories.

Upvotes: 11

Related Questions