Guice Singleton Static Injection Template

I am new to Google Guice and understand the concept of Dependency Injection conceptually, but I run into problems trying to incorporate it into my application. My specific question is about Singleton objects. Here is an example:

First, my Module module, which binds the heavy Singleton Connection interface to its implementation.

public class MyModule extends AbstractModule { @Override protected void configure() { bind(Connection.class).to(MyConnection.class).asEagerSingleton(); } } 

Now, in my main method, I create an instance of my application server and insert Connection:

 public class MyApplication { @Inject public MyApplication(Connection cxn) { } public static void main(String[] args) { Injector injector = Guice.createInjector(new MyModule()); MyApplication app = injector.getInstance(MyApplication.class); // Start application, add ShutdownHook, etc... } } 

Everything is fine so far ... Now I have several DAO classes that use my Connection object but are retrieved with static methods:

 public class MyConfiguration { private Config conf; private Connection cxn; // Would like to have this injected private MyConfiguration(Config conf) { this.conf = conf; } public static MyConfiguration getConfig(String name) { return new MyConfiguration(cxn.getConfig(name)); } } 

My first assumption was that I would simply add @Inject to cxn , but this does not work because I am not getting an instance from Guice; it just gives me NPE. As I see this, I have 2 options for getting the Connection object:

  • Derive the getConnection() method to MyApplication, essentially following the service locator pattern
  • Add requestStaticInjection(MyConfiguration) to MyModule

I chose # 2, however docs will say :

This API is not recommended for general use.

What is the best practice of providing my Singleton to the classes it needs without having to go through Injector.getInstance every time? What am I missing?

+6
source share
1 answer

You are thinking incorrectly about dependency injection. Dependency injection and a service locator are mirror images of each other: using a service locator, you request it for an object. When injecting dependencies, you are not looking for dependencies, they are simply passed on to you.

Basically, "it's turtles all the way down!" Each dependency of your class must be introduced. If MyApplication needs a MyConfiguration object, it should just accept the MyConfiguration object as a constructor parameter and not worry about how it was created.

Now, this does not mean that you can never use new manually, but you should reserve this for objects of type value that have no external dependencies. (And in these cases, I would say that you are often better off using a static factory method than a public constructor, but that is not the case.)

Now there are several ways to do this. One way is to trick MyConfiguration many tiny pieces so that instead of myConfiguration.getConfig("x") you would do an @Inject @Configuration("x") String or something like that. Alternatively, you can make MyConfiguration itself injectable, and then provide methods for accessing them for fragments for it. The correct answer to some extent depends on the type of data you are trying to model - too dependent on dependencies, and your bindings can be difficult to maintain (although there are ways to do this better); make the dependencies too coarse, and it becomes harder for you to test (for example: which is easier by providing only the “x” configuration that the tested class requires, or building the entire application configuration?).

You can do both:

 /** Annotates a configuration value. */ @BindingAnnotation @Retention(RetentionPolicy.RUNTIME) public @interface Config { String value(); } /** Installs bindings for {@link MyConfiguration}. */ final class MyConfigurationModule extends AbstractModule { @Override protected void configure() {} @Provides @Singleton MyConfiguration provideMyConfiguration() { // read MyConfiguration from disk or somewhere } @Provides @Config("x") String provideX(MyConfiguration config) { return config.getConfig("x").getName(); } } // elsewhere: /** The main application. */ final class MyApplication { private final String xConfig; @Inject MyApplication(@Config("x") String xConfig) { this.xConfig = xConfig; } // ... } 

In unit tests, you can use a similar approach:

 /** Tests for {@link MyApplication}. */ @RunWith(JUnit4.class) public final class MyApplicationTest { // Note that we don't need to construct a full MyConfiguration object here // since we're providing our own binding, not using MyConfigurationModule. // Instead, we just bind the pieces that we need for this test. @Bind @Config("x") String xConfig = "x-configuration-for-test"; @Before public void setUp() { // See https://github.com/google/guice/wiki/BoundFields Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); } @Inject MyApplication app; @Test public void testMyApp() { // test app here } } 

Dependency injection also encourages another best practice, which I highly recommend, which is to develop your type system so that invalid states cannot be represented (as much as possible). If the entire MyApplication configuration needs to be passed in its constructor, it will never have a MyApplication object that does not have the correct configuration. This allows you to “load” your class invariants, which greatly facilitates the discussion of the behavior of your objects.

Finally, a note on Injector.getInstance() . Ideally, you use Injector exactly once in your program: immediately after creating it. That is, you should be able to do Guice.createInjector(...).getInstance(MyApplication.class).start() and never store the link to Injector anywhere. I tend to create applications using the Guava ServiceManager abstraction (see also this question ), so the only thing I need to do is:

 public static void main(String[] args) throws Exception { Injector injector = Guice.createInjector(...); ServiceManager manager = injector.getInstance(ServiceManager.class); manager.startAsync().awaitHealthy(); } 
+11
source

Source: https://habr.com/ru/post/983017/


All Articles