You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by al...@apache.org on 2021/02/19 10:24:10 UTC

[ignite-extensions] branch master updated: IGNITE-14146 Migrate Spring Cache integration to ignite-extensions - Fixes #42.

This is an automated email from the ASF dual-hosted git repository.

alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git


The following commit(s) were added to refs/heads/master by this push:
     new 8e742ce  IGNITE-14146 Migrate Spring Cache integration to ignite-extensions - Fixes #42.
8e742ce is described below

commit 8e742cefaceea48874539db8bfa8ac00fce03b0f
Author: Mikhail Petrov <pm...@gmail.com>
AuthorDate: Fri Feb 19 13:21:08 2021 +0300

    IGNITE-14146 Migrate Spring Cache integration to ignite-extensions - Fixes #42.
    
    Signed-off-by: Aleksey Plekhanov <pl...@gmail.com>
---
 modules/spring-cache-ext/README.txt                |  40 +++
 modules/spring-cache-ext/licenses/apache-2.0.txt   | 202 +++++++++++
 .../modules/core/src/test/config/log4j-test.xml    |  97 +++++
 .../modules/core/src/test/config/tests.properties  |  22 ++
 modules/spring-cache-ext/pom.xml                   |  85 +++++
 .../apache/ignite/cache/spring/SpringCache.java    | 172 +++++++++
 .../ignite/cache/spring/SpringCacheManager.java    | 389 ++++++++++++++++++++
 .../apache/ignite/cache/spring/package-info.java   |  23 ++
 .../apache/ignite/TestInjectionLifecycleBean.java  |  42 +++
 .../spring/GridSpringCacheManagerAbstractTest.java | 398 +++++++++++++++++++++
 .../GridSpringCacheManagerMultiJvmSelfTest.java    | 131 +++++++
 .../spring/GridSpringCacheManagerSelfTest.java     |  64 ++++
 .../GridSpringCacheManagerSpringBeanSelfTest.java  |  46 +++
 .../cache/spring/GridSpringCacheTestKey.java       |  61 ++++
 .../spring/GridSpringCacheTestKeyGenerator.java    |  40 +++
 .../cache/spring/GridSpringCacheTestService.java   | 181 ++++++++++
 .../spring/GridSpringDynamicCacheTestService.java  |  98 +++++
 .../SpringCacheManagerContextInjectionTest.java    | 128 +++++++
 .../ignite/cache/spring/SpringCacheTest.java       | 184 ++++++++++
 .../spring/spring-caching-ignite-spring-bean.xml   |  90 +++++
 .../apache/ignite/cache/spring/spring-caching.xml  |  57 +++
 .../apache/ignite/cache/spring/spring-caching1.xml |  56 +++
 .../apache/ignite/cache/spring/spring-caching2.xml |  56 +++
 .../org/apache/ignite/spring-injection-test.xml    |  43 +++
 .../testsuites/IgniteSpringCacheTestSuite.java     |  40 +++
 modules/spring-tx-ext/README.txt                   |   4 +-
 parent/pom.xml                                     |   5 +
 pom.xml                                            |   1 +
 28 files changed, 2753 insertions(+), 2 deletions(-)

