diff --git a/docs/src/main/asciidoc/spring-cloud-commons.adoc b/docs/src/main/asciidoc/spring-cloud-commons.adoc index 9243d771..ffcdea4d 100644 --- a/docs/src/main/asciidoc/spring-cloud-commons.adoc +++ b/docs/src/main/asciidoc/spring-cloud-commons.adoc @@ -635,7 +635,7 @@ public class MyClass { The Spring WebFlux can work with both reactive and non-reactive `WebClient` configurations, as the topics describe: * <> -* <> +* <> [[webflux-with-reactive-loadbalancer]] ==== Spring WebFlux `WebClient` with `ReactorLoadBalancerExchangeFilterFunction` diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfiguration.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfiguration.java index b37ffe53..639ec345 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfiguration.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfiguration.java @@ -16,12 +16,21 @@ package org.springframework.cloud.loadbalancer.config; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + import com.github.benmanes.caffeine.cache.Caffeine; import com.stoyanr.evictor.ConcurrentMapWithTimedEviction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; import org.springframework.boot.autoconfigure.condition.AllNestedConditions; @@ -39,8 +48,11 @@ import org.springframework.cloud.loadbalancer.cache.DefaultLoadBalancerCacheMana import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager; import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheProperties; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.core.type.filter.AssignableTypeFilter; +import org.springframework.util.ClassUtils; /** * An AutoConfiguration that automatically enables caching when Spring Boot, and Spring @@ -149,3 +161,52 @@ public class LoadBalancerCacheAutoConfiguration { } } + +// Remove after adding hints to GraalVM reachability metadata repo +class CaffeineHints implements RuntimeHintsRegistrar { + + private static final Log LOG = LogFactory.getLog(CaffeineHints.class); + + private static final String CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME = "com.github.benmanes.caffeine.cache.BoundedLocalCache"; + + private static final String CAFFEINE_CACHE_BASE_PACKAGE = "com/github/benmanes/caffeine/cache"; + + private static final String CAFFEINE_NODE_CLASS_NAME = "com.github.benmanes.caffeine.cache.Node"; + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + if (!ClassUtils.isPresent("com.github.benmanes.caffeine.cache.Caffeine", classLoader)) { + return; + } + hints.reflection() + .registerType(TypeReference.of(Caffeine.class), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.BoundedLocalCache"), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.LocalCacheFactory"), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.Node"), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)); + getCaffeineSubtypes().forEach(cacheType -> hints.reflection().registerType(TypeReference.of(cacheType), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))); + } + + private Set getCaffeineSubtypes() { + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + try { + provider.addIncludeFilter(new AssignableTypeFilter(Class.forName(CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME))); + provider.addIncludeFilter(new AssignableTypeFilter(Class.forName(CAFFEINE_NODE_CLASS_NAME))); + } + catch (ClassNotFoundException e) { + LOG.warn("Could not get class for name: " + CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME); + } + return provider.findCandidateComponents(CAFFEINE_CACHE_BASE_PACKAGE).stream().filter(Objects::nonNull) + .map(BeanDefinition::getBeanClassName).filter(Objects::nonNull).collect(Collectors.toSet()); + } + +} diff --git a/spring-cloud-loadbalancer/src/main/resources/META-INF/spring/aot.factories b/spring-cloud-loadbalancer/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 00000000..70ab7722 --- /dev/null +++ b/spring-cloud-loadbalancer/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ +org.springframework.cloud.loadbalancer.config.CaffeineHints \ No newline at end of file diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfigurationTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfigurationTests.java index dbcf9f00..981af732 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfigurationTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/LoadBalancerCacheAutoConfigurationTests.java @@ -38,7 +38,6 @@ import static org.assertj.core.api.Assertions.assertThat; * * @author Olga Maciaszek-Sharma */ - class LoadBalancerCacheAutoConfigurationTests { @Test