Skip to content

Commit d349a9f

Browse files
committed
Fix runtime hints for native images. (#3806)
Signed-off-by: Olga Maciaszek-Sharma <olga.maciaszek-sharma@broadcom.com> # Conflicts: # spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfiguration.java # spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcAotRuntimeHintsRegistrar.java
1 parent 3430401 commit d349a9f

File tree

3 files changed

+126
-85
lines changed

3 files changed

+126
-85
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
@@ -28,9 +28,9 @@
2828
import org.springframework.boot.web.client.ClientHttpRequestFactorySettings;
2929
import org.springframework.boot.web.client.RestClientCustomizer;
3030
import org.springframework.cloud.gateway.server.mvc.common.ArgumentSupplierBeanPostProcessor;
31-
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcAotRuntimeHintsRegistrar;
3231
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties;
3332
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcPropertiesBeanDefinitionRegistrar;
33+
import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcRuntimeHintsProcessor;
3434
import org.springframework.cloud.gateway.server.mvc.config.RouterFunctionHolderFactory;
3535
import org.springframework.cloud.gateway.server.mvc.filter.FormFilter;
3636
import org.springframework.cloud.gateway.server.mvc.filter.ForwardedRequestHeadersFilter;
@@ -51,7 +51,6 @@
5151
import org.springframework.context.ApplicationEventPublisher;
5252
import org.springframework.context.annotation.Bean;
5353
import org.springframework.context.annotation.Import;
54-
import org.springframework.context.annotation.ImportRuntimeHints;
5554
import org.springframework.core.env.Environment;
5655
import org.springframework.http.client.ClientHttpRequestFactory;
5756
import org.springframework.http.client.JdkClientHttpRequestFactory;
@@ -67,7 +66,6 @@
6766
@AutoConfiguration(after = { RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class })
6867
@ConditionalOnProperty(name = "spring.cloud.gateway.mvc.enabled", matchIfMissing = true)
6968
@Import(GatewayMvcPropertiesBeanDefinitionRegistrar.class)
70-
@ImportRuntimeHints(GatewayMvcAotRuntimeHintsRegistrar.class)
7169
public class GatewayServerMvcAutoConfiguration {
7270

7371
@Bean
@@ -219,4 +217,9 @@ public XForwardedRequestHeadersFilterProperties xForwardedRequestHeadersFilterPr
219217
return new XForwardedRequestHeadersFilterProperties();
220218
}
221219

220+
@Bean
221+
static GatewayMvcRuntimeHintsProcessor gatewayMvcRuntimeHintsProcessor() {
222+
return new GatewayMvcRuntimeHintsProcessor();
223+
}
224+
222225
}

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

Lines changed: 0 additions & 82 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
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.context.annotation.ClassPathScanningCandidateComponentProvider;
37+
import org.springframework.core.type.filter.AssignableTypeFilter;
38+
39+
/**
40+
* A {@link BeanFactoryInitializationAotProcessor} responsible for registering reflection
41+
* hints for Gateway MVC beans.
42+
*
43+
* @author Jürgen Wißkirchen
44+
* @author Olga Maciaszek-Sharma
45+
* @since 4.3.0
46+
*/
47+
public class GatewayMvcRuntimeHintsProcessor implements BeanFactoryInitializationAotProcessor {
48+
49+
private static final Log LOG = LogFactory.getLog(GatewayMvcRuntimeHintsProcessor.class);
50+
51+
private static final String GATEWAY_MVC_FILTER_PACKAGE_NAME = "org.springframework.cloud.gateway.server.mvc.filter";
52+
53+
private static final String GATEWAY_MVC_PREDICATE_PACKAGE_NAME = "org.springframework.cloud.gateway.server.mvc.predicate";
54+
55+
private static final Map<String, Set<String>> beansConditionalOnClasses = Map.of(
56+
"io.github.bucket4j.BucketConfiguration",
57+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.Bucket4jFilterFunctions"),
58+
"org.springframework.cloud.client.circuitbreaker.CircuitBreaker",
59+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.CircuitBreakerFilterFunctions"),
60+
"org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient",
61+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.LoadBalancerFilterFunctions"),
62+
"org.springframework.retry.support.RetryTemplate",
63+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.RetryFilterFunctions"),
64+
"org.springframework.security.oauth2.client.OAuth2AuthorizedClient",
65+
Set.of("org.springframework.cloud.gateway.server.mvc.filter.TokenRelayFilterFunctions"));
66+
67+
private static final Set<Class<?>> PROPERTIES = Set.of(FilterProperties.class, PredicateProperties.class,
68+
RouteProperties.class);
69+
70+
@Override
71+
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
72+
return (generationContext, beanFactoryInitializationCode) -> {
73+
ReflectionHints hints = generationContext.getRuntimeHints().reflection();
74+
Set<Class<?>> typesToRegister = Stream
75+
.of(getTypesToRegister(GATEWAY_MVC_FILTER_PACKAGE_NAME),
76+
getTypesToRegister(GATEWAY_MVC_PREDICATE_PACKAGE_NAME), PROPERTIES)
77+
.flatMap(Set::stream)
78+
.collect(Collectors.toSet());
79+
typesToRegister.forEach(clazz -> hints.registerType(TypeReference.of(clazz),
80+
hint -> hint.withMembers(MemberCategory.DECLARED_FIELDS, MemberCategory.INVOKE_DECLARED_METHODS,
81+
MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)));
82+
};
83+
}
84+
85+
private static Set<Class<?>> getTypesToRegister(String packageName) {
86+
Set<Class<?>> classesToAdd = new HashSet<>();
87+
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
88+
provider.addIncludeFilter(new AssignableTypeFilter(Object.class));
89+
Set<BeanDefinition> components = provider.findCandidateComponents(packageName);
90+
for (BeanDefinition component : components) {
91+
Class<?> clazz;
92+
try {
93+
clazz = Class.forName(component.getBeanClassName());
94+
if (shouldRegisterClass(clazz)) {
95+
classesToAdd.add(clazz);
96+
}
97+
}
98+
catch (NoClassDefFoundError | ClassNotFoundException exception) {
99+
if (LOG.isDebugEnabled()) {
100+
LOG.debug(exception);
101+
}
102+
}
103+
}
104+
return classesToAdd;
105+
}
106+
107+
private static boolean shouldRegisterClass(Class<?> clazz) {
108+
Set<String> conditionClasses = beansConditionalOnClasses.getOrDefault(clazz.getName(), Collections.emptySet());
109+
for (String conditionClass : conditionClasses) {
110+
try {
111+
GatewayMvcRuntimeHintsProcessor.class.getClassLoader().loadClass(conditionClass);
112+
}
113+
catch (ClassNotFoundException e) {
114+
return false;
115+
}
116+
}
117+
return true;
118+
}
119+
120+
}

0 commit comments

Comments
 (0)