A first class Inversion of Control (IoC) Facility for POJOs?
Written by Chris Schalk, Oracle
Corporation
July 2005
Inversion of Control and POJOs
Before server-side Java development really took off, developers were always
tasked with managing all of their classes. This was the norm with non-distributed
Java applications. However as Java has evolved into more of a server-side technology,
Java server "containers" became more and more responsible for running
applications. Another fact was that in order for server-side Java applications
to work properly they need a bit more "plumbing". For example if one
writes a Java servlet, they can't just type "java myservlet" and expect
it to run. Obviously a servlet container is needed to provide the plumbing necessary
for handing Http requests to the servlet for it to execute. As Java containers
matured and became more sophisticated, there has become a growing need to be
able to manage server-side objects in a simple and efficient way. What has transpired
is the rise a certain "description of service", for lack of a better
term, titled "Inversion of Control" (Ioc) where control and management
of useful server-side objects is largely handed over to a container instead
of being left to the application developer.
As containers took on more responsibility of managing server-side objects,
so has the popularity of simple Java objects which are often referred to as
Plain Old Java Objects, or simply "POJOs".
POJOs have come screaming back into popularity mainly because some of the container
based development started to get too cumbersome, as seen with previous versions
of Enterprise JavaBeans, and thus a POJO revolt ensued where Java developers
realizing that pretty much all of their server-side programming needs can be
accomplished with POJOs serving as the simple but universally useful object
to be at the core of server-side applications.
As IoC and POJO's have risen in popularity, there have been many articles written
on various containers/frameworks implementing IoC, they tend to highlight Spring,
HiveMind and PicoContainer. What is surprisingly absent are articles describing
how JavaServer Faces' Managed Bean facility is also an IoC framework which makes
management of JavaBeans or POJOs a snap for JSF developers. The remainder of
this article will describe the key aspects of IoC and how JSF's Managed Beans
Facility is also qualified to be mentioned in the same league as the other IoC
containers/frameworks.
What are some of the key concepts associated with IoC?
IoC is still a relatively new concept and the definition can sometimes be a
little fluid, but in general here are the core facets of IoC.
Control of instantiation (constructor) and setter based injection of
managed objects. - This negates the need for developers to explicitly
instantiate server-side objects and set their respective properties. Customizable
instantiation is another facet of IoC containers in that they can control
when objects are loaded such as upon invocation of application sometimes referred
to as "eager instantiation" or in a lazily loaded fashion.
Dependency Handling- Since objects can be dependent, this a definite
requirement for IoC. Handling cyclical dependencies is also a benefit.
Lifecycle Support - The ability to easily set the duration of lifecycle
of the managed objects.
Configuration Support - A simple way to configure the objects that
will be managed by the IoC container.
Many times IoC is referred to as being supported by certain "containers",
and I use the term "containers" loosely as these are really frameworks
which run on containers, but regardless, the common examples we see being discussed
usually involved the big IoC three: Spring, HiveMind and PicoContainer. What
is still a little surprising is that compared to the general definitions of
IoC/DI I don't see much mentioned about JSF's managed beans facility being mentioned
as an "IoC" container, but I would argue that it really is.
What follows is both a detailed examination of the JSF Managed Bean facility
presented in a way to show how it fully addresses the requirements of an "IoC"
container.
JSF's Managed Bean Facility - A true IoC Container
Control of Instantiation
The first criteria for IoC is being able to manage all aspects of instantiation
of Java Objects. JSF's managed beans facility offers a robust solution for creating
and initializing managed beans. Before JSF when dealing with JavaBeans in pages,
you could use JSP's <jsp:usebean> tag which allowed for the instantiation
of a JavaBean for a specified scope.
JSF has improved on this simple feature of JSP quite a bit. With JSF, you can
now register any Java class with a no-argument constructor as a managed bean
by referencing it in an XML configuration file - faces-config.xml. Let's
say I have a Java Class (or POJO), "User" which has fields for firstName,
lastName, phone and email:
package mb;
public class User
{ String firstName; String lastName; String phone; String email;
public User()
{
}
public void setFirstName(String firstName)
{
this.firstName = firstName;
}
public String getFirstName()
{
return firstName;
}
public void setLastName(String lastName)
{
this.lastName = lastName;
}
public String getLastName()
{
return lastName;
}
public void setPhone(String phone)
{
this.phone = phone;
}
public String getPhone()
{
return phone;
}
public void setEmail(String email)
{
this.email = email;
}
public String getEmail()
{
return email;
}
}
I can register this bean in a faces-config.xml as:
Now I can use the bean named "Userbean" in my JSF application at
any time. This is done by using JSF's Expression Language. For example to bind
a JSF user interface component (UI Component) to one of the properties of my
Userbean, I use the expression #{Userbean.firstName} as a value argument to
a JSF UI Component. Here is an example of binding the firstName property of
the bean to a JSF inputText (UIInput) UI Component.
<h:inputText value="#{Userbean.firstName}"/>
So now upon any form submissions the value entered into the input text field
will be applied to the property of my Java Bean.
Managed Bean Property Initialization
An important feature of JSF's managed bean facility is it's ability to initialize
properties from the configuration file. So for example I could define initial
property values for my Userbean by adding the additional managed properties
to the configuration.
So to set an initial set of values for some of my bean properties I can use
the following.
To see the effect of this, I could build a JSF page which has several inputText
fields bound to the properties and run the page. At first run, I will see the
initial values set from my configuration:
Here is the JSF source of a form with input fields bound to my bean properties:
Notice the Email field does not have an initial value.. Recall we didn't
set this initially in our faces-config.xml.
Another feature regarding managed properties worth pointing out is in additional
to managing simple properties, as shown above, JSF's managed bean facility can
also handle both Lists and Maps as managed properties. For example I could add
a "tasks" List to my Userbean as such:
List tasks;
I would also add the necessary setters and getters as well as make sure to
import java.util.List. I could then initialize the List with values as
a managed property.
<managed-property>
<property-name>tasks</property-name>
<list-entries>
<value>Finish JSF IoC POJO Article</value>
<value>Finish .Net J2EE Article</value>
<value>Revise RSS How To</value>
</list-entries>
</managed-property>
And to see the initialized values at runtime I could add a dataTable component
to my JSP page..
And to display the initialized Blog Map properties I would add the following
code to my JSF page. Notice the expression used to display the Map value.:
<p>A few recent blog entries:</p>
<p>Key: ODD</p>
<p>Location: <h:outputText value="#{Userbean.recentBlogEntries['ODD']}"/></p>
<p>Key: ASPvsJSF</p>
<p>Location: <h:outputText value="#{Userbean.recentBlogEntries['ASPvsJSF']}"/></p>
Note: I could also just dump the entire contents of the Map by just using the
expression: #{Userbean.recentBlogEntries}in an outputText component.
Here is what the runtime looks like with some of the blogEntriesMap values
displayed:
In addition to using Maps and Lists and as managed properties of existing beans,
it is also possible to declare new Lists and Maps as managed beans themselves.
For example I could create a new "Events" List entirely in my Faces-config.
Here is a new, independent List which is defined entirely in the Faces-config
and is not a property of my Userbean:
Notice that the managed-bean-class is the concrete "ArrayList" and
not the "List" interface.
As you can see, the JSF managed beans facility provides a very high degree
of control of instantiation and initialization. Next we'll review how JSF handles
dependencies between managed beans.
Dependency Handling
The ability to handle depencies in between Java objects is also a requirement
for IoC. Although JSF does not handle cyclical dependencies, managed beans can
refer to each other using expression language. For example I could create a
new List of tasks called "newtasks" purely in my faces-config.xml,
I could then refer to values in the new list as expressions while setting properties
of my existing "tasks" list.
Here's how this would be done:
First I can declare a "newtasks" List in my faces-config.xml.
I can then refer to it in other locations such as in the "tasks"
property of the original "Userbean" managed bean. Here is how I can
add the new tasks to the other set of List values. Notice that I can refer to
the individual List items using an array style element referencing with brackets"[
]".
At runtime, I will see the concatenated list of tasks.
You may be inclined to think that I have to declare the "newtasks"
List before the value are assigned to the existing "tasks" properties,
but no worries, the JSF managed beans facility can make sure the dependent bean
is available for the calling bean regardless of what order it was defined in
the faces-config.xml file. Another important point about managed beans referencing
each other is that a managed bean can only refer to other beans provide their
scope is equal or has a longer lifespan than the calling object. This brings
us to our next requirement for IoC.
LifeCycle Support
A requirement for IoC is handling lifecycle support. In fact JSF's managed
bean facility handles lifecycle support extremely well in that it is specifically
geared toward defining lifecycle scope for HTTP requests as opposed to a more
generic form which is common in other IoC containers. Similar to the scope parameters
as specified by the JSP UseBean tag, the JSF scope sets the lifecycle with the
following:
none: A managed bean with this scope is not stored anywhere, it is
created "on demand" whenever it is needed.
request: A bean with a request scope will have it's value stored
only for the duration of a single request. Upon subsequent requests on the
same object, a new clean version will be instantiated.
session: A bean with session scope will be stored onto the session
meaning that the bean's properties will stay alive during multiple requests.
A shopping cart bean would be set to a session scope. Objects stored onto
the session are naturally threadsafe and will expire when the session times
out if not cleared by the application explicitly. You wouldn't want anyone
else looking in your shopping cart!
application: Objects created with application scope will exist during
the entire lifetime of the container. An example of when usage makes sense
could be for a JDBC datasource object.
You may notice that the "page" scope is not available in JSF. This
is by design as managed beans are not tied specifically to pages whereas in
traditional JSP, you would define the scope of a bean using a UseBean tag, and
in this sense it makes sense to offer a page scope since the bean originated
from the page.
As you can define the lifecycle of the beans using the scope setting, you must
take care to only refer to other beans which exist either the same scope or
larger scope. You can not refer to another managed bean with a smaller scope.
Managed beans registered with scope:
Can only refer to other managed beans with these scopes:
none
none
request
none, request, session, application
session
none, session, application
application
none, application
Configuration Support
With the previous examples, you've seen how JSF's Managed Bean facility uses
a simple XML file format to register and initialize properties of managed beans.
This works well with a framework geared towards Web applications where the focus
is on the user interface and not necessarily back end coding. This aspect minizes
Java coding to only be necessary when you wish to invoke custom application
logic, otherwise the primary job of JSF which is to present a Web UI to a client
and have it automatically synchronized with a set of managed objects which are
bound the components presented in the page can require literally no Java coding.
Summary
With the rise in popularity of simple POJOs and a container-centric view on
how to manage these objects. The general set of requirements associated with
the term Inversion of Control has matured and it is very clear now that JavaServer
Faces' Managed Bean facility rises to the challenge of being a first class IoC
container. While it may not completely offer all of the fine grained control
(such as cyclical dependencies) of some of the other IoC containers, it is still
perfectly suited to be a IoC container for a Web user-interface framework oriented
towards managing objects to be used within the context of Web requests. Additionally
we mustn't forget that it is possible to use both JSF managed bean facility
in conjunction with other more strictly server-oriented frameworks as well,
including the big IoC three.