Skip to content

Commit 3c188d3

Browse files
committed
Fix runtime hints for native images.
Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com>
1 parent 2dfc3f3 commit 3c188d3

File tree

3 files changed

+130
-86
lines changed

3 files changed

+130
-86
lines changed

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232
import org.springframework.boot.http.client.HttpRedirects;
3333
import org.springframework.boot.web.client.RestClientCustomizer;
3434
import org.springframework.cloud.gateway.server.mvc.common.ArgumentSupplierBeanPostProcessor;
35-
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcAotRuntimeHintsRegistrar;
3635
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties;
3736
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcPropertiesBeanDefinitionRegistrar;
37+
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcRuntimeHintsProcessor;
3838
import org.springframework.cloud.gateway.server.mvc.config.RouterFunctionHolderFactory;
3939
import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
4040
import org.springframework.cloud.gateway.server.mvc.filter.FilterBeanFactoryDiscoverer;
@@ -60,7 +60,6 @@
6060
import org.springframework.context.ApplicationEventPublisher;
6161
import org.springframework.context.annotation.Bean;
6262
import org.springframework.context.annotation.Import;
63-
import org.springframework.context.annotation.ImportRuntimeHints;
6463
import org.springframework.core.env.ConfigurableEnvironment;
6564
import org.springframework.core.env.Environment;
6665
import org.springframework.core.env.MapPropertySource;
@@ -80,7 +79,6 @@
8079
PredicateAutoConfiguration.class })
8180
@ConditionalOnProperty(name = GatewayMvcProperties.PREFIX + ".enabled", matchIfMissing = true)
8281
@Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class)
83-
@ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class)
8482
public class GatewayServerMvcAutoConfiguration {
8583

8684
@Bean
@@ -209,6 +207,11 @@ public XForwardedRequestHeadersFilterProperties xForwardedRequestHeadersFilterPr
209207
return new XForwardedRequestHeadersFilterProperties();
210208
}
211209

210+
@Bean
211+
static GatewayMvcRuntimeHintsProcessor gatewayMvcRuntimeHintsProcessor() {
212+
return new GatewayMvcRuntimeHintsProcessor();
213+
}
214+
212215
static class GatewayHttpClientEnvironmentPostProcessor implements EnvironmentPostProcessor {
213216

214217
static final boolean APACHE = ClassUtils.isPresent("org.apache.hc.client5.http.impl.classic.HttpClients", null);

spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcAotRuntimeHintsRegistrar.java

Lines changed: 0 additions & 83 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright 2013-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.gateway.server.mvc.config;
18+
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.Map;
22+
import java.util.Set;
23+
import java.util.stream.Collectors;
24+
import java.util.stream.Stream;
25+
26+
import org.apache.commons.logging.Log;
27+
import org.apache.commons.logging.LogFactory;
28+
29+
import org.springframework.aot.hint.MemberCategory;
30+
import org.springframework.aot.hint.ReflectionHints;
31+
import org.springframework.aot.hint.TypeReference;
32+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
33+
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
34+
import org.springframework.beans.factory.config.BeanDefinition;
35+
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
36+
import org.springframework.cloud.gateway.server.mvc.filter.FilterAutoConfiguration;
37+
import org.springframework.cloud.gateway.server.mvc.predicate.PredicateAutoConfiguration;
38+
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
39+
import org.springframework.core.type.filter.AssignableTypeFilter;
40+
41+
/**
42+
* A {@link BeanFactoryInitializationAotProcessor} responsible for registering reflection
43+
* hints for Gateway MVC beans.
44+
*
45+
* @author Jürgen Wißkirchen
46+
* @author Olga Maciaszek-Sharma
47+
* @since 4.3.0
48+
*/
49+
public class GatewayMvcRuntimeHintsProcessor implements BeanFactoryInitializationAotProcessor {
50+
51+
private static final Log LOG = LogFactory.getLog(GatewayMvcRuntimeHintsProcessor.class);
52+
53+
private static final String GATEWAY_MVC_FILTER_PACKAGE_NAME = "org.springframework.cloud.gateway.server.mvc.filter";
54+
55+
private static final String GATEWAY_MVC_PREDICATE_PACKAGE_NAME = "org.springframework.cloud.gateway.server.mvc.predicate";
56+
57+
private static final Map<String, Set<String>> beansConditionalOnClasses = Map.of(
58+
"io.github.bucket4j.BucketConfiguration",
59+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions"),
60+
"org.springframework.cloud.client.circuitbreaker.CircuitBreaker",
61+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions"),
62+
"org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient",
63+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions"),
64+
"org.springframework.retry.support.RetryTemplate",
65+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions"),
66+
"org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
67+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions"));
68+
69+
private static final Set<Class<?>> PROPERTIES = Set.of(FilterProperties.class, PredicateProperties.class,
70+
RouteProperties.class);
71+
72+
@Override
73+
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
74+
return (generationContext, beanFactoryInitializationCode) -> {
75+
ReflectionHints hints = generationContext.getRuntimeHints().reflection();
76+
Set<Class<?>> typesToRegister = Stream
77+
.of(getTypesToRegister(GATEWAY_MVC_FILTER_PACKAGE_NAME),
78+
getTypesToRegister(GATEWAY_MVC_PREDICATE_PACKAGE_NAME), PROPERTIES)
79+
.flatMap(Set::stream)
80+
.collect(Collectors.toSet());
81+
typesToRegister.forEach(clazz -> hints.registerType(TypeReference.of(clazz),
82+
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
83+
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)));
84+
};
85+
}
86+
87+
private static Set<Class<?>> getTypesToRegister(String packageName) {
88+
Set<Class<?>> classesToAdd = new HashSet<>();
89+
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
90+
provider.addIncludeFilter(new AssignableTypeFilter(Object.class));
91+
provider.addExcludeFilter(new AssignableTypeFilter(FilterAutoConfiguration.class));
92+
provider.addExcludeFilter(new AssignableTypeFilter(PredicateAutoConfiguration.class));
93+
Set<BeanDefinition> components = provider.findCandidateComponents(packageName);
94+
for (BeanDefinition component : components) {
95+
Class<?> clazz;
96+
try {
97+
clazz = Class.forName(component.getBeanClassName());
98+
if (shouldRegisterClass(clazz)) {
99+
classesToAdd.add(clazz);
100+
}
101+
}
102+
catch (NoClassDefFoundError | ClassNotFoundException exception) {
103+
if (LOG.isDebugEnabled()) {
104+
LOG.debug(exception);
105+
}
106+
}
107+
}
108+
return classesToAdd;
109+
}
110+
111+
private static boolean shouldRegisterClass(Class<?> clazz) {
112+
Set<String> conditionClasses = beansConditionalOnClasses.getOrDefault(clazz.getName(), Collections.emptySet());
113+
for (String conditionClass : conditionClasses) {
114+
try {
115+
GatewayMvcRuntimeHintsProcessor.class.getClassLoader().loadClass(conditionClass);
116+
}
117+
catch (ClassNotFoundException e) {
118+
return false;
119+
}
120+
}
121+
return true;
122+
}
123+
124+
}

0 commit comments

Comments
 (0)