google-code-export / google-guice

Automatically exported from code.google.com/p/google-guice
Apache License 2.0
2 stars 1 forks source link

Convenient way to expose multibindings and mapbindings from PrivateModules #369

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Multibinder and MapBinder are not exposable from PrivateModules. Introduce
a convenient way to fix that.

Maybe an expose method on each, which fails if the binder is not a
PrivateBinder.

Original issue reported on code.google.com by net...@gmail.com on 7 May 2009 at 6:46

GoogleCodeExporter commented 9 years ago
A few wrinkles for the implementation...
 - we need to avoid binding the Set/Map in the private module, since that would conflict with the same binding 
in the outer module
 - we need to somehow bind the Set/Map in the outer module if it doesn't exist already

Original comment by limpbizkit on 7 May 2009 at 7:15

GoogleCodeExporter commented 9 years ago
Why not just expose the Set/Map binding?

Original comment by net...@gmail.com on 7 May 2009 at 7:20

GoogleCodeExporter commented 9 years ago
Would this be possible if the MapBinder had an 'outer' binder and private 
binder available?

It's not ideal, but I could see doing something like

install(new MyMapBinderModule(binder()); 

in an abstract module and the MyMapBinderModule/MapBinder could look something 
like

public class MyMapBinderModule extends PrivateModule
{
    private final Binder m_publicBinder;

    public MyMapBinderModule(Binder publicBinder)
    {
        m_publicBinder = publicBinder;
    }

    @Override
    protected void configure()
    {
        MapBinder<String, MessageHandler> mapBinder =
            MapBinder.newMapBinder(m_publicBinder, binder(), Foo.class, Bar.class);
    }
{

Alternately, if we could somehow just do

expose(mapBinder) or mapBinder.expose() or something that would be awesome.

I don't know if this is too 'hacky', but it's leagues above the hacks I have in 
place to do this right now.

Original comment by Industri...@gmail.com on 18 Feb 2011 at 6:54

GoogleCodeExporter commented 9 years ago
What hacks are you doing now?

Original comment by sberlin on 18 Feb 2011 at 6:59

GoogleCodeExporter commented 9 years ago
Basically, I do this...

public class MyModule extends AbstractModule
{
    private final String m_configurationValue;
    public MyModule(String configurationValue)
    {
        m_configurationValue = configurationValue;
    }

    @Override
    protected void configure()
    {
        MapBinder<String, MessageHandler> mapBinder =
            MapBinder.newMapBinder(binder(), Foo.class, Bar.class);

        // Great, I'm creating an injector in my configure...
        Injector injector = Guice.createInjector(getPrivateModule());

        // We could have bound this to an instance of Bar.class instead, 
        // but that would require remote calls in its providers be executed
        // in our unit tests and that would not work correctly. This limits
        // our Module unit testing.
        mapBinder.addBinding(new Foo()).toProvider(
                injector.getInstance(MyBarProvider.class).getProvider());
    }

    // This class exists just to manipulate an injector within Configure    
    private static class MyBarProvider {
        private @Inject Provider<Bar> provider;

        public Provider<Bar> getProvider() {
            return provider;
        }
    }

    private Module getPrivateModule()
    {
        return new PrivateModule() {
            @Override
            protected void configure()
            {
                // Bar.class depends on an A provider One, but in other
                // modules depends on A provider two because of different
                // implementations.
                bind(A.class).toProvider(AImplProviderOne.class);
                // Bar.class depends on an B provider Three, but in other
                // modules depends other providers.
                bind(B.class).toProvider(BImplThreeProvider.class);

                // B implementations need a parameter passed into the module
                // that indicate something, like initial size or file to read
                // or something.
               bind(String.class).annotatedWith(BParameter.class).toInstance(configurationValue);

                bind(bar.class);
                expose(bar.class);
            }
        }
    }
}

Original comment by Industri...@gmail.com on 18 Feb 2011 at 7:25

GoogleCodeExporter commented 9 years ago

Original comment by sberlin on 22 Feb 2011 at 1:39

GoogleCodeExporter commented 9 years ago

Original comment by sberlin on 22 Feb 2011 at 1:40

GoogleCodeExporter commented 9 years ago
In Guice 3, this workaround has another downside. Refer to this: 
https://code.google.com/p/google-guice/wiki/ToConstructorBindings

"But there are limitations of that approach: manually constructed instances do 
not participate in AOP."

This implies if I bind to the constructor of an object, I can use AOP on it, 
even if it's a third-party component.

However, I cannot use this in tandem with the above workaround. If I bind 
something with a second injector and providers to work around the limitations 
of the MapBinder/MultiBinder, the objects created by that second injector don't 
participate in AOP specified in a separate module created for that express 
purpose. This is a somewhat limiting problem from the perspective of using 
Guice with AOP.

Original comment by Industri...@gmail.com on 6 May 2011 at 3:51

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
My solution is to use expose in the configure method of the PrivateModule with 
the help of TypeLiteral and Types:

    expose((TypeLiteral<Map<String, MessageHandler>>) TypeLiteral.get(
        Types.mapOf(String.class, MessageHandler.class)));

I even have crazier stuff like:
    expose((TypeLiteral<Set<Entry<String, Provider<MessageHandler>>>>) TypeLiteral.get(
        Types.setOf(Types.newParameterizedTypeWithOwner(
                                Map.class, Entry.class, String.class,
                                Types.providerOf(MessageHandler.class)))));

Original comment by justindh...@gmail.com on 17 Sep 2013 at 6:12

GoogleCodeExporter commented 9 years ago
I need this too. I'm using workaround with unique annotation for private 
multibinder now.

Original comment by Ash2kk@gmail.com on 1 Oct 2013 at 9:58