|
16 | 16 |
|
17 | 17 | package org.springframework.cloud.loadbalancer.config; |
18 | 18 |
|
| 19 | +import java.util.Objects; |
| 20 | +import java.util.Set; |
| 21 | +import java.util.stream.Collectors; |
| 22 | + |
19 | 23 | import com.github.benmanes.caffeine.cache.Caffeine; |
20 | 24 | import com.stoyanr.evictor.ConcurrentMapWithTimedEviction; |
21 | 25 | import org.apache.commons.logging.Log; |
22 | 26 | import org.apache.commons.logging.LogFactory; |
23 | 27 |
|
| 28 | +import org.springframework.aot.hint.MemberCategory; |
| 29 | +import org.springframework.aot.hint.RuntimeHints; |
| 30 | +import org.springframework.aot.hint.RuntimeHintsRegistrar; |
| 31 | +import org.springframework.aot.hint.TypeReference; |
24 | 32 | import org.springframework.beans.factory.InitializingBean; |
| 33 | +import org.springframework.beans.factory.config.BeanDefinition; |
25 | 34 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; |
26 | 35 | import org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration; |
27 | 36 | import org.springframework.boot.autoconfigure.condition.AllNestedConditions; |
|
39 | 48 | import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager; |
40 | 49 | import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheProperties; |
41 | 50 | import org.springframework.context.annotation.Bean; |
| 51 | +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; |
42 | 52 | import org.springframework.context.annotation.Conditional; |
43 | 53 | import org.springframework.context.annotation.Configuration; |
| 54 | +import org.springframework.core.type.filter.AssignableTypeFilter; |
| 55 | +import org.springframework.util.ClassUtils; |
44 | 56 |
|
45 | 57 | /** |
46 | 58 | * An AutoConfiguration that automatically enables caching when Spring Boot, and Spring |
@@ -149,3 +161,52 @@ static class LoadBalancerCacheEnabled { |
149 | 161 | } |
150 | 162 |
|
151 | 163 | } |
| 164 | + |
| 165 | +// Remove after adding hints to GraalVM reachability metadata repo |
| 166 | +class CaffeineHints implements RuntimeHintsRegistrar { |
| 167 | + |
| 168 | + private static final Log LOG = LogFactory.getLog(CaffeineHints.class); |
| 169 | + |
| 170 | + private static final String CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME = "com.github.benmanes.caffeine.cache.BoundedLocalCache"; |
| 171 | + |
| 172 | + private static final String CAFFEINE_CACHE_BASE_PACKAGE = "com/github/benmanes/caffeine/cache"; |
| 173 | + |
| 174 | + private static final String CAFFEINE_NODE_CLASS_NAME = "com.github.benmanes.caffeine.cache.Node"; |
| 175 | + |
| 176 | + @Override |
| 177 | + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { |
| 178 | + if (!ClassUtils.isPresent("com.github.benmanes.caffeine.cache.Caffeine", classLoader)) { |
| 179 | + return; |
| 180 | + } |
| 181 | + hints.reflection() |
| 182 | + .registerType(TypeReference.of(Caffeine.class), |
| 183 | + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, |
| 184 | + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) |
| 185 | + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.BoundedLocalCache"), |
| 186 | + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, |
| 187 | + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) |
| 188 | + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.LocalCacheFactory"), |
| 189 | + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, |
| 190 | + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)) |
| 191 | + .registerType(TypeReference.of("com.github.benmanes.caffeine.cache.Node"), |
| 192 | + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, |
| 193 | + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)); |
| 194 | + getCaffeineSubtypes().forEach(cacheType -> hints.reflection().registerType(TypeReference.of(cacheType), |
| 195 | + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_METHODS, |
| 196 | + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS))); |
| 197 | + } |
| 198 | + |
| 199 | + private Set<String> getCaffeineSubtypes() { |
| 200 | + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); |
| 201 | + try { |
| 202 | + provider.addIncludeFilter(new AssignableTypeFilter(Class.forName(CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME))); |
| 203 | + provider.addIncludeFilter(new AssignableTypeFilter(Class.forName(CAFFEINE_NODE_CLASS_NAME))); |
| 204 | + } |
| 205 | + catch (ClassNotFoundException e) { |
| 206 | + LOG.warn("Could not get class for name: " + CAFFEINE_BOUNDED_LOCAL_CACHE_CLASS_NAME); |
| 207 | + } |
| 208 | + return provider.findCandidateComponents(CAFFEINE_CACHE_BASE_PACKAGE).stream().filter(Objects::nonNull) |
| 209 | + .map(BeanDefinition::getBeanClassName).filter(Objects::nonNull).collect(Collectors.toSet()); |
| 210 | + } |
| 211 | + |
| 212 | +} |
0 commit comments