diff --git a/modules/spring-cache-ext/README.txt b/modules/spring-cache-ext/README.txt
new file mode 100644
index 0000000..85cb178
--- /dev/null
+++ b/modules/spring-cache-ext/README.txt
@@ -0,0 +1,40 @@
+Apache Ignite Spring Cache Module
+---------------------------
+
+Apache Ignite Spring Cache extension provides an integration with Spring Cache framework.
+
+Importing Spring Cache extension In Maven Project
+----------------------------------------
+
+If you are using Maven to manage dependencies of your project, you can add Spring Cache extension dependency like this (replace '${ignite-spring-cache-ext.version}' and '${ignite.version}' with actual version of Ignite Spring Cache extension and Ignite you are interested in, respectively):
+
+ <!-- Please note that for Ignite versions earlier than 2.11, the ignite-spring-cache-ext dependency must be added to classpath before ignite-spring, due to duplication of Spring Cache integration classes. If you are using Maven to manage dependencies, it just needs to place ignite-spring-cache-ext before ignite-spring dependency in your pom file. --!>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+                        http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    ...
+    <dependencies>
+        ...
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring-cache-ext</artifactId>
+            <version>${ignite-spring-cache-ext.version}</version>
+        </dependency>
+
+         <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${ignite.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${ignite.version}</version>
+        </dependency>
+        ...
+    </dependencies>
+    ...
+</project>
diff --git a/modules/spring-cache-ext/licenses/apache-2.0.txt b/modules/spring-cache-ext/licenses/apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/modules/spring-cache-ext/licenses/apache-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/modules/spring-cache-ext/modules/core/src/test/config/log4j-test.xml b/modules/spring-cache-ext/modules/core/src/test/config/log4j-test.xml
new file mode 100755
index 0000000..3061bd4
--- /dev/null
+++ b/modules/spring-cache-ext/modules/core/src/test/config/log4j-test.xml
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!DOCTYPE log4j:configuration PUBLIC "-//APACHE//DTD LOG4J 1.2//EN"
+    "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd">
+<!--
+    Log4j configuration.
+-->
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
+    <!--
+        Logs System.out messages to console.
+    -->
+    <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDOUT. -->
+        <param name="Target" value="System.out"/>
+
+        <!-- Log from DEBUG and higher. -->
+        <param name="Threshold" value="DEBUG"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+
+        <!-- Do not log beyond INFO level. -->
+        <filter class="org.apache.log4j.varia.LevelRangeFilter">
+            <param name="levelMin" value="DEBUG"/>
+            <param name="levelMax" value="INFO"/>
+        </filter>
+    </appender>
+
+    <!--
+        Logs all System.err messages to console.
+    -->
+    <appender name="CONSOLE_ERR" class="org.apache.log4j.ConsoleAppender">
+        <!-- Log to STDERR. -->
+        <param name="Target" value="System.err"/>
+
+        <!-- Log from WARN and higher. -->
+        <param name="Threshold" value="WARN"/>
+
+        <!-- The default pattern: Date Priority [Category] Message\n -->
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!--
+        Logs all output to specified file.
+    -->
+    <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
+        <param name="Threshold" value="DEBUG"/>
+        <param name="File" value="ignite/work/log/ignite.log"/>
+        <param name="Append" value="true"/>
+        <param name="MaxFileSize" value="10MB"/>
+        <param name="MaxBackupIndex" value="10"/>
+        <layout class="org.apache.log4j.PatternLayout">
+            <param name="ConversionPattern" value="[%d{ISO8601}][%-5p][%t][%c{1}] %m%n"/>
+        </layout>
+    </appender>
+
+    <!-- Disable all open source debugging. -->
+    <category name="org">
+        <level value="INFO"/>
+    </category>
+
+    <category name="org.eclipse.jetty">
+        <level value="INFO"/>
+    </category>
+
+    <!-- Default settings. -->
+    <root>
+        <!-- Print at info by default. -->
+        <level value="INFO"/>
+
+        <!-- Append to file and console. -->
+        <appender-ref ref="FILE"/>
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="CONSOLE_ERR"/>
+    </root>
+</log4j:configuration>
diff --git a/modules/spring-cache-ext/modules/core/src/test/config/tests.properties b/modules/spring-cache-ext/modules/core/src/test/config/tests.properties
new file mode 100644
index 0000000..0faf5b8
--- /dev/null
+++ b/modules/spring-cache-ext/modules/core/src/test/config/tests.properties
@@ -0,0 +1,22 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Local address to bind to.
+local.ip=127.0.0.1
+
+# TCP communication port
+comm.tcp.port=30010
diff --git a/modules/spring-cache-ext/pom.xml b/modules/spring-cache-ext/pom.xml
new file mode 100644
index 0000000..0b5a190
--- /dev/null
+++ b/modules/spring-cache-ext/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!--
+    POM file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.ignite</groupId>
+        <artifactId>ignite-extensions-parent</artifactId>
+        <version>1</version>
+        <relativePath>../../parent</relativePath>
+    </parent>
+
+    <artifactId>ignite-spring-cache-ext</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <url>http://ignite.apache.org</url>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${ignite.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-spring</artifactId>
+            <version>${ignite.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <version>${xstream.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-log4j</artifactId>
+            <version>${ignite.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.ignite</groupId>
+            <artifactId>ignite-core</artifactId>
+            <version>${ignite.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <testResources>
+            <testResource>
+                <directory>src/test/java</directory>
+                <excludes>
+                    <exclude>**/*.java</exclude>
+                </excludes>
+            </testResource>
+        </testResources>
+    </build>
+</project>
diff --git a/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCache.java b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCache.java
new file mode 100644
index 0000000..9a8f2a8
--- /dev/null
+++ b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCache.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.io.Serializable;
+import java.util.concurrent.Callable;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteLock;
+import org.springframework.cache.Cache;
+import org.springframework.cache.support.SimpleValueWrapper;
+
+/**
+ * Spring cache implementation.
+ */
+class SpringCache implements Cache {
+    /** */
+    private static final Object NULL = new NullValue();
+
+    /** */
+    private final IgniteCache<Object, Object> cache;
+
+    /** */
+    private final SpringCacheManager mgr;
+
+    /**
+     * @param cache Cache.
+     * @param mgr Manager
+     */
+    SpringCache(IgniteCache<Object, Object> cache, SpringCacheManager mgr) {
+        assert cache != null;
+
+        this.cache = cache;
+        this.mgr = mgr;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getName() {
+        return cache.getName();
+    }
+
+    /** {@inheritDoc} */
+    @Override public Object getNativeCache() {
+        return cache;
+    }
+
+    /** {@inheritDoc} */
+    @Override public ValueWrapper get(Object key) {
+        Object val = cache.get(key);
+
+        return val != null ? fromValue(val) : null;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T> T get(Object key, Class<T> type) {
+        Object val = cache.get(key);
+
+        if (NULL.equals(val))
+            val = null;
+
+        if (val != null && type != null && !type.isInstance(val))
+            throw new IllegalStateException("Cached value is not of required type [cacheName=" + cache.getName() +
+                ", key=" + key + ", val=" + val + ", requiredType=" + type + ']');
+
+        return (T)val;
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override public <T> T get(final Object key, final Callable<T> valLdr) {
+        Object val = cache.get(key);
+
+        if (val == null) {
+            IgniteLock lock = mgr.getSyncLock(cache.getName(), key);
+
+            lock.lock();
+
+            try {
+                val = cache.get(key);
+
+                if (val == null) {
+                    try {
+                        T retVal = valLdr.call();
+
+                        val = wrapNull(retVal);
+
+                        cache.put(key, val);
+                    }
+                    catch (Exception e) {
+                        throw new ValueRetrievalException(key, valLdr, e);
+                    }
+                }
+            }
+            finally {
+                lock.unlock();
+            }
+        }
+
+        return (T)unwrapNull(val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void put(Object key, Object val) {
+        if (val == null)
+            cache.withSkipStore().put(key, NULL);
+        else
+            cache.put(key, val);
+    }
+
+    /** {@inheritDoc} */
+    @Override public ValueWrapper putIfAbsent(Object key, Object val) {
+        Object old;
+
+        if (val == null)
+            old = cache.withSkipStore().getAndPutIfAbsent(key, NULL);
+        else
+            old = cache.getAndPutIfAbsent(key, val);
+
+        return old != null ? fromValue(old) : null;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void evict(Object key) {
+        cache.remove(key);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void clear() {
+        cache.removeAll();
+    }
+
+    /**
+     * @param val Cache value.
+     * @return Wrapped value.
+     */
+    private static ValueWrapper fromValue(Object val) {
+        assert val != null;
+
+        return new SimpleValueWrapper(unwrapNull(val));
+    }
+
+    private static Object unwrapNull(Object val) {
+        return NULL.equals(val) ? null : val;
+    }
+
+    private <T> Object wrapNull(T val) {
+        return val == null ? NULL : val;
+    }
+
+    /** */
+    private static class NullValue implements Serializable {
+        /** {@inheritDoc} */
+        @Override public boolean equals(Object o) {
+            return this == o || (o != null && getClass() == o.getClass());
+        }
+    }
+}
diff --git a/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCacheManager.java b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCacheManager.java
new file mode 100644
index 0000000..80f62d7
--- /dev/null
+++ b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/SpringCacheManager.java
@@ -0,0 +1,389 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.IgniteLock;
+import org.apache.ignite.IgniteSpring;
+import org.apache.ignite.Ignition;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.configuration.NearCacheConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.springframework.cache.Cache;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * Implementation of Spring cache abstraction based on Ignite cache.
+ * <h1 class="header">Overview</h1>
+ * Spring cache abstraction allows to enable caching for Java methods
+ * so that the result of a method execution is stored in some storage. If
+ * later the same method is called with the same set of parameters,
+ * the result will be retrieved from that storage instead of actually
+ * executing the method. For more information, refer to
+ * <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html">
+ * Spring Cache Abstraction documentation</a>.
+ * <h1 class="header">How To Enable Caching</h1>
+ * To enable caching based on Ignite cache in your Spring application,
+ * you will need to do the following:
+ * <ul>
+ *     <li>
+ *         Start an Ignite node with proper configuration in embedded mode
+ *         (i.e., in the same JVM where the application is running). It can
+ *         already have predefined caches, but it's not required - caches
+ *         will be created automatically on first access if needed.
+ *     </li>
+ *     <li>
+ *         Configure {@code SpringCacheManager} as a cache provider
+ *         in the Spring application context.
+ *     </li>
+ * </ul>
+ * {@code SpringCacheManager} can start a node itself on its startup
+ * based on provided Ignite configuration. You can provide path to a
+ * Spring configuration XML file, like below (path can be absolute or
+ * relative to {@code IGNITE_HOME}):
+ * <pre name="code" class="xml">
+ * &lt;beans xmlns="http://www.springframework.org/schema/beans"
+ *        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ *        xmlns:cache="http://www.springframework.org/schema/cache"
+ *        xsi:schemaLocation="
+ *         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ *         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"&gt;
+ *     &lt;-- Provide configuration file path. --&gt;
+ *     &lt;bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"&gt;
+ *         &lt;property name="configurationPath" value="examples/config/spring-cache.xml"/&gt;
+ *     &lt;/bean&gt;
+ *
+ *     &lt;-- Use annotation-driven caching configuration. --&gt;
+ *     &lt;cache:annotation-driven/&gt;
+ * &lt;/beans&gt;
+ * </pre>
+ * Or you can provide a {@link IgniteConfiguration} bean, like below:
+ * <pre name="code" class="xml">
+ * &lt;beans xmlns="http://www.springframework.org/schema/beans"
+ *        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ *        xmlns:cache="http://www.springframework.org/schema/cache"
+ *        xsi:schemaLocation="
+ *         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ *         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"&gt;
+ *     &lt;-- Provide configuration bean. --&gt;
+ *     &lt;bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"&gt;
+ *         &lt;property name="configuration"&gt;
+ *             &lt;bean id="gridCfg" class="org.apache.ignite.configuration.IgniteConfiguration"&gt;
+ *                 ...
+ *             &lt;/bean&gt;
+ *         &lt;/property&gt;
+ *     &lt;/bean&gt;
+ *
+ *     &lt;-- Use annotation-driven caching configuration. --&gt;
+ *     &lt;cache:annotation-driven/&gt;
+ * &lt;/beans&gt;
+ * </pre>
+ * Note that providing both configuration path and configuration bean is illegal
+ * and results in {@link IllegalArgumentException}.
+ * <p>
+ * If you already have Ignite node running within your application,
+ * simply provide correct Ignite instance name, like below (if there is no Grid
+ * instance with such name, exception will be thrown):
+ * <pre name="code" class="xml">
+ * &lt;beans xmlns="http://www.springframework.org/schema/beans"
+ *        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ *        xmlns:cache="http://www.springframework.org/schema/cache"
+ *        xsi:schemaLocation="
+ *         http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+ *         http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"&gt;
+ *     &lt;-- Provide Ignite instance name. --&gt;
+ *     &lt;bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager"&gt;
+ *         &lt;property name="igniteInstanceName" value="myGrid"/&gt;
+ *     &lt;/bean>
+ *
+ *     &lt;-- Use annotation-driven caching configuration. --&gt;
+ *     &lt;cache:annotation-driven/&gt;
+ * &lt;/beans&gt;
+ * </pre>
+ * This can be used, for example, when you are running your application
+ * in a J2EE Web container and use {@ignitelink org.apache.ignite.startup.servlet.ServletContextListenerStartup}
+ * for node startup.
+ * <p>
+ * If neither {@link #setConfigurationPath(String) configurationPath},
+ * {@link #setConfiguration(IgniteConfiguration) configuration}, nor
+ * {@link #setIgniteInstanceName(String) igniteInstanceName} are provided, cache manager
+ * will try to use default Grid instance (the one with the {@code null}
+ * name). If it doesn't exist, exception will be thrown.
+ * <h1>Starting Remote Nodes</h1>
+ * Keep in mind that the node started inside your application is an entry point
+ * to the whole topology it connects to. You can start as many remote standalone
+ * nodes as you need using {@code bin/ignite.{sh|bat}} scripts provided in
+ * Ignite distribution, and all these nodes will participate
+ * in caching the data.
+ */
+public class SpringCacheManager implements CacheManager, ApplicationListener<ContextRefreshedEvent>, ApplicationContextAware {
+    /** Default locks count. */
+    private static final int DEFAULT_LOCKS_COUNT = 512;
+
+    /** IgniteLock name prefix. */
+    private static final String SPRING_LOCK_NAME_PREFIX = "springSync";
+
+    /** Caches map. */
+    private final ConcurrentMap<String, SpringCache> caches = new ConcurrentHashMap<>();
+
+    /** Grid configuration file path. */
+    private String cfgPath;
+
+    /** Ignite configuration. */
+    private IgniteConfiguration cfg;
+
+    /** Ignite instance name. */
+    private String igniteInstanceName;
+
+    /** Count of IgniteLocks are used for sync get */
+    private int locksCnt = DEFAULT_LOCKS_COUNT;
+
+    /** Dynamic cache configuration template. */
+    private CacheConfiguration<Object, Object> dynamicCacheCfg;
+
+    /** Dynamic near cache configuration template. */
+    private NearCacheConfiguration<Object, Object> dynamicNearCacheCfg;
+
+    /** Ignite instance. */
+    private Ignite ignite;
+
+    /** Spring context. */
+    private ApplicationContext springCtx;
+
+    /** Locks for value loading to support sync option. */
+    private ConcurrentHashMap<Integer, IgniteLock> locks = new ConcurrentHashMap<>();
+
+    /** {@inheritDoc} */
+    @Override public void setApplicationContext(ApplicationContext ctx) {
+        this.springCtx = ctx;
+    }
+
+    /**
+     * Gets configuration file path.
+     *
+     * @return Grid configuration file path.
+     */
+    public String getConfigurationPath() {
+        return cfgPath;
+    }
+
+    /**
+     * Sets configuration file path.
+     *
+     * @param cfgPath Grid configuration file path.
+     */
+    public void setConfigurationPath(String cfgPath) {
+        this.cfgPath = cfgPath;
+    }
+
+    /**
+     * Gets configuration bean.
+     *
+     * @return Grid configuration bean.
+     */
+    public IgniteConfiguration getConfiguration() {
+        return cfg;
+    }
+
+    /**
+     * Sets configuration bean.
+     *
+     * @param cfg Grid configuration bean.
+     */
+    public void setConfiguration(IgniteConfiguration cfg) {
+        this.cfg = cfg;
+    }
+
+    /**
+     * Gets grid name.
+     *
+     * @return Grid name.
+     * @deprecated Use {@link #getIgniteInstanceName()}.
+     */
+    @Deprecated
+    public String getGridName() {
+        return getIgniteInstanceName();
+    }
+
+    /**
+     * Sets grid name.
+     *
+     * @param gridName Grid name.
+     * @deprecated Use {@link #setIgniteInstanceName(String)}.
+     */
+    @Deprecated
+    public void setGridName(String gridName) {
+        setIgniteInstanceName(gridName);
+    }
+
+    /**
+     * Gets Ignite instance name.
+     *
+     * @return Ignite instance name.
+     */
+    public String getIgniteInstanceName() {
+        return igniteInstanceName;
+    }
+
+    /**
+     * Sets Ignite instance name.
+     *
+     * @param igniteInstanceName Ignite instance name.
+     */
+    public void setIgniteInstanceName(String igniteInstanceName) {
+        this.igniteInstanceName = igniteInstanceName;
+    }
+
+    /**
+     * Gets locks count.
+     *
+     * @return locks count.
+     */
+    public int getLocksCount() {
+        return locksCnt;
+    }
+
+    /**
+     * @param locksCnt locks count.
+     */
+    public void setLocksCount(int locksCnt) {
+        this.locksCnt = locksCnt;
+    }
+
+    /**
+     * Gets dynamic cache configuration template.
+     *
+     * @return Dynamic cache configuration template.
+     */
+    public CacheConfiguration<Object, Object> getDynamicCacheConfiguration() {
+        return dynamicCacheCfg;
+    }
+
+    /**
+     * Sets dynamic cache configuration template.
+     *
+     * @param dynamicCacheCfg Dynamic cache configuration template.
+     */
+    public void setDynamicCacheConfiguration(CacheConfiguration<Object, Object> dynamicCacheCfg) {
+        this.dynamicCacheCfg = dynamicCacheCfg;
+    }
+
+    /**
+     * Gets dynamic near cache configuration template.
+     *
+     * @return Dynamic near cache configuration template.
+     */
+    public NearCacheConfiguration<Object, Object> getDynamicNearCacheConfiguration() {
+        return dynamicNearCacheCfg;
+    }
+
+    /**
+     * Sets dynamic cache configuration template.
+     *
+     * @param dynamicNearCacheCfg Dynamic cache configuration template.
+     */
+    public void setDynamicNearCacheConfiguration(NearCacheConfiguration<Object, Object> dynamicNearCacheCfg) {
+        this.dynamicNearCacheCfg = dynamicNearCacheCfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onApplicationEvent(ContextRefreshedEvent event) {
+        if (ignite == null) {
+
+            if (cfgPath != null && cfg != null) {
+                throw new IllegalArgumentException("Both 'configurationPath' and 'configuration' are " +
+                    "provided. Set only one of these properties if you need to start a Ignite node inside of " +
+                    "SpringCacheManager. If you already have a node running, omit both of them and set" +
+                    "'igniteInstanceName' property.");
+            }
+
+            try {
+                if (cfgPath != null) {
+                    ignite = IgniteSpring.start(cfgPath, springCtx);
+                }
+                else if (cfg != null)
+                    ignite = IgniteSpring.start(cfg, springCtx);
+                else
+                    ignite = Ignition.ignite(igniteInstanceName);
+            }
+            catch (IgniteCheckedException e) {
+                throw U.convertException(e);
+            }
+        }
+    }
+
+    /** {@inheritDoc} */
+    @Override public Cache getCache(String name) {
+        assert ignite != null;
+
+        SpringCache cache = caches.get(name);
+
+        if (cache == null) {
+            CacheConfiguration<Object, Object> cacheCfg = dynamicCacheCfg != null ?
+                new CacheConfiguration<>(dynamicCacheCfg) : new CacheConfiguration<>();
+
+            NearCacheConfiguration<Object, Object> nearCacheCfg = dynamicNearCacheCfg != null ?
+                new NearCacheConfiguration<>(dynamicNearCacheCfg) : null;
+
+            cacheCfg.setName(name);
+
+            cache = new SpringCache(nearCacheCfg != null ? ignite.getOrCreateCache(cacheCfg, nearCacheCfg) :
+                ignite.getOrCreateCache(cacheCfg), this);
+
+            SpringCache old = caches.putIfAbsent(name, cache);
+
+            if (old != null)
+                cache = old;
+        }
+
+        return cache;
+    }
+
+    /** {@inheritDoc} */
+    @Override public Collection<String> getCacheNames() {
+        assert ignite != null;
+
+        return new ArrayList<>(caches.keySet());
+    }
+
+    /**
+     * Provides {@link org.apache.ignite.IgniteLock} for specified cache name and key.
+     *
+     * @param name cache name
+     * @param key  key
+     * @return {@link org.apache.ignite.IgniteLock}
+     */
+    IgniteLock getSyncLock(String name, Object key) {
+        int hash = Objects.hash(name, key);
+
+        final int idx = hash % getLocksCount();
+
+        return locks.computeIfAbsent(idx, i -> ignite.reentrantLock(SPRING_LOCK_NAME_PREFIX + idx, true, false, true));
+    }
+}
diff --git a/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/package-info.java b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/package-info.java
new file mode 100644
index 0000000..164c804
--- /dev/null
+++ b/modules/spring-cache-ext/src/main/java/org/apache/ignite/cache/spring/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * <!-- Package description. -->
+ * Contains implementation of Spring cache abstraction and <code>@Cacheable</code> annotation.
+ */
+
+package org.apache.ignite.cache.spring;
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/TestInjectionLifecycleBean.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/TestInjectionLifecycleBean.java
new file mode 100644
index 0000000..2b8c932
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/TestInjectionLifecycleBean.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite;
+
+import org.apache.ignite.lifecycle.LifecycleBean;
+import org.apache.ignite.lifecycle.LifecycleEventType;
+import org.apache.ignite.resources.SpringApplicationContextResource;
+import org.springframework.context.ApplicationContext;
+
+import static org.junit.Assert.assertNotNull;
+
+/** Lifecycle bean for testing. */
+public class TestInjectionLifecycleBean implements LifecycleBean {
+    /** */
+    @SpringApplicationContextResource
+    private ApplicationContext appCtx;
+
+    /** Checks that context was injected. */
+    public void checkState() {
+        assertNotNull(appCtx);
+    }
+
+    /** {@inheritDoc} */
+    @Override public void onLifecycleEvent(LifecycleEventType evt) {
+        checkState();
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerAbstractTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerAbstractTest.java
new file mode 100644
index 0000000..1041501
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerAbstractTest.java
@@ -0,0 +1,398 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Spring cache test.
+ */
+public abstract class GridSpringCacheManagerAbstractTest extends GridCommonAbstractTest {
+    /** */
+    protected static final String CACHE_NAME = "testCache";
+
+    /** */
+    protected static final String DYNAMIC_CACHE_NAME = "dynamicCache";
+
+    /** */
+    private static final Object NULL;
+
+    /**
+     */
+    static {
+        try {
+            NULL = U.field(SpringCache.class, "NULL");
+        }
+        catch (IgniteCheckedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** */
+    protected GridSpringCacheTestService svc;
+
+    /** */
+    protected GridSpringDynamicCacheTestService dynamicSvc;
+
+    /** {@inheritDoc} */
+    @Override public String getTestIgniteInstanceName() {
+        return "testGrid";
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSimpleKey() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i, svc.simpleKey(i));
+            assertEquals("value" + i, svc.simpleKey(i));
+        }
+
+        assertEquals(3, svc.called());
+
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals("value" + i, c.get(i));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSimpleKeyNullValue() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertNull(svc.simpleKeyNullValue(i));
+            assertNull(svc.simpleKeyNullValue(i));
+        }
+
+        assertEquals(3, svc.called());
+
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals(NULL, c.get(i));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testComplexKey() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i + "suffix" + i, svc.complexKey(i, "suffix" + i));
+            assertEquals("value" + i + "suffix" + i, svc.complexKey(i, "suffix" + i));
+        }
+
+        assertEquals(3, svc.called());
+
+        IgniteCache<GridSpringCacheTestKey, String> c = grid().cache(CACHE_NAME);
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals("value" + i + "suffix" + i, c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testComplexKeyNullValue() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertNull(svc.complexKeyNullValue(i, "suffix" + i));
+            assertNull(svc.complexKeyNullValue(i, "suffix" + i));
+        }
+
+        assertEquals(3, svc.called());
+
+        IgniteCache<GridSpringCacheTestKey, String> c = grid().cache(CACHE_NAME);
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals(NULL, c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSimpleKeyPut() throws Exception {
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i + "odd", svc.simpleKeyPut(i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals("value" + i + "odd", c.get(i));
+
+            assertEquals("value" + i + "even", svc.simpleKeyPut(i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals("value" + i + "even", c.get(i));
+        }
+
+        assertEquals(6, svc.called());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSimpleKeyPutNullValue() throws Exception {
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++) {
+            assertNull(svc.simpleKeyPutNullValue(i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals(NULL, c.get(i));
+
+            assertNull(svc.simpleKeyPutNullValue(i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals(NULL, c.get(i));
+        }
+
+        assertEquals(6, svc.called());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testComplexKeyPut() throws Exception {
+        IgniteCache<GridSpringCacheTestKey, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i + "suffix" + i + "odd", svc.complexKeyPut(i, "suffix" + i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals("value" + i + "suffix" + i + "odd", c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+
+            assertEquals("value" + i + "suffix" + i + "even", svc.complexKeyPut(i, "suffix" + i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals("value" + i + "suffix" + i + "even", c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+        }
+
+        assertEquals(6, svc.called());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testComplexKeyPutNullValue() throws Exception {
+        IgniteCache<GridSpringCacheTestKey, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++) {
+            assertNull(svc.complexKeyPutNullValue(i, "suffix" + i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals(NULL, c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+
+            assertNull(svc.complexKeyPutNullValue(i, "suffix" + i));
+
+            assertEquals(i + 1, c.size());
+            assertEquals(NULL, c.get(new GridSpringCacheTestKey(i, "suffix" + i)));
+        }
+
+        assertEquals(6, svc.called());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSimpleKeyEvict() throws Exception {
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++)
+            c.put(i, "value" + i);
+
+        assertEquals(3, c.size());
+
+        assertEquals("value0", c.get(0));
+        assertEquals("value1", c.get(1));
+        assertEquals("value2", c.get(2));
+
+        svc.simpleKeyEvict(2);
+
+        assertEquals(2, c.size());
+
+        assertEquals("value0", c.get(0));
+        assertEquals("value1", c.get(1));
+        assertNull(c.get(2));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testComplexKeyEvict() throws Exception {
+        IgniteCache<GridSpringCacheTestKey, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++)
+            c.put(new GridSpringCacheTestKey(i, "suffix" + i), "value" + i);
+
+        assertEquals(3, c.size());
+
+        assertEquals("value0", c.get(new GridSpringCacheTestKey(0, "suffix" + 0)));
+        assertEquals("value1", c.get(new GridSpringCacheTestKey(1, "suffix" + 1)));
+        assertEquals("value2", c.get(new GridSpringCacheTestKey(2, "suffix" + 2)));
+
+        svc.complexKeyEvict(2, "suffix" + 2);
+
+        assertEquals(2, c.size());
+
+        assertEquals("value0", c.get(new GridSpringCacheTestKey(0, "suffix" + 0)));
+        assertEquals("value1", c.get(new GridSpringCacheTestKey(1, "suffix" + 1)));
+        assertNull(c.get(new GridSpringCacheTestKey(2, "suffix" + 2)));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testEvictAll() throws Exception {
+        IgniteCache<Integer, String> c = grid().cache(CACHE_NAME);
+
+        for (int i = 0; i < 3; i++)
+            c.put(i, "value" + i);
+
+        assertEquals(3, c.size());
+
+        assertEquals("value0", c.get(0));
+        assertEquals("value1", c.get(1));
+        assertEquals("value2", c.get(2));
+
+        svc.evictAll();
+
+        assertEquals(0, c.size());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testDynamicCache() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i, dynamicSvc.cacheable(i));
+            assertEquals("value" + i, dynamicSvc.cacheable(i));
+        }
+
+        assertEquals(3, dynamicSvc.called());
+
+        IgniteCache<Integer, String> c = grid().cache(DYNAMIC_CACHE_NAME);
+
+        // Check that correct config is used.
+        assertEquals(2, c.getConfiguration(CacheConfiguration.class).getBackups());
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals("value" + i, c.get(i));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testDynamicCachePut() throws Exception {
+        for (int i = 0; i < 3; i++) {
+            assertEquals("value" + i, dynamicSvc.cachePut(i));
+            assertEquals("value" + i, dynamicSvc.cachePut(i));
+        }
+
+        assertEquals(6, dynamicSvc.called());
+
+        IgniteCache<Integer, String> c = grid().cache(DYNAMIC_CACHE_NAME);
+
+        // Check that correct config is used.
+        assertEquals(2, c.getConfiguration(CacheConfiguration.class).getBackups());
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 3; i++)
+            assertEquals("value" + i, c.get(i));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testDynamicCacheEvict() throws Exception {
+        CacheConfiguration<Integer, String> cacheCfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
+
+        cacheCfg.setName(DYNAMIC_CACHE_NAME);
+
+        IgniteCache<Integer, String> c = grid().createCache(cacheCfg);
+
+        for (int i = 0; i < 3; i++)
+            c.put(i, "value" + i);
+
+        assertEquals(3, c.size());
+
+        for (int i = 0; i < 2; i++) {
+            dynamicSvc.cacheEvict(i);
+            dynamicSvc.cacheEvict(i);
+        }
+
+        assertEquals(4, dynamicSvc.called());
+
+        assertEquals(1, c.size());
+
+        assertEquals("value2", c.get(2));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testDynamicCacheEvictAll() throws Exception {
+        CacheConfiguration<Integer, String> cacheCfg = new CacheConfiguration<>(DEFAULT_CACHE_NAME);
+
+        cacheCfg.setName(DYNAMIC_CACHE_NAME);
+
+        IgniteCache<Integer, String> c = grid().createCache(cacheCfg);
+
+        for (int i = 0; i < 3; i++)
+            c.put(i, "value" + i);
+
+        assertEquals(3, c.size());
+
+        dynamicSvc.cacheEvictAll();
+        dynamicSvc.cacheEvictAll();
+
+        assertEquals(2, dynamicSvc.called());
+
+        assertEquals(0, c.size());
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerMultiJvmSelfTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerMultiJvmSelfTest.java
new file mode 100644
index 0000000..2bc78ce
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerMultiJvmSelfTest.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.IgniteInternalFuture;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.apache.ignite.testframework.junits.multijvm.IgniteProcessProxy;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Spring cache test in multi jvm environment.
+ */
+public class GridSpringCacheManagerMultiJvmSelfTest extends GridCommonAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected boolean isMultiJvm() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getTestIgniteInstanceName(int idx) {
+        return getTestIgniteInstanceName() + idx;
+    }
+
+    /** {@inheritDoc} */
+    @Override public String getTestIgniteInstanceName() {
+        return "testGrid";
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testSyncCache() throws Exception {
+        IgniteEx loc = startGrid(0);
+
+        final int threads = 4;
+        final int entries = 1000;
+        final int remoteNum = 2;
+
+        final CountDownLatch latch = new CountDownLatch(1);
+
+        List<IgniteInternalFuture<Integer>> futures = new ArrayList<>(remoteNum);
+
+        for (int i = 0; i < remoteNum; i++) {
+            final int gridIdx = i + 1;
+
+            final IgniteEx remote = startGrid(gridIdx);
+
+            IgniteInternalFuture<Integer> calledCntFut = GridTestUtils.runAsync(new Callable<Integer>() {
+                @Override public Integer call() throws Exception {
+                    latch.await();
+
+                    return executeRemotely((IgniteProcessProxy)remote, new TestIgniteCallable<Integer>() {
+                        @Override public Integer call(Ignite ignite) throws Exception {
+                            BeanFactory factory =
+                                new ClassPathXmlApplicationContext(
+                                    "org/apache/ignite/cache/spring/spring-caching" + gridIdx + ".xml");
+
+                            final GridSpringDynamicCacheTestService dynamicSvc =
+                                (GridSpringDynamicCacheTestService)factory.getBean("dynamicTestService");
+
+                            final CyclicBarrier barrier = new CyclicBarrier(threads);
+
+                            GridTestUtils.runMultiThreaded(
+                                new Callable() {
+                                    @Override public Object call() throws Exception {
+                                        for (int i = 0; i < entries; i++) {
+                                            barrier.await();
+
+                                            assertEquals("value" + i, dynamicSvc.cacheableSync(i));
+                                            assertEquals("value" + i, dynamicSvc.cacheableSync(i));
+                                        }
+
+                                        return null;
+                                    }
+                                },
+                                threads,
+                                "get-sync");
+
+                            return dynamicSvc.called();
+                        }
+                    });
+
+                }
+            });
+
+            futures.add(calledCntFut);
+        }
+
+        latch.countDown();
+
+        int totalCalledCnt = 0;
+
+        for (IgniteInternalFuture<Integer> future : futures)
+            totalCalledCnt += future.get();
+
+        IgniteCache<Object, Object> cache = loc.cache("dynamicCache");
+
+        assertEquals(entries, cache.size());
+        assertEquals(entries, totalCalledCnt);
+
+        for (int i = 0; i < entries; i++)
+            assertEquals("value" + i, cache.get(i));
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSelfTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSelfTest.java
new file mode 100644
index 0000000..75c722a
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSelfTest.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+/**
+ * Spring cache test.
+ */
+public class GridSpringCacheManagerSelfTest extends GridSpringCacheManagerAbstractTest {
+    /** {@inheritDoc} */
+    @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+        IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+        CacheConfiguration cache = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+        cache.setName(CACHE_NAME);
+
+        cfg.setCacheConfiguration(cache);
+
+        return cfg;
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        startGrid();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        BeanFactory factory = new ClassPathXmlApplicationContext("org/apache/ignite/cache/spring/spring-caching.xml");
+
+        svc = (GridSpringCacheTestService)factory.getBean("testService");
+        dynamicSvc = (GridSpringDynamicCacheTestService)factory.getBean("dynamicTestService");
+
+        svc.reset();
+        dynamicSvc.reset();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        grid().cache(CACHE_NAME).removeAll();
+
+        grid().destroyCache(DYNAMIC_CACHE_NAME);
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSpringBeanSelfTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSpringBeanSelfTest.java
new file mode 100644
index 0000000..8c5fc10
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheManagerSpringBeanSelfTest.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.context.support.GenericXmlApplicationContext;
+
+/**
+ * Spring cache test.
+ */
+public class GridSpringCacheManagerSpringBeanSelfTest extends GridSpringCacheManagerAbstractTest {
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTest() throws Exception {
+        ApplicationContext appCtx = new ClassPathXmlApplicationContext("org/apache/ignite/cache/spring/spring-caching-ignite-spring-bean.xml");
+
+        // To produce multiple calls of ApplicationListener::onApplicationEvent
+        GenericXmlApplicationContext child = new GenericXmlApplicationContext();
+        child.setParent(appCtx);
+        child.refresh();
+
+        svc = (GridSpringCacheTestService)appCtx.getBean("testService");
+        dynamicSvc = (GridSpringDynamicCacheTestService)appCtx.getBean("dynamicTestService");
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKey.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKey.java
new file mode 100644
index 0000000..3f55112
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKey.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.io.Serializable;
+
+/**
+ * Complex key.
+ */
+public class GridSpringCacheTestKey implements Serializable {
+    /** */
+    private final Integer p1;
+
+    /** */
+    private final String p2;
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     */
+    public GridSpringCacheTestKey(Integer p1, String p2) {
+        assert p1 != null;
+        assert p2 != null;
+
+        this.p1 = p1;
+        this.p2 = p2;
+    }
+
+    /** {@inheritDoc} */
+    @Override public boolean equals(Object o) {
+        if (this == o)
+            return true;
+
+        if (o == null || getClass() != o.getClass())
+            return false;
+
+        GridSpringCacheTestKey key = (GridSpringCacheTestKey)o;
+
+        return p1.equals(key.p1) && p2.equals(key.p2);
+    }
+
+    /** {@inheritDoc} */
+    @Override public int hashCode() {
+        return 31 * p1 + p2.hashCode();
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKeyGenerator.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKeyGenerator.java
new file mode 100644
index 0000000..7bab6cb
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestKeyGenerator.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.lang.reflect.Method;
+import org.springframework.cache.interceptor.KeyGenerator;
+
+/**
+ * Key generator.
+ */
+public class GridSpringCacheTestKeyGenerator implements KeyGenerator {
+    /** {@inheritDoc} */
+    @Override public Object generate(Object target, Method mtd, Object... params) {
+        assert params != null;
+        assert params.length > 0;
+
+        if (params.length == 1)
+            return params[0];
+        else {
+            assert params.length == 2;
+
+            return new GridSpringCacheTestKey((Integer)params[0], (String)params[1]);
+        }
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestService.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestService.java
new file mode 100644
index 0000000..544997d
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringCacheTestService.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+
+/**
+ * Test service.
+ */
+public class GridSpringCacheTestService {
+    /** */
+    private final AtomicInteger cnt = new AtomicInteger();
+
+    /**
+     * @return How many times service was called.
+     */
+    public int called() {
+        return cnt.get();
+    }
+
+    /**
+     * Resets service.
+     */
+    public void reset() {
+        cnt.set(0);
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @Cacheable("testCache")
+    public String simpleKey(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return "value" + key;
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @Cacheable("testCache")
+    public String simpleKeyNullValue(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return null;
+    }
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     * @return Value.
+     */
+    @Cacheable("testCache")
+    public String complexKey(Integer p1, String p2) {
+        assert p1 != null;
+        assert p2 != null;
+
+        cnt.incrementAndGet();
+
+        return "value" + p1 + p2;
+    }
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     * @return Value.
+     */
+    @Cacheable("testCache")
+    public String complexKeyNullValue(Integer p1, String p2) {
+        assert p1 != null;
+        assert p2 != null;
+
+        cnt.incrementAndGet();
+
+        return null;
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @CachePut("testCache")
+    public String simpleKeyPut(Integer key) {
+        assert key != null;
+
+        int cnt0 = cnt.incrementAndGet();
+
+        return "value" + key + (cnt0 % 2 == 0 ? "even" : "odd");
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @CachePut("testCache")
+    public String simpleKeyPutNullValue(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return null;
+    }
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     * @return Value.
+     */
+    @CachePut("testCache")
+    public String complexKeyPut(Integer p1, String p2) {
+        assert p1 != null;
+        assert p2 != null;
+
+        int cnt0 = cnt.incrementAndGet();
+
+        return "value" + p1 + p2 + (cnt0 % 2 == 0 ? "even" : "odd");
+    }
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     * @return Value.
+     */
+    @CachePut("testCache")
+    public String complexKeyPutNullValue(Integer p1, String p2) {
+        assert p1 != null;
+        assert p2 != null;
+
+        cnt.incrementAndGet();
+
+        return null;
+    }
+
+    /**
+     * @param key Key.
+     */
+    @CacheEvict("testCache")
+    public void simpleKeyEvict(Integer key) {
+        // No-op.
+    }
+
+    /**
+     * @param p1 Parameter 1.
+     * @param p2 Parameter 2.
+     */
+    @CacheEvict("testCache")
+    public void complexKeyEvict(Integer p1, String p2) {
+        // No-op.
+    }
+
+    /**
+     */
+    @CacheEvict(value = "testCache", allEntries = true)
+    public void evictAll() {
+        // No-op.
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringDynamicCacheTestService.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringDynamicCacheTestService.java
new file mode 100644
index 0000000..b15a9c0
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/GridSpringDynamicCacheTestService.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.CachePut;
+import org.springframework.cache.annotation.Cacheable;
+
+/**
+ * Test service.
+ */
+public class GridSpringDynamicCacheTestService {
+    /** */
+    private final AtomicInteger cnt = new AtomicInteger();
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @Cacheable("dynamicCache")
+    public String cacheable(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return "value" + key;
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @Cacheable(value = "dynamicCache", sync = true)
+    public String cacheableSync(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return "value" + key;
+    }
+
+    /**
+     * @param key Key.
+     * @return Value.
+     */
+    @CachePut("dynamicCache")
+    public String cachePut(Integer key) {
+        assert key != null;
+
+        cnt.incrementAndGet();
+
+        return "value" + key;
+    }
+
+    /**
+     * @param key Key.
+     */
+    @CacheEvict("dynamicCache")
+    public void cacheEvict(Integer key) {
+        cnt.incrementAndGet();
+    }
+
+    /**
+     */
+    @CacheEvict(value = "dynamicCache", allEntries = true)
+    public void cacheEvictAll() {
+        cnt.incrementAndGet();
+    }
+
+    /**
+     * @return Calls count.
+     */
+    public int called() {
+        return cnt.get();
+    }
+
+    /**
+     */
+    public void reset() {
+        cnt.set(0);
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheManagerContextInjectionTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheManagerContextInjectionTest.java
new file mode 100644
index 0000000..d13afcd
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheManagerContextInjectionTest.java
@@ -0,0 +1,128 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.TestInjectionLifecycleBean;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.internal.IgnitionEx;
+import org.apache.ignite.lifecycle.LifecycleBean;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ *
+ */
+public class SpringCacheManagerContextInjectionTest extends GridCommonAbstractTest {
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testBeanInjectionUsingConfigPath() throws Exception {
+        new AnnotationConfigApplicationContext(TestPathConfiguration.class);
+
+        Ignite grid = IgnitionEx.grid("springInjectionTest");
+
+        IgniteConfiguration cfg = grid.configuration();
+
+        LifecycleBean[] beans = cfg.getLifecycleBeans();
+
+        assertEquals(2, beans.length);
+
+        TestInjectionLifecycleBean bean1 = (TestInjectionLifecycleBean)beans[0];
+        TestInjectionLifecycleBean bean2 = (TestInjectionLifecycleBean)beans[1];
+
+        bean1.checkState();
+        bean2.checkState();
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testBeanInjectionUsingConfiguration() throws Exception {
+        BeanFactory factory = new AnnotationConfigApplicationContext(TestCfgConfiguration.class);
+
+        TestInjectionLifecycleBean bean1 = (TestInjectionLifecycleBean)factory.getBean("bean1");
+        TestInjectionLifecycleBean bean2 = (TestInjectionLifecycleBean)factory.getBean("bean2");
+
+        bean1.checkState();
+        bean2.checkState();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        stopAllGrids();
+
+        super.afterTest();
+    }
+
+    /** */
+    @SuppressWarnings("WeakerAccess")
+    @Configuration
+    static class TestPathConfiguration {
+        /** */
+        @Bean(name = "mgr")
+        public SpringCacheManager springCacheManager() {
+            SpringCacheManager mgr = new SpringCacheManager();
+
+            mgr.setConfigurationPath("org/apache/ignite/spring-injection-test.xml");
+
+            return mgr;
+        }
+    }
+
+    /** */
+    @SuppressWarnings("WeakerAccess")
+    @Configuration
+    static class TestCfgConfiguration {
+        /** */
+        @Bean(name = "mgr")
+        public SpringCacheManager springCacheManager() {
+            IgniteConfiguration cfg = new IgniteConfiguration();
+
+            cfg.setLocalHost("127.0.0.1");
+
+            cfg.setIgniteInstanceName("scmt");
+
+            cfg.setLifecycleBeans(bean1(), bean2());
+
+            SpringCacheManager mgr = new SpringCacheManager();
+
+            mgr.setConfiguration(cfg);
+
+            return mgr;
+        }
+
+        /** */
+        @Bean(name = "bean1")
+        LifecycleBean bean1() {
+            return new TestInjectionLifecycleBean();
+        }
+
+        /** */
+        @Bean(name = "bean2")
+        LifecycleBean bean2() {
+            return new TestInjectionLifecycleBean();
+        }
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheTest.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheTest.java
new file mode 100644
index 0000000..8710273
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/SpringCacheTest.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.cache.spring;
+
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCache;
+import org.apache.ignite.internal.util.typedef.G;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Tests for {@link SpringCache}
+ */
+public class SpringCacheTest extends GridCommonAbstractTest {
+    /** */
+    private static Ignite ignite;
+
+    /** Wrapped cache. */
+    private IgniteCache nativeCache;
+
+    /** Working cache. */
+    private SpringCache springCache;
+
+    /** */
+    private String cacheName;
+
+    /** {@inheritDoc} */
+    @Override protected void beforeTestsStarted() throws Exception {
+        super.beforeTestsStarted();
+
+        ignite = startGrid();
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTestsStopped() throws Exception {
+        G.stop(true);
+    }
+
+    /** {@inheritDoc} */
+    @SuppressWarnings("unchecked")
+    @Override protected void beforeTest() throws Exception {
+        super.beforeTest();
+
+        cacheName = String.valueOf(System.currentTimeMillis());
+        nativeCache = ignite.getOrCreateCache(cacheName);
+        springCache = new SpringCache(nativeCache, null);
+    }
+
+    /** {@inheritDoc} */
+    @Override protected void afterTest() throws Exception {
+        super.afterTest();
+
+        ignite.destroyCache(cacheName);
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetName() throws Exception {
+        assertEquals(cacheName, springCache.getName());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetNativeCache() throws Exception {
+        assertEquals(nativeCache, springCache.getNativeCache());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetByKey() throws Exception {
+        String key = "key";
+        String value = "value";
+
+        springCache.put(key, value);
+        assertEquals(value, springCache.get(key).get());
+
+        assertNull(springCache.get("wrongKey"));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testGetByKeyType() throws Exception {
+        String key = "key";
+        String value = "value";
+
+        springCache.put(key, value);
+        assertEquals(value, springCache.get(key, String.class));
+
+        try {
+            springCache.get(key, Integer.class);
+            fail("Missing exception");
+        }
+        catch (Exception e) {
+            assertTrue(e.getMessage().startsWith("Cached value is not of required type [cacheName=" + cacheName));
+        }
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPut() throws Exception {
+        String key = "key";
+        assertNull(springCache.get(key));
+
+        String value = "value";
+        springCache.put(key, value);
+
+        assertEquals(value, springCache.get(key).get());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testPutIfAbsent() throws Exception {
+        String key = "key";
+        String expected = "value";
+
+        assertNull(springCache.putIfAbsent(key, expected));
+
+        assertEquals(expected, springCache.putIfAbsent(key, "wrongValue").get());
+
+        assertEquals(expected, springCache.get(key).get());
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testEvict() throws Exception {
+        String key = "key";
+        assertNull(springCache.get(key));
+
+        springCache.put(key, "value");
+        assertNotNull(springCache.get(key));
+
+        springCache.evict(key);
+        assertNull(springCache.get(key));
+    }
+
+    /**
+     * @throws Exception If failed.
+     */
+    @Test
+    public void testClear() throws Exception {
+        String key;
+        springCache.put((key = "key1"), "value1");
+        assertNotNull(springCache.get(key));
+        springCache.put((key = "key2"), "value2");
+        assertNotNull(springCache.get(key));
+        springCache.put((key = "key3"), "value3");
+        assertNotNull(springCache.get(key));
+
+        springCache.clear();
+
+        assertNull(springCache.get("key1"));
+        assertNull(springCache.get("key2"));
+        assertNull(springCache.get("key3"));
+    }
+}
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching-ignite-spring-bean.xml b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching-ignite-spring-bean.xml
new file mode 100644
index 0000000..2671a5d
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching-ignite-spring-bean.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:cache="http://www.springframework.org/schema/cache"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
+
+    <bean id="mySpringBean" class="org.apache.ignite.IgniteSpringBean">
+        <property name="configuration">
+            <bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+                <property name="peerClassLoadingEnabled" value="true"/>
+                <property name="igniteInstanceName" value="testGrid"/>
+
+                <property name="cacheConfiguration">
+                    <list>
+                        <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                            <property name="name" value="testCache"/>
+                            <property name="atomicityMode" value="TRANSACTIONAL"/>
+                        </bean>
+                    </list>
+                </property>
+
+                <property name="discoverySpi">
+                    <bean class="org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi">
+                        <property name="ipFinder">
+                            <bean class="org.apache.ignite.spi.discovery.tcp.ipfinder.vm.TcpDiscoveryVmIpFinder">
+                                <property name="addresses">
+                                    <list>
+                                        <value>127.0.0.1:47500..47509</value>
+                                    </list>
+                                </property>
+                            </bean>
+                        </property>
+                    </bean>
+                </property>
+            </bean>
+        </property>
+    </bean>
+
+    <!--
+        Test service with cacheable methods.
+    -->
+    <bean id="testService" class="org.apache.ignite.cache.spring.GridSpringCacheTestService"/>
+
+    <!--
+        Test service with cacheable methods (dynamic cache).
+    -->
+    <bean id="dynamicTestService" class="org.apache.ignite.cache.spring.GridSpringDynamicCacheTestService"/>
+
+    <!--
+        Cache manager.
+    -->
+    <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
+        <property name="igniteInstanceName" value="testGrid"/>
+        <property name="dynamicCacheConfiguration">
+            <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                <property name="backups" value="2"/>
+            </bean>
+        </property>
+    </bean>
+
+    <!--
+        Key generator.
+    -->
+    <bean id="keyGenerator" class="org.apache.ignite.cache.spring.GridSpringCacheTestKeyGenerator"/>
+
+    <!--
+        Enable annotation-driver configuration for caching.
+    -->
+    <cache:annotation-driven key-generator="keyGenerator"/>
+</beans>
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching.xml b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching.xml
new file mode 100644
index 0000000..f232275
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:cache="http://www.springframework.org/schema/cache"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
+    <!--
+        Test service with cacheable methods.
+    -->
+    <bean id="testService" class="org.apache.ignite.cache.spring.GridSpringCacheTestService"/>
+
+    <!--
+        Test service with cacheable methods (dynamic cache).
+    -->
+    <bean id="dynamicTestService" class="org.apache.ignite.cache.spring.GridSpringDynamicCacheTestService"/>
+
+    <!--
+        Cache manager.
+    -->
+    <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
+        <property name="igniteInstanceName" value="testGrid"/>
+        <property name="dynamicCacheConfiguration">
+            <bean class="org.apache.ignite.configuration.CacheConfiguration">
+                <property name="backups" value="2"/>
+            </bean>
+        </property>
+    </bean>
+
+    <!--
+        Key generator.
+    -->
+    <bean id="keyGenerator" class="org.apache.ignite.cache.spring.GridSpringCacheTestKeyGenerator"/>
+
+    <!--
+        Enable annotation-driver configuration for caching.
+    -->
+    <cache:annotation-driven key-generator="keyGenerator"/>
+</beans>
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching1.xml b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching1.xml
new file mode 100644
index 0000000..679fd97
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching1.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:cache="http://www.springframework.org/schema/cache"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
+    <!--
+        Test service with cacheable methods.
+    -->
+    <bean id="testService" class="org.apache.ignite.cache.spring.GridSpringCacheTestService"/>
+
+    <!--
+        Test service with cacheable methods (dynamic cache).
+    -->
+    <bean id="dynamicTestService" class="org.apache.ignite.cache.spring.GridSpringDynamicCacheTestService"/>
+
+    <!--
+        Cache manager.
+    -->
+    <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
+        <property name="igniteInstanceName" value="testGrid1"/>
+        <property name="dynamicCacheConfiguration">
+            <bean class="org.apache.ignite.configuration.CacheConfiguration">
+            </bean>
+        </property>
+    </bean>
+
+    <!--
+        Key generator.
+    -->
+    <bean id="keyGenerator" class="org.apache.ignite.cache.spring.GridSpringCacheTestKeyGenerator"/>
+
+    <!--
+        Enable annotation-driver configuration for caching.
+    -->
+    <cache:annotation-driven key-generator="keyGenerator"/>
+</beans>
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching2.xml b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching2.xml
new file mode 100644
index 0000000..6a9e25a
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/cache/spring/spring-caching2.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements.  See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache License, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License.  You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:cache="http://www.springframework.org/schema/cache"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
+    <!--
+        Test service with cacheable methods.
+    -->
+    <bean id="testService" class="org.apache.ignite.cache.spring.GridSpringCacheTestService"/>
+
+    <!--
+        Test service with cacheable methods (dynamic cache).
+    -->
+    <bean id="dynamicTestService" class="org.apache.ignite.cache.spring.GridSpringDynamicCacheTestService"/>
+
+    <!--
+        Cache manager.
+    -->
+    <bean id="cacheManager" class="org.apache.ignite.cache.spring.SpringCacheManager">
+        <property name="igniteInstanceName" value="testGrid2"/>
+        <property name="dynamicCacheConfiguration">
+            <bean class="org.apache.ignite.configuration.CacheConfiguration">
+            </bean>
+        </property>
+    </bean>
+
+    <!--
+        Key generator.
+    -->
+    <bean id="keyGenerator" class="org.apache.ignite.cache.spring.GridSpringCacheTestKeyGenerator"/>
+
+    <!--
+        Enable annotation-driver configuration for caching.
+    -->
+    <cache:annotation-driven key-generator="keyGenerator"/>
+</beans>
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/spring-injection-test.xml b/modules/spring-cache-ext/src/test/java/org/apache/ignite/spring-injection-test.xml
new file mode 100644
index 0000000..14072ff
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/spring-injection-test.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<!--
+    Ignite Spring configuration file to startup grid cache.
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+       xmlns:util="http://www.springframework.org/schema/util"
+       xsi:schemaLocation="
+        http://www.springframework.org/schema/beans
+        http://www.springframework.org/schema/beans/spring-beans.xsd
+        http://www.springframework.org/schema/util
+        http://www.springframework.org/schema/util/spring-util.xsd">
+    <bean id="grid.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
+        <property name="lifecycleBeans">
+            <array>
+                <bean id="bean1" class="org.apache.ignite.TestInjectionLifecycleBean"/>
+                <bean id="bean2" class="org.apache.ignite.TestInjectionLifecycleBean"/>
+            </array>
+        </property>
+
+        <property name="localHost" value="127.0.0.1"/>
+
+        <property name="igniteInstanceName" value="springInjectionTest"/>
+    </bean>
+</beans>
diff --git a/modules/spring-cache-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringCacheTestSuite.java b/modules/spring-cache-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringCacheTestSuite.java
new file mode 100644
index 0000000..6d7a797
--- /dev/null
+++ b/modules/spring-cache-ext/src/test/java/org/apache/ignite/testsuites/IgniteSpringCacheTestSuite.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.testsuites;
+
+import org.apache.ignite.cache.spring.GridSpringCacheManagerMultiJvmSelfTest;
+import org.apache.ignite.cache.spring.GridSpringCacheManagerSelfTest;
+import org.apache.ignite.cache.spring.GridSpringCacheManagerSpringBeanSelfTest;
+import org.apache.ignite.cache.spring.SpringCacheManagerContextInjectionTest;
+import org.apache.ignite.cache.spring.SpringCacheTest;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+/**
+ * Ignite Spring Cache tests.
+ */
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+    GridSpringCacheManagerSelfTest.class,
+    GridSpringCacheManagerSpringBeanSelfTest.class,
+    SpringCacheManagerContextInjectionTest.class,
+    SpringCacheTest.class,
+    GridSpringCacheManagerMultiJvmSelfTest.class
+})
+public class IgniteSpringCacheTestSuite {
+}
diff --git a/modules/spring-tx-ext/README.txt b/modules/spring-tx-ext/README.txt
index 5c1e72f..9375526 100644
--- a/modules/spring-tx-ext/README.txt
+++ b/modules/spring-tx-ext/README.txt
@@ -10,7 +10,7 @@ There are two implementations of Apache Ignite Spring Transactions Manager - org
 Importing Spring Transactions extension In Maven Project
 ----------------------------------------
 
-If you are using Maven to manage dependencies of your project, you can add Spring Transactions extension dependency like this (replace '${ignite-spring-tx-ext.version}', '${ignite-spring.version}' and '${ignite.version}'  with actual version of Ignite Spring Transactions extension, Spring Transactions and Ignite you are interested in, respectively):
+If you are using Maven to manage dependencies of your project, you can add Spring Transactions extension dependency like this (replace '${ignite-spring-tx-ext.version}', '${spring.version}' and '${ignite.version}'  with actual version of Ignite Spring Transactions extension, Spring Transactions and Ignite you are interested in, respectively):
 
 <!-- Please note that for Ignite versions earlier than 2.11, the ignite-spring-tx-ext dependency must be added to classpath before ignite-spring, due to duplication of Spring Transactions integration classes. If you are using Maven to manage dependencies, it just needs to place ignite-spring-tx-ext before ignite-spring dependency in your pom file. --!>
 
@@ -25,7 +25,7 @@ If you are using Maven to manage dependencies of your project, you can add Sprin
         <dependency>
             <groupId>org.apache.ignite</groupId>
             <artifactId>ignite-spring-tx-ext</artifactId>
-            <version>${ignite-spring-transactions.version}</version>
+            <version>${ignite-spring-tx-ext.version}</version>
         </dependency>
 
          <dependency>
diff --git a/parent/pom.xml b/parent/pom.xml
index 7492556..785559e 100644
--- a/parent/pom.xml
+++ b/parent/pom.xml
@@ -145,6 +145,7 @@
         <zstd.version>1.3.7-2</zstd.version>
         <opencensus.version>0.22.0</opencensus.version>
         <surefire.version>3.0.0-M4</surefire.version>
+        <xstream.version>1.4.8</xstream.version>
 
         <!-- Maven plugins versions -->
         <maven.javadoc.plugin.version>2.10.4</maven.javadoc.plugin.version>
@@ -372,6 +373,10 @@
                                 <title>Spring Transactions Integration</title>
                                 <packages>org.apache.ignite.transactions.spring*</packages>
                             </group>
+                            <group>
+                                <title>Spring Cache Integration</title>
+                                <packages>org.apache.ignite.cache.spring*</packages>
+                            </group>
                         </groups>
                         <bottom>
                             <![CDATA[
diff --git a/pom.xml b/pom.xml
index 4985347..8682549 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,7 @@
         <module>modules/spring-data-commons</module>
         <module>modules/performance-statistics-ext</module>
         <module>modules/spring-tx-ext</module>
+        <module>modules/spring-cache-ext</module>
     </modules>
 
     <profiles>