diff --git a/src/main/java/org/springframework/data/annotation/Persistent.java b/src/main/java/org/springframework/data/annotation/Persistent.java index 59bd14265..76341af19 100644 --- a/src/main/java/org/springframework/data/annotation/Persistent.java +++ b/src/main/java/org/springframework/data/annotation/Persistent.java @@ -20,6 +20,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.aot.hint.annotation.Reflective; import org.springframework.stereotype.Indexed; /** @@ -30,6 +31,7 @@ import org.springframework.stereotype.Indexed; */ @Indexed @Retention(RetentionPolicy.RUNTIME) +@Reflective @Target(value = { ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER }) public @interface Persistent { } diff --git a/src/main/java/org/springframework/data/aot/DataRuntimeHints.java b/src/main/java/org/springframework/data/aot/DataRuntimeHints.java new file mode 100644 index 000000000..9b784f19c --- /dev/null +++ b/src/main/java/org/springframework/data/aot/DataRuntimeHints.java @@ -0,0 +1,105 @@ +/* + * 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.aot; + +import java.util.Arrays; +import java.util.Properties; + +import org.springframework.aop.SpringProxy; +import org.springframework.aop.framework.Advised; +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.BeanFactory; +import org.springframework.core.DecoratingProxy; +import org.springframework.core.io.InputStreamSource; +import org.springframework.data.domain.AuditorAware; +import org.springframework.data.mapping.context.MappingContext; +import org.springframework.data.repository.core.RepositoryMetadata; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.data.repository.core.support.RepositoryFragment; +import org.springframework.data.repository.core.support.RepositoryFragmentsFactoryBean; +import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport; +import org.springframework.data.repository.query.QueryByExampleExecutor; +import org.springframework.lang.Nullable; + +/** + * @author Christoph Strobl + * @since 3.0 + */ +public class DataRuntimeHints implements RuntimeHintsRegistrar { + + /* + NativeHint(trigger = MappingContext.class, + types = { + @TypeHint(types = { + RepositoryFactoryBeanSupport.class, + RepositoryFragmentsFactoryBean.class, + RepositoryFragment.class, + TransactionalRepositoryFactoryBeanSupport.class, + QueryByExampleExecutor.class, + MappingContext.class, + RepositoryMetadata.class, + RepositoryMetadata.class, + }), + @TypeHint(types = {ReadingConverter.class, WritingConverter.class}), + @TypeHint(types = {Properties.class, BeanFactory.class, InputStreamSource[].class}), + @TypeHint(types = Throwable.class, access = { TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.DECLARED_FIELDS}), + @TypeHint(typeNames = { + "org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper", + }, access = { TypeAccess.DECLARED_CONSTRUCTORS, TypeAccess.DECLARED_METHODS, TypeAccess.PUBLIC_METHODS }) + }, + jdkProxies = @JdkProxyHint(typeNames = { + "org.springframework.data.annotation.QueryAnnotation", + "org.springframework.core.annotation.SynthesizedAnnotation" } + ), + initialization = @InitializationHint(types = AbstractMappingContext.class, initTime = InitializationTime.BUILD) + ) + */ + + @Override + public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) { + + System.out.println("Spring data Runtime Hints"); + + hints.reflection() + .registerTypes(Arrays.asList(TypeReference.of(RepositoryFactoryBeanSupport.class), + TypeReference.of(RepositoryFragmentsFactoryBean.class), TypeReference.of(RepositoryFragment.class), + TypeReference.of(TransactionalRepositoryFactoryBeanSupport.class), + TypeReference.of(QueryByExampleExecutor.class), TypeReference.of(MappingContext.class), + TypeReference.of(RepositoryMetadata.class)), builder -> { + builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS); + }); + hints.reflection().registerTypes( + Arrays.asList(TypeReference.of(Properties.class), TypeReference.of(BeanFactory.class), + TypeReference.of(InputStreamSource[].class)), + builder -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + + hints.reflection().registerType(Throwable.class, + builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.DECLARED_FIELDS)); + + hints.reflection().registerType( + TypeReference.of("org.springframework.data.projection.SpelEvaluatingMethodInterceptor$TargetWrapper"), + builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.INVOKE_PUBLIC_METHODS)); + + hints.proxies().registerJdkProxy(TypeReference.of("org.springframework.data.annotation.QueryAnnotation"), + TypeReference.of("org.springframework.core.annotation.SynthesizedAnnotation")); + + hints.proxies().registerJdkProxy(TypeReference.of(AuditorAware.class), TypeReference.of(SpringProxy.class), TypeReference.of(Advised.class), TypeReference.of(DecoratingProxy.class)); + } +} diff --git a/src/main/java/org/springframework/data/aot/PublicMethodReflectiveProcessor.java b/src/main/java/org/springframework/data/aot/PublicMethodReflectiveProcessor.java new file mode 100644 index 000000000..0388610a5 --- /dev/null +++ b/src/main/java/org/springframework/data/aot/PublicMethodReflectiveProcessor.java @@ -0,0 +1,33 @@ +/* + * 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.aot; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.ReflectionHints; +import org.springframework.aot.hint.annotation.SimpleReflectiveProcessor; + +/** + * @author Christoph Strobl + */ +public class PublicMethodReflectiveProcessor extends SimpleReflectiveProcessor { + + @Override + protected void registerTypeHint(ReflectionHints hints, Class type) { + System.out.println("invoking reflective hint annotation for : " + type); + hints.registerType(type, builder -> builder.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)); + + } +} diff --git a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java index c4bfea655..61ab47041 100644 --- a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java @@ -286,7 +286,7 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo .registerType(repositoryInformation.getRepositoryBaseClass(), hint -> hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)) .registerType(repositoryInformation.getDomainType(), hint -> - hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS)); + hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS)); // Repository Fragments for (RepositoryFragment fragment : getRepositoryInformation().getFragments()) { @@ -308,7 +308,7 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo SpringProxy.class, Advised.class, DecoratingProxy.class); // Transactional Repository Proxy - repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> { + //repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> { // TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy // registration suffice? @@ -327,7 +327,7 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo contribution.getRuntimeHints().proxies() .registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0])); } - }); + //}); // Reactive Repositories if (repositoryInformation.isReactiveRepository()) { @@ -396,39 +396,25 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo .registerJdkProxy(type, TargetAware.class, SpringProxy.class, DecoratingProxy.class); } - protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { - - repositoryContext.getResolvedTypes().stream() - .filter(it -> !isJavaOrPrimitiveType(it)) - .forEach(it -> contributeType(it, generationContext)); - - repositoryContext.getResolvedAnnotations().stream() - .filter(this::isSpringDataManagedAnnotation) - .map(MergedAnnotation::getType) - .forEach(it -> contributeType(it, generationContext)); - } - - private boolean isJavaOrPrimitiveType(Class type) { + static boolean isJavaOrPrimitiveType(Class type) { return TypeUtils.type(type).isPartOf("java") || type.isPrimitive() || ClassUtils.isPrimitiveArray(type); } - private boolean isSpringDataManagedAnnotation(MergedAnnotation annotation) { + static boolean isSpringDataManagedAnnotation(MergedAnnotation annotation) { return annotation != null && (isInSpringDataNamespace(annotation.getType()) || annotation.getMetaTypes().stream() - .anyMatch(this::isInSpringDataNamespace)); + .anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace)); } - private boolean isInSpringDataNamespace(Class type) { + static boolean isInSpringDataNamespace(Class type) { return type != null && type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE); } - protected void contributeType(Class type, GenerationContext generationContext) { - - logTrace("Contributing type information for [%s]", type); + static void contributeType(Class type, GenerationContext generationContext) { TypeContributor.contribute(type, it -> true, generationContext); } diff --git a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java index 4e9752f98..77d89034b 100644 --- a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java @@ -34,6 +34,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.annotation.MergedAnnotation; import org.springframework.data.repository.config.RepositoryConfigurationExtension; import org.springframework.data.repository.config.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; @@ -51,7 +52,7 @@ import org.springframework.util.StringUtils; * via the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}. *

