Saturday, July 14, 2012


Using Spring to Manage JSF Beans

The traditional way to integrate JSF and Spring was to define JSF beans in faces-config as managed beans and refer to the spring beans using the managed-property configuration. With the help of the spring’s delegatingvariableresolver the managed property is resolved from spring application context and JSF’s IOC injects the bean to the JSF Managed bean instance. I’ve written an article it about this way before.First approach is modelled as follows
jsfspring1.jpg
And the better approach described in this article
jsfspring2.jpg
Although the way described above sounds nice at first glance, I believe it has a major issue, do we really need to have two IOC containers in your application to do dependency injection. I mean why not just one right? Actually this was necessary before spring 2 but with the addition of custom scopes, a better way for integration has come up. Now spring has the ability manage the JSF backing beans in it’s container, this results in a faces-config.xml with no managed-bean configurations. I’ve created a simple DVDStore application to show a sample configuration. Let’s start with the domain class DVD
01package com.cc.blog.dvdstore.domain
02 
03public class DVD {
04 
05    private String title;
06    private Integer discs;
07 
08    public DVD() {
09 
10    }
11 
12    public DVD(String title, Integer discs) {
13        this.title = title;
14        this.discs = discs;
15    }
16 
17    public Integer getDiscs() {
18        return discs;
19    }
20 
21    public void setDiscs(Integer discs) {
22        this.discs = discs;
23    }
24 
25    public String getTitle() {
26        return title;
27    }
28 
29    public void setTitle(String title) {
30        this.title = title;
31    }
32 
33}
DVDService lives at service layer
1package com.cc.blog.dvdstore.service;
2 
3import com.cc.blog.dvdstore.domain.DVD;
4 
5public interface IDVDService {
6 
7     public void saveDVD(DVD dvd);
8 
9}

01package com.cc.blog.dvdstore.service;
02 
03import com.cc.blog.dvdstore.domain.DVD;
04 
05public class DVDService implements IDVDService{
06 
07    public void saveDVD(DVD dvd) {
08        //Refer to a DAO or repository implementation and persist the DVD
09    }
10 
11}

And the JSF Backing to handle the creation of a new DVD record
01package com.cc.blog.dvdstore.view;
02 
03import com.cc.blog.dvdstore.domain.DVD;
04import com.cc.blog.dvdstore.service.DVDService;
05 
06public class SaveDVDBean {
07 
08    private DVD dvd;
09    private IDVDService service;
10 
11    public SaveDVDBean() {
12        //NOop
13    }
14 
15    public DVD getDvd() {
16        if(dvd == null)
17           dvd = new DVD();
18 
19        return dvd;
20    }
21 
22    public void setDvd(DVD dvd) {
23        this.dvd = dvd;
24    }
25 
26    public IDVDService getService() {
27        return service;
28    }
29 
30    public void setService(IDVDService service) {
31        this.service = service;
32    }
33 
34    public String save() {
35        service.saveDVD(dvd);
36 
37        return null;
38    }
39 
40}
Fragment from the JSF page backed by SaveDVDBean,
01<h:form>
02    <h:panelGrid columns="2">
03        <h:outputLabel for="title">h:outputLabel>
04        <h:inputText id="title" value="#{saveDVDBean.dvd.title}">h:inputText>
05 
06        <h:outputLabel for="discs">h:outputLabel>
07        <h:inputText id="discs" value="#{saveDVDBean.dvd.discs}">h:inputText>
08    h:panelGrid>
09    <h:commandButton value="Save" action="#{saveDVDBean.save}">h:commandButton>
10h:form>
web.xml
01xml version="1.0" encoding="UTF-8"?>
04 
05<context-param>
06    <param-name>contextConfigLocationparam-name>
07    <param-value>/WEB-INF/applicationContext.xmlparam-value>
08context-param>
09 
10<listener>
11    <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
12listener>
13<listener>
14    <listener-class>org.springframework.web.context.request.RequestContextListener<listener-class>
15listener>
16 
17<servlet>
18    <servlet-name>Faces Servletservlet-name>
19    <servlet-class>javax.faces.webapp.FacesServletservlet-class>
20    <load-on-startup>1<load-on-startup>
21servlet>
22<servlet-mapping>
23    <servlet-name>Faces Servletservlet-name>
24    <url-pattern>*.jsfurl-pattern>
25servlet-mapping>
26web-app>
applicationContext.xml
01xml version="1.0" encoding="UTF-8"?>
04default-autowire="byName"
05xsi:schemaLocation="
09 
10    <bean id="dvdService" class="com.cc.blog.dvdstore.service.DVDService">bean>
11 
12    <bean id="saveDVDBean" class="com.cc.blog.dvdstore.view.SaveDVDBean" scope="request">
13        <property name="service">
14            <ref bean="dvdService"/>
15        property>
16    bean>
17beans>

faces-config.xml
01xml version="1.0" encoding="UTF-8"?>
02
03 
05    <application>
06        <variable-resolver>
07            org.springframework.web.jsf.DelegatingVariableResolver
08        variable-resolver>
09    application>
10faces-config>
As seen above, faces-config has no managed-bean stuff, instead saveDVDBean is defined in spring’s application context in request scope. So here is why using Spring’s IOC to manage JSF backing beans is cool:)
- With spring aop, you can apply aop tricks to your jsf beans, for example you can do aop audit logging based on jsf events like SaveDVDBean’s save method. Add an annotation like @Auditable to the save() method, write an interceptor and then you can log who and when tried to save a dvd.
- Use autowiring on JSF beans
- Constructor Injection
- Lifecycle events like afterPropertiesSet using lifecycle interfaces or configurations like init-method, destroy-method
- Much more actually, see spring ioc container documents for more.