diff --git a/graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/cache/SpringValueCache.java b/graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/cache/SpringValueCache.java
new file mode 100644
index 00000000..b4dca41c
--- /dev/null
+++ b/graphql-kickstart-spring-support/src/main/java/graphql/kickstart/spring/cache/SpringValueCache.java
@@ -0,0 +1,76 @@
+package graphql.kickstart.spring.cache;
+
+import static java.util.concurrent.CompletableFuture.runAsync;
+import static java.util.concurrent.CompletableFuture.supplyAsync;
+
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Function;
+import lombok.RequiredArgsConstructor;
+import org.dataloader.ValueCache;
+import org.springframework.cache.Cache;
+import org.springframework.cache.ValueWrapper;
+
+/**
+ * A {@link ValueCache} which uses a Spring {@link Cache} for caching.
+ *
+ * @see GraphQL Java
+ * docs
+ */
+@RequiredArgsConstructor
+public class SpringValueCache implements ValueCache {
+
+ private final Cache cache;
+ private Function keyTransformer;
+
+ @Override
+ public CompletableFuture get(K key) {
+ return supplyAsync(() -> {
+ Object finalKey = this.getKey(key);
+ ValueWrapper valueWrapper = this.cache.get(finalKey);
+
+ if (valueWrapper == null) {
+ throw new CacheEntryNotFoundException(this.cache.getName(), finalKey);
+ }
+
+ return (V) valueWrapper.get();
+ });
+ }
+
+ @Override
+ public CompletableFuture set(K key, V value) {
+ return supplyAsync(() -> {
+ this.cache.put(this.getKey(key), value);
+ return value;
+ });
+ }
+
+ @Override
+ public CompletableFuture delete(K key) {
+ return runAsync(() -> this.cache.evictIfPresent(this.getKey(key)));
+ }
+
+ @Override
+ public CompletableFuture clear() {
+ return runAsync(this.cache::invalidate);
+ }
+
+ public SpringValueCache setKeyTransformer(Function transformer) {
+ this.keyTransformer = transformer;
+ return this;
+ }
+
+ private Object getKey(K key) {
+ return (
+ this.keyTransformer == null
+ ? key
+ : this.keyTransformer.apply(key)
+ );
+ }
+
+ public static class CacheEntryNotFoundException extends RuntimeException {
+ public CacheEntryNotFoundException(String cacheName, Object key) {
+ super("Entry could not be found in cache named \"" + cacheName + "\" for key: " + key);
+ }
+ }
+}