*

- * With {@link RepositoryRegistrationAotContribution#contribute(AotRepositoryContext, GenerationContext)}, stores + * With {@link RepositoryRegistrationAotProcessor#contribute(AotRepositoryContext, GenerationContext)}, stores * can provide custom logic for contributing additional (eg. reflection) configuration. By default, reflection * configuration will be added for types reachable from the repository declaration and query methods as well as * all used {@link Annotation annotations} from the {@literal org.springframework.data} namespace. @@ -83,6 +84,19 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr : null; } + protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { + + repositoryContext.getResolvedTypes().stream() + .filter(it -> !RepositoryRegistrationAotContribution + .isJavaOrPrimitiveType(it)) + .forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext)); + + repositoryContext.getResolvedAnnotations().stream() + .filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation) + .map(MergedAnnotation::getType) + .forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext)); + } + private boolean isRepositoryBean(RegisteredBean bean) { return bean != null && getConfigMap().containsKey(bean.getBeanName()); } @@ -93,7 +107,7 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr RepositoryRegistrationAotContribution contribution = RepositoryRegistrationAotContribution.fromProcessor(this).forBean(repositoryBean); - return contribution.withModuleContribution(contribution::contribute); + return contribution.withModuleContribution(this::contribute); } @Override diff --git a/src/main/java/org/springframework/data/aot/TypeCollector.java b/src/main/java/org/springframework/data/aot/TypeCollector.java index e7392658c..697280586 100644 --- a/src/main/java/org/springframework/data/aot/TypeCollector.java +++ b/src/main/java/org/springframework/data/aot/TypeCollector.java @@ -121,7 +121,7 @@ public class TypeCollector { private void processType(ResolvableType type, InspectionCache cache, Consumer callback) { - if (ResolvableType.NONE.equals(type) || cache.contains(type)) { + if (ResolvableType.NONE.equals(type) || cache.contains(type) || type.toClass().isSynthetic()) { return; } diff --git a/src/main/java/org/springframework/data/aot/TypeContributor.java b/src/main/java/org/springframework/data/aot/TypeContributor.java index ca4a7e4bf..26eab785c 100644 --- a/src/main/java/org/springframework/data/aot/TypeContributor.java +++ b/src/main/java/org/springframework/data/aot/TypeContributor.java @@ -29,7 +29,7 @@ import org.springframework.core.annotation.SynthesizedAnnotation; * @author Christoph Strobl * @since 3.0 */ -class TypeContributor { +public class TypeContributor { public static final String DATA_NAMESPACE = "org.springframework.data"; @@ -39,7 +39,7 @@ class TypeContributor { * @param type * @param contribution */ - static void contribute(Class type, GenerationContext contribution) { + public static void contribute(Class type, GenerationContext contribution) { contribute(type, Collections.emptySet(), contribution); } @@ -51,7 +51,7 @@ class TypeContributor { * @param contribution */ @SuppressWarnings("unchecked") - static void contribute(Class type, Predicate> filter, GenerationContext contribution) { + public static void contribute(Class type, Predicate> filter, GenerationContext contribution) { if (type.isPrimitive()) { return; @@ -76,7 +76,7 @@ class TypeContributor { } contribution.getRuntimeHints().reflection().registerType(type, hint -> - hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS)); + hint.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS, MemberCategory.DECLARED_FIELDS)); } /** @@ -87,15 +87,15 @@ class TypeContributor { * @param annotationNamespaces * @param contribution */ - static void contribute(Class type, Set annotationNamespaces, GenerationContext contribution) { + public static void contribute(Class type, Set annotationNamespaces, GenerationContext contribution) { contribute(type, it -> isPartOfOrMetaAnnotatedWith(it, annotationNamespaces), contribution); } - private static boolean isPartOf(Class type, Set namespaces) { + public static boolean isPartOf(Class type, Set namespaces) { return namespaces.stream().anyMatch(namespace -> type.getPackageName().startsWith(namespace)); } - protected static boolean isPartOfOrMetaAnnotatedWith(Class annotation, Set namespaces) { + public static boolean isPartOfOrMetaAnnotatedWith(Class annotation, Set namespaces) { if (isPartOf(annotation, namespaces)) { return true; diff --git a/src/main/java/org/springframework/data/convert/ReadingConverter.java b/src/main/java/org/springframework/data/convert/ReadingConverter.java index 61495d52d..a099d2d2b 100644 --- a/src/main/java/org/springframework/data/convert/ReadingConverter.java +++ b/src/main/java/org/springframework/data/convert/ReadingConverter.java @@ -22,7 +22,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.aot.hint.annotation.Reflective; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.aot.PublicMethodReflectiveProcessor; /** * Annotation to clarify intended usage of a {@link Converter} as reading converter in case the conversion types leave @@ -32,6 +34,7 @@ import org.springframework.core.convert.converter.Converter; */ @Target(TYPE) @Documented +@Reflective({PublicMethodReflectiveProcessor.class}) @Retention(RetentionPolicy.RUNTIME) public @interface ReadingConverter { diff --git a/src/main/java/org/springframework/data/convert/WritingConverter.java b/src/main/java/org/springframework/data/convert/WritingConverter.java index 0e8e962d9..c05b47a89 100644 --- a/src/main/java/org/springframework/data/convert/WritingConverter.java +++ b/src/main/java/org/springframework/data/convert/WritingConverter.java @@ -22,7 +22,9 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.aot.hint.annotation.Reflective; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.aot.PublicMethodReflectiveProcessor; /** * Annotation to clarify intended usage of a {@link Converter} as writing converter in case the conversion types leave @@ -32,6 +34,7 @@ import org.springframework.core.convert.converter.Converter; */ @Target(TYPE) @Documented +@Reflective({PublicMethodReflectiveProcessor.class}) @Retention(RetentionPolicy.RUNTIME) public @interface WritingConverter { diff --git a/src/main/java/org/springframework/data/mapping/callback/EntityCallback.java b/src/main/java/org/springframework/data/mapping/callback/EntityCallback.java index 7a79b2fdc..84ab469cb 100644 --- a/src/main/java/org/springframework/data/mapping/callback/EntityCallback.java +++ b/src/main/java/org/springframework/data/mapping/callback/EntityCallback.java @@ -15,6 +15,9 @@ */ package org.springframework.data.mapping.callback; +import org.springframework.aot.hint.annotation.Reflective; +import org.springframework.data.aot.PublicMethodReflectiveProcessor; + /** * Marker interface for entity callbacks to be implemented in specific callback subtypes. Intended for internal usage * within store specific implementations. @@ -59,6 +62,7 @@ package org.springframework.data.mapping.callback; * @see org.springframework.core.Ordered * @see org.springframework.core.annotation.Order */ +@Reflective({PublicMethodReflectiveProcessor.class}) public interface EntityCallback { } diff --git a/src/main/java/org/springframework/data/repository/config/NamedQueriesBeanDefinitionBuilder.java b/src/main/java/org/springframework/data/repository/config/NamedQueriesBeanDefinitionBuilder.java index 975ff1d98..02574b564 100644 --- a/src/main/java/org/springframework/data/repository/config/NamedQueriesBeanDefinitionBuilder.java +++ b/src/main/java/org/springframework/data/repository/config/NamedQueriesBeanDefinitionBuilder.java @@ -15,9 +15,12 @@ */ package org.springframework.data.repository.config; +import java.io.IOException; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.core.io.ClassPathResource; import org.springframework.data.repository.core.NamedQueries; import org.springframework.lang.Nullable; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java index 464106731..3d1908dbb 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java @@ -27,6 +27,7 @@ import java.util.stream.Stream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; @@ -119,7 +120,11 @@ class RepositoryBeanDefinitionBuilder { extension.getDefaultNamedQueryLocation()); configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations); - builder.addPropertyValue("namedQueries", definitionBuilder.build(configuration.getSource())); + String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(extension.getModulePrefix() + ".named-queries", registry); + BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource()); + registry.registerBeanDefinition(namedQueriesBeanName, namedQueries); + + builder.addPropertyValue("namedQueries", new RuntimeBeanReference(namedQueriesBeanName)); registerCustomImplementation(configuration).ifPresent(it -> { builder.addPropertyReference("customImplementation", it); diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java index 804ac244b..6b9a7a2cb 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -50,7 +50,6 @@ import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.metrics.StartupStep; import org.springframework.data.ManagedTypes; import org.springframework.data.aot.TypeScanner; -import org.springframework.data.repository.config.RepositoryConfigurationDelegate.LazyRepositoryInjectionPointResolver.ManagedTypesBean; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.util.Lazy; import org.springframework.lang.NonNull; @@ -116,8 +115,8 @@ public class RepositoryConfigurationDelegate { * * @param environment can be {@literal null}. * @param resourceLoader can be {@literal null}. - * @return the given {@link Environment} if not {@literal null}, a configured {@link Environment}, - * or a default {@link Environment}. + * @return the given {@link Environment} if not {@literal null}, a configured {@link Environment}, or a default + * {@link Environment}. */ private static Environment defaultEnvironment(@Nullable Environment environment, @Nullable ResourceLoader resourceLoader) { @@ -213,13 +212,14 @@ public class RepositoryConfigurationDelegate { repoScan.end(); if (logger.isInfoEnabled()) { - logger.info(LogMessage.format("Finished Spring Data repository scanning in %s ms. Found %s %s repository interfaces.", - watch.getLastTaskTimeMillis(), configurations.size(), extension.getModuleName())); + logger.info( + LogMessage.format("Finished Spring Data repository scanning in %s ms. Found %s %s repository interfaces.", + watch.getLastTaskTimeMillis(), configurations.size(), extension.getModuleName())); } // TODO: AOT Processing -> guard this one with a flag so it's not always present // TODO: With regard to AOT Processing, perhaps we need to be smart and detect whether "core" AOT components are - // (or rather configuration is) present on the classpath to enable Spring Data AOT component registration. + // (or rather configuration is) present on the classpath to enable Spring Data AOT component registration. registerAotComponents(registry, extension, metadataByRepositoryBeanName); return definitions; @@ -239,16 +239,14 @@ public class RepositoryConfigurationDelegate { Supplier>> args = () -> { Set packages = metadataByRepositoryBeanName.values().stream() - .flatMap(it -> it.getBasePackages().stream()) - .collect(Collectors.toSet()); + .flatMap(it -> it.getBasePackages().stream()).collect(Collectors.toSet()); return new TypeScanner(resourceLoader.getClassLoader()) - .scanForTypesAnnotatedWith(extensionSupport.getIdentifyingAnnotations()) - .inPackages(packages); + .scanForTypesAnnotatedWith(extensionSupport.getIdentifyingAnnotations()).inPackages(packages); }; - registry.registerBeanDefinition(targetManagedTypesBeanName, BeanDefinitionBuilder - .rootBeanDefinition(ManagedTypesBean.class).addConstructorArgValue(args).getBeanDefinition()); +// registry.registerBeanDefinition(targetManagedTypesBeanName, BeanDefinitionBuilder +// .rootBeanDefinition(ManagedTypesBean.class).addConstructorArgValue(args).getBeanDefinition()); } } @@ -259,8 +257,7 @@ public class RepositoryConfigurationDelegate { if (!registry.isBeanNameInUse(repositoryAotProcessorBeanName)) { BeanDefinitionBuilder repositoryAotProcessor = BeanDefinitionBuilder - .rootBeanDefinition(extension.getRepositoryAotProcessor()) - .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + .rootBeanDefinition(extension.getRepositoryAotProcessor()).setRole(BeanDefinition.ROLE_INFRASTRUCTURE); repositoryAotProcessor.addPropertyValue("configMap", metadataByRepositoryBeanName); @@ -314,8 +311,8 @@ public class RepositoryConfigurationDelegate { * than a single type is considered a multi-store configuration scenario which will trigger stricter repository * scanning. * - * @return {@literal true} if multiple data store repository implementations are present in the application. - * This typically means an Spring application is using more than 1 type of data store. + * @return {@literal true} if multiple data store repository implementations are present in the application. This + * typically means an Spring application is using more than 1 type of data store. */ private boolean multipleStoresDetected() { @@ -360,12 +357,12 @@ public class RepositoryConfigurationDelegate { } /** - * Returns a new {@link LazyRepositoryInjectionPointResolver} that will have its configurations augmented with - * the given ones. + * Returns a new {@link LazyRepositoryInjectionPointResolver} that will have its configurations augmented with the + * given ones. * * @param configurations must not be {@literal null}. - * @return a new {@link LazyRepositoryInjectionPointResolver} that will have its configurations augmented with - * the given ones. + * @return a new {@link LazyRepositoryInjectionPointResolver} that will have its configurations augmented with the + * given ones. */ LazyRepositoryInjectionPointResolver withAdditionalConfigurations( Map> configurations) { @@ -397,18 +394,19 @@ public class RepositoryConfigurationDelegate { return lazyInit; } - static class ManagedTypesBean implements ManagedTypes { + } - private final Lazy>> types; + public static class ManagedTypesBean implements ManagedTypes { - public ManagedTypesBean(Supplier>> types) { - this.types = Lazy.of(types); - } + private final Lazy>> types; - @Override - public void forEach(@NonNull Consumer> action) { - types.get().forEach(action); - } + public ManagedTypesBean(Supplier>> types) { + this.types = Lazy.of(types); + } + + @Override + public void forEach(@NonNull Consumer> action) { + types.get().forEach(action); } } } diff --git a/src/main/resources/META-INF/spring/aot.factories b/src/main/resources/META-INF/spring/aot.factories index 5d378a6dd..1d7fea66b 100644 --- a/src/main/resources/META-INF/spring/aot.factories +++ b/src/main/resources/META-INF/spring/aot.factories @@ -1,2 +1,4 @@ org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\ org.springframework.data.aot.SpringDataBeanFactoryInitializationAotProcessor +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + org.springframework.data.aot.DataRuntimeHints