Injecting an instance using Gin/Guice

quarks picture quarks · May 1, 2012 · Viewed 7k times · Source

I typically have this kind of code pattern in my GWT project:

Menu errorMenu = new Menu(user, userController, -1);
Menu  searchMenu = new Menu(user, userController, 0);

errorView.setMenu(errorMenu);
searchView.setMenu(searchMenu);

How do I inject a Menu instance in the ErrorView and other "views" using Gin/Guice:

public ErrorView implements View {
 // Inject menu instance here
 private Menu menu;
}

Such that, I don't have to manually create and set objects?

Also for the Menu class, how can I inject the "user" and "userController" objects so I don't have to pass it on each Menu instance every time it is instantiated.

Answer

hsestupin picture hsestupin · May 2, 2012

With help of this tutorial http://code.google.com/p/google-gin/wiki/GinTutorial your problem does not look so difficult. There are several steps you should run for injecting menu instance to the View.

  1. Add @Inject annotation to the menu field.

    public ErrorView implements View {
    
      @Inject
      private Menu menu;
    }
    
    public SearchView implements View {
    
      @Inject
      private Menu menu;
    }
    

    But in this case menu fields will be null during View object initialization (in constructor). Therefore I prefer to add this field to the constructor parametres.

    public ErrorView implements View {
    
      private final Menu menu;
    
      @Inject
      public ErrorView(Menu menu) {
        this.menu = menu;
      }
    }
    
    public SearchView implements View {
    
      private final Menu menu;
    
      @Inject
      public SearchView(Menu menu) {
        this.menu = menu;
      }
    }
    

    Ofcourse it will not work in case of you have many other parameters in the constructor of ErrorView, because all of them need to be injected as well.

  2. Now we must make sure that GIN knows that menu field in ErrorView should be injected to new Menu(user, userController, -1) and another one in SearchView to - new Menu(user, userController, 0). We can do this by the several ways:

    • Add annotations @Named("searchMenu") and @Named("errorMenu") to your menu fields.

      public ErrorView implements View {
      
        @Inject
        @Named("errorMenu")
        private Menu menu;
      }
      

      or

      public ErrorView implements View {
      
        private final Menu menu;
      
        @Inject
        public ErrorView(@Named("errorMenu") Menu menu) {
          this.menu = menu;
        }
      } 
      

      In your GIN module you should provide the definition of this annotation.

          public class ApplicationGinModule extends AbstractGinModule {
      
            protected void configure() {
              bind(Menu.class).annotatedWith(Names.named("errorMenu")).to(DefaultErrorMenu.class);
              bind(Menu.class).annotatedWith(Names.named("searchMenu")).to(DefaultSearchMenu.class);
      
              //assume that User and UserController classes have default constructors  
              //otherwise you should provide correct injection depending on your business-logic
              bind(User.class).in(Singleton.class); 
              bind(UserController.class).in(Singleton.class);  
            }
          }
      
          public class DefaultErrorMenu extends Menu {
      
            @Inject
            public DefaultErrorMenu(User user, UserController userController) {
              super(user, userController, -1);
            }
          }
      
          public class DefaultSearchMenu extends Menu {
      
            @Inject
            public DefaultSearchMenu(User user, UserController userController) {
              super(user, userController, 0);
            }
          }
      
    • Create your own annotations @SearchMenu and @ErrorMenu to your menu fields and define them in your module.

      Annotation sample:

      @Retention(RetentionPolicy.RUNTIME)
      @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE})
      @BindingAnnotation
      public @interface ErrorMenu {
      }
      

      Usage:

      public ErrorView implements View {
      
        @Inject
        @ErrorMenu
        private Menu menu;
      }
      

      or

      public ErrorView implements View {
      
        private final Menu menu;
      
        @Inject
        public ErrorView(@ErrorMenu Menu menu) {
          this.menu = menu;
        }
      } 
      

      Then define annotation the way exactly how you defined @Named("ErrorMenu"):

      bind(Menu.class).annotatedWith(ErrorMenu.class).to(DefaultErrorMenu.class);
      

In some examples I make menu field final and remove setter to it, but if you really need mutable state for menu you can leave it unchanged.