1

I am creating a base API in JavaSE, which includes modules like MVP architecture, Service & Repository Layer, Event Model to fire events between presenter etc.

I am trying to implement all best practices of Software Design. Recently, I read about Dependency Injection & its advantages and decided to use Guice as DI framework.

After spending two days learning Guice, I am still confused with some design issues.

  1. If I create an injector using Guice.createInjector(modules) with the core modules of my API,

    • How to make this injector instance available to my other API classes? Can an injector be injected and is it a good practice?
    • How to make this injector instance available to API user, so that they can use this injector to create instances?
    • Should I even make it available to API user, as it may be a bad practice?
    • Hence using it for AOP approach like this and custom injections
  2. Is creating injectors a heavyweight operation, if so should there be only a few injectors per application?

  3. Should injectors be created on startup stage of the application or could be created in between without affecting the application performance?

Edit

To understand usage of Provider instead of injecting injector here is an short MVP example:

interface ProductModel {

  // Some method declarations

}

interface ProductView {

  // Some method declarations

}

class ProductPresenter {

  // Using Guice for constructor injection
  @Inject
  ProductPresenter(ProductModel model, ProductView view) {
    this.model = model;
    this.view = view;
  }

}

class ProductViewImpl implements ProductView {

  // Method Implementations

}

class ProductModelImpl implements ProductModel {

  // Method Implementations

}

Now, as understood from below answers to use Provider<X> instead of injecting injector, here is the Provider class.

class ProductPresenterProvider implements Provider<ProductPresenter> {

  ProductPresenter get() {
    // But, How to provide the ProductPresenter without model and view instances!
  }

}
  • You typically create the injector once, for example in your main, to setup the modules you want and then just use @Inject where you need... – assylias Jun 27 '15 at 23:08
  • And how to access the injector instance when I need to get an instance of some class? As injector was created in main! – Akshat Jun 28 '15 at 1:53
  • @Akshat Why would you need to access the injector again? – Tavian Barnes Jun 28 '15 at 15:41
  • Well, so that I can create instances with dependencies I have specified! – Akshat Jun 28 '15 at 15:43
  • Why? Is injector not meant to be accessed again? Is there another approach? – Akshat Jun 28 '15 at 15:44
1

It's a bad practice to inject an injector, or otherwise pass it round in some way that makes it accessible to code outside of startup. That confuses what's going on; do that and when something goes wrong, you have to debug your code via debugging the dependency injection state.

The right, static, way to do things is to inject either a hand-written factory object, or a Provider. The difference being that the latter will set the created object up as a DI object, i.e. processing @Inject annotations.

Internally, the implementation of a provider presumably uses the injector, but as it is only doing it for a single thing it is easier to reason about and debug. And if you draw the dependency graph for your system, because Provider is Provider<X>, you should get the actual graph of logical dependencies with your class depending on X. Not a mess with a 'magic happens' node at a single injector used for everything.

Note that when you do this, you don't need to supply a binding or implementation for the Provider; it has built-in default.

  • But still we have to use injector created by Guice.createInjector() method to get instance of the class in which this Provider<X> is being injected! Like getting instance of BillingService class in this example Am I wrong here? – Akshat Jun 28 '15 at 11:40
  • Nope, it's providers all the way down. – soru Jun 28 '15 at 11:42
  • I still don't understand, how to get instances without the injector instance! And what do you mean by "it's providers all the way down"? – Akshat Jun 28 '15 at 11:44
  • To be precise, your startup code should use the injector to create one or more fixed top-level object(s). Then everything after that one function is either injected, created via an injected provider, or a simple object not using DI. – soru Jun 28 '15 at 11:45
  • I've added an example, can you please explain how to get ProductPresenter instance using Provider in your answer! – Akshat Jun 28 '15 at 12:13
0

Ok, those are many questions you have and I guess some of the points can be approached in more than one way.

Creating the injector is not cheap. Guice will do some analysis to detect errors as well as create all singletons. So you should create the injector at startup.

I usually have a single injector for my application.

Regarding your question 1.:

Yes the injector can be injected. But I see it as a bad practice. Reason for this is that calls to injector.getInstance() and injector.injectMembers() make it hard to reason about the dependencies of a class.

  • if you need to create more than one instance or you need to control the time when an instance is created inject a Provider instead of X.
  • if the object you want to create needs both runtime parameters and dependencies use assisted inject.
  • for many kinds of objects (like DTO, POJO and alike) its absolutely ok to create them by calling new or a conventional factory.

In this sense the injector is implicitly available to any instance created by Guice this includes the API user.

And finally AOP. Aspects are a powerful feature but also can hide some of the business logic I suggest to use them only for very repetitive tasks which all developers know well.

Also take into account that aspects only work on:

  • instances created by Guice
  • non private, non static, non private methods

Finally, Guice won't inform you if you add an annotation to either of the above. Your aspect will silently be ignored

  • Can you give an example of providing injector using a provider! And how other classes should use this provider. – Akshat Jun 28 '15 at 6:45
  • Also I didn't understand what you described here! " In this sense the injector is implicitly available to any instance created by guice this includes the API user." May be the same example can clear this point! – Akshat Jun 28 '15 at 6:47
0

I will give a second answer, but you really should have opened a second question...

First of all: you rarely ever implement a provider. Guice will automagically create them for you. To explain this: When I bind a class Foo as follows:

bind(Foo.class).to(FooImpl.class)

You can inject in you class either Foo or Provider<Foo>. Guice will create the provider for you.

The reason why you should not implement the provider yourself, is that AOP is not working on instances that where created with a call to new. When you use a debugger to look at the run time instance of an injected object which uses interceptors you will notice that Guice created a subclass on the fly allowing it to intercept the method calls.

To your example:

The main problem is, that ProductModel should not be injected. It is a domain object and holds data. These kind of objects should not be created/managed by DI. Instead you code is responsible to create/retrieve and store the model from its persistent state. The presenter has now two different dependencies. One is the model which lives outside of DI and one is the view which can be handled by DI (just as a side note: if you need the same instance of the view to be injected on more than one object you must place it in a (custom-) scope. But answers to scopes deserve a new question).

To solve the above dilemma Guice offers assisted injection. This allows you to write a code like to following:

public class ProductPresenter {

    public interface Factory {
        createPresenter(ProductModel model);
    }

    @Inject
    ProductPresenter(@Assisted ProductModel model, ProductView view) {
        this.model = model;
        this.view = view;
    }
}

you then bind the factory as follows:

install(new FactoryModuleBuilder().build(ProductPresenter.Factory.class));

Guice will create an implementation of the ProductPresenter.Factory interface on the fly. The created instance will get the arguments passed to the factory method put into the constructor. Also AOP is working on the created instance. This way you can mix DI with non DI objects.

  • why didn't you just edit your prior answer over here to add this? – gnat Jun 28 '15 at 21:45
  • Ok, nice explanation! But still I've to use 'injector.createInstance()' to create 'ProductPresenter' instance? Am I right? – Akshat Jun 29 '15 at 1:41
  • And because ProductModel is tightly coupled to ProductPresenter why not just initialize it in constructor instead of passing to the constructor like this this.model = new ProductModel();. That way we don't have to use AssistedInjection! – Akshat Jun 29 '15 at 4:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.