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.
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>
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" 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">
The declaration of the | |
Maps the Axon namespace to the XSD where the namespace is defined. |
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"> <property name="eventBus" ref="eventBus"/> </bean> <bean class="org.axonframework.sample.app.query.AddressTableUpdater"/> </beans>
This bean post processor will scan the application context for beans
with an | |
The reference to the event bus is optional, if only a single
| |
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"> <constructor-arg> <bean class="org.axonframework.sample.app.query.AddressTableUpdater"/> </constructor-arg> <property name="eventBus" ref="eventBus"/> </bean> </beans>
The adapter turns any bean with | |
You need to explicitly reference the event bus to which you like to register the event listener |
Warning | |
---|---|
Be careful when wiring event listeners "manually" while there is also an
|
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"> <property name="commandBus" ref="commandBus"/> </bean> <bean class="org.axonframework.sample.app.command.ContactCommandHandler"/> </beans>
This bean post processor will scan the application context for beans
with a | |
The reference to the command bus is optional, if only a single
| |
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"> <constructor-arg> <bean class="org.axonframework.sample.app.command.ContactCommandHandler"/> </constructor-arg> <property name="commandBus" ref="commandBus"/> </bean> </beans>
The adapter turns any bean with | |
You need to explicitly reference the event bus to which you like to register the event listener |
Warning | |
---|---|
Be careful when wiring command handlers "manually" while there is also an
|
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 name | Usage | Expected value type | Description |
---|---|---|---|
commandBus | Conditional | Reference to a CommandBus Bean | Needed only if the application context contains more than one command bus. |
eventBus | Conditional | Reference to an EventBus Bean | Needed only if the application context contains more than one event bus. |
executor | Optional | Reference to a java.util.concurrent.Executor instance bean | An executor to be used with asynchronous event listeners |
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"/>
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>
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.
In case you aren't using annotation-based command handler configuration, you can
configure your command bus with a map of command handler subscriptions. You do this
using the subscriptions
child element of commandBus
, in
which you then include a list of Spring-standard entry
elements. You
use the command type as the key and the handler as the value in each mapping.
<axon:command-bus id="commandBus"> <axon:subscriptions> <entry> <key>org...MyExampleCommand</key> <value><ref>commandHandler</ref></value> </entry> </axon:subscriptions> </axon:command-bus>
The subscriptions element is used to set up a map of subscriptions. | |
The command class that a handler is to be subscribed to. | |
The command handler interested in the command type. |
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
.
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"/>
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> <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>
In this sample, a SpringAggregateSnapshotter
is used. This snapshotter
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.