9. Using Spring

The AxonFramework has many integration points with the Spring Framework. All major building blocks in Axon are Spring configurable. Furthermore, there are some Bean Post Processors that scan the application context for building blocks and automatically wires them.

In addition, the Axon Framework makes use of Spring's Extensible Schema-based configuration feature to make Axon application configuration even easier. Axon Framework has a Spring context configuration namespace of its own that allows you to create common configurations using Spring's XML configuration syntax, but in a more functionally expressive way than by wiring together explicit bean declarations.

9.1. Adding support for the Java Platform Common Annotations

Axon uses JSR 250 annotations (@PostConstruct and @PreDestroy) to annotate lifecycle methods of some of the building blocks. Spring doesn't always automatically evaluate these annotations. To force Spring to do so, add the <context:annotation-config/> tag to your application context, as shown in the example below:

<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:context="http://www.springframework.org/schema/context">

    <context:annotation-config/>

</beans>

9.2. Using the Axon namespace shortcut

As mentioned earlier, the Axon Framework provides a separate namespace full of elements that allow you to configure your Axon applications quickly when using Spring. In order to use this namespace you must first add the declaration for this namespace to your Spring XML configuration files.

Assume you already have an XML configuration file like this:

<?xml version="1.0" encoding="UTF-8"?>
			
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">			

	...
               
</beans>

To modify this configuration file to use elements from the Axon namespace, just add the following declarations:

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:axon="http://www.axonframework.org/schema/core"               (1)
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
	http://www.axonframework.org/schema/core http://www.axonframework.org/schema/axon-core.xsd">

1

The declaration of the axon namespace reference that you will use through the configuration file.

2

Maps the Axon namespace to the XSD where the namespace is defined.

9.3. Wiring event and command handlers

9.3.1. Event handlers

Using the annotated event listeners is very easy when you use Spring. All you need to do is configure the AnnotationEventListenerBeanPostProcessor in your application context. This post processor will discover beans with @EventHandler annotated methods and automatically connect them to the event bus.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean class="org...AnnotationEventListenerBeanPostProcessor"> (1)
        <property name="eventBus" ref="eventBus"/> (2)
    </bean>

    <bean class="org.axonframework.sample.app.query.AddressTableUpdater"/> (3)

</beans>

1

This bean post processor will scan the application context for beans with an @EventHandler annotated method.

2

The reference to the event bus is optional, if only a single EventBus implementation is configured in the application context. The bean postprocessor will automatically find and wire it. If there is more than one EventBus in the context, you must specify the one to use in the postprocessor.

3

This event listener will be automatically recognized and subscribed to the event bus.

You can also wire event listeners "manually", by explicitly defining them within a AnnotationEventListenerAdapter bean, as shown in the code sample below.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean class="org.axonframework...annotation.AnnotationEventListenerAdapter"> (1)
        <constructor-arg>
            <bean class="org.axonframework.sample.app.query.AddressTableUpdater"/>
        </constructor-arg>
        <property name="eventBus" ref="eventBus"/> (2)
    </bean>

</beans>

1

The adapter turns any bean with @EventHandler methods into an EventListener

2

You need to explicitly reference the event bus to which you like to register the event listener

[Warning]Warning

Be careful when wiring event listeners "manually" while there is also an AnnotationEventListenerBeanPostProcessor in the application context. This will cause the event listener to be wired twice.

9.3.2. Command handlers

Wiring command handlers is very much like wiring event handlers: there is an AnnotationCommandHandlerBeanPostProcessor which will automatically register classes containing command handler methods (i.e. methods annotated with the @CommandHandler annotation) with a command bus.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean class="org...AnnotationCommandHandlerBeanPostProcessor"> (1)
        <property name="commandBus" ref="commandBus"/> (2)
    </bean>

    <bean class="org.axonframework.sample.app.command.ContactCommandHandler"/> (3)

</beans>

1

This bean post processor will scan the application context for beans with a @CommandHandler annotated method.

2

The reference to the command bus is optional, if only a single CommandBus implementation is configured in the application context. The bean postprocessor will automatically find and wire it. If there is more than one CommandBus in the context, you must specify the one to use in the postprocessor.

3

This command handler will be automatically recognized and subscribed to the command bus.

As with event listeners, you can also wire command handlers "manually" by explicitly defining them within a AnnotationCommandHandlerAdapter bean, as shown in the code sample below.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean class="org.axonframework...annotation.AnnotationCommandHandlerAdapter"> (1)
        <constructor-arg>
            <bean class="org.axonframework.sample.app.command.ContactCommandHandler"/>
        </constructor-arg>
        <property name="commandBus" ref="commandBus"/> (2)
    </bean>

