Pass parameter to constructor with Guice
Asked Answered
K

6

42

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?

Kenogenesis answered 11/2, 2012 at 5:12 Comment(0)
A
69

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

Arnett answered 11/2, 2012 at 19:8 Comment(2)
Does not work for me. I have NullPointerException because Foo constructor never called. Probably you need to use your Module somewhere?Inhesion
Please fix the missing return type of "create" method (FooFactory interface). It must be: Foo create(String bar);Peeling
G
11

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.

Gap answered 11/2, 2012 at 16:46 Comment(2)
yeah, it sucks, it s so convoluted, structuremap is much more flexible than guice.Kenogenesis
Where can I download the StructureMap for Java?Mayo
E
10

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.

Ethmoid answered 25/5, 2017 at 6:12 Comment(0)
M
2

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.

Merlynmermaid answered 11/2, 2012 at 8:15 Comment(0)
L
0

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();
Lacilacie answered 8/6, 2022 at 23:1 Comment(0)
C
0

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.

Capping answered 11/10, 2023 at 13:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.