Monday, December 13, 2010

What does Spring do?

Spring provides a lot of functionality, so I'll quickly review each major area
in turn.

Mission statement
Firstly, let's be clear on Spring's scope. Although Spring covers a lot of
ground, we have a clear vision as to what it should and shouldn't address.

Spring's main aim is to make J2EE easier to use and promote good programming
practice. It does this by enabling a POJO-based programming model that is
applicable in a wide range of environments.

Spring does not reinvent the wheel. Thus you'll find no logging
packages in Spring, no connection pools, no distributed transaction
coordinator. All these things are provided by open source projects (such as
Commons Logging, which we use for all our log output, or Commons DBCP), or by
your application server. For the same reason, we don't provide an O/R mapping
layer. There are good solutions to this problem such as TopLink, Hibernate and
JDO.

Spring does aim to make existing technologies easier to use. For example,
although we are not in the business of low-level transaction coordination, we
do provide an abstraction layer over JTA or any other transaction strategy.

Spring doesn't directly compete with other open source projects unless we feel
we can provide something new. For example, like many developers, we have never
been happy with Struts, and felt that there was room for improvement in MVC
web frameworks. (With Spring MVC adoption growing rapidly, it seems that many
agree with us.) In some areas, such as its lightweight IoC container and AOP
framework, Spring does have direct competition, but Spring was a pioneer in
those areas.

Spring benefits from internal consistency. All the developers are singing from
the same hymn sheet, the fundamental ideas remaining faithful to those of
Expert One-on-One J2EE Design and Development. And we've been able to
use some central concepts, such as Inversion of Control, across multiple
areas.

Spring is portable between application servers. Of course ensuring portability
is always a challenge, but we avoid anything platform-specific or non-standard
in the developer's view, and support users on WebLogic, Tomcat, Resin, JBoss,
Jetty, Geronimo, WebSphere and other application servers. Spring's
non-invasive, POJO, approach enables us to take advantage of
environment-specific features without sacrificing portability, as in the case
of enhanced WebLogic transaction management functionality in Spring 1.2 that
uses BEA proprietary APIs under the covers.

Inversion of control container
The core of Spring is the org.springframework.beans package, designed for working with JavaBeans.
This package typically isn't used directly by users, but underpins much Spring
functionality.

The next higher layer of abstraction is the bean factory. A Spring bean
factory is a generic factory that enables objects to be retrieved by name, and
which can manage relationships between objects.

Bean factories support two modes of object:

  • Singleton: in this case, there's one shared instance of the object
    with a particular name, which will be retrieved on lookup. This is the
    default, and most often used, mode. It's ideal for stateless service
    objects.
  • Prototype or non-singleton: in this case, each retrieval will result
    in the creation of an independent object. For example, this could be used to
    allow each caller to have its own distinct object reference.
Because the Spring container manages relationships between objects, it can add
value where necessary through services such as transparent pooling for managed
POJOs, and support for hot swapping, where the container introduces a level of
indirection that allows the target of a reference to be swapped at runtime
without affecting callers and without loss of thread safety. One of the
beauties of Dependency Injection (discussed shortly) is that all this is
possible transparently, with no API involved.

As org.springframework.beans.factory.BeanFactory is a simple interface, it can be implemented in different ways. The BeanDefinitionReader
interface separates the metadata format from BeanFactory implementations themselves,
so the generic BeanFactory implementations Spring provides can be used with different
types of metadata. You could easily implement your own BeanFactory or
BeanDefinitionReader, although few users find a need to. The most commonly
used BeanFactory definitions are:

  • XmlBeanFactory. This parses a simple, intuitive XML structure defining the classes and
    properties of named objects. We provide a DTD to make authoring easier.
  • DefaultListableBeanFactory: This provides the ability to parse bean definitions in properties
    files, and create BeanFactories programmatically.
Each bean definition can be a POJO (defined by class name and JavaBean
initialisation properties or constructor arguments), or a FactoryBean. The FactoryBean
interface adds a level of indirection. Typically this is used to create
proxied objects using AOP or other approaches: for example, proxies that add
declarative transaction management. This is conceptually similar to EJB
interception, but works out much simpler in practice, and is more powerful.

BeanFactories can optionally participate in a hierarchy, "inheriting"
definitions from their ancestors. This enables the sharing of common
configuration across a whole application, while individual resources such as
controller servlets also have their own independent set of objects.

