Integration with Spring

You can integrate Hazelcast with Spring and this section explains the configuration of Hazelcast within Spring context.

Supported Versions are Spring 2.5 and higher releases and the latest tested Spring version is 4.3.

Some old versions of Spring may require minor changes in the Hazelcast configuration. The code and configuration snippets provided in this section are tested using Spring 4.3.

Configuring Spring

Code Sample: See our sample application for Spring Configuration.

Enabling Spring Integration

Classpath Configuration:

To enable Spring integration, the hazelcast-spring-5.0.5.jar must be on the classpath.

If you use Maven, add the following lines to your pom.xml:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
    <version>5.0.5</version>
</dependency>

If you want to use hazelcast-spring with hazelcast-enterprise you need to exclude the transitive hazelcast dependency:

<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-enterprise</artifactId>
    <version>5.0.5</version>
</dependency>
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
    <version>5.0.5</version>
    <exclusions>
        <exclusion>
          <groupId>com.hazelcast</groupId>
          <artifactId>hazelcast</artifactId>
        </exclusion>
    </exclusions>
</dependency>

If you use other build systems, you have to adjust the definition of dependencies to your needs.

Troubleshooting

When the Spring Integration JARs are not correctly installed in the Java classpath, you may see either of the following exceptions:

org.xml.sax.SAXParseException; systemId: http://hazelcast.com/schema/spring/hazelcast-spring.xsd; lineNumber: 2; columnNumber: 35; s4s-elt-character: Non-whitespace characters are not allowed in schema elements other than 'xs:appinfo' and 'xs:documentation'. Saw '301 Moved Permanently'.
org.springframework.beans.factory.parsing.BeanDefinitionParsingException: Configuration problem: Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.hazelcast.com/schema/spring]
org.xml.sax.SAXParseException; lineNumber: 25; columnNumber: 33; schema_reference.4: Failed to read schema document 'http://www.hazelcast.com/schema/spring/hazelcast-spring.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.

In this case, please ensure that the required classes are in the classpath, as explained above.

Declaring Beans by Spring beans Namespace

Bean Declaration:

You can declare Hazelcast Objects using the default Spring beans namespace. Example code for a Hazelcast Instance declaration is listed below.

<bean id="instance" class="com.hazelcast.core.Hazelcast" factory-method="newHazelcastInstance">
    <constructor-arg>
        <bean class="com.hazelcast.config.Config">
            <property name="clusterName" value="dev"/>
            <!-- and so on ... -->
        </bean>
    </constructor-arg>
</bean>

<bean id="map" factory-bean="instance" factory-method="getMap">
    <constructor-arg value="map"/>
</bean>

Declaring Beans by hazelcast Namespace

Hazelcast has its own namespace hazelcast for bean definitions. You can easily add the namespace declaration xmlns:hz="http://www.hazelcast.com/schema/spring" to the beans element in the context file so that hz namespace shortcut can be used as a bean declaration.

Here is an example schema definition:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:hz="http://www.hazelcast.com/schema/spring"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.hazelcast.com/schema/spring
                http://www.hazelcast.com/schema/spring/hazelcast-spring.xsd">

Supported Configurations with hazelcast Namespace

  • Configuring Hazelcast Instance

    <hz:hazelcast id="instance">
        <hz:config>
            <hz:cluster-name name="dev"/>
            <hz:network port="5701" port-auto-increment="false">
                <hz:join>
                    <hz:multicast enabled="false"
                        multicast-group="224.2.2.3"
                        multicast-port="54327"/>
                    <hz:tcp-ip enabled="true">
                        <hz:members>10.10.1.2, 10.10.1.3</hz:members>
                    </hz:tcp-ip>
                </hz:join>
            </hz:network>
            <hz:map name="map"
                backup-count="2"
                read-backup-data="true"
                merge-policy="com.hazelcast.spi.merge.PassThroughMergePolicy">
                <hz:eviction eviction-policy="NONE" size="0"/>
            </hz:map>
        </hz:config>
    </hz:hazelcast>
  • Configuring Hazelcast Client

    <hz:client id="client">
        <hz:cluster-name name="${cluster.name}"/>
        <hz:network connection-timeout="1000"
                    redo-operation="true"
                    smart-routing="true">
            <hz:member>10.10.1.2:5701</hz:member>
            <hz:member>10.10.1.3:5701</hz:member>
        </hz:network>
    </hz:client>
  • Hazelcast Supported Type Configurations and Examples

    • map

    • multiMap

    • replicatedmap

    • queue

    • topic

    • reliableTopic

    • set

    • list

    • executorService

    • durableExecutorService

    • scheduledExecutorService

    • ringbuffer

    • cardinalityEstimator

    • idGenerator

    • flakeIdGenerator

    • atomicLong

    • atomicReference

    • semaphore

    • countDownLatch

    • lock

      <hz:map id="map" instance-ref="client" name="map" lazy-init="true" />
      <hz:multiMap id="multiMap" instance-ref="instance" name="multiMap"
          lazy-init="false" />
      <hz:replicatedMap id="replicatedmap" instance-ref="instance"
          name="replicatedmap" lazy-init="false" />
      <hz:queue id="queue" instance-ref="client" name="queue"
          lazy-init="true" depends-on="instance"/>
      <hz:topic id="topic" instance-ref="instance" name="topic"
          depends-on="instance, client"/>
      <hz:reliableTopic id="reliableTopic" instance-ref="instance" name="reliableTopic"/>
      <hz:set id="set" instance-ref="instance" name="set" />
      <hz:list id="list" instance-ref="instance" name="list"/>
      <hz:executorService id="executorService" instance-ref="client"
          name="executorService"/>
      <hz:durableExecutorService id="durableExec" instance-ref="instance" name="durableExec"/>
      <hz:scheduledExecutorService id="scheduledExec" instance-ref="instance" name="scheduledExec"/>
      <hz:ringbuffer id="ringbuffer" instance-ref="instance" name="ringbuffer"/>
      <hz:cardinalityEstimator id="cardinalityEstimator" instance-ref="instance" name="cardinalityEstimator"/>
      <hz:idGenerator id="idGenerator" instance-ref="instance"
          name="idGenerator"/>
      <hz:flakeIdGenerator id="flakeIdGenerator" instance-ref="instance"
          name="flakeIdGenerator"/>
      <hz:atomicLong id="atomicLong" instance-ref="instance" name="atomicLong"/>
      <hz:atomicReference id="atomicReference" instance-ref="instance"
          name="atomicReference"/>
      <hz:semaphore id="semaphore" instance-ref="instance" name="semaphore"/>
      <hz:countDownLatch id="countDownLatch" instance-ref="instance"
          name="countDownLatch"/>
      <hz:lock id="lock" instance-ref="instance" name="lock"/>
  • Supported Spring Bean Attributes

    Hazelcast also supports lazy-init, scope and depends-on bean attributes.

    <hz:hazelcast id="instance" lazy-init="true" scope="singleton">
        ...
    </hz:hazelcast>
    <hz:client id="client" scope="prototype" depends-on="instance">
        ...
    </hz:client>
  • Configuring MapStore and NearCache

    For map-store, you should set either the class-name or the implementation attribute.

    <hz:config id="config">
        <hz:map name="map1">
            <hz:map-store enabled="true" class-name="com.foo.DummyStore"
                write-delay-seconds="0" />
    
            <hz:near-cache time-to-live-seconds="0"
                max-idle-seconds="60" invalidate-on-change="true" >
                <hz:eviction eviction-policy="LRU" size="5000"/>
            </hz:near-cache>
        </hz:map>
    
        <hz:map name="map2">
            <hz:map-store enabled="true" implementation="dummyMapStore"
                write-delay-seconds="0" />
        </hz:map>
    </hz:config>
    
    <bean id="dummyMapStore" class="com.foo.DummyStore" />

Enabling SpringAware Objects

