javaspringguiceioc-containerjsr330

What's the difference between Inject and Provider in JSR-330


all

I don't know what's the difference between Inject and Provider in JSR-330. I am using google guice, and everyday using @Inject, and I know in JSR-330, it has Provider<T>.

My question is

  1. what's the meaning of Provider<T> ?
  2. when can when user Provider<T> ?
  3. what's the difference with @Inject ?

Thanks in advance.


Solution

  • Everything is already explained into the javadoc, I quote:

    Compared to injecting T directly (implicitly using @Inject only), injecting Provider<T> enables:

    1. retrieving multiple instances.
    2. lazy or optional retrieval of an instance.
    3. breaking circular dependencies.
    4. abstracting scope so you can look up an instance in a smaller scope from an instance in a containing scope.

    Example for #1:

    Here you get several instances of Seat from the same provider so it is used as a factory.

    class Car {
        @Inject 
        Car(Provider<Seat> seatProvider) {
            Seat driver = seatProvider.get();
            Seat passenger = seatProvider.get();
            ...
        }
    }
    

    Example for #2:

    Here you use a provider to avoid creating directly the instance of the class MyClassLongToCreate as we know that it is a slow operation, so we will get it lazily thanks to the get method only when it is needed.

    class MyClass {
        @Inject
        private Provider<MyClassLongToCreate> lazy;
        ...
    }
    

    Example for #3:

    Here is a circular dependency that cannot be solved easily by the container such that some containers could just throw an exception as they don't know how to solve it by their own.

    class C1 {
        private final C2 c2;
        @Inject
        C1(C2 c2) {
            this.c2 = c2;
            ...
        }
    }
    
    class C2 {
        private final C1 c1;
        @Inject
        C2(C1 c1) {
            this.c1 = c1;
            ...
        }
    }
    

    To fix it we use a Provider on at least one of the constructors to break the circular dependency as next:

    class C1 {
        private final Provider<C2> c2;
        @Inject
        C1(Provider<C2> c2) {
            this.c2 = c2;
            ...
        }
    }
    

    This will allow the container to fully create an instance of C1 first (as we don't actually need to create an instance of C2 to inject a provider of C2) once done the container will be able to create an instance of C2 from this instance of C1.

    Example for #4:

    Here you have a class C2 that is scoped to the session which depends on C1 that itslef scoped to the request, we use a provider to allow us to get the instance of C1 corresponding to the current request as it will change from one request to another.

    @RequestScoped
    public class C1 {
        ...  
    }
    
    @SessionScoped
    public class C2 {
        @Inject
        private Provider<C1> provider;
        ...
        public void doSomething() {
            // Get the instance corresponding to the current request
            C1 c1 = provider.get();
            ...
        }
    }