3

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.

tereško
  • 58,060
  • 25
  • 98
  • 150
quarks
  • 33,478
  • 73
  • 290
  • 513
  • Since there is just one instance of User and UserController in my application, so is there a way to inject it also – quarks May 01 '12 at 16:40

1 Answers1

10

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.

hsestupin
  • 1,115
  • 10
  • 19
  • In the "public class DefaultErrorMenu extends Menu" is the user and userController injected as well? – quarks May 02 '12 at 09:25
  • In my controller classes I typically have this pattern: XyzController(final Application application, final XyxModel model) – quarks May 02 '12 at 09:33
  • user and userController objects will be injected if: 1) they have default constructor without parameters, 2) they have @Inject annotation and they are described in your GinModule class: bind(User.class).somethingelse..... – hsestupin May 02 '12 at 10:18
  • I don't understand what the pattern is it? Anyway you should add @Inject annotation to the constructor of XyzController class. – hsestupin May 02 '12 at 10:30
  • BTW, I adapted this code also for injecting my application models, but it throws NullPointerException when accessing the model that is injected with @Inject and bind using bind(UserModel.class).in(Singleton.class); – quarks May 02 '12 at 12:32
  • Maybe you can help me also with this one: http://stackoverflow.com/questions/10414089/field-level-injection-with-gin – quarks May 02 '12 at 12:48
  • Sorry, but I need full code fragments of your GIN module and UserModel classes for determining the NPE reason. Does your UserModel class have default constructor without parameters? – hsestupin May 02 '12 at 14:20