Kishore Bandi
Kishore Bandi

Reputation: 5721

Pass Parameters to constructor in Guice with no modifications to the Interface/Impl class

I would like to pass constructor argument when binding an interface with Impl in Guice.

There are a couple of solutions, but I don't have code access to these interfaces/Impl's. They're developed by other team's and I've included them in My project.

  1. @Named/@Assisted - Both needs a change in the source code (Constructor) of the Impl to include these annotations. (I don't have access to this code)
  2. Implement Provider - which returns instance of that Impl by loading the required arguments. This Worked. But the problem is I've 200+ such existing DI's and I'll have to write 200+ Providers.

Currently we're using Spring DI and are in the process of moving to Guice. So I need to define something like

<bean name="clientWrapper" class="com.wrapper.client.ClientWrapper">
    <constructor-arg index="0" value="${server.ip}" />
    <constructor-arg index="1" value="${server.port}" />
</bean>

in Guice. But Pass those Constructor Args.

bind(IClientWrapper.class).to(ClientWrapper.class);

How to achieve this without using Providers?

Upvotes: 3

Views: 3750

Answers (1)

Michael Lloyd Lee mlk
Michael Lloyd Lee mlk

Reputation: 14661

Your best option I think is a mix of Provides methods and toConstructor binding.

Use the @Provides method binding when you have an object who has dependencies that can not be worked out by type alone.

public class ProvidesModule extends AbstractModule {
    @Provides
    IClientWrapper clientWrapper(@Named("server.ip") String ip,
                                 @Named("server.port") int port) {
       return new ClientWrapper(ip, port);
    }
}

In overall code size this is not significantly more than Spring and is type safe.

When the constructor only has dependencies that can be worked out by type alone then use toConstructor binds

protected void configure() {
    try {
      bind(TransactionLog.class).toConstructor(
          DatabaseTransactionLog.class.getConstructor(DatabaseConnection.class));
    } catch (NoSuchMethodException e) {
      addError(e);
    }
}

One last option:

Our Legacy thingie:

interface LegacyThing {
}

class LegacyThingImp implements LegacyThing {
    public LegacyThingImp(String test) {
        System.out.println(test);
    }
}

Is my magic provider on GitHub. This takes an implementation class and the list of dependencies (as Keys) and then finds the right constructor by Magic (or reflection).

public class TestMagic {
    public static void main(final String... arg) {
        Guice.createInjector(
                new AbstractModule() {
                    @Override
                    protected void configure() {
                        bind(String.class).annotatedWith(Names.named("testString")).toInstance("Guice!");

                        bind(LegacyThing.class).toProvider(new MagicLegacyProvider<>(LegacyThingImp.class, Key.get(String.class, Names.named("testString"))));
                    }
                }).getInstance(LegacyThing.class);
    }
}

Upvotes: 6

Related Questions