diff --git a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/writing-custom-predicates-and-filters.adoc b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/writing-custom-predicates-and-filters.adoc index 597b00fe..1364150e 100644 --- a/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/writing-custom-predicates-and-filters.adoc +++ b/docs/modules/ROOT/pages/spring-cloud-gateway-server-webmvc/writing-custom-predicates-and-filters.adoc @@ -240,13 +240,12 @@ The above route will add a `X-Response-Id` header to the response. Note the use == How To Register Custom Predicates and Filters for Configuration -To use custom Predicates and Filters in external configuration you need to create a special Supplier class and register it in `META-INF/spring.factories`. +To use custom Predicates and Filters in external configuration you need to create a special Supplier class and register it a bean in the application context. === Registering Custom Predicates To register custom predicates you need to implement `PredicateSupplier`. The `PredicateDiscoverer` looks for static methods that return `RequestPredicates` to register. - SampleFilterSupplier.java [source,java] ---- @@ -254,7 +253,6 @@ package com.example; import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier; -@Configuration class SamplePredicateSupplier implements PredicateSupplier { @Override @@ -265,7 +263,24 @@ class SamplePredicateSupplier implements PredicateSupplier { } ---- -You then need to add the class in `META-INF/spring.factories`. +To register the `PredicateSupplier` for use in config files, you then need to add the class as a bean as in the example below: + +.PredicateConfiguration.java +[source,java] +---- +package com.example; + +@Configuration +class PredicateConfiguration { + + @Bean + public SamplePredicateSupplier samplePredicateSupplier() { + return new SamplePredicateSupplier(); + } +} +---- + +The requirement to add the class to `META-INF/spring.factories` is deprecated and will be removed in the next major release. .META-INF/spring.factories [source] @@ -285,7 +300,6 @@ package com.example; import org.springframework.cloud.gateway.server.mvc.filter.SimpleFilterSupplier; -@Configuration class SampleFilterSupplier extends SimpleFilterSupplier { public SampleFilterSupplier() { @@ -294,7 +308,24 @@ class SampleFilterSupplier extends SimpleFilterSupplier { } ---- -You then need to add the class in `META-INF/spring.factories`. +To register the `FilterSupplier` for use in config files, you then need to add the class as a bean as in the example below: + +.FilterConfiguration.java +[source,java] +---- +package com.example; + +@Configuration +class FilterConfiguration { + + @Bean + public SampleFilterSupplier sampleFilterSupplier() { + return new SampleFilterSupplier(); + } +} +---- + +The requirement to add the class to `META-INF/spring.factories` is deprecated and will be removed in the next major release. .META-INF/spring.factories [source] 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 dabf7d2d..dfdbd1bc 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 @@ -18,6 +18,7 @@ package org.springframework.cloud.gateway.server.mvc; import java.util.Map; +import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.AutoConfiguration; @@ -35,6 +36,8 @@ import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcAotRuntimeH 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; @@ -47,9 +50,12 @@ import org.springframework.cloud.gateway.server.mvc.filter.TransferEncodingNorma 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.filter.XForwardedRequestHeadersFilterProperties; +import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.handler.ProxyExchange; import org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction; import org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange; +import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration; +import org.springframework.cloud.gateway.server.mvc.predicate.PredicateBeanFactoryDiscoverer; import org.springframework.cloud.gateway.server.mvc.predicate.PredicateDiscoverer; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; @@ -70,7 +76,8 @@ import org.springframework.web.client.RestClient; * @author Jürgen Wißkirchen */ @AutoConfiguration(after = { HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, - RestClientAutoConfiguration.class }) + RestClientAutoConfiguration.class, FilterAutoConfiguration.class, HandlerFunctionAutoConfiguration.class, + PredicateAutoConfiguration.class }) @ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true) @Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class) @ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class) @@ -83,8 +90,11 @@ public class GatewayServerMvcAutoConfiguration { } @Bean - public RouterFunctionHolderFactory routerFunctionHolderFactory(Environment env) { - return new RouterFunctionHolderFactory(env); + public RouterFunctionHolderFactory routerFunctionHolderFactory(Environment env, BeanFactory beanFactory, + FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer, + PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer) { + return new RouterFunctionHolderFactory(env, beanFactory, filterBeanFactoryDiscoverer, + predicateBeanFactoryDiscoverer); } @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/GatewayMvcAotRuntimeHintsRegistrar.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcAotRuntimeHintsRegistrar.java index e4482d3a..c171c240 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcAotRuntimeHintsRegistrar.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcAotRuntimeHintsRegistrar.java @@ -29,9 +29,9 @@ import org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions import org.springframework.cloud.gateway.server.mvc.filter.BodyFilterFunctions; import org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions; import org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions; +import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration; import org.springframework.cloud.gateway.server.mvc.filter.FilterFunctions; import org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions; -import org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerHandlerSupplier; import org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions; import org.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions; import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions; @@ -46,11 +46,12 @@ import org.springframework.util.ClassUtils; */ public class GatewayMvcAotRuntimeHintsRegistrar implements RuntimeHintsRegistrar { + // TODO: fix AOT HINTS private static final Set> FUNCTION_PROVIDERS = Set.of(HandlerFunctions.class, - LoadBalancerHandlerSupplier.class, FilterFunctions.class, BeforeFilterFunctions.class, - AfterFilterFunctions.class, TokenRelayFilterFunctions.class, BodyFilterFunctions.class, - CircuitBreakerFilterFunctions.class, GatewayRouterFunctions.class, LoadBalancerFilterFunctions.class, - GatewayRequestPredicates.class, Bucket4jFilterFunctions.class); + FilterAutoConfiguration.LoadBalancerHandlerConfiguration.class, FilterFunctions.class, + BeforeFilterFunctions.class, AfterFilterFunctions.class, TokenRelayFilterFunctions.class, + BodyFilterFunctions.class, CircuitBreakerFilterFunctions.class, GatewayRouterFunctions.class, + LoadBalancerFilterFunctions.class, GatewayRequestPredicates.class, Bucket4jFilterFunctions.class); private static final Set> PROPERTIES = Set.of(FilterProperties.class, PredicateProperties.class, RouteProperties.class); 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 6bc52a52..0973f657 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 @@ -28,10 +28,15 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import java.util.function.Function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanNotOfRequiredTypeException; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.handler.IgnoreTopLevelConverterNotFoundBindHandler; @@ -39,8 +44,10 @@ 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; import org.springframework.cloud.gateway.server.mvc.invoke.InvocationContext; import org.springframework.cloud.gateway.server.mvc.invoke.OperationArgumentResolver; import org.springframework.cloud.gateway.server.mvc.invoke.OperationParameter; @@ -49,10 +56,13 @@ import org.springframework.cloud.gateway.server.mvc.invoke.ParameterValueMapper; import org.springframework.cloud.gateway.server.mvc.invoke.convert.ConversionServiceParameterValueMapper; import org.springframework.cloud.gateway.server.mvc.invoke.reflect.OperationMethod; import org.springframework.cloud.gateway.server.mvc.invoke.reflect.ReflectiveOperationInvoker; +import org.springframework.cloud.gateway.server.mvc.predicate.PredicateBeanFactoryDiscoverer; import org.springframework.cloud.gateway.server.mvc.predicate.PredicateDiscoverer; +import org.springframework.core.convert.ConversionService; 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; @@ -102,8 +112,37 @@ public class RouterFunctionHolderFactory { private final ParameterValueMapper parameterValueMapper = new ConversionServiceParameterValueMapper(); + private final BeanFactory beanFactory; + + private final FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer; + + private final PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer; + + private final ConversionService conversionService; + + @Deprecated public RouterFunctionHolderFactory(Environment env) { + this(env, null, null, null); + } + + public RouterFunctionHolderFactory(Environment env, BeanFactory beanFactory, + FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer, + PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer) { this.env = env; + this.beanFactory = beanFactory; + this.filterBeanFactoryDiscoverer = filterBeanFactoryDiscoverer; + this.predicateBeanFactoryDiscoverer = predicateBeanFactoryDiscoverer; + if (beanFactory instanceof ConfigurableBeanFactory configurableBeanFactory) { + if (configurableBeanFactory.getConversionService() != null) { + this.conversionService = configurableBeanFactory.getConversionService(); + } + else { + this.conversionService = DefaultConversionService.getSharedInstance(); + } + } + else { + this.conversionService = DefaultConversionService.getSharedInstance(); + } } /** @@ -153,41 +192,65 @@ public class RouterFunctionHolderFactory { // TODO: cache? // translate handlerFunction String scheme = routeProperties.getUri().getScheme(); - Map handlerArgs = new HashMap<>(); - Optional handlerOperationMethod = findOperation(handlerOperations, - scheme.toLowerCase(Locale.ROOT), handlerArgs); - if (handlerOperationMethod.isEmpty()) { - // single RouteProperties param - handlerArgs.clear(); - String routePropsKey = StringUtils.uncapitalize(RouteProperties.class.getSimpleName()); - handlerArgs.put(routePropsKey, routeProperties); - handlerOperationMethod = findOperation(handlerOperations, scheme.toLowerCase(Locale.ROOT), handlerArgs); - if (handlerOperationMethod.isEmpty()) { - throw new IllegalStateException("Unable to find HandlerFunction for scheme: " + scheme); - } - } - NormalizedOperationMethod normalizedOpMethod = handlerOperationMethod.get(); - Object response = invokeOperation(normalizedOpMethod, normalizedOpMethod.getNormalizedArgs()); - HandlerFunction handlerFunction = null; // filters added by HandlerDiscoverer need to go last, so save them + HandlerFunction handlerFunction = null; List> lowerPrecedenceFilters = new ArrayList<>(); List> higherPrecedenceFilters = new ArrayList<>(); - if (response instanceof HandlerFunction) { - handlerFunction = (HandlerFunction) response; - } - else if (response instanceof HandlerDiscoverer.Result result) { - handlerFunction = result.getHandlerFunction(); - lowerPrecedenceFilters.addAll(result.getLowerPrecedenceFilters()); - higherPrecedenceFilters.addAll(result.getHigherPrecedenceFilters()); + + if (beanFactory != null) { + try { + // TODO: configurable bean name? + String name = scheme + "HandlerFunctionDefinition"; + Function factory = beanFactory.getBean(name, Function.class); + HandlerFunctionDefinition definition = (HandlerFunctionDefinition) factory.apply(routeProperties); + handlerFunction = definition.handlerFunction(); + lowerPrecedenceFilters.addAll(definition.lowerPrecedenceFilters()); + higherPrecedenceFilters.addAll(definition.higherPrecedenceFilters()); + } + catch (NoSuchBeanDefinitionException | BeanNotOfRequiredTypeException | ClassCastException e) { + log.trace(LogMessage.format("Unable to locate bean of HandlerFunction for scheme %s", scheme), e); + } } + if (handlerFunction == null) { - throw new IllegalStateException( - "Unable to find HandlerFunction for scheme: " + scheme + " and response " + response); + Map handlerArgs = new HashMap<>(); + Optional handlerOperationMethod = findOperation(handlerOperations, + scheme.toLowerCase(Locale.ROOT), handlerArgs); + if (handlerOperationMethod.isEmpty()) { + // single RouteProperties param + handlerArgs.clear(); + String routePropsKey = StringUtils.uncapitalize(RouteProperties.class.getSimpleName()); + handlerArgs.put(routePropsKey, routeProperties); + handlerOperationMethod = findOperation(handlerOperations, scheme.toLowerCase(Locale.ROOT), handlerArgs); + if (handlerOperationMethod.isEmpty()) { + throw new IllegalStateException("Unable to find HandlerFunction for scheme: " + scheme); + } + } + + NormalizedOperationMethod normalizedOpMethod = handlerOperationMethod.get(); + Object response = invokeOperation(normalizedOpMethod, normalizedOpMethod.getNormalizedArgs()); + + if (response instanceof HandlerFunction) { + handlerFunction = (HandlerFunction) response; + } + else if (response instanceof HandlerDiscoverer.Result result) { + handlerFunction = result.getHandlerFunction(); + lowerPrecedenceFilters.addAll(result.getLowerPrecedenceFilters()); + higherPrecedenceFilters.addAll(result.getHigherPrecedenceFilters()); + } + if (handlerFunction == null) { + throw new IllegalStateException( + "Unable to find HandlerFunction for scheme: " + scheme + " and response " + response); + } } // translate predicates - MultiValueMap predicateOperations = predicateDiscoverer.getOperations(); + MultiValueMap predicateOperations = new LinkedMultiValueMap<>(); + if (predicateBeanFactoryDiscoverer != null) { + predicateOperations.addAll(predicateBeanFactoryDiscoverer.getOperations()); + } + predicateOperations.addAll(predicateDiscoverer.getOperations()); final AtomicReference predicate = new AtomicReference<>(); routeProperties.getPredicates().forEach(predicateProperties -> { @@ -214,7 +277,11 @@ public class RouterFunctionHolderFactory { lowerPrecedenceFilters.forEach(builder::filter); // translate filters - MultiValueMap filterOperations = filterDiscoverer.getOperations(); + MultiValueMap filterOperations = new LinkedMultiValueMap<>(); + if (filterBeanFactoryDiscoverer != null) { + 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); @@ -295,7 +362,7 @@ public class RouterFunctionHolderFactory { return operationInvoker.invoke(context); } - private static Object bindConfigurable(OperationMethod operationMethod, Map args, + private Object bindConfigurable(OperationMethod operationMethod, Map args, OperationParameter operationParameter) { Class configurableType = operationParameter.getType(); Configurable configurable = operationMethod.getMethod().getAnnotation(Configurable.class); @@ -305,8 +372,8 @@ public class RouterFunctionHolderFactory { Bindable bindable = Bindable.of(configurableType); List propertySources = Collections .singletonList(new MapConfigurationPropertySource(args)); - // TODO: potentially deal with conversion service - Binder binder = new Binder(propertySources, null, DefaultConversionService.getSharedInstance()); + + Binder binder = new Binder(propertySources, null, conversionService); Object config = binder.bindOrCreate("", bindable, new IgnoreTopLevelConverterNotFoundBindHandler()); return config; } 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 new file mode 100644 index 00000000..8f4126cf --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FilterAutoConfiguration.java @@ -0,0 +1,107 @@ +/* + * 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.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) + public static class LoadBalancerHandlerConfiguration { + + @Bean + public Function lbHandlerFunctionDefinition() { + return routeProperties -> new HandlerFunctionDefinition.Default("lb", HandlerFunctions.http(), + Collections.emptyList(), + Collections.singletonList(LoadBalancerFilterFunctions.lb(routeProperties.getUri().getHost()))); + } + + } + + @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/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java deleted file mode 100644 index c2a0ad58..00000000 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/LoadBalancerHandlerSupplier.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 java.lang.reflect.Method; -import java.net.URI; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; -import org.springframework.cloud.gateway.server.mvc.handler.HandlerDiscoverer; -import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions; -import org.springframework.cloud.gateway.server.mvc.handler.HandlerSupplier; - -public class LoadBalancerHandlerSupplier implements HandlerSupplier { - - @Override - public Collection get() { - return Arrays.asList(getClass().getMethods()); - } - - public static HandlerDiscoverer.Result lb(RouteProperties routeProperties) { - return lb(routeProperties.getUri()); - } - - public static HandlerDiscoverer.Result lb(URI uri) { - // TODO: how to do something other than http - return new HandlerDiscoverer.Result(HandlerFunctions.http(), Collections.emptyList(), - Collections.singletonList(LoadBalancerFilterFunctions.lb(uri.getHost()))); - } - -} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java deleted file mode 100644 index 0adc142e..00000000 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/DefaultHandlerSupplier.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.handler; - -import java.lang.reflect.Method; -import java.net.URI; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; -import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; -import org.springframework.web.servlet.function.HandlerFilterFunction; -import org.springframework.web.servlet.function.HandlerFunction; -import org.springframework.web.servlet.function.ServerResponse; - -class DefaultHandlerSupplier implements HandlerSupplier { - - @Override - public Collection get() { - return Arrays.asList(getClass().getMethods()); - } - - public static HandlerDiscoverer.Result fn(RouteProperties routeProperties) { - // fn:fnName - return fn(routeProperties.getUri().getSchemeSpecificPart()); - } - - public static HandlerDiscoverer.Result fn(String functionName) { - return new HandlerDiscoverer.Result(HandlerFunctions.fn(functionName), Collections.emptyList(), - Collections.emptyList()); - } - - public static HandlerDiscoverer.Result forward(RouteProperties routeProperties) { - return forward(routeProperties.getId(), routeProperties.getUri()); - } - - public static HandlerDiscoverer.Result forward(String id, URI uri) { - return new HandlerDiscoverer.Result(HandlerFunctions.forward(uri.getPath()), Collections.emptyList()); - } - - public static HandlerDiscoverer.Result http(RouteProperties routeProperties) { - return http(routeProperties.getId(), routeProperties.getUri()); - } - - public static HandlerDiscoverer.Result http(String id, URI uri) { - HandlerFunction http = HandlerFunctions.http(); - return getResult(id, uri, http); - } - - public static HandlerDiscoverer.Result https(RouteProperties routeProperties) { - return https(routeProperties.getId(), routeProperties.getUri()); - } - - public static HandlerDiscoverer.Result https(String id, URI uri) { - return getResult(id, uri, HandlerFunctions.https()); - } - - public static HandlerDiscoverer.Result no(RouteProperties routeProperties) { - return no(routeProperties.getId(), routeProperties.getUri()); - } - - public static HandlerDiscoverer.Result no(String id, URI uri) { - return getResult(id, uri, HandlerFunctions.no()); - } - - // for properties - public static HandlerDiscoverer.Result stream(RouteProperties routeProperties) { - // stream:bindingName - return stream(routeProperties.getUri().getSchemeSpecificPart()); - } - - public static HandlerDiscoverer.Result stream(String bindingName) { - return new HandlerDiscoverer.Result(HandlerFunctions.stream(bindingName), Collections.emptyList(), - Collections.emptyList()); - } - - private static HandlerDiscoverer.Result getResult(String id, URI uri, - HandlerFunction handlerFunction) { - HandlerFilterFunction setId = setIdFilter(id); - HandlerFilterFunction setRequest = setRequestUrlFilter(uri); - return new HandlerDiscoverer.Result(handlerFunction, Arrays.asList(setId, setRequest), Collections.emptyList()); - } - - private static HandlerFilterFunction setIdFilter(String id) { - return (request, next) -> { - MvcUtils.setRouteId(request, id); - return next.handle(request); - }; - } - - private static HandlerFilterFunction setRequestUrlFilter(URI uri) { - return (request, next) -> { - MvcUtils.setRequestUrl(request, uri); - return next.handle(request); - }; - } - -} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionAutoConfiguration.java new file mode 100644 index 00000000..3e4d3781 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionAutoConfiguration.java @@ -0,0 +1,93 @@ +/* + * 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.handler; + +import java.net.URI; +import java.util.Arrays; +import java.util.Collections; +import java.util.function.Function; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.cloud.gateway.server.mvc.common.MvcUtils; +import org.springframework.cloud.gateway.server.mvc.config.RouteProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.web.servlet.function.HandlerFilterFunction; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.ServerResponse; + +@AutoConfiguration +public class HandlerFunctionAutoConfiguration { + + @Bean + public Function fnHandlerFunctionDefinition() { + return routeProperties -> new HandlerFunctionDefinition.Default("fn", + HandlerFunctions.fn(routeProperties.getUri().getSchemeSpecificPart())); + } + + @Bean + public Function forwardHandlerFunctionDefinition() { + return routeProperties -> new HandlerFunctionDefinition.Default("forward", + HandlerFunctions.forward(routeProperties.getUri().getPath())); + } + + @Bean + public Function httpHandlerFunctionDefinition() { + return routeProperties -> getResult("http", routeProperties.getId(), routeProperties.getUri(), + HandlerFunctions.http()); + } + + @Bean + public Function httpsHandlerFunctionDefinition() { + return routeProperties -> getResult("https", routeProperties.getId(), routeProperties.getUri(), + HandlerFunctions.https()); + } + + @Bean + public Function noHandlerFunctionDefinition() { + return routeProperties -> getResult("no", routeProperties.getId(), routeProperties.getUri(), + HandlerFunctions.no()); + } + + @Bean + public Function streamHandlerFunctionDefinition() { + return routeProperties -> new HandlerFunctionDefinition.Default("stream", + HandlerFunctions.stream(routeProperties.getUri().getSchemeSpecificPart())); + } + + private static HandlerFunctionDefinition getResult(String scheme, String id, URI uri, + HandlerFunction handlerFunction) { + HandlerFilterFunction setId = setIdFilter(id); + HandlerFilterFunction setRequest = setRequestUrlFilter(uri); + return new HandlerFunctionDefinition.Default(scheme, handlerFunction, Arrays.asList(setId, setRequest), + Collections.emptyList()); + } + + private static HandlerFilterFunction setIdFilter(String id) { + return (request, next) -> { + MvcUtils.setRouteId(request, id); + return next.handle(request); + }; + } + + private static HandlerFilterFunction setRequestUrlFilter(URI uri) { + return (request, next) -> { + MvcUtils.setRequestUrl(request, uri); + return next.handle(request); + }; + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionDefinition.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionDefinition.java new file mode 100644 index 00000000..12d79fe6 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/HandlerFunctionDefinition.java @@ -0,0 +1,45 @@ +/* + * 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.handler; + +import java.util.Collections; +import java.util.List; + +import org.springframework.web.servlet.function.HandlerFilterFunction; +import org.springframework.web.servlet.function.HandlerFunction; +import org.springframework.web.servlet.function.ServerResponse; + +public interface HandlerFunctionDefinition { + + HandlerFunction handlerFunction(); + + List> lowerPrecedenceFilters(); + + List> higherPrecedenceFilters(); + + record Default(String scheme, HandlerFunction handlerFunction, + List> lowerPrecedenceFilters, + List> higherPrecedenceFilters) + implements + HandlerFunctionDefinition { + + public Default(String scheme, HandlerFunction handlerFunction) { + this(scheme, handlerFunction, Collections.emptyList(), Collections.emptyList()); + } + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateAutoConfiguration.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateAutoConfiguration.java new file mode 100644 index 00000000..4a3c612d --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateAutoConfiguration.java @@ -0,0 +1,41 @@ +/* + * 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.predicate; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.context.annotation.Bean; + +@AutoConfiguration +public class PredicateAutoConfiguration { + + @Bean + public PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer(BeanFactory beanFactory) { + return new PredicateBeanFactoryDiscoverer(beanFactory); + } + + @Bean + MvcPredicateSupplier mvcPredicateSupplier() { + return new MvcPredicateSupplier(); + } + + @Bean + GatewayRequestPredicates.PredicateSupplier gatewayRequestPredicateSupplier() { + return new GatewayRequestPredicates.PredicateSupplier(); + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscoverer.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscoverer.java new file mode 100644 index 00000000..90f77967 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscoverer.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.predicate; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.gateway.server.mvc.common.BeanFactoryGatewayDiscoverer; +import org.springframework.web.servlet.function.RequestPredicate; + +public class PredicateBeanFactoryDiscoverer extends BeanFactoryGatewayDiscoverer { + + protected PredicateBeanFactoryDiscoverer(BeanFactory beanFactory) { + super(beanFactory); + } + + @Override + public void discover() { + doDiscover(PredicateSupplier.class, RequestPredicate.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 b7002f15..c208d466 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,21 +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.handler.HandlerSupplier=\ - org.springframework.cloud.gateway.server.mvc.handler.DefaultHandlerSupplier,\ - org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerHandlerSupplier - -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 - org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.gateway.server.mvc.GatewayServerMvcAutoConfiguration.GatewayHttpClientEnvironmentPostProcessor,\ org.springframework.cloud.gateway.server.mvc.common.MultipartEnvironmentPostProcessor diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 94282943..81772855 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,5 +1,8 @@ org.springframework.cloud.gateway.server.mvc.GatewayServerMvcAutoConfiguration org.springframework.cloud.gateway.server.mvc.GatewayMvcClassPathWarningAutoConfiguration +org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration +org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoConfiguration org.springframework.cloud.gateway.server.mvc.handler.GatewayMultipartAutoConfiguration +org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration org.springframework.cloud.gateway.server.mvc.config.DefaultFunctionConfiguration \ No newline at end of file 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..917c0a54 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,8 @@ 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.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration; import org.springframework.context.ConfigurableApplicationContext; import static org.assertj.core.api.Assertions.assertThat; @@ -109,7 +112,8 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void filterEnabledPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class, + HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class)) .withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false", @@ -161,7 +165,8 @@ public class GatewayServerMvcAutoConfigurationTests { @Test void bootHttpClientPropertiesWork() { new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(GatewayServerMvcAutoConfiguration.class, + .withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.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", diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FilterDiscovererTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FilterDiscovererTests.java new file mode 100644 index 00000000..0edce139 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FilterDiscovererTests.java @@ -0,0 +1,45 @@ +/* + * 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.filter; + +import org.junit.jupiter.api.Test; + +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.gateway.server.mvc.invoke.reflect.OperationMethod; +import org.springframework.util.MultiValueMap; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class FilterDiscovererTests { + + @Test + void contextLoads() { + MultiValueMap operations = new FilterDiscoverer().getOperations(); + assertThat(operations).isNotEmpty(); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + static class Config { + + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscovererTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscovererTests.java new file mode 100644 index 00000000..dcbe4ba1 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/predicate/PredicateBeanFactoryDiscovererTests.java @@ -0,0 +1,49 @@ +/* + * 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.predicate; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.gateway.server.mvc.invoke.reflect.OperationMethod; +import org.springframework.util.MultiValueMap; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) +public class PredicateBeanFactoryDiscovererTests { + + @Autowired + PredicateBeanFactoryDiscoverer discoverer; + + @Test + void contextLoads() { + MultiValueMap operations = discoverer.getOperations(); + assertThat(operations).isNotEmpty(); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + static class Config { + + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/TestPredicateSupplier.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/TestPredicateSupplier.java new file mode 100644 index 00000000..2ace83bd --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/test/TestPredicateSupplier.java @@ -0,0 +1,37 @@ +/* + * 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.test; + +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; + +import org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier; +import org.springframework.web.servlet.function.RequestPredicate; + +public class TestPredicateSupplier implements PredicateSupplier { + + public static RequestPredicate alwaysTrue() { + return request -> true; + } + + @Override + public Collection get() { + return List.of(TestPredicateSupplier.class.getMethods()); + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/META-INF/spring.factories b/spring-cloud-gateway-server-mvc/src/test/resources/META-INF/spring.factories index bca78c94..b99d378a 100644 --- a/spring-cloud-gateway-server-mvc/src/test/resources/META-INF/spring.factories +++ b/spring-cloud-gateway-server-mvc/src/test/resources/META-INF/spring.factories @@ -1,2 +1,5 @@ +org.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier=\ + org.springframework.cloud.gateway.server.mvc.test.TestPredicateSupplier + org.springframework.cloud.gateway.server.mvc.filter.FilterSupplier=\ org.springframework.cloud.gateway.server.mvc.test.TestFilterSupplier \ No newline at end of file