Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Guice inject single instance into multiple objects without using @Singleton

I was reading the Guice documentation, and came across a section labeled Eliminate the Cycle (Recommended) which peaked my interest because it's exactly the issue that led me to the documentation today.

Basically, to eliminate cyclic dependencies, you "extract the Dependency Case into a separate class." Okay, nothing new there.

So, in the example, we have.

public class Store {
        private final Boss boss;
        private final CustomerLine line;
        //...

        @Inject public Store(Boss boss, CustomerLine line) {
                this.boss = boss; 
                this.line = line;
                //...
        }

        public void incomingCustomer(Customer customer) { line.add(customer); } 
}

public class Boss {
        private final Clerk clerk;
        @Inject public Boss(Clerk clerk) {
                this.clerk = clerk;
        }
}

public class Clerk {
        private final CustomerLine line;

        @Inject Clerk(CustomerLine line) {
                this.line = line;
        }

        void doSale() {
                Customer sucker = line.getNextCustomer();
                //...
        }
}

You have a Store and a Clerk and each need to have a reference to a single instance of CustomerLine. No problems with this concept, and easily doable with classic Dependency Injection:

CustomerLine customerLine = new CustomerLine();
Clerk clerk = new Clerk(customerLine);
Boss boss = new Boss(clerk);
Store store = new Store(boss, customerLine);

That's easy enough, but now, I need to do this with Guice injection. Thus, my issue is with implementing the following:

you may want to make sure that the Store and Clerk both use the same CustomerLine instance.

Yes, that is exactly what I want to do. But how do I do that in a Guice module?

public class MyModule extends AbstractModule implements Module {
    @Override
    protected void configure() {
        //Side Question: Do I need to do this if there if Boss.class is the implementation?
        bind(Boss.class);
        bind(CustomerLine.class).to(DefaultCustomerLine.class); //impl
    }
}

I create an injector with my module:

Injector injector = Guice.createInjector(new MyModule());

Now, I want an instance of Store:

Store store = injector.getInstance(Store.class);

This will inject a new instance of CustomerLine and Boss into this instance of Store. Boss, however, gets an instance of Clerk which also gets injected an instance of CustomerLine. At this point, it would be a new instance, unique from the instance injected into Store.

Question Revisited

  • How can Store and Clerk share the same instance in this sequence, without using @Singleton?

Please, let me know if more information is required, or this question isn't stated clearly enough and I will be sure to revise.

like image 719
crush Avatar asked Aug 21 '13 13:08

crush


1 Answers

You should use a provider

public class StoreProvider implements Provider<Store> {
  @Inject 
  private Boss boss ;

  public Store get() {
    return new Store(boss, boss.getClerk().getCustomerLine());
  }
}

And then bind it in your module

bind(Store.class).toProvider(StoreProvider.class);

like image 182
NiziL Avatar answered Sep 27 '22 18:09

NiziL