Get an overview of Spring Integration's out-of-the-box event-driven messaging architecture, which you can use to coordinate messages, channels, adapters, and gateways. Then practice using ActiveMQ and JMS in a Spring Integration solution, followed by a short introduction to stitching together multiple application workflows for both lightweight and heavyweight payloads.
Spring Integration is an enterprise integration framework that provides out-of-the-box implementation of the patterns in the now-classic Enterprise Integration Patterns book. Building on Spring's Inversion of Control design pattern, Spring Integration abstracts message sources and destinations and uses message passing and message manipulation to integrate various components within the application environment. Applications built with Spring Integration are able to send messages between components, either across a message bus to another server in your environment or even to another class within the same virtual machine.
I'll get you started with Spring Integration in this second of three Open source Java projects installments focusing on Spring projects. I'll start with an overview of the core components of Spring Integration's support for event-driven architecture (EDA), then develop a simple example to familiarize you with how Spring Integration works. I'll conclude by demonstrating a more complex scenario that integrates components across an ActiveMQ message bus via JMS.
Spring Integration's event-driven architecture
Event-driven architecture is one of the most powerful and successful patterns used for enterprise integration, and is the main focus of examples in this article. In an event-driven architecture, a system publishes events as they happen. Components within a given system listen for specific events, or types of events, occurring within that system. When an event of interest occurs, the components are alerted and can respond as necessary.
Event-driven architecture affords a high degree of loose coupling and enhances system scalability because message producers don't need to know anything about their consumers. This makes integrating a new component with an existing or legacy system relatively easy: existing systems publish events and new components are configured to listen for those events. Because all interactions in an event-driven architecture are asynchronous, components can process messages on their own time. If load increases substantially, a component may take longer to process a message, but it will eventually happen.
While an application may slow down, it should never go down.
Spring Integration's support for event-driven architecture rests on three core components:
- Messages are objects sent from one component to another.
- Channels are the means by which messages are sent, they can be synchronous or asynchronous.
- Adapters route the output from one channel to the input of another one.
Figure 1 illustrates the relationship between messages, channels, and adapters in Spring Integration.
Figure 1. Messages, channels, and adapters