This motivation for the use of JavaBeans is described in Chapter 4 of
Expert One-on-One J2EE Design and Development, which is available on
the ServerSide as a free PDF
(/articles/article.tss?l=RodJohnsonInterview).

Through its bean factory concept, Spring is an Inversion of Control
container. (I don't much like the term container, as it conjures up
visions of heavyweight containers such as EJB containers. A Spring BeanFactory
is a container that can be created in a single line of code, and requires no
special deployment steps.) Spring is most closely identified with a flavor of
Inversion of Control known as Dependency Injection--a name coined by
Martin Fowler, Rod Johnson and the PicoContainer team in late 2003.

The concept behind Inversion of Control is often expressed in the Hollywood
Principle
: "Don't call me, I'll call you." IoC moves the responsibility
for making things happen into the framework, and away from application code.
Whereas your code calls a traditional class library, an IoC framework calls
your code. Lifecycle callbacks in many APIs, such as the setSessionContext() method for
session EJBs, demonstrate this approach.

Dependency Injection is a form of IoC that removes explicit dependence on
container APIs; ordinary Java methods are used to inject dependencies
such as collaborating objects or configuration values into application object
instances. Where configuration is concerned this means that while in
traditional container architectures such as EJB, a component might call the
container to say "where's object X, which I need to do my work", with
Dependency Injection the container figures out that the component needs
an X object, and provides it to it at runtime. The container does this
figuring out based on method signatures (usually JavaBean properties or
constructors) and, possibly, configuration data such as XML.

The two major flavors of Dependency Injection are Setter Injection
(injection via JavaBean setters); and Constructor Injection (injection
via constructor arguments). Spring provides sophisticated support for both,
and even allows you to mix the two when configuring the one object.

As well as supporting all forms of Dependency Injection, Spring also provides
a range of callback events, and an API for traditional lookup where necessary.
However, we recommend a pure Dependency Injection approach in general.

Dependency Injection has several important benefits. For example:

  • Because components don't need to look up collaborators at runtime, they're
    much simpler to write and maintain. In Spring's version of IoC, components
    express their dependency on other components via exposing JavaBean setter
    methods or through constructor arguments. The EJB equivalent would be a JNDI
    lookup, which requires the developer to write code that makes environmental
    assumptions.
  • For the same reasons, application code is much easier to test. For example,
    JavaBean properties are simple, core Java and easy to test: simply write a
    self-contained JUnit test method that creates the object and sets the
    relevant properties.
  • A good IoC implementation preserves strong typing. If you need to use a
    generic factory to look up collaborators, you have to cast the results to
    the desired type. This isn't a major problem, but it is inelegant. With IoC
    you express strongly typed dependencies in your code and the framework is
    responsible for type casts. This means that type mismatches will be raised
    as errors when the framework configures the application; you don't have to
    worry about class cast exceptions in your code.
  • Dependencies are explicit. For example, if an application class tries to
    load a properties file or connect to a database on instantiation, the
    environmental assumptions may not be obvious without reading the code
    (complicating testing and reducing deployment flexibility). With a
    Dependency Injection approach, dependencies are explicit, and evident in
    constructor or JavaBean properties.
  • Most business objects don't depend on IoC container APIs. This makes it easy
    to use legacy code, and easy to use objects either inside or outside the IoC
    container. For example, Spring users often configure the Jakarta Commons
    DBCP DataSource as a Spring bean: there's no need to write any custom code
    to do this. We say that an IoC container isn't invasive: using it
    won't invade your code with dependency on its APIs. Almost any POJO can
    become a component in a Spring bean factory. Existing JavaBeans or objects
    with multi-argument constructors work particularly well, but Spring also
    provides unique support for instantiating objects from static factory
    methods or even methods on other objects managed by the IoC container.
This last point deserves emphasis. Dependency Injection is unlike traditional
container architectures, such as EJB, in this minimization of dependency of
application code on container. This means that your business objects can
potentially be run in different Dependency Injection frameworks - or outside
any framework - without code changes.

In my experience and that of Spring users, it's hard to overemphasize the
benefits that IoC--and, especially, Dependency Injection--brings to
application code.

Dependency Injection is not a new concept, although it's only recently made
prime time in the J2EE community. There are alternative DI containers:
notably, PicoContainer and HiveMind. PicoContainer is particularly lightweight
and emphasizes the expression of dependencies through constructors rather than
JavaBean properties. It does not use metadata outside Java code, which limits
its functionality in comparison with Spring. HiveMind is conceptually more
similar to Spring (also aiming at more than just IoC), although it lacks the
comprehensive scope of the Spring project or the same scale of user community.
EJB 3.0 will provide a basic DI capability as well.

