From 25abaca600b468024dd70751832567053950b6f2 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Thu, 19 Jan 2023 12:20:30 -0500 Subject: [PATCH] Injects CacheManager using @Qualifier. If the user has a custom CacheManager for other uses, this avoids an injection error due to multiple CacheManager beans defined. Fixes gh-2841 --- .../main/asciidoc/spring-cloud-gateway.adoc | 2 + .../LocalResponseCacheAutoConfiguration.java | 10 ++- ...ocalResponseCacheGatewayFilterFactory.java | 2 +- ...alResponseCacheAutoConfigurationTests.java | 64 +++++++++++++++++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfigurationTests.java diff --git a/docs/src/main/asciidoc/spring-cloud-gateway.adoc b/docs/src/main/asciidoc/spring-cloud-gateway.adoc index ac699e98..18d5001b 100644 --- a/docs/src/main/asciidoc/spring-cloud-gateway.adoc +++ b/docs/src/main/asciidoc/spring-cloud-gateway.adoc @@ -2182,6 +2182,8 @@ This filter also implements the automatic calculation of the `max-age value in t If `max-age` is present on the original response, the value is rewritten with the number of seconds set in the `timeToLive` configuration parameter. In subsequent calls, this value is recalculated with the number of seconds left until the response expires. +WARNING: If your project creates custom `CacheManager` beans, it will either need to be marked with `@Primary` or injected using `@Qualifier`. + === Forward Routing Filter The `ForwardRoutingFilter` looks for a URI in the exchange attribute `ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR`. diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfiguration.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfiguration.java index f2c4e328..3b0fb544 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfiguration.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfiguration.java @@ -23,6 +23,7 @@ import com.github.benmanes.caffeine.cache.Weigher; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -52,9 +53,12 @@ public class LocalResponseCacheAutoConfiguration { private static final String RESPONSE_CACHE_NAME = "response-cache"; + /* for testing */ static final String RESPONSE_CACHE_MANAGER_NAME = "gatewayCacheManager"; + @Bean public LocalResponseCacheGatewayFilterFactory localResponseCacheGatewayFilterFactory( - ResponseCacheManagerFactory responseCacheManagerFactory, CacheManager cacheManager, + ResponseCacheManagerFactory responseCacheManagerFactory, + @Qualifier(RESPONSE_CACHE_MANAGER_NAME) CacheManager cacheManager, LocalResponseCacheProperties properties) { return new LocalResponseCacheGatewayFilterFactory(responseCacheManagerFactory, responseCache(cacheManager), properties.getTimeToLive()); @@ -70,8 +74,8 @@ public class LocalResponseCacheAutoConfiguration { return new CacheKeyGenerator(); } - @Bean - public static CacheManager concurrentMapCacheManager(LocalResponseCacheProperties cacheProperties) { + @Bean(name = RESPONSE_CACHE_MANAGER_NAME) + public static CacheManager gatewayCacheManager(LocalResponseCacheProperties cacheProperties) { CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager(); caffeineCacheManager.setCaffeine(caffeine(cacheProperties)); return caffeineCacheManager; diff --git a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/cache/LocalResponseCacheGatewayFilterFactory.java b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/cache/LocalResponseCacheGatewayFilterFactory.java index 98f52a2c..99d088b7 100644 --- a/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/cache/LocalResponseCacheGatewayFilterFactory.java +++ b/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/filter/factory/cache/LocalResponseCacheGatewayFilterFactory.java @@ -64,7 +64,7 @@ public class LocalResponseCacheGatewayFilterFactory return new ResponseCacheGatewayFilter(cacheManagerFactory.create(globalCache, configuredTimeToLive)); } else { - Cache routeCache = LocalResponseCacheAutoConfiguration.concurrentMapCacheManager(cacheProperties) + Cache routeCache = LocalResponseCacheAutoConfiguration.gatewayCacheManager(cacheProperties) .getCache(config.getRouteId() + "-cache"); return new ResponseCacheGatewayFilter( cacheManagerFactory.create(routeCache, cacheProperties.getTimeToLive())); diff --git a/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfigurationTests.java b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfigurationTests.java new file mode 100644 index 00000000..fd25d939 --- /dev/null +++ b/spring-cloud-gateway-server/src/test/java/org/springframework/cloud/gateway/config/LocalResponseCacheAutoConfigurationTests.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013-2020 the original author or authors. + * + * 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 + * + * https://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.springframework.cloud.gateway.config; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cache.CacheManager; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; + +public class LocalResponseCacheAutoConfigurationTests { + + @Test + void onlyOneCacheManagerBeanCreated() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations.of(LocalResponseCacheAutoConfiguration.class)) + .run(context -> context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME)); + } + + @Test + void twoCacheManagerBeans() { + new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(CustomCacheManagerConfig.class, LocalResponseCacheAutoConfiguration.class)) + .run(context -> { + context.containsBean(LocalResponseCacheAutoConfiguration.RESPONSE_CACHE_MANAGER_NAME); + context.containsBean("myCacheManager"); + }); + } + + @Configuration(proxyBeanMethods = false) + static class CustomCacheManagerConfig { + + @Bean + @Primary + CacheManager myCacheManager() { + return new CaffeineCacheManager(); + } + + @Bean + Object myCacheConsumer(CacheManager cacheManager) { + return ""; + } + + } + +}