Note that when Component 1 sends a message to the specified channel, the adapter routes it to Component 2. Essentially, the adapter says that any message sent to that channel should be directed to Component 2.
Hello, Spring Integration!
No Java technology introduction would be complete without a "Hello World" example. In this case, we'll use Spring Integration to put together a small program that routes a text message from one component to another. This exercise will make the workings of Spring Integration's messages, channels, and adapters clearer. (See the latest Javadoc for Spring Integration for more detailed information about each component.)
First, Listing 1 shows the contents of an applicationContext.xml
file, which is the glue holding together our three application components.
Listing 1. applicationContext.xml
<beans:beans xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<!-- Component scan to find all Spring components -->
<context:component-scan base-package="com.geekcap.springintegrationexample" />
<!-- A Spring Integration channel -->
<channel id="helloWorldChannel" />
<!-- A Spring Integration adapter that routes messages sent to the helloWorldChannel to the bean named "helloServiceImpl"'s hello() method -->
<service-activator input-channel="helloWorldChannel" ref="helloServiceImpl" method="hello" />
</beans:beans>
Note that the <beans>
node defines the schemas and namespaces employed by the XML file. The default context (xmlns
) is defined to be http://www.springframework.org/schema/integration
, which means that we do not need to prefix the channel
or service-activator
nodes. (Later we'll switch back to using the beans
default context, at which point we'll need to give our Spring Integration nodes their own prefix. I just wanted to make the XML easier to read in the beginning.)
Listing 1 defines three components:
- Using component-scan we can annotate our beans in code with annotations like
@Service
or@Component
. When we annotate our beans we'll need to run a component scan so that Spring can find them. Thecomponent-scan
node takes a base package to scan and will scan the classpath for that package and all subpackages. In our case we're going to define and annotate two beans:HelloService
andGreeterService
. - The
HelloService
bean prints "Hello, name" to the standard output. TheGreeterService
bean sends a name toHelloService
. (You'll see these interactions in the code snips below.) - helloWorldChannel is a channel to which our code can send messages.
- service-activator is an adapter that says that all messages sent to
helloWorldChannel
should be forwarded to thehelloServiceImpl
'shello()
method. The default name that Spring chooses for your beans is the name of the class, but note that it starts with a lowercase letter.
Listing 2 shows the contents of the HelloService
interface. An interface is not required -- i.e., we could send messages directly to a bean without involving an interface -- but it's customary when using Spring to define interfaces, giving us the flexibility to change an implementation later. (Using an interface in this case will also make unit testing easier.)
Listing 2. HelloService.java
package com.geekcap.springintegrationexample.service;
public interface HelloService
{
public void hello( String name );
}
The HelloService
interface defines a single method: hello()
, which accepts a String
parameter. Spring is smart enough to look at the method and its parameter signature to perform a conversion of the message to a String
value.
Listing 3 shows the HelloServiceImpl
class that implements the HelloService
interface.
Listing 3. HelloServiceImpl.java
package com.geekcap.springintegrationexample.service;
import org.springframework.stereotype.Service;
@Service
public class HelloServiceImpl implements HelloService
{
@Override
public void hello(String name)
{
System.out.println( "Hello, " + name );
}
}
The HelloServiceImpl
implements the hello()
method by printing "Hello, name" to the standard output. It is annotated with the @Service
annotation, so the component-scan
defined in the applicationContext.xml
file will find it. Notice that the service looks very standard and there's no indication that it will be involved in a Spring Integration action.
Listing 4 shows the contents of GreeterService
, which is the interface that our greeters need to implement.
Listing 4. GreeterService.java
package com.geekcap.springintegrationexample.service;
public interface GreeterService
{
public void greet( String name );
}
Listing 5 shows the implementation of the GreeterService
interface.
Listing 5. GreeterServiceImpl.java
package com.geekcap.springintegrationexample.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.stereotype.Service;
@Service
public class GreeterServiceImpl implements GreeterService
{
@Autowired
private MessageChannel helloWorldChannel;
@Override
public void greet(String name)
{
helloWorldChannel.send( MessageBuilder.withPayload( name ).build() );
}
}
More about the code
The GreeterServiceImpl
class is annotated with the @Service
annotation, so that Spring will identify it as a service. It has auto-wired into it a MessageChannel
named helloWorldChannel
. Because the name of the channel matches the one defined in the applicationContext.xml
file, Spring will just find it for you. If you wanted to override that name, you could add a @Qualifier
annotation to the MessageChannel
to give it the name of the channel bean with which you want to communicate. When the GreeterServiceImpl
's greet()
method is invoked, it creates and sends a message to the helloWorldChannel
.
The MessageChannel
is an interface that defines two variants of the send()
method: one that accepts a timeout and one that does not (which can, depending on the implementation, block indefinitely). The MessageBuilder
class, an implementation of the Builder design pattern, helps you build Message
s. In this case, we passed MessageBuilder
a single String
, but it could be used to specify message headers, expiration dates, priority, correlation IDs, reply and error channels, and more. Once we're finished configuring the MessageBuilder
, invoking the build()
method returns a Message
that can be sent to any channel.
Listing 6 shows the source code for a command-line application that pulls all of our code together.
Listing 6. App.java
package com.geekcap.springintegrationexample.main;
import com.geekcap.springintegrationexample.service.GreeterService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Main entry-point into the test application
*/
public class App
{
public static void main( String[] args )
{
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext( "applicationContext.xml" );
GreeterService greeterService = applicationContext.getBean( "greeterServiceImpl", GreeterService.class );
greeterService.greet( "Spring Integration!" );
}
}
The App
class loads the applicationContext.xml
file from the classpath, which is located in the src/main/resources
path so that it will be automatically embedded in the JAR file by Maven. Next, it retrieves the greeterServiceImpl
bean from the application context. And finally, it invokes the GreeterService
's greet()
method.
A Spring Integration
Figure 2 shows the original Spring Integration diagram from Figure 1, retrofitted for the specifics of this example.
Figure 2. Hello, Spring Integration!

Here's a summary of the integrated application's flow:
- The
App
class invokes theGreeterService
'sgreet()
method, passing it theString
"Spring Integration!" - The
GreeterService
has wired into it aMessageChannel
namedhelloWorldChannel
. It uses aMessageBuilder
to build aMessage
that contains theString
"Spring Integration!", which itsend
s to theMessageChannel
. - The
service-activator
has been configured such that any message sent to thehelloWorldChannel
will be routed to theHelloService
'shello()
method. - The
HelloServiceImpl
class'shello()
method is invoked and "Hello, Spring Integration!" is printed out to the screen.
Listing 7 shows a Maven pom.xml
file that builds this sample application: