What Does Spring Do?
Total Page:16
File Type:pdf, Size:1020Kb
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 packagetypically isn't used directly by users, but underpins much Spring functionality. 1 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 containerintroduces 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 constructorarguments), 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). 2 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 thecontainer 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); andConstructor 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 3 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.