You can mark Hazelcast Distributed Objects with @SpringAware if the object wants to apply:

  • bean properties

  • factory callbacks such as ApplicationContextAware, BeanNameAware

  • bean post-processing annotations such as InitializingBean, @PostConstruct.

Hazelcast Distributed ExecutorService, or more generally any Hazelcast managed object, can benefit from these features. To enable SpringAware objects, you must first configure HazelcastInstance using hazelcast namespace as explained in Configuring Spring and add <hz:spring-aware /> tag.

SpringAware Examples

  • Configure a Hazelcast Instance via Spring Configuration and define someBean as Spring Bean.

  • Add <hz:spring-aware /> to Hazelcast configuration to enable @SpringAware.

    <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"
           xmlns:hz="http://www.hazelcast.com/schema/spring"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context-3.0.xsd
                    http://www.hazelcast.com/schema/spring
                    http://www.hazelcast.com/schema/spring/hazelcast-spring.xsd">
    
        <context:component-scan base-package="..."/>
    
        <hz:hazelcast id="instance">
            <hz:config>
                <hz:spring-aware />
                <hz:cluster-name name="dev"/>
                <hz:network port="5701" port-auto-increment="false">
                    <hz:join>
                        <hz:multicast enabled="false" />
                        <hz:tcp-ip enabled="true">
                            <hz:members>10.10.1.2, 10.10.1.3</hz:members>
                        </hz:tcp-ip>
                    </hz:join>
                </hz:network>
                ...
            </hz:config>
        </hz:hazelcast>
    
        <bean id="someBean" class="com.hazelcast.examples.spring.SomeBean"
          scope="singleton" />
        ...
    </beans>

Distributed Map SpringAware Example:

  • Create a class called SomeValue which contains Spring Bean definitions like ApplicationContext and SomeBean.

    @SpringAware
    @Component("someValue")
    @Scope("prototype")
    public class SomeValue implements Serializable, ApplicationContextAware {
    
        private transient ApplicationContext context;
        private transient SomeBean someBean;
        private transient boolean init = false;
    
        public void setApplicationContext( ApplicationContext applicationContext )
            throws BeansException {
            context = applicationContext;
        }
    
        @Autowired
        public void setSomeBean( SomeBean someBean)  {
            this.someBean = someBean;
        }
    
        @PostConstruct
        public void init() {
            someBean.doSomethingUseful();
            init = true;
        }
    }
  • Get SomeValue Object from Context and put it into Hazelcast Distributed Map on the first member.

    HazelcastInstance hazelcastInstance =
        (HazelcastInstance) context.getBean( "instance" );
    SomeValue value = (SomeValue) context.getBean( "someValue" );
    IMap<String, SomeValue> map = hazelcastInstance.getMap( "values" );
    map.put( "key", value );
  • Read SomeValue Object from Hazelcast Distributed Map and assert that init method is called since it is annotated with @PostConstruct.

    HazelcastInstance hazelcastInstance =
        (HazelcastInstance) context.getBean( "instance" );
    IMap<String, SomeValue> map = hazelcastInstance.getMap( "values" );
    SomeValue value = map.get( "key" );
    Assert.assertTrue( value.init );

ExecutorService SpringAware Example:

  • Create a Callable Class called SomeTask which contains Spring Bean definitions like ApplicationContext, SomeBean.

    @SpringAware
    public class SomeTask
        implements Callable<Long>, ApplicationContextAware, Serializable {
    
        private transient ApplicationContext context;
        private transient SomeBean someBean;
    
        public Long call() throws Exception {
            return someBean.value;
        }
    
        public void setApplicationContext( ApplicationContext applicationContext )
            throws BeansException {
            context = applicationContext;
        }
    
        @Autowired
        public void setSomeBean( SomeBean someBean ) {
            this.someBean = someBean;
        }
    }
  • Submit SomeTask to two Hazelcast Members and assert that someBean is autowired.

    HazelcastInstance hazelcastInstance =
        (HazelcastInstance) context.getBean( "instance" );
    SomeBean bean = (SomeBean) context.getBean( "someBean" );
    
    Future<Long> f = hazelcastInstance.getExecutorService("executorService")
        .submit(new SomeTask());
    Assert.assertEquals(bean.value, f.get().longValue());
    
    // choose a member
    Member member = hazelcastInstance.getCluster().getMembers().iterator().next();
    
    Future<Long> f2 = (Future<Long>) hazelcast.getExecutorService("executorService")
        .submitToMember(new SomeTask(), member);
    Assert.assertEquals(bean.value, f2.get().longValue());
