Java – Guice MapBinder with list values

Guice MapBinder with list values… here is a solution to the problem.

Guice MapBinder with list values

I have a service that needs to inject mulitmap – Map<String, List<Enricher>>

public class EnrichService {
    private Map<String, List<Enricher>> typeEnrichers;

@Inject
    public EnrichService(Map<String, List<Enricher>> typeEnrichers) {
        this.typeEnrichers = typeEnrichers;
    }

public void enrich(Entity entity) {
        List<Enricher> enrichers = typeEnrichers.get(entity.type);
        //.. enriching entity with enrichers
    }
}

class Entity {
    String id;
    String type = "shapedColorful";
    String color;
    String shape;
}

interface Enricher {
    void enrich(Entity entity);
}

class ColorEnricher implements Enricher {
    @Inject
    private  ColorService colorService;
    public void enrich(Entity entity) {
        entity.color = colorService.getColor(entity.id);
    }
}

class ShapeEnricher implements Enricher {
    @Inject
    private ShapeService shapeService;
    public void enrich(Entity entity) {            
        entity.shape = shapeService.getShape(entity.id);
    }
}

I need help configuring typeEnrichers Binder in juice
This is what I was trying but stuck

bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
            binder(), 
            new TypeLiteral<String>() {},
            new TypeLiteral<List<Enricher>>() {});

mapBinder.addBinding("shapedColorful", to(/*how to bind list of Enrichers here?? */))

Any help, how do I bind (bind) such a multi-graph?

Solution

You are trying to mix MapBinder with Multibinder.

I recommend that you create a provider for each MapBinder relationship. In fact, Multibinder itself is a List Provider, specifically, its RealMultibinder implementation is unfortunately private and forbidden. If it’s not package private, maybe we can use it this way. Most likely it won’t work anyway … IMHO, it would be fine.

bind(ColorService).to(ColorServiceImpl.class);
bind(ShapeService).to(ShapeServiceImpl.class);
MapBinder<RelationType, List<Enricher>> mapBinder = MapBinder.newMapBinder(
            binder(), 
            new TypeLiteral<String>() {},
            new TypeLiteral<List<Enricher>>() {});

mapBinder.addBinding("shapedColorful", toProvider(Multibinder.newSetBinder(this.binder(), Enricher.class).addBinding().to(ColorService.class).addBinding().to( ShapeService.class).asEagerSingleton()))

You can still create and use providers:

public class ShapeColorfulProvider implements Provider<List<Enricher>> {
 @Inject private ColorService colorService;
 @Inject private ShapeService shapeService;

public List<Enricher> get() {
   return Lists.newArrayList(colorService,shapeService);
 }

}

And then

   mapBinder.addBinding("shapedColorful", toProvider(ShapeColorfulProvider.class))

Related Problems and Solutions