vrijdag 15 april 2011

Seam 3 / Weld alternative for @Startup annotation

In Seam 2, if you wanted a seam component to be initialized when the application was started up, you could use the @Startup annotation.

In Seam 3 (or better: weld), this annotation is not supported. If you want a bean to be started up when the application starts up, you could write a method which observes an event:

  public class BeanToBeInitializedAtStartup {
public void onStartup(@Observes @Initialized WebApplication webApplication){
//do nothing, bean will be initialized when application is started
    }

  }

However, this means you will need such a method for every bean, which is a lot more of overhead than the simple @Startup annotation.

A solution for this is to use a simple helper class, which initializes all beans which implement some interface you define yourself:

First, you will need an interface. This interface does not require any methods.

  public interface Startup { }


Next, all of the beans you want to be initialized should implement this interface

  public class BeanToBeInitializedAtStartup implements Startup { }
That looks a lot better! Now of course you need some helper class, which initializes all beans which implement the Startup annotation:

  public class BeanStartupHelper {


    public void onStartup(@Observes @Initialized WebApplication webApplication, 
                          BeanManager beanManager) {
      
      for(Bean bean : beanManager.getBeans(Startup.class)){
       CreationalContext context = beanManager.createCreationalContext(bean);
       beanManager.getReference(bean, Startup.class, context);
     }
 
    }
  }

The BeanStartupHelper is actually also a bean, and as you can see, this bean contains a method which observes the initialization of the webapplication. A reference to the BeanManager object is injected as a parameter of this method. We use the BeanManager object to create the beans we need, which are all beans which implement the Startup annotation.

Result
There is a clear separation between the beans themselves and the implementation of the startup method. The only thing needed for your beans is the fact that they need to implement a Startup interface. You only need one method which observes the initialization of your application, in which all 'startup' beans will be initialized!


4 opmerkingen:

  1. Does this really work?

    IMHO what happens in case of normal-scoped components (e.g. @ApplicationScoped) is that your call to BM.getReference() retrieves a proxy object. This does not necesarilly create a new instance of the underlying object. The object is actually created later on, when you invoke a method on the object. As a result, the bean is not initialized at startup. This approach IMHO works on @Dependent - scoped beans only.

    BeantwoordenVerwijderen
  2. Hi Jozef,

    this works for me at least. In my current project, I have a number of @ApplicationScoped beans, and their constructor is called correctly at the startup of my application.

    BeantwoordenVerwijderen
  3. Bart,



    I can see the problem now. What really happens is that CDI implementations use proxies for normal-scoped beans. The proxy is constructed as a subclass of the original class. When a method is invoked on a proxy, the proxy does a lookup of the underlying instance and invokes the method on it.



    This has a side effect. When the proxy is created, the class constructor is called. As a result, a constructor of a bean X may be called more than once for a single class instance - once when the proxy is created and again when the class instance itself is being created.



    What you see is constructor being called when a proxy is created. This happens as a result of calling beanManager.getReference(). The constructor get called as a proxy is created, however the bean itself, which is created lazily is not instantiated at this point. You can verify this easily by moving your code into a @PostConstruct method to see if it still gets executed. (A @PostConstruct method gets called during bean initialization, not for the proxy creation).

    BeantwoordenVerwijderen
  4. BTW, in case anyone is wondering...

    These annotations are part of Seam Servlet:

    http://docs.jboss.org/seam/3/servlet/latest/reference/en-US/html/servlet-events.html#events.application_initialization

    BeantwoordenVerwijderen