Adds support for loading PredicateSuppliers via beans

See gh-3250
This commit is contained in:
spencergibb
2025-04-15 08:40:43 -04:00
parent 8dbb5e6e69
commit f4de53ebce
11 changed files with 236 additions and 13 deletions

View File

@@ -54,6 +54,8 @@ import org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctionAutoC
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;
@@ -74,7 +76,8 @@ import org.springframework.web.client.RestClient;
* @author Jürgen Wißkirchen
*/
@AutoConfiguration(after = { HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
RestClientAutoConfiguration.class, FilterAutoConfiguration.class, HandlerFunctionAutoConfiguration.class })
RestClientAutoConfiguration.class, FilterAutoConfiguration.class, HandlerFunctionAutoConfiguration.class,
PredicateAutoConfiguration.class })
@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
@Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class)
@ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class)
@@ -88,8 +91,10 @@ public class GatewayServerMvcAutoConfiguration {
@Bean
public RouterFunctionHolderFactory routerFunctionHolderFactory(Environment env, BeanFactory beanFactory,
FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer) {
return new RouterFunctionHolderFactory(env, beanFactory, filterBeanFactoryDiscoverer);
FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer,
PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer) {
return new RouterFunctionHolderFactory(env, beanFactory, filterBeanFactoryDiscoverer,
predicateBeanFactoryDiscoverer);
}
@Bean

View File

@@ -55,6 +55,7 @@ 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.support.DefaultConversionService;
import org.springframework.core.env.Environment;
@@ -113,16 +114,20 @@ public class RouterFunctionHolderFactory {
private final FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer;
private final PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer;
@Deprecated
public RouterFunctionHolderFactory(Environment env) {
this(env, null, null);
this(env, null, null, null);
}
public RouterFunctionHolderFactory(Environment env, BeanFactory beanFactory,
FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer) {
FilterBeanFactoryDiscoverer filterBeanFactoryDiscoverer,
PredicateBeanFactoryDiscoverer predicateBeanFactoryDiscoverer) {
this.env = env;
this.beanFactory = beanFactory;
this.filterBeanFactoryDiscoverer = filterBeanFactoryDiscoverer;
this.predicateBeanFactoryDiscoverer = predicateBeanFactoryDiscoverer;
}
/**
@@ -226,7 +231,11 @@ public class RouterFunctionHolderFactory {
}
// translate predicates
MultiValueMap<String, OperationMethod> predicateOperations = predicateDiscoverer.getOperations();
MultiValueMap<String, OperationMethod> predicateOperations = new LinkedMultiValueMap<>();
if (predicateBeanFactoryDiscoverer != null) {
predicateOperations.addAll(predicateBeanFactoryDiscoverer.getOperations());
}
predicateOperations.addAll(predicateDiscoverer.getOperations());
final AtomicReference<RequestPredicate> predicate = new AtomicReference<>();
routeProperties.getPredicates().forEach(predicateProperties -> {
@@ -254,7 +263,9 @@ public class RouterFunctionHolderFactory {
// translate filters
MultiValueMap<String, OperationMethod> filterOperations = new LinkedMultiValueMap<>();
filterOperations.addAll(filterBeanFactoryDiscoverer.getOperations());
if (filterBeanFactoryDiscoverer != null) {
filterOperations.addAll(filterBeanFactoryDiscoverer.getOperations());
}
filterOperations.addAll(filterDiscoverer.getOperations());
routeProperties.getFilters().forEach(filterProperties -> {
Map<String, Object> args = new LinkedHashMap<>(filterProperties.getArgs());

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -15,10 +15,6 @@
#
#
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

View File

@@ -3,5 +3,6 @@ org.springframework.cloud.gateway.server.mvc.GatewayMvcClassPathWarningAutoConfi
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

View File

@@ -46,6 +46,7 @@ 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.handler.HandlerFunctionAutoConfiguration;
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
@@ -111,7 +112,7 @@ public class GatewayServerMvcAutoConfigurationTests {
@Test
void filterEnabledPropertiesWork() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class,
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
RestClientAutoConfiguration.class, SslAutoConfiguration.class))
@@ -164,7 +165,7 @@ public class GatewayServerMvcAutoConfigurationTests {
@Test
void bootHttpClientPropertiesWork() {
new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class,
.withConfiguration(AutoConfigurations.of(FilterAutoConfiguration.class, PredicateAutoConfiguration.class,
HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class,
HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class,
RestClientAutoConfiguration.class, SslAutoConfiguration.class))

View File

@@ -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<String, OperationMethod> operations = new FilterDiscoverer().getOperations();
assertThat(operations).isNotEmpty();
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class Config {
}
}

View File

@@ -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<String, OperationMethod> operations = discoverer.getOperations();
assertThat(operations).isNotEmpty();
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class Config {
}
}

View File

@@ -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<Method> get() {
return List.of(TestPredicateSupplier.class.getMethods());
}
}

View File

@@ -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