How get already installed MapBinder to add additional bindings into it - java

I use com.google.inject:guice. In my project, I included a dependency that have a module (a class that extends com.google.inject.AbstractModule) that defines a MapBinder like that
public class ParentGuiceModule extends AbstractModule {
#Override
protected void configure() {
MapBinder.newMapBinder(binder(), TypeLiteral.get(String.class), TypeLiteral.get(SomeModuleClass.class));
...
}
}
In my module class, I want to get that MapBinder and add new bindings to it. I mean I want to write something like that:
public class MyGuiceModule extends AbstractModule {
#Override
protected void configure() {
MapBinder<String, SomeModuleClass> parentModules = MapBinder.get(binder(), TypeLiteral.get(String.class), TypeLiteral.get(SomeModuleClass.class));
parentModules.addBinding("MyId").to(MyClass.class);
}
}
How can I do that? I can not change the parent module.
I looked into MapBinder class, seems it does not have any methods to get already installed MapBinder.

This is exactly what MapBinder is designed for—after all, if you knew everything that was going to be inside a MapBinder from within a single Module, you could just write #Provides Map<Foo, Bar> or bind(new TypeLiteral<Map<Foo, Bar>>(){}) and be done with it.
From the MapBinder top-level docs:
Contributing mapbindings from different modules is supported. For example, it is okay to have both CandyModule and ChipsModule both create their own MapBinder<String, Snack>, and to each contribute bindings to the snacks map. When that map is injected, it will contain entries from both modules.
Don't be discouraged by the name newMapBinder: As long as you have the exact same parameters to newMapBinder and have both of your Modules installed in the same Injector, you will wind up with one Map that contains bindings from both modules.

Related

Android: Load implementations of interface across libraries using Guava

I want to collect all implementations of concrete interface in my Android application. I've got something like this:
List<T> list = ClassPath.from(ClassLoader.getSystemClassLoader())
.getTopLevelClasses(Reflection.getPackageName(parent))
.stream()
.map(ClassPath.ClassInfo::load)
.filter(current -> isImplementation(parent, current))
.map(aClass -> (T) aClass)
.collect(Collectors.toList());
but It always return 0 classes. Even if I want to retrieve all classes:
ClassPath.from(ClassLoader.getSystemClassLoader())
.getAllClasses()
.stream()
.map(ClassPath.ClassInfo::load)
.collect(Collectors.toList());
It's always zero. When I run it locally from my library in unit test it's ok. Probably, it's problem with ClassLoader. It doesn't provide informations about all packages provided by application.
I don't want to use DexFile because it's deprecated . There's no other information about entries() replacement function.
Is there any possibility to work around this?
TLDR:
You can use dagger dependency (or newer, hilt) to install components into one domain, such as SingletonComponent and inject it as constructor parameter by implementation. You can even inject multiple implementations as set.
Real answer:
I have created library common and test. Those libraries are pinned in my application.
In common module you can create any interface, like:
public interface Item {
}
Set dependency common to test. Reload dependencies. Now you can see Item in your test library. Write custom class that implements interface:
public class CustomItem implements Item{
//...
}
Create module in your test library:
#Module
#InstallIn(SingletonComponent.class)
public class TestModule {
#Provides
#Singleton
#IntoSet
public Item customItem() {
return new CustomItem();
}
}
Set dependency common and test in application and add module with set of your implementations:
#Module
#InstallIn(SingletonComponent.class)
public class ApplicationSingletonModule {
#Provides
#Singleton
public CustomClassProvider customClassProvider(Set<Item> items) {
return new CustomClassProvider(items);
}
}
You can add multiple Item implementations and inject it across libraries without any problem.

Use annotation to feed Google guice MapBinder

