Search code examples
javaguiceguice-3

Override binding or child module in Guice


Im writing a component using Google Guice that lives next to a web application that does not use any dependency injection tool.

The Guice Module in the component has a few "fixed" bindings that will not change and a couple that are dynamic since they change in every request from the web application.

The easy (and bad) way that I solved this is that everytime that the web application asks the component to do something for the first time, the component builds the new Guice Module, creates the instance and returns it to the web app:

public static X init(@NotNull final Foo perRequestObject, @NotNull final Bar perRequestObject2)
{
    final Injector injector = Guice.createInjector(new AbstractModule()
    {
        @Override
        protected void configure()
        {
            install(new NonChanging Module());
            bind(Foo.class).toInstance(perRequestObject);
            bind(Bar.class).toInstance(perRequestObject2);
        }
    });
    return return injector.getInstance(X.class);
}

I think that this is a bad approach since building the Injector per request is expensive. What I would like to have is an injector already created that I can override at runtime. I found some stuff around:

1- Override the dynamic bindings (answer https://stackoverflow.com/a/531110/1587864). This still needs to create a new injector so I would have the same problem.

2- Implement some kind of Factory that is already binded in the Injector and has the ability to access the "dynamic" properties that come from the web application and are per request.

I'm not sure how to implement the second one, does this concept exist in Guice?

Thanks


Solution

  • As the comments say, the full proper solution is a Scope. Assuming you've copied the implementation of SimpleScope from this article into your project, then here's what I'd do in your component. As you do above, I assume Foo and Bar are the objects you need created on a per-request basis, and X is the class of the thing you ultimately need Guice to create. I assume further that X has on it a method called doTheActualRequestLogic that is the method you wish to call to ultimately return back to the servlet:

    public class MyWebComponent
    {
      private final Provider<X> theGuiceCreatedObject;
      private final SimpleScope perRequestScope;
    
      public MyWebComponent() {
        perRequestScope = new SimpleScope();
        Injector injector = Guice.createInjector(new AbstractModule()
        {
            @Override
            protected void configure()
            {
                install(new NonChangingModule());
                bind(Foo.class).toProvider(SimpleScope.seededKeyProvider())
                    .in(perRequestScope);
                bind(Bar.class).toProvider(SimpleScope.seededKeyProvider())
                    .in(perRequestScope);
            }
        });
        theGuiceCreatedObject = injector.getProvider(X.class);
      }
    
      // I assume methods called makeFoo and makeBar that can make
      // a Foo or Bar for a request
    
      // called by the web service to say "handle this request"
      public RequestResult handleRequest(DataToMakeFooAndBarWith requestData) {
        try {
          perRequestScope.enter();
          perRequestScope.seed(Foo.class, makeFoo(requestData));
          perRequestScope.seed(Bar.class, makeBar(requestData));
          return theGuiceCreatedObject.get().doTheActualRequestLogic(requestData);
        } finally {
          perRequestScope.exit();
        }
      }
    }