Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice: Do I have to annotate every class of an object graph with @Inject?

I'd like to introduce Guice for the use of an existing mid-sized project. For my demands I need a custom scope (session is too big, while request to small for my project).

Imagine that I request guice to provide me an instance of Class A which has direct and indirect dependencies to many other classes (composition).

My custom provider is able to provide the instance of classes which are used as constructor arguments of all involved classes.

Question:

  • Do I really have to put an @Inject (and my custom scope) annotation on the constructors of all involved classes or is there a way that guice only requires these annotations on the top-level class which I request and that all further dependencies are resolved by "asking" my custom scope for a provider of the dependent types?

If this is true this would increase the effort of introducing Guice because I have to adjust more than 1000 classes. Any help and experiences during the introduction of guice is appreciated.

like image 448
MRalwasser Avatar asked Aug 10 '11 15:08

MRalwasser


People also ask

How does @inject annotation work?

Injectable constructors are annotated with @Inject and accept zero or more dependencies as arguments. @Inject can apply to at most one constructor per class. @Inject is optional for public, no-argument constructors when no other constructors are present. This enables injectors to invoke default constructors.

How does @inject work Guice?

Using GuiceIn each of your constructors that need to have something injected in them, you just add an @Inject annotation and that tells Guice to do it's thing. Guice figures out how to give you an Emailer based on the type. If it's a simple object, it'll instantiate it and pass it in.

What is @inject annotation in Guice?

Annotation Type Inject. @Target(value={METHOD,CONSTRUCTOR,FIELD}) @Retention(value=RUNTIME) @Documented public @interface Inject. Annotates members of your implementation class (constructors, methods and fields) into which the Injector should inject values.


2 Answers

First of all, it's possible to use Guice without putting an @Inject annotation anywhere. Guice supports Provider bindings, @Provides methods and constructor bindings, all of which allow you to bind types however you choose. However, for its normal operation it requires @Inject annotations to serve as metadata telling it what dependencies a class requires and where it can inject them.

There reason for this is that otherwise, it cannot deterministically tell what it should inject and where. For example, classes may have multiple constructors and Guice needs some way of choosing one to inject that doesn't rely on any guessing. You could say "well, my classes only have one constructor so it shouldn't need @Inject on that", but what happens when someone adds a new constructor to a class? Then Guice no longer has its basis for deciding and the application breaks. Additionally, this all assumes that you're only doing constructor injection. While constructor injection is certainly the best choice in general, Guice allows injection of methods (and fields) as well, and the problem of needing to specify the injection points of a class explicitly is stronger there since most classes will have many methods that are not used for injection and at most a few that are.

In addition to @Inject's importance in telling Guice, it also serves as documentation of how a class is intended to be used--that the class is part of an application's dependency injection wired infrastructure. It also helps to be consistent in applying @Inject annotations across your classes, even if it wouldn't currently be absolutely necessary on some that just use a single constructor. I'd also note that you can use JSR-330's @javax.inject.Inject annotation in Guice 3.0 if a standard Java annotation is preferable to a Guice-specific one to you.

I'm not too clear on what you mean by asking the scope for a provider. Scopes generally do not create objects themselves; they control when to ask the unscoped provider of a dependency for a new instance and how to control the scope of that instance. Providers are part of how they operate, of course, but I'm not sure if that's what you mean. If you have some custom way of providing instances of objects, Provider bindings and @Provides methods are the way to go for that and don't require @Inject annotations on the classes themselves.

like image 111
ColinD Avatar answered Oct 25 '22 16:10

ColinD


NO YOU DONT

GUICE does not ask you to inject every single object. GUICE will try and create only injected objects. So you can @Inject objects that you want to be injected.

On the scope bit - Scope essentially controls how your objects gets created by GUICE. When you write your own custom scope you can have a datastructure that controls the way objects are created. When you scope a class with your custom annotation, GUICE will call your scope method before creation with a Provider for that class. You can then decide if you want to create a new object or use an existing object from a datastructure (such as hashmap or something). If you want to use an existing one you get that and return the object, else you do a provider.get() and return.

Notice this

public <T> Provider<T> scope(final Key<T> key, final Provider<T> unscoped) {
    return new Provider<T>() {
      public T get() {
        Map<Key<?>, Object> scopedObjects = getScopedObjectMap(key);

        @SuppressWarnings("unchecked")
        T current = (T) scopedObjects.get(key);
        if (current == null && !scopedObjects.containsKey(key)) {
          current = unscoped.get();
          scopedObjects.put(key, current);
        }
        // what you return here is going to be injected ....
        // in this scope object you can have a datastructure that holds all references 
        // and choose to return that instead depending on your logic and external 
        // dependencies such as session variable etc...
        return current;
      }
    };
  }

Here's a tutorial ...

http://code.google.com/p/google-guice/wiki/CustomScopes

like image 24
Sid Malani Avatar answered Oct 25 '22 14:10

Sid Malani