In a Java project, build with Gradle 5.2, using Google Guice.
I use the MapBinder (http://google.github.io/guice/api-docs/latest/javadoc/com/google/inject/multibindings/MapBinder.html):
MapBinder<String, Snack> mapbinder
= MapBinder.newMapBinder(binder(), String.class, Snack.class);
mapbinder.addBinding("twix").toInstance(new Twix());
mapbinder.addBinding("snickers").toProvider(SnickersProvider.class);
mapbinder.addBinding("skittles").to(Skittles.class);
This is working fine, but now, I want a "plugin architecture", so avoid to import all Snack classes, but rather declare it in the class directly, such as:
#SnackImpl("Twix")
class Twix extends Snack {
}
How?
This won't exactly be possible without some expensive classpath scanning: If the injector doesn't have any reference to your Twix class, it would not be able to bind it into a Map without scanning through every JAR on the classpath in search of #SnackImpl-annotated classes. You could try this with Guava's ClassPath, but if you use a network-based or custom classloader, this may not be tractable at all. In any case I wouldn't recommend it.
One alternative is to use Java's built-in ServiceLoader framework, which lets individual JARs list out the fully-qualified implementations for a given service (interface). You can even use Google's Auto framework to generate that service file for you based on annotations.
That takes care of listing the implementations, but you'll still need to bind them into the MapBinder. Luckily, MapBinder doesn't require a single definition, and will automatically merge multiple MapBinder definitions during module construction time:
Contributing mapbindings from different modules is supported. For example, it is okay to have both CandyModule and ChipsModule both create their own MapBinder, and to each contribute bindings to the snacks map. When that map is injected, it will contain entries from both modules.
(from the MapBinder docs)
With that in mind, I would recommend that each plugin bundle gets its own Guice module where it registers into a MapBinder, and then you add those Guice modules to the main injector using ServiceLoader to get those modules at injector creation time.
// Assume CandyPluginModule extends AbstractModule
#AutoService(CandyPluginModule.class)
public TwixPluginModule extends CandyPluginModule {
#Override public void configure() {
MapBinder<String, Snack> mapBinder
= MapBinder.newMapBinder(binder(), String.class, Snack.class);
mapBinder.addBinding("twix").to(Twix.class);
}
}
You could also take advantage of the superclass:
#AutoService(CandyPluginModule.class)
public TwixPluginModule extends CandyPluginModule {
#Override public void configureSnacks() { // defined on CandyPluginModule
bindSnack("twix").to(Twix.class);
}
}
Alternatively, you could list implementations like Twix directly with AutoService and then create a Module that reads all of the ServiceLoader implementations into your MapBinder, but that may restrict the flexibility of your plugins and doesn't gain you any decentralization of the bindings that MapBinder doesn't give you already.

How to auto install all implementations of an interface with guice?

