12 Replies Latest reply on Nov 16, 2009 9:07 AM by Adam Warski

    Constructor injection for application-scoped beans

    Adam Warski Master

      Hello,


      isn't it possible to inject a dependent-scoped bean into an application-scoped bean using constructor injection? If yes, why not? I can't think of anything wrong that could happen.


      Here's my test app:


      @Dependent class A { }
      
      // Here's the constructor injection
      @ApplicationScoped class B { @Inject public B(A a) { } }
      
      // Then I want to use the bean in another bean
      @ApplicationScoped class C { @Inject private B b; }




      The error I get:


      The injection point field test.C.b has non-proxyable dependencies
       at org.jboss.weld.Validator.validateInjectionPoint(Validator.java:241)



      Adam

        • 1. Re: Constructor injection for application-scoped beans
          Adam Warski Master

          Ah! B must have also a no-arg constructor. Probably because of the proxy?


          Adam

          • 2. Re: Constructor injection for application-scoped beans
            Nicklas Karlsson Master

            Yep, it's normalscoped and therfore proxied.


            As a side-note, we are working on an extension for no-args-constructorless creation using Unsafe.allocateInstance() or ReflectionFactory.newConstructorForSerialization() if they are available on the CP.

            • 3. Re: Constructor injection for application-scoped beans
              Gavin King Master

              Yep, add a constructor, introduce an interface, or inject Instance<B> instead of B.

              • 4. Re: Constructor injection for application-scoped beans
                Adam Warski Master

                Thanks for the answers. All three ways work ;)


                One more question though. I understand why a proxy is injected instead of the bean when injecting an app-scoped bean into a req-scoped bean, or a bean from a narrower scope into a bean with a wider scope.


                But why a proxy is needed when injecting an app-scoped bean into another app-scoped bean? Or one session-scoped bean into another session-scoped bean? They will always be serialized together.


                Adam

                • 5. Re: Constructor injection for application-scoped beans
                  Nicklas Karlsson Master

                  Of course there could be optimizations but I think currently it's just in case ;-). I guess the recieving injection point could be examined to see if it's really required...

                  • 6. Re: Constructor injection for application-scoped beans
                    Nicklas Karlsson Master

                    Of course there is the problem of A getting injected into B getting injected into C which is conversation scoped and then goes long-running...

                    • 7. Re: Constructor injection for application-scoped beans
                      Gavin King Master

                      But why a proxy is needed when injecting an app-scoped bean into another app-scoped bean? Or one session-scoped bean into another session-scoped bean? They will always be serialized together.

                      Well, in some particular implementation of CDI that may be the case - indeed it will be the most common implementation - but it's certainly not something that the spec requires. Don't assume that CDI context is exactly the same thing as a servlet context with the same name. The spec doesn't say that.


                      Also, the proxy might be needed to deal with circularities. (A injects B which injects C which injects A.) Unlike JSR-330, JSR-299 does require the container to deal with circularities.


                      Now, of course, a certain CDI implementation, like Weld, doesn't need to actually insert a real proxy object in this case. It's permitted to optimize it away, as per spec section 6.5.5. But that's a non-portable optimization.


                      In more general terms, we needed a simple, portable rule to give to users. We didn't want a set of rules with lots of caveats about the respective scopes of the beans and the existence of circularities. We want to say normal scoped beans need proxies and be done. Otherwise the user ends up writing a bunch of code that depends upon the respective scopes of the beans, and that breaks when the scope of a bean changes. Yes, unfortunately breakage like this is still possible if you change from @Dependent to a normal scope :-(


                      Does that make sense?

                      • 8. Re: Constructor injection for application-scoped beans
                        Gavin King Master

                        By the way, it's a good question, and one I wrestled with. It's the kind of issue where you might do something different if you were designing a single implementation, instead of a specification, and didn't have to worry about portability issues.

                        • 9. Re: Constructor injection for application-scoped beans
                          Gavin King Master

                          Lemme take a second stab, from a usecase point of view.


                          There are three possible reasons for wanting a normal-scoped bean:



                          1. you're accessing it directly from EL

                          2. you're sharing it between multiple clients

                          3. its client has a different scope



                          Now, if none of 1,2,3 apply, and the bean has one client, with the same lifecycle, then just leave the bean with the default @Dependent scope. No proxy required, no scope annotation required, no constructor required.


                          Furthermore, we can forget case 1, since no proxy is required when a bean is called from EL.


                          In case 2, we have lots of potential for circularities, or for clients with a passivating scope, or for clients with a wider scope. If you're writing a bean that has various clients, you might as well just stick the constructor in, and forget about it.


                          Even in case 3, the client might have a passivating scope or a wider scope, so there are going to be a bunch of secret rules that the container has for deciding whether a proxy is really going to be required or not. I really don't want to expose you to those rules.


                          And I'm not sure that you're really able to distinguish between cases 2 and 3 when you create the bean. Once a bean is sitting in a normal scope, it's there for everyone to get at.


                          HTH

                          • 10. Re: Constructor injection for application-scoped beans
                            Adam Warski Master

                            Ah, I understand. So the app, req, sess, cov scopes are just normal scopes and are treated uniformly, as if they could be serialized any time; makes sense. (Although maybe here the passivating attribute could play a role? The beans can be serialized only if passivated is true, but yes, that's also an optimization :) ).


                            Also I didn't notice that you could have circular dependencies (unlike in Seam, where this caused an exception). However from experiments it seems that the @PostConstruct method is called when a proxy is first used (from another @PostConstruct method), not when the bean is injected - maybe it would be good to put that into documentation.


                            Adam

                            • 11. Re: Constructor injection for application-scoped beans
                              Gavin King Master

                              For normal-scoped beans it is the proxy that is injected. The actual bean instance could be instantiated lazily. However, that is up to the implementation.

                              • 12. Re: Constructor injection for application-scoped beans
                                Adam Warski Master

                                I see. I thought it's a way to try to partially solve the problem of calling lifecycle callbacks in the presence of circular dependencies, but your answer makes more sense ;).


                                Adam