A hands-on introduction to Spring's flexible component model Catch up to Spring, with this hands-on introduction to Spring’s custom namespaces, which make it easier than ever to build and deploy custom components in your Spring 2.x environment.Spring is an open source framework that has been embraced by many Java developers as a means to simplify server-side Java application development. The Spring framework has evolved to be a comprehensive platform for building Java applications and services across the entire spectrum of enterprise needs. Much of Spring’s popularity is due to its ability to hide infrastructure details from developers, narrowing the focus to just the task at hand.Enterprise developers use Spring technologies and concepts like inversion of control (IOC), aspect-oriented programming (AOP), and dependency injection to implement business and application logic components as simple Java beans. Relationships between beans are defined using configuration settings and runtime introspection. Spring 2.0 extended the framework’s approach to simplicity by providing more sophisticated introspection techniques and annotation support. Spring 2.5, released in November of last year, introduced the ability to define and demarcate components using XSD data.In this article, I introduce the new features in Spring 2.5 by showing you how to use Spring’s custom namespaces and flexible component model to create and deploy a custom component in a Spring environment. I also introduce several of Spring’s pre-defined custom namespaces and show you how to use Java 5 annotations to enable your Spring components for easy detection and deployment in any Spring environment.Custom namespaces and Spring’s flexible component modelSpring 2.0 introduced the ability to define custom XML namespaces. Similar to Java packages, custom namespaces make it possible to define components without concern of colliding with other named components. Spring ships with a few of its own namespaces defined, such as jee, aop, util, and others, which are discussed later in the article. In Spring 2.0, custom namespaces are handled using namespace-handling classes, supplied by third-party vendors, that generate metadata to be consumed by the Spring framework. This metadata is processed by parser classes that are also supplied by a component developer.Spring 2.5 introduced a flexible component model that allows the use of annotations to configure components. Developers using the Spring 2.5 component model can enable any Spring-managed object as a JMX MBean. Spring 2.5 also supports the OSGi Service Platform and interacts with the Service Component Architecture (SCA) standard.Together, new features introduced in Spring 2.0 and Spring 2.5 have greatly simplified Spring development. In the next sections I’ll walk through a Spring development scenario consisting of the following steps: Create an XSD file that defines a component.Write the Java code encapsulating the logic for the component.Create a namespace handler that maps the schema to the beans.Create a bean definition parser to handle the parsing of bean configuration data.Create two deployment-descriptor property files.Package it all up as a .jar file.At the end of this process we’ll have a basic component that can be distributed and deployed to a Spring 2.x environment as a .jar file and used within a Spring application context.Define a component using custom namespacesThe first step is to create an XSD file that defines the namespace and attributes for your component. The component to be defined will occupy a new namespace, http://www.jeffhanson.com/schema/service/foobar, and have one property: message.The new component is defined in the XSD file shown in Listing 1. Listing 1. The component definition in an XSD file<xsd:schema xmlns="http://www.jeffhanson.com/schema/service/foobar" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.jeffhanson.com/schema/service/foobar" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:element name="component"> <xsd:complexType> <xsd:attribute name="id" type="xsd:ID" use="required"/> <xsd:attribute name="message" type="xsd:string"/> </xsd:complexType> </xsd:element> </xsd:schema> The definition in Listing 1 specifies two attributes for an element named component. Once packaged and deployed to the Spring classpath, this definition will be parsed and consumed by the Spring framework.Encapsulate component logic in a Spring beanOnce you have defined the component in your XSD file the next step is to write the logic for the component, encapsulated in a simple Java bean. The Spring bean in Listing 2 exposes one property, message, along with a sayHello method that prints the message property to System.out.Listing 2. The component logic encapsulated in a Java beanpackage com.jeffhanson.spring.beans; public class Foobar implements IHelloWorld { private String message = "Hello World!"; public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } public void sayHello() { System.out.println(message); } } The value of the message property will be specified by the Spring deployment container, using Spring’s application context configuration file. Implement a custom namespace handlerFor Spring to be able to consume the schema definition of the component, you must provide a NamespaceHandler to the Spring container. The handler, an implementation of the org.springframework.beans.factory.xml.NamespaceHandler interface, is responsible for parsing the elements of a component namespace.The NamespaceHandler interface defines three methods:init() is invoked by the Spring container after construction of the component but before any custom elements are parsed.parse() is invoked by the Spring container to parse a given Element and register any resulting BeanDefinitions with Spring’s bean-definition registry for a given parser context.decorate() is invoked by the Spring container to parse a specified node and decorate/augment the supplied BeanDefinitionHolder object, returning the decorated definition.Spring provides a number of convenience classes that handle much of the default processing for a simple component definition. One of these, the NamespaceHandlerSupport class, will be used by the simple component namespace handler shown in Listing 3. Listing 3. A spring custom namespace handlerpackage com.jeffhanson.spring.beans; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.w3c.dom.Element; public class FoobarNamespaceHandler extends NamespaceHandlerSupport { public FoobarNamespaceHandler() { registerBeanDefinitionParser("component", new FoobarBeanDefinitionParser()); } public void init() { } private static class FoobarBeanDefinitionParser extends AbstractSimpleBeanDefinitionParser { protected Class getBeanClass(Element element) { return Foobar.class; } } } Notice that the namespace handler implementation in Listing 3 encapsulates an inner class that is presented to the Spring container as the parser for the component element.Implement a custom bean definition parserNext, you must provide a bean definition parser to the Spring container to handle the parsing of bean configuration data for each top-level element defined in the component’s XSD file.Again, Spring provides a number of convenience classes that handle much of the default processing for a simple component definition. The AbstractSimpleBeanDefinitionParser is a convenience class to use when there is a one-to-one mapping between the component class properties and attributes of the element to be parsed. Given that this is true of our component I can use the simple bean definition parser as shown in Listing 4. Listing 4. AbstractSimpleBeanDefinitionParser handles a one-to-one mappingprivate static class FoobarBeanDefinitionParser extends org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser { protected Class getBeanClass(Element element) { return Foobar.class; } } With a simple component such as the one defined in this article, the only requirement for a subclass of AbstractSimpleBeanDefinitionParser is to return the class that defines the actual implementation of the component; in this case, the Foobar class.Define two deployment descriptor filesWith the component defined and implemented and the namespace handler and parser is in place, you’re ready to give the Spring framework the information it needs to consume the component. This is accomplished by registering the namespace handler and XSD file with Spring using two specialized properties files:spring.handlers: A key-value-pair properties file containing a mapping of XML Schema URIs to namespace handler classes. For the Foobar component this is defined as: http://www.jeffhanson.com/schema/service/foobar=com.jeffhanson.spring.beans. FoobarNamespaceHandler Notice that the definition maps the namespace with the namespace-handler class.spring.schemas: A key-value-pair properties file containing a mapping of XML schema locations to XSD file. This allows Spring to find the definition on the classpath. For the Foobar component, this file contains: http://www.jeffhanson.com/schema/service/foobar/foobar.xsd=com/jeffhanson/ spring/beans/foobar.xsd The two files are to be located in the META-INF directory of the .jar file that will contain the component. With the bean logic, namespace handler, bean definition parser, and deployment-descriptor properties files in place, you are at the last step in developing a Spring component: compiling and packaging the component for distribution.Packaging a Spring component in a .jar fileTo package the component, create a .jar file containing the XSD file, compiled Java bean classes and ancillary classes, and deployment descriptor files.The directory structure for the component .jar file will look as shown in Figure 1 (click for a larger image). Figure 1. Directory structure for the component .jar fileNow the .jar file for the component can be placed in the Spring classpath and the component made available for use by a Spring application context.Using a custom component in a Spring contextIn order to make use of components defined by a custom namespace, you have to alter the Spring context configuration file slightly. Instead of referencing the Spring beans DTD in the context file like this<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd"> <beans> <!-- <bean/> definitions here --> </beans> you’ll apply the XSD for your custom namespace. You apply the XSD file to the Spring context using standard XML namespace and schema-definition preamble, as shown in Listing 5. Listing 5. Spring configuration file with custom namespace definition<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:fbar="http://www.jeffhanson.com/schema/service/foobar" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.jeffhanson.com/schema/service/foobar http://www.jeffhanson.com/schema/service/foobar/foobar.xsd"> <bean id="testbean" class="com.jeffhanson.spring.app.TestBean"> <property name="messenger"> <fbar:component id="foobarbean" message="Ciao baby!" /> </property> </bean> </beans> Now your component will be detected by the Spring container and can be used by any application or service in a Spring 2.x environment. Listing 6 illustrates the use of the standard Spring XmlBeanFactory to load and use the custom component.Listing 6. Spring XmlBeanFactory loads the custom componentpublic static void main(String[] args) throws Exception { BeanFactory factory = new XmlBeanFactory(new FileSystemResource("testapp.xml")); com.jeffhanson.spring.app.TestBean bean = (com.jeffhanson.spring.app.TestBean)factory.getBean("testbean"); com.jeffhanson.spring.beans.Foobar messenger = (com.jeffhanson.spring.beans.Foobar)bean.getMessenger(); System.out.println("Messenger says..."); messenger.sayHello(); } Spring’s pre-defined custom namespacesSpring ships with a number of custom namespaces already defined with a palette of components defined in each. These can be consumed in the same manner as above. I’ll introduce some of these components in the next sections.Spring’s util schemaSpring provides a set of custom components that deal with tasks that are encountered in many common Java and Java EE programming situations. The util schema defines components for handling configuration duties such as configuring collections, referencing constants, and so on.The preamble entries for the util namespace are specified as shown in bold in Listing 7.Listing 7. Excerpt of the util namespace<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <b>xmlns:util="http://www.springframework.org/schema/util"</b> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd <b>http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.0.xsd"></b> <!-- <bean/> definitions here --> </beans> See the Resources section for more information about the util schema. Spring’s jee schemaThe jee schema defines components for handling Java EE-related configuration tasks, such as defining EJB references and performing JNDI lookups.The preamble entries for the jee namespace are specified as shown in bold in the following snippet:Listing 8. Excerpt of the jee namespace<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <b>xmlns:jee="http://www.springframework.org/schema/jee"</b> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd <b>http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"></b> <!-- <bean/> definitions here --> </beans> See the Resources section for more information about the jee schema.Spring’s lang schemaThe lang schema defines components for handling tasks related to exposing objects written in dynamic languages such as Groovy and JRuby. The preamble entries for the lang namespace are specified as shown in bold in the following snippet:Listing 9. Excerpt of the lang namespace<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <b>xmlns:lang="http://www.springframework.org/schema/lang"</b> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd <b>http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.0.xsd"></b> <!-- <bean/> definitions here --> </beans> See the Resources section for more information about the lang schema.Spring’s aop schemaThe aop schema defines components for handling tasks related to AOP configuration in Spring, including Spring’s proxy-based framework, and integrating Spring with AspectJ.The preamble entries for the aop namespace are specified as shown in bold in the following snippet:Listing 10. Excerpt of the aop namespace<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <b>xmlns:aop="http://www.springframework.org/schema/aop"</b> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd <b>http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"></b> <!-- <bean/> definitions here --> </beans> See the Resources section for more information about the aop schema.Spring’s tx schemaThe tx schema defines components that provide support for transactions in the Spring framework.The preamble entries for the tx namespace are specified as shown in bold in the following snippet:Listing 11. Excerpt of the tx namespace<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <b>xmlns:tx="http://www.springframework.org/schema/tx"</b> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd <b>http://www.springframework.org/schema/tx http://www.springframework.org/schema/util/spring-tx-2.0.xsd"></b> <!-- <bean/> definitions here --> </beans> See the Resources section for more information about the tx schema.Classpath scanning for componentsSpring 2.5 provides support for scanning the classpath in order to identify components. You can facilitate scanning by applying the proprietary stereotype annotation @Component to a given class.The @Component annotation specifies that the annotated class is a component, as Listing 12 shows.Listing 12. Enable component scanningpackage com.jeffhanson.spring.beans; @Component public class Foobar implements IHelloWorld { private String message = "Hello World!"; public void setMessage(String message) { this.message = message; } public String getMessage() { return message; } public void sayHello() { System.out.println(message); } } When Spring discovers components using classpath scanning, the bean definitions are generated for the components, which are automatically registered with Spring. Note the following configuration file:Listing 13. Declarations to enable component scanning with Spring<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.jeffhanson.spring.beans" /> <!-- <bean/> definitions here --> </beans> The <context:component-scan annotation declaration in Listing 12 enables autodetection of any annotated classes in the package com.jeffhanson.spring.beans.Classpath scanning simplifies the work needed to declare your classes as components in the Spring framework. This mechanism is most effective when you do not anticipate frequent changes to the relationships between beans and/or components.In conclusionThe Spring framework is an open source staple for many Java server-side developers, and one of the foremost platforms for building Java applications and services for the enterprise. Spring’s popularity is largely owed to the fact that it hides a great deal of infrastructure detail, allowing developers to focus primarily on immediate tasks. Spring’s simplicity is facilitated by its pervasive use of inversion of control, aspect-oriented programming, and dependency injection for simple Java beans.The Spring 2.0 and Spring 2.5 releases have further promoted the simplicity of Spring. In this article you have walked through a Spring component development scenario using some of the new features found in Spring 2.0 and Spring 2.5, including custom namespaces and support for XSD data and Java 5 annotations. You’ve also seen several of the new built-in schemas found in Spring 2.5, for custom components that handle common programming tasks such as AOP integration and transaction handling.Jeff Hanson has more than 20 years experience as a software engineer, including working as senior engineer for the Windows OpenDoc project and as chief architect for the Zareus SOA platform. The author of numerous articles and books, he is the chief architect for Max Software Inc., where he leads design and implementation teams building desktop and server applications for the content-control industry using C++, PHP, and Java EE. Open SourceSoftware DevelopmentWeb DevelopmentJava