Search code examples
javaguice

Guice - Default binding for MapBinder


I am using MapBinder to map keys to their respective implementations. Right now I have something like this:

MapBinder<String, Processor> processor
                = MapBinder.newMapBinder(binder(), String.class, Processor.class);
        processor.addBinding("a1").to(a1Processor.class).in(Scopes.SINGLETON);
        processor.addBinding("a2").to(a2Processor.class).in(Scopes.SINGLETON);
        processor.addBinding("a3").to(a3Processor.class).in(Scopes.SINGLETON);

It is working the way that it is supposed to. But right now I am thinking of creating a DefaultProcessor and binding any String other than a2 and a3 to this DefaultProcessor. Is it possible to do this?


Solution

  • From the Javadocs of MapBinder

    An API to bind multiple map entries separately, only to later inject them as a complete map.

    So what you are asking in your question is not possible to achieve via MapBinder.

    Although you can write a wrapper around the Map<String, Processor> and use it.

    Suggestion:

    @Singleton
    class StringProcessorWrapper {
    
        private final Map<String, Processor> processorMap;
        private final Processor defaultProcessor;
    
        @Inject
        public StringProcessorWrapper(Map<String, Processor> processorMap, @Named("default") Processor defaultProcessor) {
            this.processorMap = processorMap;
            this.defaultProcessor = defaultProcessor;
        }
    
        public Processor get(String key) {
            return processorMap.getOrDefault(key, defaultProcessor);
        }
    }
    

    For this to work you will have to add a binding in your guice module's configure method like this:

    bind(Processor.class).annotatedWith(Names.named("default")).to(DefaultProcessor.class).in(Scopes.SINGLETON);
    

    Now you can inject the StringProcessor wrapper and use it.

    This suggestion is valueable when you need to use the Map at a lot of places. For a single class, you can just inject the default and use it when key is missing.

    If you just want to use the map and avoid adding a separate default binding, you can add a 4th binding in your MapBinder with "default" key and in the get method of StringProcessorWrapper do something like this:

    processorMap.getOrDefault(key, processorMap.get("default"));