Spring BeanFactories are very lightweight. Users have successfully used them
inside applets, as well as standalone Swing applications. (They also work fine
within an EJB container.) There are no special deployment steps and no
detectable startup time associated with the container itself (although certain
objects configured by the container may of course take time to initialize).
This ability to instantiate a container almost instantly in any tier of an
application can be very valuable.

The Spring BeanFactory concept is used throughout Spring, and is a key reason
that Spring is so internally consistent. Spring is also unique among IoC
containers in that it uses IoC as a basic concept throughout a full-featured
framework.

Most importantly for application developers, one or more BeanFactories provide
a well-defined layer of business objects. This is analogous to, but much
simpler (yet more powerful), than a layer of local session beans. Unlike EJBs,
the objects in this layer can be interrelated, and their relationships managed
by the owning factory. Having a well-defined layer of business objects is very
important to a successful architecture.

A Spring ApplicationContext is a subinterface of BeanFactory, which provides
support for:

  • Message lookup, supporting internationalization
  • An eventing mechanism, allowing application objects to publish and
    optionally register to be notified of events
  • Automatic recognition of special application-specific or generic bean
    definitions that customize container behavior
  • Portable file and resource access
XmlBeanFactory example
Spring users normally configure their applications in XML "bean definition"
files. The root of a Spring XML bean definition document is a <beans> element. The
<beans> element contains one or more <bean> definitions. We normally specify the
class and properties of each bean definition. We must also specify the id,
which will be the name that we'll use this bean with in our code.

Let's look at a simple example, which configures three application objects
with relationships commonly seen in J2EE applications:

  • A J2EE DataSource
  • A DAO that uses the DataSource
  • A business object that uses the DAO in the course of its work
In the following example, we use a BasicDataSource from the Jakarta Commons DBCP project.
(ComboPooledDataSource from the C3PO project is also an excellent option.) BasicDataSource, like many other
existing classes, can easily be used in a Spring bean factory, as it offers
JavaBean-style configuration. The close method that needs to be called on
shutdown can be registered via Spring's "destroy-method" attribute, to avoid
the need for BasicDataSource to implement any Spring interface.

<beans>

  <bean id="myDataSource"
 class="org.apache.commons.dbcp.BasicDataSource"
 destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/mydb" />
    <property name="username" value="someone" />
  </bean>
All the properties of BasicDataSource we're interested in are Strings, so we specify their
values with the "value" attribute. (This shortcut was introduced in Spring
1.2. It's a convenient alternative to the <value> subelement, which is usable even
for values that are problematic in XML attributes.) Spring uses the standard
JavaBean PropertyEditor mechanism to convert String representations to other
types if necessary.

Now we define the DAO, which has a bean reference to the DataSource.
Relationships between beans are specified using the "ref" attribute or <ref>
element:

<bean id="exampleDataAccessObject"
  class="example.ExampleDataAccessObject">
    <property name="dataSource" ref="myDataSource" />
  </bean>
The business object has a reference to the DAO, and an int property
(exampleParam). In this case, I've used the subelement syntax familiar to
those who've used Spring prior to 1.2:

<bean id="exampleBusinessObject"
  class="example.ExampleBusinessObject">
    <property name="dataAccessObject"><ref bean="exampleDataAccessObject"/></property>
    <property name="exampleParam"><value>10</value></property>
  </bean>

</beans>
Relationships between objects are normally set explicitly in configuration, as
in this example. We consider this to be a Good Thing in most cases. However,
Spring also provides what we call "autowire" support, where it figures out the
dependencies between beans. The limitation with this - as with PicoContainer -
is that if there are multiple beans of a particular type it's impossible to
work out which instance a dependency of that type should be resolved to. On
the positive side, unsatisfied dependencies can be caught when the factory is
initialized. (Spring also offers an optional dependency check for explicit
configuration, which can achieve this goal.)

We could use the autowire feature as follows in the above example, if we
didn't want to code these relationships explicitly:

<bean id="exampleBusinessObject"
 class="example.ExampleBusinessObject"
 autowire="byType">

    <property name="exampleParam" value="10" />
 </bean>
With this usage, Spring will work out that the dataSource property of
exampleBusinessObject should be set to the implementation of DataSource it
finds in the present BeanFactory. It's an error if there is none, or more than
one, bean of the required type in the present BeanFactory. We still need to
set the exampleParam property, as it's not a reference.

