views:

977

answers:

2

I'm trying to integrate Guice into a JSF 1.2 (Sun RI) application, and I want to be able to do the following to my managed-beans:

  1. Inject dependencies using the Guice @Inject annotation, then
  2. Perform initialisation using the @PostConstruct annotation

My problem is that the @PostConstruct method is always invoked before the @Inject annotations are processed. Does anyone know of a solution to this problem?

The managed-bean:

public final class Foo {

    @Inject private BazService bazService;
    private Baz baz;

    @PostConstruct
    public void init() {
        bar = bazService.loadBaz();
    }

    public void setBazService(BazService bazService) {
        this.bazService = bazService;
    }
}

The managed-bean declaration:

<managed-bean>
  <managed-bean-name>foo</managed-bean-name>
  <managed-bean-class>bean.Foo</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>id</property-name>
    <value>#{param.id}</value>
  </managed-property>
</managed-bean>

The Guice bindings:

public final class MyGuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(BazService.class).to(DummyBazService.class).in(Scopes.SINGLETON);
    }
}

I've tried the following:

I'm happy to consider other options if this appears to be the wrong way of approaching things... Any help appreciated.


Edit

I was using Guice 1.0. I've now upgraded to Guice 2.0 but the problem remains. I've found some discussion that seems to relate to my issue... but I don't understand how to use this information :(

+1  A: 

Unfortunately, I do not have an answer for you, but I will say this...

Getting the objects to inject themselves looks like it might work - you needn't necessarily use a superclass:

public final class Foo {

  @Inject
  private BazService bazService;
  private Baz baz;

  @PostConstruct
  public void init() {
    InjectorFinder.getInjector().injectMembers(this);
    baz = bazService.loadBaz();
  }

  public void setBazService(BazService bazService) {
    this.bazService = bazService;
  }

}

I also see no reason to keep a reference to the injector, so consider a utility class:

public class InjectorFinder {
  public static Injector getInjector() {
    FacesContext facesContext = FacesContext
        .getCurrentInstance();
    ExternalContext extContext = facesContext
        .getExternalContext();
    Map<String, Object> applicationMap = extContext
        .getApplicationMap();
    return (Injector) applicationMap.get(Injector.class
        .getName());
  }
}

Does anything happen with @PostConstruct and serialization? It is something I haven't thought much about, but it may affect session beans.


This does not look like a sound technique. If I read the code right, dependencies will be injected every time the object is resolved.


I suspect this patch suffers from the problems I've mentioned (different resolver, same author, anyway). Bugs aside, trying to use two different implementations of JSF (Sun and Apache) at the same time is not going to work.


I have seen a couple of Guice/JSF projects in the wild (guicesf; jsf-sugar; maybe there are more?) but have not tried them, so I have no idea if they will help you or even how stable they are.

Whatever you do, it might be an idea to keep an eye on Web Beans (overview here) as this is likely to affect future bean handling features in JSF (if I understand Guice's place in the stack correctly, you could use it to implement Web Beans - not that I am recommending you do).

McDowell
Thanks for your suggestions. I'm reluctant to include the Guice injection code in each of my beans - I preferred the superclass option as it avoided repeating that code. Regarding your second example, introducing a dependency on InjectorFinder seems a bit perverse somehow :)
harto
guicejsf could be worth a look. jsf-sugar looks like it introduces some replacement XML document for faces-config.xml, so I might avoid that...
harto
+1  A: 

After doing a bunch of reading, I've reached the conclusion that in might be best to avoid @PostConstruct if you use JSF and Guice.

In the example you gave, you can inject Baz:

public final class Foo {

    @Inject private Baz baz;
}

If Baz can't be created by Guice, but must be created by BazService, you can use a provider method:

public final class MyGuiceModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(BazService.class).to(DummyBazService.class).in(Scopes.SINGLETON);
    }

    @Provides
    Baz provideBaz(BazService bazService) {
        return bazService.loadBaz();
    }
}
NamshubWriter
I'm not sure that constructor injection is an option - my understanding is that JSF requires a public no-args constructor for managed-bean instantiation. It would be my preferred option if it were possible.
harto
You are right. It's unfortunate that you can't tell JSF to have some other factory create the beans. Makes me glad I don't use JSF in my current projects.
NamshubWriter
The suggestions I made that didn't use constructor injection are still options. Updated my answer.
NamshubWriter
Thanks for your suggestions.
harto