</beans>

1

The adapter turns any bean with @EventHandler methods into an EventListener

2

You need to explicitly reference the event bus to which you like to register the event listener

[Warning]Warning

Be careful when wiring command handlers "manually" while there is also an AnnotationCommandHandlerBeanPostProcessor in the application context. This will cause the command handler to be wired twice.

9.3.3. Annotation support using the axon namespace

The previous two sections explained how you wire bean post processors to activate annotation support for your command handlers and event listeners. Using support from the Axon namespace you can accomplish the same in one go, using the annotation-config element:

<axon:annotation-config />

The annotation-config element has the following attributes that allow you to configure annotation support further:

Table 9.1. Attributes for annotation-config

Attribute nameUsageExpected value typeDescription
commandBusConditionalReference to a CommandBus BeanNeeded only if the application context contains more than one command bus.
eventBusConditionalReference to an EventBus BeanNeeded only if the application context contains more than one event bus.
executorOptionalReference to a java.util.concurrent.Executor instance beanAn executor to be used with asynchronous event listeners


9.4. Wiring the event bus

In a typical Axon application there is only one event bus. Wiring it is just a matter of creating a bean of a subtype of EventBus. The SimpleEventBus is the provided implementation.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean id="eventBus" class="org.axonframework.eventhandling.SimpleEventBus"/>

</beans>

Setting up an event bus can also be accomplished using support from the axon namespace:

<axon:event-bus id="eventBus"/>

9.5. Wiring the command bus

The basics

The command bus doesn't take any configuration to use. However, it allows you to configure a number of interceptors that should take action based on each incoming command.

<beans xmlns="http://www.springframework.org/schema/beans">

    <bean id="eventBus" class="org.axonframework.commandhandling.CommandBus">
        <property name="interceptors">
            <list>
                <bean class="org.axonframework...SpringTransactionalInterceptor">
                    <property name="transactionManager" ref="transactionManager"/>
                </bean>
                <bean class="other-interceptors"/>
            </list>
        </property>
    </bean>

</beans>

Using the Axon namespace

Setting up a basic command bus using the Axon namspace is a piece of cake: you can use the commandBus element:

<axon:command-bus id="commandBus"/>

Configuring command interceptors for your command bus is also possible using the <axon:command-bus> element, like so:

<axon:command-bus id="commandBus">
	<axon:interceptors>
		<ref>interceptor-zero</ref>
		<ref>interceptor-one</ref>
		<ref>interceptor-two</ref>
	</axon:interceptors>
</axon:command-bus>

Of course you are not limited to bean references; you can also include local bean definitions if you want.

9.6. Wiring the Repository

Wiring a repository is very similar to any other bean you would use in a Spring application. Axon only provides abstract implementations for repositories, which means you need to extend one of them. See Chapter 5, Repositories and Event Stores for the available implementations.

Repository implementations that do support event sourcing just need the event bus to be configured, as well as any dependencies that your own implementation has.

<bean id="simpleRepository" class="my.package.SimpleRepository">
    <property name="eventBus" ref="eventBus"/>
</bean>

Repositories that support event sourcing will also need an event store, which takes care of the actual storage and retrieval of events. The example below shows a repository configuration of a repository that extends the EventSourcingRepository.

<bean id="contactRepository" class="org.axonframework.sample.app.command.ContactRepository">
    <property name="eventBus" ref="eventBus"/>
    <property name="eventStore" ref="eventStore"/>
</bean>

In many cases, you can use the GenericEventSourcingRepository. Below is an example of XML application context configuration to wire such a repository.

<bean id="myRepository" class="org.axonframework.eventsourcing.GenericEventSourcingRepository">
    <constructor-arg value="fully.qualified.class.Name"/>
    <property name="eventBus" ref="eventBus"/>
    <property name="eventStore" ref="eventStore"/>
</bean>

<!-- or, when using the axon namespace -->

<axon:event-sourcing-repository id="myRepository" 
                                aggregate-type="fully.qualified.class.Name"
                                event-bus="eventBus" event-store="eventStore"/>

The repository will delegate the storage of events to the configured eventStore, while these events are dispatched using the provided eventBus.

9.7. Wiring the Event Store

