I am new do dependency injection and I'm trying to solve the following problem with Google Guice:
In a web application I have got tabs which need to be reused multiple times but each time with different dependencies on their UI component and model. Something like the code here:
class MenuTabView
{
private MenuTab fooTab;
private MenuTab barTab;
}
class MenuTab
{
private UiComponent component;
#Inject
public MenuTab(UiComponent component)
{
this.component = component;
}
}
class UiComponent
{
private Model model;
#Inject
public UiComponent(Model model)
{
this.model = model;
}
}
class FooComponent extends UIComponent {}
class BarComponent extends UIComponent {}
class FooModel implements Model {}
class BarModel implements Model {}
How can I inject FooModel and FooComponent into fooTab
and BarModel and BarCompoment into barTab?
I've read a lot about the different techniques available in Google Guice but none of them seams to fit this problem which, to my naive eyes, should be a simple one. I've tried to give fooTab and barTab binding annotations but they will only work if I inject the tab but not the dependencies of the tab. What would be the most convenient way to solve this problem?
This sounds like the common "robot legs" problem to me ... this is covered in the FAQ and can be solved by using private modules:
class LegModule extends PrivateModule {
private final Class<? extends Annotation> annotation;
LegModule(Class<? extends Annotation> annotation) {
this.annotation = annotation;
}
#Override
protected void configure() {
bind(Leg.class).annotatedWith(annotation).to(Leg.class);
expose(Leg.class).annotatedWith(annotation);
bindFoot();
}
abstract void bindFoot();
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(
new LegModule(Left.class) {
#Override void bindFoot() {
bind(Foot.class).toInstance(new Foot("leftie"));
}
},
new LegModule(Right.class) {
#Override void bindFoot() {
bind(Foot.class).toInstance(new Foot("righty"));
}
});
}
Related
I use Guice in a desktop application and I want to add generic bindings for services. These services are singleton (binded) instances and are created during application startup manually. I use the IoC container only to create the GUI. I create these services manually because during start up I want to publish the progress.
Since the GUI consume these services, they must been binded to the guice GUI module.
I can't think of a way to bind them without using a class with setter and getter for each class.
Let's say I have CarService and EngineService. What I have now is:
public class GuiceServices {
public static void main(String[] args) {
ServicesModule servicesModule = new ServicesModule();
CarService carService = new CarServiceImpl();
servicesModule.setCarService(carService);
System.out.println("progress: 50%");
EngineService engineService = new EngineServiceImpl();
servicesModule.setEngineService(engineService);
System.out.println("Progress: 100%");
Injector i = Guice.createInjector(new GuiModule(), servicesModule);
i.getInstance(MainView.class).show();
}
class ServicesModule extends AbstractModule {
private CarService carService;
private EngineService engineService;
#Override
protected void configure() {
}
public void setCarService(CarService carService) {
this.carService = carService;
}
public void setEngineService(EngineService engineService) {
this.engineService = engineService;
}
#Provides
public CarService getCarService() {
return carService;
}
#Provides
public EngineService getEngineService() {
return engineService;
}
}
}
But it is kind of pain since these services are plenty.
Is there a way to avoid this?
Ideally, a map is more convenient. Something like:
public class GuiceServices {
public static void main(String[] args) {
ServicesMap servicesMap = new ServicesMap();
CarService carService = new CarServiceImpl();
servicesMap.put(CarService.class, carService);
System.out.println("progress: 50%");
EngineService engineService = new EngineServiceImpl();
servicesMap.put(EngineService.class, engineService);
System.out.println("Progress: 100%");
Injector i = Guice.createInjector(new GuiModule(), new ServicesModule(servicesMap));
i.getInstance(MainView.class).show();
}
class ServicesModule extends AbstractModule {
private ServicesMap services;
public SerrvicesModule(ServicesMap services) {
this.services = services;
}
#Override
protected void configure() {
for (Class<?> serviceType : services.keySet())
{
bind(serviceType).toInstance(services.get(serviceType));
}
}
}
}
But I cannot find a way to create-implement this "servicesMap". Because the bind method returns a generic builder. Does Guice (or Guava) provide something for cases like this?
I know that I can use Guice to create the services and publish the progress using an injection/type listener, but my business package (module) that contains all the services has no javax.inject dependency. Plus, the creation of these services is complex hence is better to make it manually. Also, publishing a GUI progress within a Guice module sounds too complex to be in a Guice module.
So, is there a way? Instead of the System.out.println in the above snippets, there is a splash screen that is created manually as well.
Just move the code that builds each service impl inside the body of the #Provides method for the corresponding service interface. You mentioned wanting these to be singletons, so you'll also want to annotate the provider methods with #Singleton.
As for the map, you could do something like that with a Multibinder, but I'd want to understand your design better before recommending that.
I actually achieved it myself.
What I did is to create a List<Consumer<Binder>> inside the module and then consme them inside configure() method.
class ServicesModule extends AbstractModule {
private List<Consumer<Binder>> consumers = new ArrayList<>();
ServicesModule() {
}
#Override
protected void configure() {
consumers.forEach(c -> c.accept(binder()));
}
<T> void putService(Class<T> clazz, T instance) {
consumers.add(t -> t.bind(clazz).toInstance(instance));
}
}
And then, during application start up I can feed the service dependencies gradually:
public static void main(String[] args) {
ServicesModule services = new ServicesModule();
CarService carService = new CarServiceImpl();
serviceModule.putService(CarService.class, carService);
publishProgress(35);
EngineService engineService = new EngineServiceImpl(carService);
serviceModule.putService(EngineService.class, engineService);
publishProgres(50);
//...
Injector i = Guice.createInjector(new GuiModule(), services);
i.getInstance(MainView.class).show();
}
Your proposed answer requires knowledge about the dependencies of the service-impl classes to be spread outside those classes. The whole idea of dependency injection is to encapsulate implementation dependencies. For example, only EngineServiceImpl should know that it depends on CarService. Encapsulation makes your code much easier to reason about and test.
class ServiceModule extends AbstractModule {
protected void configure() {
bind(CarService.class).to(CarServiceImpl.class).in(Singleton.class);
bind(EngineService.class).to(EngineServiceImpl.class).in(Singleton.class);
}
}
class CarServiceImpl implements CarService {
#Inject
CarServiceImpl() {} // Not necessary, but adds clarity
// ...
}
class EngineServiceImpl implements EngineService {
private final CarService carService;
#Inject
EngineServiceImpl(CarService carService) {
this.carService = carService;
}
// ...
}
class Main {
public static void main(String[] args) {
// I find it clearer to get the starting-point class instance from the injector
// creation chain, then operate on that instance.
MainView mainView =
Guice.createInjector(new GuiModule(), new ServiceModule())
.getInstance(MainView.class);
mainView.show();
}
}
I've started to setup Dagger 2 and faced a strange issue that looks like a bug to me.
I have 1 main component and 2 subcomponents which I 'plus' in parent component. I use different scopes for each subcomponent. The problem is that I can easily do fields injection for the 1st subcomponent but I can't do the same for the second. The injected fields stay nulls.
Main component:
#Singleton
#Component(modules = { WalletSaverAppModule.class })
public interface MyAppComponent {
TrackingComponent plus(TrackingModule module);
DashboardComponent plus(DashboardModule module);
}
1st subcomponent (works well):
#PerActivity #Subcomponent(modules = { DashboardModule.class })
public interface DashboardComponent {
void inject(MainActivity activity);
}
2nd subcomponent (fields injection -> null):
#PerService #Subcomponent(modules = { TrackingModule.class })
public interface TrackingComponent {
void inject(IntentService context);
}
How I do fields injection for the 2nd subcomponent:
public class TrackingService extends IntentService {
#Inject CallCase mCallCase;
#Inject CallModelMapper mCallModelMapper;
...
#Override protected void onHandleIntent(Intent intent) {
((MyApp) getApplication()).getAppComponent().plus(new TrackingModule(this)).inject(this);
// ---> here the both fields are null
...
Objects that I'm injecting:
#Singleton public class CallCase {
private CallRepository mCallRepository;
#Inject public CallCase(final CallRepository userRepository) {
mCallRepository = userRepository;
}
public Observable<Call> execute() {
...
}
}
#Singleton public class CallModelMapper {
#Inject CallModelMapper() {
}
public CallModel transform(#NonNull final Call callEntity) {
...
}
}
Both objects have #Singleton scope (as their constructor fields). Could it be a scope conflict?
--- UPDATE ---
I've checked the class generated by Dagger2 (DaggerMyAppComponent) that I'm using in MyApp to build application component. I found the difference between implementations of 1st and 2nd components.
1st:
private final class DashboardComponentImpl implements DashboardComponent {
private final DashboardModule dashboardModule;
private Provider<DashboardMvp.Presenter> providesPresenterProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;
private DashboardComponentImpl(DashboardModule dashboardModule) {
this.dashboardModule = Preconditions.checkNotNull(dashboardModule);
initialize();
}
private void initialize() {...}
#Override
public void inject(MainActivity activity) {...}
}
2nd:
private final class TrackingComponentImpl implements TrackingComponent {
private final TrackingModule trackingModule;
private TrackingComponentImpl(TrackingModule trackingModule) {
this.trackingModule = Preconditions.checkNotNull(trackingModule);
// ---> look, missing call initialize() <---
}
#Override
public void inject(IntentService context) {...}
}
Why Dagger 2 takes different the subcomponents that implemented in the same way? Only one difference I can see is the scope. I would appreciate any inputs about this problem.
Thanks in advance!
In TrackingComponent why are you inject to IntentService. Maybe changing to TrackingService will help
void inject(TrackingService context);
I have an interface
public interface Abstraction {
void execute();
}
I have built a composite implementation and want to registered this object as the bean, #Named
#Named
public class Composite implements Abstraction {
private List<Abstraction> list;
#Inject
public Composite(List<Abstraction> list) {
this.list = list;
}
public void execute() {
list.forEach(Abstraction::execute);
}
}
How do I set it up so that the set of implementations to the abstraction gets injected properly into the Composite above? I will be having another object that takes the abstraction as a dependency and I want it to receive the #Named Composite above with the 2 Implementations below injected into the ctor.
public class Implementation1 implements Abstraction {
public void execute() { }
}
public class Implementation2 implements Abstraction {
public void execute() { }
}
If you create a bean for each of your implementations, your example will work out of the box. For example, annotate your implementations with #Named or #Component and mark them for scanning (component scan their package)
#Configuration
#ComponentScan
public class StackOverflow {
public static void main(String[] args) throws Exception {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(StackOverflow.class);
System.out.println(ctx.getBean(Composite.class).list);
}
}
interface Abstraction {
void execute();
}
#Named
class Composite implements Abstraction {
List<Abstraction> list;
#Inject
public Composite(List<Abstraction> list) {
this.list = list;
}
public void execute() {
list.forEach(Abstraction::execute);
}
}
#Named
class Implementation1 implements Abstraction {
public void execute() {
}
}
#Named
class Implementation2 implements Abstraction {
public void execute() {
}
}
The Composite's list will contain both implementations.
Alternatively, since you only have two implementations, you could name their beans and inject them separately. For example
#Component("one")
class Implementation1 implements Abstraction {
public void execute() {
}
}
#Component("two")
class Implementation2 implements Abstraction {
public void execute() {
}
}
and inject them in the Composite
List<Abstraction> list = new ArrayList<>(2);
#Inject
public Composite(#Qualifier("one") Abstraction one, #Qualifier("two") Abstraction two) {
list.add(one);
list.add(two);
}
I suggest this solution just because the order of initialization of Abstraction beans might mess up your context initialization. For example if Implementation1 somehow depended on the initialization of Composite, the context would complain. This is rare and you can control it in other ways. Still, being explicit about the beans might be clearer in this case.
I have this scenario where I want to inject a TypeListener with dependencies, but it will never work because the TypeListener is used to actually perform the injection.
How can I get this thing done? Is there a guicey-way?
Notes:
I'm using Guice 4.0
MyManager will be used after Guice::createInjector.
Both MyManager::registerType and MyManager::use are called exclusively before Guice::createInjector returns.
MyDependency is present to show that MyManager cannot be instanciated with new. I will also be used after Guice::createInjector has returned.
I created the following SSCCE to showcase my issue:
import com.google.inject.*;
import com.google.inject.matcher.*;
import com.google.inject.spi.*;
public class MyClass {
public static void main(String[] args) {
Guice.createInjector(new MyModule());
}
static class MyModule extends AbstractModule {
#Override protected void configure() {
TypeListener listener = new MyTypeListener();
requestInjection(listener);
bindListener(Matchers.any(), listener);
}
}
static class MyTypeListener implements TypeListener {
#Inject MyManager manager;
#Override public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
Class<?> rawType = type.getRawType();
manager.registerType(rawType);
encounter.register(new InjectionListener<I>() {
#Override public void afterInjection(I injectee) {
manager.use(rawType, injectee);
}
});
}
}
#Singleton static class MyManager {
#Inject MyManager(MyDependency dependency) { }
void registerType(Class<?> type) { }
void use(Class<?> type, Object injectee) { }
}
static class MyDependency { }
}
I think at least some of the time (in tests or code analysis) type listeners have no cohesion to the types they are listening to, so there's no reason to have one injector. You'd use one injector to create the listener and one injector to create the code to be tested/analyzed.
If you really want one injector (e.g. if the types in the injector you wish to listen to and the types needed by the listener are cohesive) then your best bet is AbstractModule's getProvider() method. So, if MyTypeListener needs an instance of Foo, this is what MyModule would look like:
static class MyModule extends AbstractModule {
#Override protected void configure() {
TypeListener listener = new MyTypeListener(getProvider(Foo.class));
bindListener(Matchers.any(), listener);
}
}
If you haven't used getProvider(), be forewarned that you cannot call .get() on the provider until the injector is constructed. As long as you don't call it from the context of the listener's constructor you should be fine.
New to Guice, so I'm looking into its expressive power. Suppose I have classes as follows:
public class Data {
#Inject
public Data(#Named("First") String first, #Named("Second") String second) { ... }
}
public class DataUser1 {
#Inject
public DataUser1(Data data) { ... }
}
public class DataUser2 {
#Inject
public DataUser2(Data data) { ... }
}
How do I create a module such that when I call injector.getInstance(DataUser1.class) I get something equivalent to new DataUser1(new Data("foo", "bar")) while having injector.getInstance(DataUser2.class) I get something equivalent to new DataUser2(new Data("foo2", "bar2"))?
Also related, how do I create a module for which I may need to get two instances of DataUser1, each of which using different Data values?
You use private modules for creating graphs of objects which are almost the same but differ in particular details.
public class DataUser1Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class);
expose(DataUser1.class);
}
}
public class DataUser2Module extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser2.class);
expose(DataUser2.class);
}
}
Injector injector = Guice.createInjector(new DataUser1Module(), new DataUser2Module());
DataUser1 dataUser1 = injector.getInstance(DataUser1.class);
DataUser2 dataUser2 = injector.getInstance(DataUser2.class);
You do the same thing if you need two instances of DataUser1 with different Datas, but you use annotations to differentiate between them:
public class DataUser1Module1 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo");
bindConstant().annotatedWith(Names.named("Second")).to("bar");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("1")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("1"));
}
}
public class DataUser1Module2 extends PrivateModule {
#Override
protected void configure() {
bindConstant().annotatedWith(Names.named("First")).to("foo2");
bindConstant().annotatedWith(Names.named("Second")).to("bar2");
bind(Data.class);
bind(DataUser1.class).annotatedWith(Names.named("2")).to(DataUser1.class);
expose(DataUser1.class).annotatedWith(Names.named("2"));
}
}
Injector injector = Guice.createInjector(new DataUser1Module1(), new DataUser1Module2());
DataUser1 dataUser11 = injector.getInstance(Key.get(DataUser1.class, Names.named("1"));
DataUser1 dataUser12 = injector.getInstance(Key.get(DataUser1.class, Names.named("2"));
This pattern is described in Guice FAQ.
See also these questions:
Binding a constructor argument based on the Annotation of the class
How do I bind Different Interfaces using Google Guice?