Spring managed properties/fields are marked as transient.

Adding Caching to Spring

As of version 3.1, Spring Framework provides support for adding caching into an existing Spring application. Spring 3.2 and later versions support JCache compliant caching providers. You can also use JCache caching backed by Hazelcast if your Spring version supports JCache.

Declarative Spring Cache Configuration

<cache:annotation-driven cache-manager="cacheManager" />

<hz:hazelcast id="instance">
    ...
</hz:hazelcast>

<bean id="cacheManager" class="com.hazelcast.spring.cache.HazelcastCacheManager">
    <constructor-arg ref="instance"/>
</bean>

Hazelcast uses its Map implementation for underlying cache. You can configure a map with your cache’s name if you want to set additional configuration such as ttl.

<cache:annotation-driven cache-manager="cacheManager" />

<hz:hazelcast id="instance">
    <hz:config>
        ...

        <hz:map name="city" time-to-live-seconds="0" in-memory-format="BINARY" />
    </hz:config>
</hz:hazelcast>

<bean id="cacheManager" class="com.hazelcast.spring.cache.HazelcastCacheManager">
    <constructor-arg ref="instance"/>
</bean>
public interface IDummyBean {
    @Cacheable("city")
    String getCity();
}

Defining Timeouts for Cache Read Operation

You can define a timeout value for the get operations from your Spring cache. This may be useful for some cases, such as SLA requirements. Hazelcast provides a property to specify this timeout: hazelcast.spring.cache.prop. This can be specified as a Java property (using -D) or you can add this property to your Spring properties file (usually named as application.properties).

An example usage is given below:

hazelcast.spring.cache.prop=defaultReadTimeout=2,cache1=10,cache2=20

The argument defaultReadTimeout applies to all your Spring caches. If you want to define different timeout values for some specific Spring caches, you can provide them as a comma separated list as shown in the above example usage. The values are in milliseconds. If you want to have no timeout for a cache, simply set it to 0 or a negative value.

Declarative Hazelcast JCache Based Caching Configuration

<cache:annotation-driven cache-manager="cacheManager" />

<hz:hazelcast id="instance">
    ...
</hz:hazelcast>

<hz:cache-manager id="hazelcastJCacheCacheManager" instance-ref="instance" name="hazelcastJCacheCacheManager"/>

<bean id="cacheManager" class="org.springframework.cache.jcache.JCacheCacheManager">
    <constructor-arg ref="hazelcastJCacheCacheManager" />
</bean>

You can use JCache implementation in both member and client mode. A cache manager should be bound to an instance. Instance can be referenced by instance-ref attribute or provided by hazelcast.instance.name property which is passed to CacheManager. Instance should be specified using one of these methods.

Instance name provided in properties overrides instance-ref attribute.

You can specify an URI for each cache manager with uri attribute.

<hz:cache-manager id="cacheManager2" name="cacheManager2" uri="testURI">
    <hz:properties>
        <hz:property name="hazelcast.instance.name">named-spring-hz-instance</hz:property>
        <hz:property name="testProperty">testValue</hz:property>
    </hz:properties>
</hz:cache-manager>

Annotation-Based Spring Cache Configuration