All event sourcing repositorties need an event store. Wiring the JpaEventStore and the FileSystemEventStore is very similar, but the JpaEventStore needs to run in a Spring managed transaction. Unless you use the SpringTransactionalInterceptor on your command bus, you need to declare the annotation-driven transaction-manager as shown in the sample below.

<bean id="eventStore" class="org.axonframework.eventstore.jpa.JpaEventStore"/>

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/>

<!-- declare transaction manager, data source, EntityManagerFactoryBean, etc -->

Using the Axon namespace support, you can quickly configure event stores backed either by the file system or a JPA layer using the one of the following elements:

<axon:jpa-event-store id="jpaEventStore"/>

<axon:filesystem-event-store id="fileSystemEventStore" baseDir="/data"/>

9.8. Configuring Snapshotting

Configuring snapshotting using Spring is not complex, but does require a number of beans to be configured in your application context.

The EventCountSnapshotterTrigger needs to be configured as a proxy for your event store. That means all repositories should load and save aggregate from the EventCountSnapshotterTrigger, instead of the acutal event store.

<bean id="myRepository" class="org.axonframework...GenericEventSourcingRepository">
    <!-- properties omitted for brevity -->
    <property name="snapshotterTrigger">
        <bean class="org.axonframework.eventsourcing.EventCountSnapshotterTrigger">
            <property name="trigger" value="20" />
        </bean>
    </property>
</bean>

<!-- or, when using the namespace -->

<axon:event-sourcing-repository> <!-- attributes omitted for brevity -->
    <axon:snapshotter-trigger id="snapshotterTrigger" event-count-threshold="20" snapshotter-ref="snapshotter"/>
</axon:event-sourcing-repository>

The sample above configures an EventCountSnapshotter trigger that will trigger Snapshot creation when 20 or more events are required to reload the aggregate's current state.

The snapshotter is configured as follows:

<bean id="snapshotter" class="org.axonframework.eventsourcing.SpringAggregateSnapshotter">
    <property name="eventStore" ref="eventStore"/>
    <property name="executor" ref="taskExecutor"/>
</bean>

<!-- or, when using the namespace -->

<axon:snapshotter id="snapshotter" event-store="eventStore" executor="taskExecutor"/>

<!-- the task executor attribute is optional. When used you can define (for example) a thread pool to perform the snapshotting -->
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    <property name="corePoolSize" value="2"/>
    <property name="maxPoolSize" value="5"/>
    <property name="waitForTasksToCompleteOnShutdown" value="true"/>
</bean>

The SpringAggregateSnapshotter will automatically detect any PlatformTransactionManager in your application context, as well as AggregateFactory instances, which all repositories typically are. That means you only need very little configuration to use a Snapshotter within Spring. If you have multiple PlatformTransactionManager beans in your context, you should explicitly configure the one to use.

9.9. Configuring Sagas

To use Sagas, two infrastructure components are required: the SagaManager and the SagaRepository. Each have their own element in the Spring application context.

The SagaManager is defined as follows:

<axon:saga-manager id="sagaManager" saga-repository="sagaRepository" 
                   saga-factory="sagaFactory" executor="taskExecutor" 
                   transaction-manager="transactionManager"
                   resource-injector="resourceInjector">
    <axon:types>
        fully.qualified.ClassName,
        another.ClassName
    </axon:types>
</axon:saga-manager>

All properties, except for the id are optional. The saga-repository will default to an in-memory repository, meaning that Sagas will be lost when the VM is shut down. The saga-factory can be provided if the Saga instances do not have a no-argument accessible constructor, or when special initialization is required. An executor can be provided if Sagas should not be invoked by the event dispatching thread. When using asynchronous event handling, or event scheduling within the Saga's, it is required to provide the transaction-manager attribute. The default resource injector uses the Spring Context to autowire Saga instances with Spring Beans.

Use the types element to provide a comma-separated list of fully qualified class names of the annotated sagas.

When an in-memory Saga repository does not suffice, you can easily configure one that uses JPA as persistence mechanism as follows:

<axon:jpa-saga-repository id="sagaRepository" resource-injector="resourceInjector"
                          use-explicit-flush="true" saga-serializer="sagaSerializer"/>

The resource-injector, as with the saga manager, is optional and defaults to Spring-based autowiring. The saga-serializer defines how Saga instances need to be serialized when persisted. This defaults to an XStream based serialization mechanism. You may choose to explicitly flush any changes made in the repository immediately or postpone it until the transaction in which the changes were made are executed by setting the use-explicit-flush attribute to true or false, respectively. This property defaults to true.