From 47c1ff54dd06ae9edaa0a7fa3ea48ad08908e8a8 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Fri, 24 Jun 2022 14:11:46 +0200 Subject: [PATCH] Add AOT support for redis repositories. We now use the AOT infrastructure of Spring Framework 6 and data commons to provide AOT support building the foundation for native image compilation. Additionally we register hints for GraalVM native image. See: #2350 --- .../data/redis/aot/DataRedisRuntimeHints.java | 125 ++++++++++++++++++ ...RedisRepositoryConfigurationExtension.java | 49 ++++--- .../resources/META-INF/spring/aot.factories | 2 + 3 files changed, 158 insertions(+), 18 deletions(-) create mode 100644 src/main/java/org/springframework/data/redis/aot/DataRedisRuntimeHints.java create mode 100644 src/main/resources/META-INF/spring/aot.factories diff --git a/src/main/java/org/springframework/data/redis/aot/DataRedisRuntimeHints.java b/src/main/java/org/springframework/data/redis/aot/DataRedisRuntimeHints.java new file mode 100644 index 000000000..b8c78dbd7 --- /dev/null +++ b/src/main/java/org/springframework/data/redis/aot/DataRedisRuntimeHints.java @@ -0,0 +1,125 @@ +/* + * Copyright 2022 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.data.redis.aot; + +import java.util.Arrays; + +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.lang.Nullable; + +/** + * @author Christoph Strobl + * @since 3.0 + */ +public class DataRedisRuntimeHints implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + // REFLECTION + hints.reflection() + .registerTypes( + Arrays.asList(TypeReference.of(org.springframework.data.redis.connection.RedisConnection.class), + TypeReference.of(org.springframework.data.redis.connection.StringRedisConnection.class), + TypeReference.of(org.springframework.data.redis.connection.DefaultedRedisConnection.class), + TypeReference.of(org.springframework.data.redis.connection.DefaultedRedisClusterConnection.class), + TypeReference.of(org.springframework.data.redis.connection.RedisKeyCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisStringCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisListCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisZSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisHashCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisTxCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisPubSubCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisConnectionCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisServerCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisStreamCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisScriptingCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisGeoCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisHyperLogLogCommands.class), + TypeReference.of(org.springframework.data.redis.connection.RedisClusterCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveRedisConnection.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveKeyCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveStringCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveListCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveZSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveHashCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactivePubSubCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveServerCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveStreamCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveScriptingCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveGeoCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveHyperLogLogCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterKeyCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterStringCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterListCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterZSetCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterHashCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterServerCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterStreamCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterScriptingCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterGeoCommands.class), + TypeReference.of(org.springframework.data.redis.connection.ReactiveClusterHyperLogLogCommands.class), + TypeReference.of(org.springframework.data.redis.core.ReactiveRedisOperations.class), + TypeReference.of(org.springframework.data.redis.core.ReactiveRedisTemplate.class), + TypeReference.of(org.springframework.data.redis.core.RedisOperations.class), + TypeReference.of(org.springframework.data.redis.core.RedisTemplate.class), + TypeReference.of(org.springframework.data.redis.core.StringRedisTemplate.class), + TypeReference.of(org.springframework.data.keyvalue.annotation.KeySpace.class), + TypeReference.of(org.springframework.data.keyvalue.core.AbstractKeyValueAdapter.class), + TypeReference.of(org.springframework.data.keyvalue.core.KeyValueAdapter.class), + TypeReference.of(org.springframework.data.keyvalue.core.KeyValueOperations.class), + TypeReference.of(org.springframework.data.keyvalue.core.KeyValueTemplate.class), + TypeReference.of(org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext.class), + TypeReference.of(org.springframework.data.keyvalue.repository.KeyValueRepository.class), + TypeReference + .of(org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactoryBean.class), + TypeReference.of(org.springframework.data.keyvalue.repository.config.QueryCreatorType.class), + TypeReference.of(org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery.class), + TypeReference.of(org.springframework.data.redis.core.RedisKeyValueAdapter.class), + TypeReference.of(org.springframework.data.redis.core.RedisKeyValueTemplate.class), + TypeReference.of(org.springframework.data.redis.core.convert.KeyspaceConfiguration.class), + TypeReference.of(org.springframework.data.redis.core.convert.MappingConfiguration.class), + TypeReference.of(org.springframework.data.redis.core.convert.MappingRedisConverter.class), + TypeReference.of(org.springframework.data.redis.core.convert.RedisConverter.class), + TypeReference.of(org.springframework.data.redis.core.convert.RedisCustomConversions.class), + TypeReference.of(org.springframework.data.redis.core.convert.ReferenceResolver.class), + TypeReference.of(org.springframework.data.redis.core.convert.ReferenceResolverImpl.class), + TypeReference.of(org.springframework.data.redis.core.index.IndexConfiguration.class), + TypeReference.of(org.springframework.data.redis.core.index.ConfigurableIndexDefinitionProvider.class), + TypeReference.of(org.springframework.data.redis.core.mapping.RedisMappingContext.class), + TypeReference.of(org.springframework.data.redis.repository.support.RedisRepositoryFactoryBean.class), + TypeReference.of(org.springframework.data.redis.repository.query.RedisQueryCreator.class)), + hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_METHODS)); + + // PROXIES + hints.proxies().registerJdkProxy(TypeReference.of(org.springframework.data.redis.connection.RedisConnection.class)); + hints.proxies() + .registerJdkProxy(TypeReference.of(org.springframework.data.redis.connection.DefaultedRedisConnection.class)); + hints.proxies() + .registerJdkProxy(TypeReference.of(org.springframework.data.redis.connection.ReactiveRedisConnection.class)); + hints.proxies().registerJdkProxy( + TypeReference.of(org.springframework.data.redis.connection.StringRedisConnection.class), + TypeReference.of(org.springframework.data.redis.connection.DecoratedRedisConnection.class)); + + } +} diff --git a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java index d7de3ae2e..96a6d2b47 100644 --- a/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/redis/repository/configuration/RedisRepositoryConfigurationExtension.java @@ -22,6 +22,7 @@ import java.util.Collections; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.data.keyvalue.repository.config.KeyValueRepositoryConfigurationExtension; @@ -51,6 +52,7 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon private static final String REDIS_REFERENCE_RESOLVER_BEAN_NAME = "redisReferenceResolver"; private static final String REDIS_ADAPTER_BEAN_NAME = "redisKeyValueAdapter"; private static final String REDIS_CUSTOM_CONVERSIONS_BEAN_NAME = "redisCustomConversions"; + private static final String REDIS_MAPPING_CONFIG_BEAN_NAME = "redisMappingConfiguration"; @Override public String getModuleName() { @@ -77,8 +79,29 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon "@EnableRedisRepositories(redisTemplateRef = … ) must be configured to a non empty value"); } - registerIfNotAlreadyRegistered(() -> createRedisMappingContext(configuration), registry, MAPPING_CONTEXT_BEAN_NAME, - configuration.getSource()); + // Mapping config + + String mappingConfigBeanName = BeanDefinitionReaderUtils.uniqueBeanName(REDIS_MAPPING_CONFIG_BEAN_NAME, registry); + String indexConfigurationBeanName = BeanDefinitionReaderUtils.uniqueBeanName("redisIndexConfiguration", registry); + String keyspaceConfigurationBeanName = BeanDefinitionReaderUtils.uniqueBeanName("redisKeyspaceConfiguration", + registry); + + registerIfNotAlreadyRegistered(() -> BeanDefinitionBuilder + .rootBeanDefinition(configuration.getRequiredAttribute("indexConfiguration", Class.class)) // + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) // + .getBeanDefinition(), registry, indexConfigurationBeanName, configuration.getSource()); + + registerIfNotAlreadyRegistered(() -> BeanDefinitionBuilder + .rootBeanDefinition(configuration.getRequiredAttribute("keyspaceConfiguration", Class.class)) // + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) // + .getBeanDefinition(), registry, keyspaceConfigurationBeanName, configuration.getSource()); + + registerIfNotAlreadyRegistered( + () -> createMappingConfigBeanDef(indexConfigurationBeanName, keyspaceConfigurationBeanName), registry, + mappingConfigBeanName, configuration.getSource()); + + registerIfNotAlreadyRegistered(() -> createRedisMappingContext(mappingConfigBeanName), registry, + MAPPING_CONTEXT_BEAN_NAME, configuration.getSource()); // Register custom conversions registerIfNotAlreadyRegistered(() -> new RootBeanDefinition(RedisCustomConversions.class), registry, @@ -122,8 +145,7 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon configuration.getRequiredAttribute("enableKeyspaceEvents", EnableKeyspaceEvents.class)) // .addPropertyValue("keyspaceNotificationsConfigParameter", configuration.getAttribute("keyspaceNotificationsConfigParameter", String.class).orElse("")) // - .addPropertyValue("shadowCopy", - configuration.getRequiredAttribute("shadowCopy", ShadowCopy.class)) // + .addPropertyValue("shadowCopy", configuration.getRequiredAttribute("shadowCopy", ShadowCopy.class)) // .getBeanDefinition(); } @@ -134,26 +156,17 @@ public class RedisRepositoryConfigurationExtension extends KeyValueRepositoryCon .getBeanDefinition(); } - private static AbstractBeanDefinition createRedisMappingContext(RepositoryConfigurationSource configurationSource) { + private static AbstractBeanDefinition createRedisMappingContext(String mappingConfigRef) { return BeanDefinitionBuilder.rootBeanDefinition(RedisMappingContext.class) // - .addConstructorArgValue(createMappingConfigBeanDef(configurationSource)) // - .getBeanDefinition(); + .addConstructorArgReference(mappingConfigRef).getBeanDefinition(); } - private static BeanDefinition createMappingConfigBeanDef(RepositoryConfigurationSource configuration) { - - BeanDefinition indexDefinition = BeanDefinitionBuilder - .genericBeanDefinition(configuration.getRequiredAttribute("indexConfiguration", Class.class)) // - .getBeanDefinition(); - - BeanDefinition keyspaceDefinition = BeanDefinitionBuilder - .genericBeanDefinition(configuration.getRequiredAttribute("keyspaceConfiguration", Class.class)) // - .getBeanDefinition(); + private static AbstractBeanDefinition createMappingConfigBeanDef(String indexConfigRef, String keyspaceConfigRef) { return BeanDefinitionBuilder.genericBeanDefinition(MappingConfiguration.class) // - .addConstructorArgValue(indexDefinition) // - .addConstructorArgValue(keyspaceDefinition) // + .addConstructorArgReference(indexConfigRef) // + .addConstructorArgReference(keyspaceConfigRef) // .getBeanDefinition(); } diff --git a/src/main/resources/META-INF/spring/aot.factories b/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 000000000..af53bdd00 --- /dev/null +++ b/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + org.springframework.data.redis.aot.DataRedisRuntimeHints