Java – How to use Guice for an effective API Design

dependency-injectiongoogle-guicejava

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!
  }

}

Best Answer

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