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, either hazelcast-spring-4.1.10.jar
or hazelcast-all-4.1.10.jar must be in the classpath.
|
If you use Maven, add the following lines to your pom.xml
.
If you use hazelcast-all.jar
:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-all</artifactId>
<version>4.1.10</version>
</dependency>
If you use hazelcast-spring.jar
:
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
<version>4.1.10</version>
</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
anddepends-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 likeApplicationContext
andSomeBean
.@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 thatinit
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 thatsomeBean
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 of 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 Map
s/Collection
s 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. |