10 Replies Latest reply on Nov 18, 2009 8:25 PM by asookazian

    Can I use an @Inject-ed class, like Logger, into a no-args constructor?

    sboscarine

      Is it possible to get the constructor working in the class below?


      @Model
      public class Bean {
           @Inject
           private Logger logger;
           private String title;
           public Recipe() {
                logger.error("i was constructed");  //null pointer exception
           }
           public String getTitle() {
                logger.error("i was called"); //works just fine (if I comment out logger in constructor)
                return title;
           }
      }




      I suspect the answer is going to be a big fat no, but wanted to check just in case. 



      I love injecting the logger, but was wondering if I have to revert to a static factory call for constructors.  The documentation suggests that it'll work if I add logger to the constructor, which is probably suitable for most production settings.  In this case, I was using it to troubleshoot an error and didn't want to have to do a large refactor.

        • 1. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
          gavin.king

          Nope. Rewrite it like this:


          @Model
          public class Bean {
               
               private Logger logger;
               private String title;
          
                  @Inject
               public Recipe(Logger logger) {
                    this.logger = logger;
                    logger.error("i was constructed");  //null pointer exception
               }
               public String getTitle() {
                    logger.error("i was called"); //works just fine (if I comment out logger in constructor)
                    return title;
               }
          }

          • 2. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
            asookazian

            so this is a managed bean lifecycle question I guess.


            The injection occurs presumably after the construction of the bean instance and thus, the NPE in the constructor.  Is it possible to inject values into the instance prior to construction?  Not sure if that is possible?


            Is there a lifecycle diagram for managed beans and session beans in EE 6?

            • 3. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
              gavin.king

              BTW, at some stage, we will support injection into static fields (with some restrictions), which is good for loggers.

              • 4. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                asookazian

                Gavin King wrote on Nov 18, 2009 17:40:


                Nope. Rewrite it like this:

                @Model
                public class Bean {
                     
                     private Logger logger;
                     private String title;
                
                        @Inject
                     public Recipe(Logger logger) {
                          this.logger = logger;
                          logger.error("i was constructed");  //null pointer exception
                     }
                     public String getTitle() {
                          logger.error("i was called"); //works just fine (if I comment out logger in constructor)
                          return title;
                     }
                }




                It would be nice if we could do this instead:


                public Recipe(@Inject Logger logger) {     
                     logger.error("i was constructed");  //null pointer exception
                }



                @Target(value={METHOD,CONSTRUCTOR,FIELD})
                @Retention(value=RUNTIME)
                @Documented
                public @interface Inject



                so field level annotation is possible...

                • 5. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                  gavin.king

                  It would be nice if we could do this instead:

                  No, that would not be nice. How could the container inject just a single parameter, without calling the whole constructor?

                  • 6. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                    asookazian

                    Gavin King wrote on Nov 18, 2009 18:37:


                    It would be nice if we could do this instead:

                    No, that would not be nice. How could the container inject just a single parameter, without calling the whole constructor?


                    In any event, I still don't like your solution.  It's not bad but not great.  If you could devise a way to set the instance variable directly and bypass the local variable, that would eliminate a line of boring code.


                    private Logger logger;
                         
                            @Inject
                         public Recipe(Logger logger) {
                              this.logger = logger;
                              logger.error("i was constructed");  //null pointer exception
                         }



                    How bout the container tries to inject whenever there will be a NPE?


                    so like:


                    private Logger logger;
                                 
                         public Recipe() {          
                              logger.error("i was constructed");  //null pointer exception
                         }



                    so there would a config somewhere (say in beans.xml?) that would specify to inject any and all occurrences of Logger type when it may be null at runtime.


                    I have a feeling this is unorthodox/impossible but it seems cool...  like a backup or plan B injection strategy...  less lines of code in your classes...

                    • 7. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                      asookazian

                      Gavin King wrote on Nov 18, 2009 18:37:


                      It would be nice if we could do this instead:

                      No, that would not be nice. How could the container inject just a single parameter, without calling the whole constructor?


                      It would be very nice.  But impossible maybe.  But I'm not the container designer :).


                      container first sees this:


                      public Recipe(@Inject Logger logger) {     
                           logger.error("i was constructed");  
                      }



                      as this:


                      public Recipe(Logger logger) {     
                           logger.error("i was constructed");  
                      }



                      So the instance now exists.  Then proceeds with the injection (post-injection).


                      So execute this first:


                      public Recipe(Logger logger) {     
                            
                      }



                      then this:


                      @Inject logger.error("i was constructed");



                      Now the problem is that in my version the injection would occur for the local variable, not the instance variable, which is the one we want to be injected.


                      Although in CDI, there is no dynamic re-injection like in Seam, correct?  In Seam, all injections occur prior to public method invocation.  Every time a public method is called on that Seam component.  Not sure if it works this way with managed beans or session beans in CDI.


                      I'm going to be bashed on this post, oh well...  It really seems to me there's a better way...


                      Why doesn't this work or why isn't it allowed?


                      @Model
                      public class Bean {
                           @Inject
                           private Logger logger;
                      ...
                      }
                      



                      This is a close equivalent in Seam 2.x:


                      @Name("bean")
                      public class Bean {
                           @Logger
                           private Log log;
                      ...
                      }



                      ???

                      • 8. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                        asookazian

                        btw, when I say container first sees this.... this is my imagination at work, not saying it actually works this way now...

                        • 9. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                          gavin.king

                          We're not limited by your imagination, Arbi. We are limited by what is possible in the Java language and JVM. I can't inject a field until after the constructor has executed. End of story.



                          Why doesn't this work or why isn't it allowed?

                          @Model
                          public class Bean {
                               @Inject
                               private Logger logger;
                          ...
                          }





                          Of course it is allowed, which you would know if you had read the spec.

                          • 10. Re: Can I use an @Inject-ed class, like Logger, into a no-args constructor?
                            asookazian

                            this is allowed:


                            @Model
                            public class Bean {
                                 @Inject
                                 private Logger logger;
                            
                                    public void foo() {
                                        logger.error("bar");
                                    }
                            ...
                            }



                            this is not allowed:


                            @Model
                            public class Bean {
                                 @Inject
                                 private Logger logger;
                                    
                                    public Bean(){
                                        logger.error("foo");
                                    } 
                            ...
                            }



                            read the spec.  I'm really trying to read the spec some and the ref doc for Weld.  That would avoid a lot of these stupid questions/comments I'm sure.  I'm having problems understanding the concepts like stereotypes and @Alternatives, for example.  Not trivial.  Getting there...