Annotation-Based Configuration does not require any XML definition. To perform Annotation-Based Configuration:

  • Implement a CachingConfiguration class with related Annotations.

    @Configuration
    @EnableCaching
    public class CachingConfiguration extends CachingConfigurerSupport {
        @Bean
        public CacheManager cacheManager() {
            ClientConfig config = new ClientConfig();
            HazelcastInstance client = HazelcastClient.newHazelcastClient(config);
            return new com.hazelcast.spring.cache.HazelcastCacheManager(client);
        }
        @Bean
        public KeyGenerator keyGenerator() {
            return null;
        }
    }
  • Launch Application Context and register CachingConfiguration.

    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
    context.register(CachingConfiguration.class);
    context.refresh();

For more information about Spring Cache, see Spring Cache Abstraction.

Configuring Hibernate Second Level Cache

Code Sample: See the sample application for Hibernate 2nd Level Cache configuration.

If you are using Hibernate with Hazelcast as a second level cache provider, you can easily configure your LocalSessionFactoryBean to use a Hazelcast instance by passing Hazelcast instance name. That way, you can use the same HazelcastInstance as Hibernate L2 cache instance.

...
<bean id="sessionFactory"
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
	  scope="singleton">
    <property name="dataSource" ref="dataSource"/>
    <property name="hibernateProperties">
        <props>
            ...
            <prop key="hibernate.cache.region.factory_class">com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory</prop>
            <prop key="hibernate.cache.hazelcast.instance_name">${hz.instance.name}</prop>
        </props>
    </property>
    ...
</bean>

Hibernate RegionFactory Classes

  • com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory

  • com.hazelcast.hibernate.HazelcastCacheRegionFactory

See the Configuring RegionFactory section in the Hazelcast Hibernate GitHub repository for more information.

Configuring Hazelcast Transaction Manager

You can get rid of the boilerplate code to begin, commit or rollback transactions by using HazelcastTransactionManager which is a PlatformTransactionManager implementation to be used with Spring Transaction API.

Example Configuration for Hazelcast Transaction Manager

You need to register HazelcastTransactionManager as your transaction manager implementation and also you need to register ManagedTransactionalTaskContext to access transactional data structures within your service class.

...
<hz:hazelcast id="instance">
    ...
</hz:hazelcast>
...
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="com.hazelcast.spring.transaction.HazelcastTransactionManager">
    <constructor-arg ref="instance"/>
</bean>
<bean id="transactionalContext" class="com.hazelcast.spring.transaction.ManagedTransactionalTaskContext">
    <constructor-arg ref="transactionManager"/>
</bean>
<bean id="YOUR_SERVICE" class="YOUR_SERVICE_CLASS">
    <property name="transactionalTaskContext" ref="transactionalContext"/>
</bean>
...

Example Transactional Method

public class ServiceWithTransactionalMethod {

    private TransactionalTaskContext transactionalTaskContext;

    @Transactional
    public void transactionalPut(String key, String value) {
        transactionalTaskContext.getMap("testMap").put(key, value);
    }

    ...
}

After marking your method as Transactional either declaratively or by annotation and accessing the data structure through the TransactionalTaskContext, HazelcastTransactionManager begins, commits or rollbacks the transaction for you.

Best Practices

Spring tries to create a new Map/Collection instance and fill the new instance by iterating and converting values of the original Map/Collection (IMap, IQueue, etc.) to required types when generic type parameters of the original Map/Collection and the target property/attribute do not match.

Since Hazelcast Maps/Collections are designed to hold very large data which a single machine cannot carry, iterating through whole values can cause out of memory errors.

To avoid this issue, the target property/attribute can be declared as un-typed Map/Collection as shown below.

public class SomeBean {
    @Autowired
    IMap map; // instead of IMap<K, V> map

    @Autowired
    IQueue queue; // instead of IQueue<E> queue
    ...
}

Or, parameters of injection methods (constructor, setter) can be un-typed as shown below.

public class SomeBean {

    IMap<K, V> map;
    IQueue<E> queue;

    // Instead of IMap<K, V> map
    public SomeBean(IMap map) {
        this.map = map;
    }

    ...

    // Instead of IQueue<E> queue
    public void setQueue(IQueue queue) {
        this.queue = queue;
    }
    ...
}
See Spring issue-3407 for more information.