diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java index 00cecf8c..12bcbaaf 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties; import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcPropertiesBeanDefinitionRegistrar; import org.springframework.cloud.gateway.server.mvc.config.RouterFunctionHolderFactory; import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration; +import org.springframework.cloud.gateway.server.mvc.filter.FilterBeanFactoryDiscoverer; import org.springframework.cloud.gateway.server.mvc.filter.FormFilter; import org.springframework.cloud.gateway.server.mvc.filter.ForwardedRequestHeadersFilter; import org.springframework.cloud.gateway.server.mvc.filter.HttpHeadersFilter.RequestHttpHeadersFilter; @@ -86,8 +87,9 @@ public class GatewayServerMvcAutoConfiguration { } @Bean - public RouterFunctionHolderFactory routerFunctionHolderFactory(Environment env, BeanFactory beanFactory) { - return new RouterFunctionHolderFactory(env, beanFactory); + public RouterFunctionHolderFactory routerFunctionHolderFactory(Environment env, BeanFactory beanFactory, + FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer) { + return new RouterFunctionHolderFactory(env, beanFactory, filterBeanFactoryDiscoverer); } @Bean diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/BeanFactoryGatewayDiscoverer.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/BeanFactoryGatewayDiscoverer.java new file mode 100644 index 00000000..5a519573 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/BeanFactoryGatewayDiscoverer.java @@ -0,0 +1,38 @@ +/* + * Copyright 2013-2025 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.server.mvc.common; + +import java.util.List; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.ObjectProvider; + +public abstract class BeanFactoryGatewayDiscoverer extends AbstractGatewayDiscoverer { + + protected final BeanFactory beanFactory; + + protected BeanFactoryGatewayDiscoverer(BeanFactory beanFactory) { + this.beanFactory = beanFactory; + } + + @Override + protected List loadSuppliers(Class supplierClass) { + ObjectProvider beanProvider = beanFactory.getBeanProvider(supplierClass); + return beanProvider.orderedStream().toList(); + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java index babf4ef1..86d9b661 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/RouterFunctionHolderFactory.java @@ -43,6 +43,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS import org.springframework.boot.context.properties.source.MapConfigurationPropertySource; import org.springframework.cloud.gateway.server.mvc.common.Configurable; import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; +import org.springframework.cloud.gateway.server.mvc.filter.FilterBeanFactoryDiscoverer; import org.springframework.cloud.gateway.server.mvc.filter.FilterDiscoverer; import org.springframework.cloud.gateway.server.mvc.handler.HandlerDiscoverer; import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionDefinition; @@ -58,6 +59,7 @@ import org.springframework.cloud.gateway.server.mvc.predicate.PredicateDiscovere import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.Environment; import org.springframework.core.log.LogMessage; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; import org.springframework.web.servlet.function.HandlerFilterFunction; @@ -109,14 +111,18 @@ public class RouterFunctionHolderFactory { private final BeanFactory beanFactory; + private final FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer; + @Deprecated public RouterFunctionHolderFactory(Environment env) { - this(env, null); + this(env, null, null); } - public RouterFunctionHolderFactory(Environment env, BeanFactory beanFactory) { + public RouterFunctionHolderFactory(Environment env, BeanFactory beanFactory, + FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer) { this.env = env; this.beanFactory = beanFactory; + this.filterBeanFactoryDiscoverer = filterBeanFactoryDiscoverer; } /** @@ -247,7 +253,9 @@ public class RouterFunctionHolderFactory { lowerPrecedenceFilters.forEach(builder::filter); // translate filters - MultiValueMap filterOperations = filterDiscoverer.getOperations(); + MultiValueMap filterOperations = new LinkedMultiValueMap<>(); + filterOperations.addAll(filterBeanFactoryDiscoverer.getOperations()); + filterOperations.addAll(filterDiscoverer.getOperations()); routeProperties.getFilters().forEach(filterProperties -> { Map args = new LinkedHashMap<>(filterProperties.getArgs()); translate(filterOperations, filterProperties.getName(), args, HandlerFilterFunction.class, builder::filter); diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterAutoConfiguration.java index c2cd62a1..ad260ccb 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterAutoConfiguration.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterAutoConfiguration.java @@ -19,17 +19,58 @@ package org.springframework.cloud.gateway.server.mvc.filter; import java.util.Collections; import java.util.function.Function; +import io.github.bucket4j.BucketConfiguration; + +import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionDefinition; import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.retry.support.RetryTemplate; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; @AutoConfiguration public class FilterAutoConfiguration { + @Bean + public FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer(BeanFactory beanFactory) { + return new FilterBeanFactoryDiscoverer(beanFactory); + } + + @Bean + public FilterFunctions.FilterSupplier filterFunctionsSupplier() { + return new FilterFunctions.FilterSupplier(); + } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(BucketConfiguration.class) + static class Bucket4jFilterConfiguration { + + @Bean + public Bucket4jFilterFunctions.FilterSupplier bucket4jFilterFunctionsSupplier() { + return new Bucket4jFilterFunctions.FilterSupplier(); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(CircuitBreaker.class) + static class CircuitBreakerFilterConfiguration { + + @Bean + public CircuitBreakerFilterFunctions.FilterSupplier circuitBreakerFilterFunctionsSupplier() { + return new CircuitBreakerFilterFunctions.FilterSupplier(); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(LoadBalancerClient.class) static class LoadBalancerHandlerConfiguration { @Bean @@ -41,4 +82,26 @@ public class FilterAutoConfiguration { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(RetryTemplate.class) + static class RetryFilterConfiguration { + + @Bean + public RetryFilterFunctions.FilterSupplier retryFilterFunctionsSupplier() { + return new RetryFilterFunctions.FilterSupplier(); + } + + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(OAuth2AuthorizedClient.class) + static class TokenRelayFilterConfiguration { + + @Bean + public TokenRelayFilterFunctions.FilterSupplier tokenRelayFilterFunctionsSupplier() { + return new TokenRelayFilterFunctions.FilterSupplier(); + } + + } + } diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterBeanFactoryDiscoverer.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterBeanFactoryDiscoverer.java new file mode 100644 index 00000000..cb9f47f0 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterBeanFactoryDiscoverer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013-2023 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.server.mvc.filter; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.gateway.server.mvc.common.BeanFactoryGatewayDiscoverer; +import org.springframework.web.servlet.function.HandlerFilterFunction; + +public class FilterBeanFactoryDiscoverer extends BeanFactoryGatewayDiscoverer { + + protected FilterBeanFactoryDiscoverer(BeanFactory beanFactory) { + super(beanFactory); + } + + @Override + public void discover() { + doDiscover(FilterSupplier.class, HandlerFilterFunction.class); + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories index 452cfce5..22ae1530 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories @@ -15,13 +15,6 @@ # # -org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\ - org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions.FilterSupplier,\ - org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions.FilterSupplier,\ - org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions.FilterSupplier,\ - org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions.FilterSupplier,\ - org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions.FilterSupplier - org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\ org.springframework.cloud.gateway.server.mvc.predicate.MvcPredicateSupplier,\ org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.PredicateSupplier diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java index 7eb56405..070b719f 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java @@ -35,6 +35,7 @@ import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder; import org.springframework.boot.http.client.ClientHttpRequestFactorySettings; import org.springframework.boot.http.client.SimpleClientHttpRequestFactoryBuilder; import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.filter.FormFilter; import org.springframework.cloud.gateway.server.mvc.filter.ForwardedRequestHeadersFilter; import org.springframework.cloud.gateway.server.mvc.filter.RemoveContentLengthRequestHeadersFilter; @@ -44,6 +45,7 @@ import org.springframework.cloud.gateway.server.mvc.filter.RemoveHttp2StatusResp import org.springframework.cloud.gateway.server.mvc.filter.TransferEncodingNormalizationRequestHeadersFilter; import org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilter; import org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -109,7 +111,8 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void filterEnabledPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class)) .withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false", @@ -161,7 +164,8 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void bootHttpClientPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class)) .withPropertyValues("spring.http.client.connect-timeout=1s", "spring.http.client.read-timeout=2s",