1

Our project is setup as follows:

1) Main module: Contains a driver program which starts a Spark streaming server. It has its own Guice injector.

2) When message comes in, it goes to another module, which creates it's own Guice injector.

3) This module uses classes in other modules which themselves use dependent modules. Each of these modules creates its own Guice injector so that it can work independently, tested individually etc.

Here's a problem: Now we need a Singleton, but an object created as @Singleton is bound to an injector (Not to a ClassLoader), so when the Injector goes away the object goes away.

Questions:

1) Is our architecture bad? Should we not be creating an injector in every module?

2) How do we create a Singleton that will remain in a ClassLoader even when injector goes away?

BTW, we use Netflix Governator on top on Guice.

Note: Answer for the question which is supposedly duplicate of this question doesn't answer how a 'single' injector created on the top level module can be passed to the sub-modules. Also, if the sub-modules themselves don't have an injector, can we write standalone unit tests for them?

DilTeam
  • 2,551
  • 9
  • 42
  • 69
  • `Singletons` are anti-patterns in Java because they are not tied to a `JVM` as you think, they are tied to a `ClassLoader` which a `JVM` instance can have many instances of and is a fundamentally different scope. If you read up on the Guice documentation there is a concept of type of Module *inheritance* that allows *sub-Modules* that have the scope of the parent Module. This is what you need to use. –  Oct 21 '15 at 01:13
  • This question is somewhat confusing to me because I don't know if you mean "architectural module" or "Guice `Module`" – Tavian Barnes Oct 23 '15 at 14:10
  • Tavin - By "module" I mean a module under Maven. Each with it's own pom.xml & each with it's own Guice module as well. – DilTeam Oct 23 '15 at 15:39

1 Answers1

2

An idea for question 2 would be to use a provider that you can bind in all your different Modules. This provider would access a singleton that that exists 'outside' of Guice.

I have crafted a simple example where I provide access to a static field. You can change that to use the implementation of the Singleton pattern that suits your needs.

public class SingletonProvider implements Provider<TheSingleton> {

    private static final TheSingleton instance  = new TheSingleton();
    @Override
    public TheSingleton get() {
        return instance;
    }
}

Then we bind this to our different modules

public class Module1 extends AbstractModule {

    @Override
    protected void configure() {
       bind(TheSingleton.class).toProvider( SingletonProvider.class);
    }
}
public class Module2 extends AbstractModule {

    @Override
    protected void configure() {
       bind(TheSingleton.class).toProvider( SingletonProvider.class);
    }
}

And then I create two different injectors that return this same instance

public class Test {
    public static void main(String[] args) {
        Injector injector1 = Guice.createInjector(new Module1());

        TheSingleton instance = injector1.getInstance(TheSingleton.class);
        System.out.println("instance = " + instance);

        Injector injector2 = Guice.createInjector(new Module2());
        TheSingleton instance2 = injector2.getInstance(TheSingleton.class);
        System.out.println("instance2 = " + instance2);


    }
}
Giorgos Gaganis
  • 635
  • 7
  • 8
  • Which module do you keep SingletonProvider in? – DilTeam Oct 22 '15 at 19:03
  • @DilTeam The provider is not 'kept' in any module. Each injector will create its own instance of the provider and will use that to fetch instances of TheSingleton.class . You can think of it as class that can provide access outside of the module. – Giorgos Gaganis Oct 22 '15 at 20:09
  • Have you confirmed in the debugger that they are indeed the same objects & not copies? – DilTeam Oct 23 '15 at 05:18
  • TheSingleton instance returned from both injectors is the same object. What do you mean by copies? – Giorgos Gaganis Oct 23 '15 at 08:49
  • The System.out will call 'toString' method on 'TheSingleton' correct? So the output might be identical, but in the debugger you can see the actual 'memory address'. Did you confirm that the address is same for both? I believe in your code two different objects (with different memory addresses) will be generated, but I am not sure. Please confirm. – DilTeam Oct 23 '15 at 15:44
  • Well I believe that since the toString is identical it is good enough to prove that it is the same object. The default toString method prints the hashCode, which in turn is computed by the memory address. See http://stackoverflow.com/questions/10106802/how-is-hashcode-implemented-in-java. – Giorgos Gaganis Oct 23 '15 at 19:30
  • Ok. I will test it in my environment & if it works will accept the answer. Thanks. – DilTeam Oct 23 '15 at 23:15