I have the following interface:
public interface IFilterFactory<T extends IFilter> {
T create(IFilterConfig config);
}
And multiple implementations of it:
public class AndFilter implements IFilter {
public interface IAndFilterFactory extends IFilterFactory<AndFilter> {}
// ...
}
public class OrFilter implements IFilter {
public interface IOrFilterFactory extends IFilterFactory<OrFilter> {}
// ...
}
in my Guice module, I'm currently installing each module I add this way:
install(new FactoryModuleBuilder().build(IAndFilterFactory.class));
install(new FactoryModuleBuilder().build(IOrFilterFactory.class));
The And and Or filters are just examples but I have many more, some of them requiring specific objects to be injected, thus the factories.
Is there a way with Guice to just say "install all implementations of IFilterFactory" without having to use reflection myself ?
There's nothing for classpath scanning built-in, and despite a lot of existing library options, there's reason to believe that a perfect solution simply can't exist for Java. In any case, enumerating all available classes is known to be slow even in heavily-used libraries, and such a solution would scale with the number of classes in your application, not the number of filters you use.
(To be clear, you're not quite even asking to "install all implementations of IFilterFactory", you're asking "create and install a module with FactoryModuleBuilder for all implementations of IFilterFactory". The factory interface implementation doesn't exist until you generate it with FactoryModuleBuilder.)
You can, however, extract everything aside from the class name itself, so you only have one easy-to-maintain list of classes to bind:
List<Class<?>> filterFactories = ImmutableList.<Class<?>>of(
IAndFilterFactory.class,
IOrFilterFactory.class,
IXorFilterFactory.class,
HelpIAmTrappedInAFilterFactory.class
);
for (Class<?> clazz : filterFactories) {
install(new FactoryModuleBuilder().build(clazz));
}

Guice Binder hasBind functionality

Is there any way to check if an implementation exists for an interface?
For example, the method "hasBind" below does not exist:
public class MyModule extends AbstractModule {
#Override
protected void configure() {
// do something like this:
if (!hasBind(SomeInterface.class)) {
bind(SomeInterface.class).to(MyOtherSomeInterface.class);
}
}
}
Since the order of applied modules does not matter in guice, you cannot detect if a binding exists before the injector was created, see this post.
The proposed solution is to create a starting module with default bindings and use Modules.overwrite to apply ypur additional bindings.

Migrating a Guice-based project to Dagger

I have a Guice based project using vanilla Guice;
no Assisted-Inject, no AOP, no extra plugin extending Guice, etc.
To run it more easily on Android, Dagger seems like a better solution.
Every class has a dependency and a constructor with #Inject annotation.
No field or method injection is used.
The modules are quite simple (making Guice an overkill) and mostly contain bindings like the following:
class SomethingModule extends AbstractModule {
protected void configure() {
Bind(Handler.class)
.annotatedWith(Names.named("something"))
.to(SomeImplementation.class);
}
}
}
And later used like the following:
Injector inj = Guice.createInjector(new SomethingModule());
... = inj.getInstance(SampleInterface.class);
// and rest of the code.
Unfortunately,
I can not get my head around Daggers terminology.
Can you guide me with a direct translation / transformation of a Guice module to a Dagger module?
Dagger has:
Dagger's Components.
Dagger's Modules.
#Provides
#Inject
Guice has:
#Inject
#Named (or any custom annotation, if implemented correctly).
Our modules extending AbstractModule.
#Provides in the modules.
Guice Injector created from modules.
How do these relate?
Update: In addition to the nice answer by EpicPandaForce, these slides can help too.
Bind(Handler.class)
.annotatedWith(Names.named("something"))
.to(SomeImplementation.class);
Would translate to
#Module
public class SomethingModule {
#Provides
#Named("something")
//scope if needed
public Handler handler() {
return new SomeImplementation();
}
}
Which would be bound to an "Injector" (component):
#Component(modules={SomethingModule.class})
//scope if needed
public interface SomethingComponent {
#Named("something")
Handler handler();
void inject(ThatThingy thatThingy);
}
Which is an "injector" that you have to create with the APT-generated builder:
SomethingComponent somethingComponent = DaggerSomethingComponent.builder()
.somethingModule(new SomethingModule()) //can be omitted, has no params
.build();
somethingComponent.inject(thatThingy);
Where that thingy has
public class ThatThingy {
#Inject
#Named("something")
Handler handler;
}
Components typically exist per scope, so for example #ApplicationScope has one "injector" (component). Scoping can be achieved with subcomponents and component dependencies.
Important fact, a component has provision methods (which are the dependencies that are inherited to subscoped components if you use component dependencies), and void inject(X x); formatted methods. This is required for field injection per concrete type. A base class for example can only inject itself, and not its subclasses. You can however write a method called protected abstract void injectThis() which would call the .inject(this) on the subclass as well.
As I haven't really used Guice, I'm not sure if I missed out on anything. I think I forgot constructor injection, which is an issue because while Dagger does support it, it cannot be reconfigured. For reconfiguration, you have to use modules, and do the injection in the constructors yourself.
#Module(includes={ThoseModule.class, TheseModule.class})
public abstract class SomethingModule {
#Binds
abstract Whatever whatever(WhateverImpl impl);
}
#Singleton
public class WhateverImpl implements Whatever {
Those those;
These these;
#Inject
public Whatever(Those those, These these) {
this.those = those;
this.these = these;
}
}
#Component(modules={SomethingModule.class})
#Singleton
public interface SomethingComponent {
These these();
Those those();
Whatever whatever();
}

Categories

Resources