Posted by cagataycivici on March 26, 2007
I was wondering about how the integration of JSF and the community’s new hot IOC Framework Guice can be done and later figured out a way. The example I’ve created is simple, there’s a JSF page used for creating an Account entity, a service class AccountService that implements IAccountService, and a JSF managed bean serving as the backing bean of the JSF page. Simply the JSF bean has a dependency to IAccountService and the injection is done via Guice.
Account
package com.cc.guicejsf.domain;
import java.io.Serializable;
public class Account implements Serializable{
private String name; private String password; public Account() {} public Account(String name, String password) { this.name = name; this.password = password; } //Getters/Setters of properties } |
|
IAccountService
package com.cc.guicejsf.service;
import com.cc.guicejsf.domain.Account;
public interface IAccountService { public void save(Account account); } |
|
AccountService
package com.cc.guicejsf.service;
import com.cc.guicejsf.domain.Account;
public class AccountService implements IAccountService{
public void save(Account account) { //TODO: Persist account } } |
|
createAccount form
<%@ taglib uri=”http://java.sun.com/jsf/html” prefix=”h” %> <%@ taglib uri=”http://java.sun.com/jsf/core” prefix=”f” %> <html> <head> <title>Create an Account</title> </head> <body> <f:view> <h:form> <h:panelGrid columns=”2″> <h:outputLabel value=”Name” for=”txt_name”></h:outputLabel> <h:inputText id=”txt_name” value=”#{createAccount.account.name}” /> <h:outputLabel value=”Password” for=”txt_password”></h:outputLabel> <h:inputSecret id=”txt_password” value=”#{createAccount.account.password}” /> </h:panelGrid> <h:commandButton action=”#{createAccount.save}” value=”Save” /> </h:form> </f:view> </body> </html> |
CreateAccount
package com.cc.guicejsf.pages;
import com.cc.guicejsf.domain.Account; import com.cc.guicejsf.service.IAccountService; import com.google.inject.Inject;
public class CreateAccount extends BasePageBean{
private Account account = new Account(); private IAccountService accountService;
public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public String save() { accountService.save(account); return null; } public IAccountService getAccountService() { return accountService; } @Inject public void setAccountService(IAccountService accountService) { this.accountService = accountService; } } |
|
BaseBackingBean
package com.cc.guicejsf.pages;
import java.io.Serializable;
import javax.annotation.PostConstruct; import javax.faces.context.FacesContext; import javax.servlet.ServletContext;
import com.google.inject.Injector;
public abstract class BasePageBean implements Serializable{
private Injector injector; public BasePageBean() {} public Injector getInjector() { if(injector == null) { ServletContext servletContext = (ServletContext)FacesContext.getCurrentInstance(). getExternalContext().getContext(); injector = (Injector)servletContext.getAttribute(Injector.class.getName()); } return injector; } public void setInjector(Injector injector) { this.injector = injector; } @PostConstruct public void init() { getInjector().injectMembers(this); } } |
|
Listener (To be defined in web.xml)
package com.cc.guicejsf.config;
import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener;
import com.google.inject.Guice; import com.google.inject.Injector;
public class ModuleListener implements ServletContextListener {
public void contextDestroyed(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); servletContext.removeAttribute(Injector.class.getName()); }
public void contextInitialized(ServletContextEvent servletContextEvent) { Injector injector = Guice.createInjector(new GuiceJSFModule()); ServletContext servletContext = servletContextEvent.getServletContext(); servletContext.setAttribute(Injector.class.getName(), injector); } } |
|
GuiceJSFModule
package com.cc.guicejsf.config;
import com.cc.guicejsf.service.IAccountService; import com.cc.guicejsf.service.AccountService; import com.google.inject.AbstractModule; import com.google.inject.Scopes;
public class GuiceJSFModule extends AbstractModule{
public void configure() { bind(IAccountService.class).to(AccountService.class).in(Scopes.SINGLETON); } } |
|
The idea here is, the backing JSF bean is created by JSF IOC but it’s dependencies like IAccountService is injected by Guice. At startup, a listener creates and sets a Guice Injector to application scope(I get the listener idea from this blog). This part can be refactored so that the module lists are not hardcoded and retrieved from a context param instead. Anyway, when a JSF bean is created, the method annotated with @PostConstruct gets the injector and calls the injection procedure. This @PostConstruct is called after the bean is created but before being put into it’s scope.
The example is a kickstart for an app that uses JSF and Guice, frankly I don’t think Guice fits the JSF model well, the @PostConstruct is a JSF 1.2 feature and it’ll be problematic to do a similar thing with JSF 1.1. I’m not a spring fan but using spring 2.0 with JSF is very easy and effective compared to Guice since it’s also possible to define the JSF beans in Spring Context with different scopes, apply AOP on them, add BeanPostProcessor to add custom lifecycle logics and etc.
This entry was posted on March 26, 2007 at 7:05 am and is filed under Java.
.
You can follow any responses to this entry through the RSS 2.0 feed.
You can leave a response, or trackback from your own site.
September 30, 2007 at 3:32 pm
Nice one Çağatay - I did a slight variation of this.
October 17, 2007 at 8:22 am
Nice to see these two options. I think I more tend to go Dennis’ way.
November 8, 2007 at 7:10 pm
Cagatay, very nice tip !!
I´m only having one problem.
I´m trying to use an extended class and the @PostConstruct
annotation and it does not work.
FYI :
public class MyBean extends AppBean{
}
public class AppBean {
@PostConstruct
public void init(){
System.out.println(”Hi”);
}
}
Tks.
March 23, 2008 at 4:20 am
@PostConstruct will work with JSF 1.2 and later versions.
April 12, 2008 at 10:21 am
With Java6 the ServletContextListener class can be more generic.
This line:
Injector injector = Guice.createInjector(new GuiceJSFModule());
is the reason why you not really can reuse the Listener…
More generic way of GuiceModule Loading you can see in my blog