Autowire support has the advantage of reducing the volume of configuration. It
also means that the container can learn about application structure using
reflection, so if you add an additional constructor argument of JavaBean
property, it may be successfully populated without any need to change
configuration. The tradeoffs around autowiring need to be evaluated carefully.

Externalizing relationships from Java code has an enormous benefit over hard
coding it, as it's possible to change the XML file without changing a line of
Java code. For example, we could simply change the myDataSource bean definition to
refer to a different bean class to use an alternative connection pool, or a
test data source. We could use Spring's JNDI location FactoryBean to get a
datasource from an application server in a single alternative XML stanza, as
follows. There would be no impact on Java code or any other bean definitions.

<bean id="myDataSource"
 class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="jdbc/myDataSource" />
  </bean>
Now let's look at the Java code for the example business object. Note that
there are no Spring dependencies in the code listing below. Unlike an EJB
container, a Spring BeanFactory is not invasive: you don't normally need to
code awareness of it into application objects.

public class ExampleBusinessObject implements MyBusinessObject {

 private ExampleDataAccessObject dao;
 private int exampleParam;

 public void setDataAccessObject(ExampleDataAccessObject dao) {
  this.dao = dao;
 }

 public void setExampleParam(int exampleParam) {
  this.exampleParam = exampleParam;
 }

 public void myBusinessMethod() {
  // do stuff using dao
 }
}
Note the property setters, which correspond to the XML references in the bean
definition document. These are invoked by Spring before the object is used.

Such application beans do not need to depend on Spring: They don't need to
implement any Spring interfaces or extend Spring classes: they just need to
observe JavaBeans naming convention. Reusing one outside of a Spring
application context is easy, for example in a test environment. Just
instantiate it with its default constructor, and set its properties manually,
via setDataSource() and setExampleParam() calls. So long as you have a no-args constructor, you're free
to define other constructors taking multiple properties if you want to support
programmatic construction in a single line of code.

Note that the JavaBean properties are not declared on the business interface
callers will work with. They're an implementation detail. We can easily "plug
in" different implementing classes that have different bean properties without
affecting connected objects or calling code.

Of course Spring XML bean factories have many more capabilities than described
here, but this should give you a feel for the basic approach. As well as
simple properties, and properties for which you have a JavaBeans
PropertyEditor, Spring can handle lists, maps and java.util.Properties. Other advanced
container capabilities include:


  • Inner beans, in which a property element contains an anonymous bean
    definition not visible at top-level scope

  • Post processors: special bean definitions that customize container
    behavior

  • Method Injection, a form of IoC in which the container implements an
    abstract method or overrides a concrete method to inject a dependency. This
    is a more rarely used form of Dependency Injection than Setter or
    Constructor Injection. However, it can be useful to avoid an explicit
    container dependency when looking up a new object instance for each
    invocation, or to allow configuration to vary over time--for example, with
    the method implementation being backed by a SQL query in one environment and
    a fil system read in another.
Bean factories and application contexts are often associated with a scope
defined by the J2EE server or web container, such as:


  • The Servlet context. In the Spring MVC framework, an application context is
    defined for each web application containing common objects. Spring provides
    the ability to instantiate such a context through a listener or servlet
    without dependence on the Spring MVC framework, so it can also be used in
    Struts, WebWork or other web frameworks.

  • A Servlet: Each controller servlet in the Spring MVC framework has its own
    application context, derived from the root (application-wide) application
    context. It's also easy to accomplish this with Struts or another MVC
    framework.

  • EJB: Spring provides convenience superclasses for EJB that simplify EJB
    authoring and provide a BeanFactory loaded from an XML document in the EJB
    Jar file.
These hooks provided by the J2EE specification generally avoid the need to use
a Singleton to bootstrap a bean factory.

However, it's trivial to instantiate a BeanFactory programmatically if we
wish. For example, we could create the bean factory and get a reference to the
business object defined above in the following three lines of code:

XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("myFile.xml", getClass()));
MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject");
This code will work outside an application server: it doesn't even depend on
J2EE, as the Spring IoC container is pure Java. The Spring Rich project
(a framework for simplifying the development of Swing applications using
Spring) demonstrates how Spring can be used outside a J2EE environment, as do
Spring's integration testing features, discussed later in this article.
Dependency Injection and the related functionality is too general and valuable
to be confined to a J2EE, or server-side, environment.



No comments:

Post a Comment