问题
In my project I am using Google Guice for dependency injection. In my Module class which extends Google Guice's AbstactModule I have a MapBinder, where the key is a name of an item and the value is an implementation of my Item interface (e.g. FirstItem).
MapBinder<String, Item> itemMapBinder = MapBinder.newMapBinder(binder(), String.class, Item.class);
itemMapBinder.addBinding("FirstItem").to(FirstItem.class);
Currently every time I add new implementation of Item (e.g. SecondItem) I also have to add a new line to Module, e.g.
itemMapBinder.addBinding("SecondItem").to(SecondItem.class);
I am using this map of items in my ItemFactory.
public class ItemFactoryImpl implements ItemFactory {
private final Map<String, Item> items;
@Inject
public ItemFactoryImpl(Map<String, Item> items) {
this.items = items;
}
@Override
public Item getItem(String name) {
if (name == null) throw new IllegalArgumentException("Name cannot be null");
return items.get(name);
}
}
I was trying to find a way how to automatically add binding for a new implementation of Item interface. My idea was use a custom annotation which will be added to new implementation of Item interface and modify Module#configure somehow to add binding for all classes with this custom annotation. I finished with something like this:
// cz.milanhlinak.guiceautobinding.item.AutoBindableItem.java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoBindableItem {
}
// cz.milanhlinak.guiceautobinding.item.Item.java
public interface Item {
}
// cz.milanhlinak.guiceautobinding.item.SecondItem.java
@AutoBindableItem
public class SecondItem implements Item {
}
// cz.milanhlinak.guiceautobinding.Module.java
public class Module extends AbstractModule {
@Override
protected void configure() {
MapBinder<String, Item> itemMapBinder = MapBinder.newMapBinder(binder(), String.class, Item.class);
new Reflections("cz.milanhlinak.guiceautobinding.item")
.getTypesAnnotatedWith(AutoBindableItem.class)
.stream()
.filter(Item.class::isAssignableFrom)
.forEach(typeAnnotatedWith -> itemMapBinder
.addBinding(typeAnnotatedWith.getSimpleName())
.to((Class<? extends Item>) typeAnnotatedWith)
);
bind(ItemFactory.class).to(ItemFactoryImpl.class).in(Singleton.class);
}
}
Full code is available in my GitHub repository.
I would like to know if there is a better way how to achieve the automatic binding with Google Guice, because as you can see, I am currently using an additional library - Reflections.
UPDATE
Another option might be using of Google Guava instead of Reflections
public class Module extends AbstractModule {
@Override
protected void configure() {
MapBinder<String, Item> itemMapBinder = MapBinder.newMapBinder(binder(), String.class, Item.class);
ClassPath classPath;
try {
classPath = ClassPath.from(Thread.currentThread().getContextClassLoader());
} catch (IOException e) {
throw new RuntimeException("Unable to read class path resources", e);
}
ImmutableSet<ClassPath.ClassInfo> topLevelClasses = classPath.getTopLevelClassesRecursive("cz.milanhlinak.guiceautobinding.item");
topLevelClasses.stream()
.map(ClassPath.ClassInfo::load)
.filter(clazz -> clazz.isAnnotationPresent(AutoBindableItem.class) && Item.class.isAssignableFrom(clazz))
.forEach(clazz -> itemMapBinder
.addBinding(clazz.getSimpleName())
.to((Class<? extends Item>) clazz));
bind(ItemFactory.class).to(ItemFactoryImpl.class).in(Singleton.class);
}
}
回答1:
You're not doing anything wrong.
There is nothing wrong with using Reflections. That tool is excellent and does the job that you want.
Guice has no discovery tool, so there is no "default" way to do what you want in Guice only.
If you want to use something else than Reflections, you can fall back on java.util.ServiceLoader
, but ServiceLoader
is not really Guice-friendly because it automatically creates instances, but you usually use Guice because you want it to create instances for you instead of letting another framework do that for you.
The alternative of using Guava's Classpath
also makes sense, but Reflections is way more powerful because you can also load classes not in your class path.
Bottom line, you've already made the better choice.
来源:https://stackoverflow.com/questions/52915611/google-guice-how-to-automatically-add-binding