Reputation: 90513
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:
@Inject
annotation, then@PostConstruct
annotationMy 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:
Injector.injectMembers(Object)
(@PostConstruct
isn't processed in superclass)VariableResolver
(@PostConstruct
is processed before @Inject
)GuiceResolver
(just did not appear to work at all - perhaps something to do with this issue)I'm happy to consider other options if this appears to be the wrong way of approaching things... Any help appreciated.
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 :(
Upvotes: 2
Views: 3103
Reputation: 100462
This answer seems to have a good solution to how to solve this problem. Specifically, it suggests using the @Inject annotation on an init() method, which is guaranteed to get call after your constructor is called. Note that it may not work if you're using property injection.
Upvotes: 0
Reputation: 24306
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();
}
}
Upvotes: 1
Reputation: 108959
Unfortunately, I do not have an answer for you, but I will say this...
- manually invoking Injector.injectMembers(Object) (@PostConstruct isn't processed in superclass)
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.
- using a custom VariableResolver (@PostConstruct is processed before @Inject)
This does not look like a sound technique. If I read the code right, dependencies will be injected every time the object is resolved.
- using MyFaces' GuiceResolver (just did not appear to work at all - perhaps something to do with this issue)
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).
Upvotes: 1