From 0a423ac3f5c60cec3fa04e12d77963011b091945 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 29 Jun 2022 09:13:35 +0200 Subject: [PATCH] =?UTF-8?q?Open=20up=20AuditingBeanDefinitionRegistrarSupp?= =?UTF-8?q?ort.registerAuditHandlerBeanDefinition(=E2=80=A6)=20to=20allow?= =?UTF-8?q?=20additional=20bean=20registrations.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original Pull Request: #2624 --- .../springframework/data/aot/AotContext.java | 310 +++++++++--------- .../data/aot/AotRepositoryInformation.java | 11 +- .../data/aot/DefaultAotContext.java | 139 ++++++++ .../data/aot/DefaultAotRepositoryContext.java | 26 +- ...agedTypesBeanRegistrationAotProcessor.java | 6 +- ...nagedTypesRegistrationAotContribution.java | 10 +- .../springframework/data/aot/Predicates.java | 6 +- .../aot/RepositoryBeanDefinitionReader.java | 49 ++- ...RepositoryRegistrationAotContribution.java | 239 ++++++-------- .../RepositoryRegistrationAotProcessor.java | 67 ++-- ...BeanFactoryInitializationAotProcessor.java | 16 +- .../data/aot/TypeCollector.java | 48 +-- .../springframework/data/aot/TypeUtils.java | 30 +- .../data/aot/hint/AuditingHints.java | 5 + .../data/aot/hint/package-info.java | 5 + .../data/aot/package-info.java | 6 + ...uditingBeanDefinitionRegistrarSupport.java | 23 +- .../RepositoryBeanDefinitionBuilder.java | 32 +- ...va => RepositoryConfigurationAdapter.java} | 6 +- .../RepositoryConfigurationDelegate.java | 12 +- ...positoryFragmentConfigurationProvider.java | 34 ++ .../core/RepositoryInformationSupport.java | 1 - .../repository/core/RepositoryMetadata.java | 2 - .../support/DefaultRepositoryInformation.java | 10 +- .../data/aot/CodeContributionAssert.java | 41 +-- .../data/aot/JdkProxyAssert.java | 1 - ...BeanRegistrationAotProcessorUnitTests.java | 4 +- ...toryRegistrationAotContributionAssert.java | 58 ++-- ...istrationAotProcessorIntegrationTests.java | 58 ++-- ...ositoryConfigurationDelegateUnitTests.java | 3 +- 30 files changed, 710 insertions(+), 548 deletions(-) create mode 100644 src/main/java/org/springframework/data/aot/DefaultAotContext.java create mode 100644 src/main/java/org/springframework/data/aot/hint/package-info.java create mode 100644 src/main/java/org/springframework/data/aot/package-info.java rename src/main/java/org/springframework/data/repository/config/{RepositoryMetadata.java => RepositoryConfigurationAdapter.java} (92%) create mode 100644 src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationProvider.java diff --git a/src/main/java/org/springframework/data/aot/AotContext.java b/src/main/java/org/springframework/data/aot/AotContext.java index 48edc6fdf..fcd666c43 100644 --- a/src/main/java/org/springframework/data/aot/AotContext.java +++ b/src/main/java/org/springframework/data/aot/AotContext.java @@ -17,6 +17,7 @@ package org.springframework.data.aot; import java.lang.annotation.Annotation; import java.util.Collection; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -26,26 +27,23 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanReference; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.data.util.TypeScanner; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; -import org.springframework.util.ObjectUtils; /** - * The context in which the AOT processing happens. - * - * Grants access to the {@link ConfigurableListableBeanFactory beanFactory} and {@link ClassLoader}. Holds a few - * convenience methods to check if a type {@link #isTypePresent(String) is present} and allows resolution of them. - * - * WARNING: Unstable internal API! + * The context in which the AOT processing happens. Grants access to the {@link ConfigurableListableBeanFactory + * beanFactory} and {@link ClassLoader}. Holds a few convenience methods to check if a type + * {@link #isTypePresent(String) is present} and allows resolution of them throug {@link TypeIntrospector} and + * {@link IntrospectedBeanDefinition}. + *

+ * Mainly for internal use within the framework. * * @author Christoph Strobl * @author John Blum - * @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory + * @author Mark Paluch + * @see BeanFactory * @since 3.0 */ public interface AotContext { @@ -57,22 +55,11 @@ public interface AotContext { * @return a new instance of {@link AotContext}. * @see BeanFactory */ - static AotContext from(@NonNull BeanFactory beanFactory) { + static AotContext from(BeanFactory beanFactory) { Assert.notNull(beanFactory, "BeanFactory must not be null"); - return new AotContext() { - - private final ConfigurableListableBeanFactory bf = beanFactory instanceof ConfigurableListableBeanFactory - ? (ConfigurableListableBeanFactory) beanFactory - : new DefaultListableBeanFactory(beanFactory); - - @NonNull - @Override - public ConfigurableListableBeanFactory getBeanFactory() { - return bf; - } - }; + return new DefaultAotContext(beanFactory); } /** @@ -84,10 +71,9 @@ public interface AotContext { ConfigurableListableBeanFactory getBeanFactory(); /** - * Returns the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. - * - * By default, this is the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types} - * declared in bean definitions. + * Returns the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. By default, this is + * the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types} declared in bean + * definitions. * * @return the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. * @see ConfigurableListableBeanFactory#getBeanClassLoader() @@ -98,184 +84,196 @@ public interface AotContext { } /** - * Determines whether the given {@link String named} {@link Class type} is present on the application classpath. + * Returns the required {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. By + * default, this is the same {@link ClassLoader} used by the {@link BeanFactory} to resolve {@link Class types} + * declared in bean definitions. * - * @param typeName {@link String name} of the {@link Class type} to evaluate; must not be {@literal null}. - * @return {@literal true} if the given {@link String named} {@link Class type} is present - * on the application classpath. - * @see #getClassLoader() + * @return the {@link ClassLoader} used by this {@link AotContext} to resolve {@link Class types}. + * @throws IllegalStateException if no {@link ClassLoader} is available. */ - default boolean isTypePresent(@NonNull String typeName) { - return ClassUtils.isPresent(typeName, getClassLoader()); + default ClassLoader getRequiredClassLoader() { + + ClassLoader loader = getClassLoader(); + + if (loader == null) { + throw new IllegalStateException("Required ClassLoader is not available"); + } + + return loader; } + /** + * Returns a {@link TypeIntrospector} to obtain further detail about a {@link Class type} given its fully-qualified + * type name + * + * @param typeName {@link String name} of the {@link Class type} to evaluate; must not be {@literal null}. + * @return the type introspector for further type-based introspection. + */ + TypeIntrospector introspectType(String typeName); + /** * Returns a new {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT * processing infrastructure. * - * @return a {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT - * processing infrastructure. + * @return a {@link TypeScanner} used to scan for {@link Class types} that will be contributed to the AOT processing + * infrastructure. * @see TypeScanner */ - @NonNull default TypeScanner getTypeScanner() { - return TypeScanner.typeScanner(getClassLoader()); + return TypeScanner.typeScanner(getRequiredClassLoader()); } /** * Scans for {@link Class types} in the given {@link String named packages} annotated with the store-specific * {@link Annotation identifying annotations}. * - * @param identifyingAnnotations {@link Collection} of {@link Annotation Annotations} identifying store-specific - * model {@link Class types}; must not be {@literal null}. + * @param identifyingAnnotations {@link Collection} of {@link Annotation Annotations} identifying store-specific model + * {@link Class types}; must not be {@literal null}. * @param packageNames {@link Collection} of {@link String package names} to scan. * @return a {@link Set} of {@link Class types} found during the scan. * @see #getTypeScanner() */ - default Set> scanPackageForTypes(@NonNull Collection> identifyingAnnotations, + default Set> scanPackageForTypes(Collection> identifyingAnnotations, Collection packageNames) { return getTypeScanner().scanPackages(packageNames).forTypesAnnotatedWith(identifyingAnnotations).collectAsSet(); } /** - * Resolves the required {@link String named} {@link Class type}. + * Returns a {@link IntrospectedBeanDefinition} to obtain further detail about the underlying bean definition. A + * introspected bean definition can also point to an absent bean definition. * - * @param typeName {@link String} containing the {@literal fully-qualified class name} of the {@link Class type} - * to resolve; must not be {@literal null}. - * @return a resolved {@link Class type} for the given, required {@link String name}. - * @throws TypeNotPresentException if the {@link String named} {@link Class type} cannot be found. + * @param reference {@link BeanReference} to the managed bean. + * @return the introspected bean definition. */ - @NonNull - default Class resolveRequiredType(@NonNull String typeName) throws TypeNotPresentException { - - try { - return ClassUtils.forName(typeName, getClassLoader()); - } catch (ClassNotFoundException cause) { - throw new TypeNotPresentException(typeName, cause); - } + default IntrospectedBeanDefinition introspectBeanDefinition(BeanReference reference) { + return introspectBeanDefinition(reference.getBeanName()); } /** - * Resolves the given {@link String named} {@link Class type} if present. + * Returns a {@link IntrospectedBeanDefinition} to obtain further detail about the underlying bean definition. A + * introspected bean definition can also point to an absent bean definition. * - * @param typeName {@link String} containing the {@literal fully-qualified class name} of the {@link Class type} - * to resolve; must not be {@literal null}. - * @return an {@link Optional} value containing the {@link Class type} - * if the {@link String fully-qualified class name} is present on the application classpath. - * @see #isTypePresent(String) - * @see #resolveRequiredType(String) - * @see java.util.Optional + * @param beanName {@link String} containing the {@literal name} of the bean to evaluate; must not be {@literal null}. + * @return the introspected bean definition. */ - default Optional> resolveType(@NonNull String typeName) { - - return isTypePresent(typeName) - ? Optional.of(resolveRequiredType(typeName)) - : Optional.empty(); - } + IntrospectedBeanDefinition introspectBeanDefinition(String beanName); /** - * Resolves the {@link BeanDefinition bean's} defined {@link Class type}. - * - * @param beanReference {@link BeanReference} to the managed bean. - * @return the {@link Class type} of the {@link BeanReference referenced bean} if defined; may be {@literal null}. - * @see BeanReference + * Type-based introspector to resolve {@link Class} from a type name and to introspect the bean factory for presence + * of beans. */ - @Nullable - default Class resolveType(@NonNull BeanReference beanReference) { - return getBeanFactory().getType(beanReference.getBeanName(), false); - } + interface TypeIntrospector { - /** - * Gets the {@link BeanDefinition} for the given, required {@link String named bean}. - * - * @param beanName {@link String} containing the {@literal name} of the bean; must not be {@literal null}. - * @return the {@link BeanDefinition} for the given, required {@link String named bean}. - * @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for - * the {@link String named bean}. - * @see BeanDefinition - */ - @NonNull - default BeanDefinition getBeanDefinition(@NonNull String beanName) throws NoSuchBeanDefinitionException { - return getBeanFactory().getBeanDefinition(beanName); - } + /** + * Determines whether @link Class type} is present on the application classpath. + * + * @return {@literal true} if the {@link Class type} is present on the application classpath. + * @see #getClassLoader() + */ + boolean isTypePresent(); - /** - * Gets the {@link RootBeanDefinition} for the given, required {@link String bean name}. - * - * @param beanName {@link String} containing the {@literal name} of the bean. - * @return the {@link RootBeanDefinition} for the given, required {@link String bean name}. - * @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for - * the {@link String named bean}. - * @throws IllegalStateException if the bean is not a {@link RootBeanDefinition root bean}. - * @see RootBeanDefinition - */ - @NonNull - default RootBeanDefinition getRootBeanDefinition(@NonNull String beanName) throws NoSuchBeanDefinitionException { + /** + * Resolves the required {@link String named} {@link Class type}. + * + * @return a resolved {@link Class type} for the given. + * @throws TypeNotPresentException if the {@link Class type} cannot be found. + */ + Class resolveRequiredType() throws TypeNotPresentException; - BeanDefinition beanDefinition = getBeanDefinition(beanName); + /** + * Resolves the {@link Class type} if present. + * + * @return an {@link Optional} value containing the {@link Class type} if the type is present on the application + * classpath. + * @see #isTypePresent() + * @see #resolveRequiredType() + * @see java.util.Optional + */ + Optional> resolveType(); - if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) { - return rootBeanDefinition; + /** + * Determines whether the {@link Class type} is declared on the application classpath and performs the given, + * required {@link Consumer action} if present. + * + * @param action {@link Consumer} defining the action to perform on the resolved {@link Class type}; must not be + * {@literal null}. + * @see java.util.function.Consumer + * @see #resolveType() + */ + default void ifTypePresent(Consumer> action) { + resolveType().ifPresent(action); } - throw new IllegalStateException(String.format("%s is not a root bean", beanName)); - } + /** + * Determines whether the associated bean factory contains at least one bean of this type. + * + * @return {@literal true} if the {@link Class type} is present on the application classpath. + */ + boolean hasBean(); + + /** + * Return a {@link List} containing bean names that implement this type. + * + * @return a {@link List} of bean names. The list is empty if the bean factory does not hold any beans of this type. + */ + List getBeanNames(); - /** - * Determines whether a bean identified by the given, required {@link String name} is a - * {@link org.springframework.beans.factory.FactoryBean}. - * - * @param beanName {@link String} containing the {@literal name} of the bean to evaluate; - * must not be {@literal null}. - * @return {@literal true} if the bean identified by the given, required {@link String name} is a - * {@link org.springframework.beans.factory.FactoryBean}. - */ - default boolean isFactoryBean(@NonNull String beanName) { - return getBeanFactory().isFactoryBean(beanName); } /** - * Determines whether a Spring {@link org.springframework.transaction.TransactionManager} is present. - * - * @return {@literal true} if a Spring {@link org.springframework.transaction.TransactionManager} is present. + * Interface defining introspection methods for bean definitions. */ - default boolean isTransactionManagerPresent() { + interface IntrospectedBeanDefinition { - return resolveType("org.springframework.transaction.TransactionManager") - .filter(it -> !ObjectUtils.isEmpty(getBeanFactory().getBeanNamesForType(it))) - .isPresent(); - } + /** + * Determines whether a bean definition identified by the given, required {@link String name} is present. + * + * @return {@literal true} if the bean definition identified by the given, required {@link String name} registered + * with. + */ + boolean isPresent(); - /** - * Determines whether the given, required {@link String type name} is declared on the application classpath - * and performs the given, required {@link Consumer action} if present. - * - * @param typeName {@link String name} of the {@link Class type} to process; must not be {@literal null}. - * @param action {@link Consumer} defining the action to perform on the resolved {@link Class type}; - * must not be {@literal null}. - * @see java.util.function.Consumer - * @see #resolveType(String) - */ - default void ifTypePresent(@NonNull String typeName, @NonNull Consumer> action) { - resolveType(typeName).ifPresent(action); - } + /** + * Determines whether a bean identified by the given, required {@link String name} is a + * {@link org.springframework.beans.factory.FactoryBean}. + * + * @return {@literal true} if the bean identified by the given, required {@link String name} is a + * {@link org.springframework.beans.factory.FactoryBean}. + */ + boolean isFactoryBean(); + + /** + * Gets the {@link BeanDefinition} for the given, required {@link String named bean}. + * + * @return the {@link BeanDefinition} for the given, required {@link String named bean}. + * @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for the {@link String named + * bean}. + * @see BeanDefinition + */ + + BeanDefinition getBeanDefinition() throws NoSuchBeanDefinitionException; + + /** + * Gets the {@link RootBeanDefinition} for the given, required {@link String bean name}. + * + * @return the {@link RootBeanDefinition} for the given, required {@link String bean name}. + * @throws NoSuchBeanDefinitionException if a {@link BeanDefinition} cannot be found for the {@link String named + * bean}. + * @throws IllegalStateException if the bean is not a {@link RootBeanDefinition root bean}. + * @see RootBeanDefinition + */ + RootBeanDefinition getRootBeanDefinition() throws NoSuchBeanDefinitionException; + + /** + * Resolves the {@link BeanDefinition bean's} defined {@link Class type}. + * + * @return the {@link Class type} of the {@link BeanReference referenced bean} if defined; may be {@literal null}. + * @see BeanReference + */ + @Nullable + Class resolveType(); - /** - * Runs the given {@link Consumer action} on any {@link org.springframework.transaction.TransactionManager} beans - * defined in the application context. - * - * @param beanNamesConsumer {@link Consumer} defining the action to perform on - * the {@link org.springframework.transaction.TransactionManager} beans if present; must not be {@literal null}. - * @see java.util.function.Consumer - */ - default void ifTransactionManagerPresent(@NonNull Consumer beanNamesConsumer) { - - ifTypePresent("org.springframework.transaction.TransactionManager", txMgrType -> { - String[] txMgrBeanNames = getBeanFactory().getBeanNamesForType(txMgrType); - if (!ObjectUtils.isEmpty(txMgrBeanNames)) { - beanNamesConsumer.accept(txMgrBeanNames); - } - }); } + } diff --git a/src/main/java/org/springframework/data/aot/AotRepositoryInformation.java b/src/main/java/org/springframework/data/aot/AotRepositoryInformation.java index 1ee8f8cca..c2361a10b 100644 --- a/src/main/java/org/springframework/data/aot/AotRepositoryInformation.java +++ b/src/main/java/org/springframework/data/aot/AotRepositoryInformation.java @@ -25,14 +25,11 @@ import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryInformationSupport; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFragment; -import org.springframework.lang.NonNull; /** * {@link RepositoryInformation} based on {@link RepositoryMetadata} collected at build time. * * @author Christoph Strobl - * @see org.springframework.data.repository.core.RepositoryInformation - * @see org.springframework.data.repository.core.RepositoryMetadata * @since 3.0 */ class AotRepositoryInformation extends RepositoryInformationSupport implements RepositoryInformation { @@ -47,20 +44,19 @@ class AotRepositoryInformation extends RepositoryInformationSupport implements R } @Override - public boolean isCustomMethod(@NonNull Method method) { + public boolean isCustomMethod(Method method) { // TODO: return false; } @Override - public boolean isBaseClassMethod(@NonNull Method method) { + public boolean isBaseClassMethod(Method method) { // TODO return false; } - @NonNull @Override - public Method getTargetClassMethod(@NonNull Method method) { + public Method getTargetClassMethod(Method method) { // TODO return method; } @@ -69,7 +65,6 @@ class AotRepositoryInformation extends RepositoryInformationSupport implements R * @return configured repository fragments. * @since 3.0 */ - @NonNull public Set> getFragments() { return new LinkedHashSet<>(fragments.get()); } diff --git a/src/main/java/org/springframework/data/aot/DefaultAotContext.java b/src/main/java/org/springframework/data/aot/DefaultAotContext.java new file mode 100644 index 000000000..a6bb28d80 --- /dev/null +++ b/src/main/java/org/springframework/data/aot/DefaultAotContext.java @@ -0,0 +1,139 @@ +/* + * 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.Collections; +import java.util.List; +import java.util.Optional; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.util.ClassUtils; + +/** + * Default {@link AotContext} implementation. + * + * @author Mark Paluch + * @since 3.0 + */ +class DefaultAotContext implements AotContext { + + private final ConfigurableListableBeanFactory factory; + + public DefaultAotContext(BeanFactory beanFactory) { + factory = beanFactory instanceof ConfigurableListableBeanFactory ? (ConfigurableListableBeanFactory) beanFactory + : new DefaultListableBeanFactory(beanFactory); + } + + @Override + public ConfigurableListableBeanFactory getBeanFactory() { + return factory; + } + + @Override + public TypeIntrospector introspectType(String typeName) { + return new DefaultTypeIntrospector(typeName); + } + + @Override + public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) { + return new DefaultIntrospectedBeanDefinition(beanName); + } + + class DefaultTypeIntrospector implements TypeIntrospector { + + private final String typeName; + + DefaultTypeIntrospector(String typeName) { + this.typeName = typeName; + } + + @Override + public boolean isTypePresent() { + return ClassUtils.isPresent(typeName, getClassLoader()); + } + + @Override + public Class resolveRequiredType() throws TypeNotPresentException { + try { + return ClassUtils.forName(typeName, getClassLoader()); + } catch (ClassNotFoundException cause) { + throw new TypeNotPresentException(typeName, cause); + } + } + + @Override + public Optional> resolveType() { + return isTypePresent() ? Optional.of(resolveRequiredType()) : Optional.empty(); + } + + @Override + public boolean hasBean() { + return !getBeanNames().isEmpty(); + } + + @Override + public List getBeanNames() { + return isTypePresent() ? Arrays.asList(factory.getBeanNamesForType(resolveRequiredType())) + : Collections.emptyList(); + } + } + + class DefaultIntrospectedBeanDefinition implements IntrospectedBeanDefinition { + + private final String beanName; + + DefaultIntrospectedBeanDefinition(String beanName) { + this.beanName = beanName; + } + + @Override + public boolean isPresent() { + return factory.containsBeanDefinition(beanName); + } + + @Override + public boolean isFactoryBean() { + return factory.isFactoryBean(beanName); + } + + @Override + public BeanDefinition getBeanDefinition() throws NoSuchBeanDefinitionException { + return factory.getBeanDefinition(beanName); + } + + @Override + public RootBeanDefinition getRootBeanDefinition() throws NoSuchBeanDefinitionException { + BeanDefinition beanDefinition = getBeanDefinition(); + + if (beanDefinition instanceof RootBeanDefinition rootBeanDefinition) { + return rootBeanDefinition; + } + + throw new IllegalStateException(String.format("%s is not a root bean", beanName)); + } + + @Override + public Class resolveType() { + return factory.getType(beanName, false); + } + } +} diff --git a/src/main/java/org/springframework/data/aot/DefaultAotRepositoryContext.java b/src/main/java/org/springframework/data/aot/DefaultAotRepositoryContext.java index 13bd945eb..e93d15764 100644 --- a/src/main/java/org/springframework/data/aot/DefaultAotRepositoryContext.java +++ b/src/main/java/org/springframework/data/aot/DefaultAotRepositoryContext.java @@ -35,24 +35,21 @@ import org.springframework.data.util.Lazy; */ class DefaultAotRepositoryContext implements AotRepositoryContext { - private AotContext aotContext; - + private final AotContext aotContext; private final Lazy>> resolvedAnnotations = Lazy.of(this::discoverAnnotations); private final Lazy>> managedTypes = Lazy.of(this::discoverTypes); private RepositoryInformation repositoryInformation; - private Set basePackages; private Set> identifyingAnnotations; - private String beanName; - public AotContext getAotContext() { - return aotContext; + public DefaultAotRepositoryContext(AotContext aotContext) { + this.aotContext = aotContext; } - public void setAotContext(AotContext aotContext) { - this.aotContext = aotContext; + public AotContext getAotContext() { + return aotContext; } @Override @@ -106,6 +103,16 @@ class DefaultAotRepositoryContext implements AotRepositoryContext { return managedTypes.get(); } + @Override + public TypeIntrospector introspectType(String typeName) { + return aotContext.introspectType(typeName); + } + + @Override + public IntrospectedBeanDefinition introspectBeanDefinition(String beanName) { + return aotContext.introspectBeanDefinition(beanName); + } + protected Set> discoverAnnotations() { Set> annotations = getResolvedTypes().stream() @@ -127,7 +134,8 @@ class DefaultAotRepositoryContext implements AotRepositoryContext { if (!getIdentifyingAnnotations().isEmpty()) { - Set> classes = aotContext.getTypeScanner().scanPackages(getBasePackages()).forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet(); + Set> classes = aotContext.getTypeScanner().scanPackages(getBasePackages()) + .forTypesAnnotatedWith(getIdentifyingAnnotations()).collectAsSet(); types.addAll(TypeCollector.inspect(classes).list()); } diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java index 018fc6117..474768f7e 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessor.java @@ -20,6 +20,7 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.aot.generate.GenerationContext; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; @@ -27,7 +28,6 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotProcessor; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.core.ResolvableType; import org.springframework.data.domain.ManagedTypes; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.StringUtils; @@ -38,8 +38,6 @@ import org.springframework.util.StringUtils; * * @author Christoph Strobl * @author John Blum - * @see org.springframework.beans.factory.BeanFactoryAware - * @see org.springframework.beans.factory.aot.BeanRegistrationAotProcessor * @since 3.0 */ public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistrationAotProcessor { @@ -48,7 +46,7 @@ public class ManagedTypesBeanRegistrationAotProcessor implements BeanRegistratio @Nullable private String moduleIdentifier; @Override - public BeanRegistrationAotContribution processAheadOfTime(@NonNull RegisteredBean registeredBean) { + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean registeredBean) { if (!isMatch(registeredBean.getBeanClass(), registeredBean.getBeanName())) { return null; diff --git a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java index c8785f9a4..3b81186a2 100644 --- a/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/aot/ManagedTypesRegistrationAotContribution.java @@ -23,7 +23,7 @@ import org.springframework.beans.factory.aot.BeanRegistrationAotContribution; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.core.ResolvableType; import org.springframework.data.domain.ManagedTypes; -import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; /** * {@link BeanRegistrationAotContribution} used to contribute a {@link ManagedTypes} registration. @@ -38,8 +38,8 @@ public class ManagedTypesRegistrationAotContribution implements BeanRegistration private final ManagedTypes managedTypes; private final BiConsumer contributionAction; - public ManagedTypesRegistrationAotContribution(AotContext aotContext, ManagedTypes managedTypes, - @NonNull BiConsumer contributionAction) { + public ManagedTypesRegistrationAotContribution(AotContext aotContext, @Nullable ManagedTypes managedTypes, + BiConsumer contributionAction) { this.aotContext = aotContext; this.managedTypes = managedTypes; @@ -50,14 +50,12 @@ public class ManagedTypesRegistrationAotContribution implements BeanRegistration return this.aotContext; } - @NonNull protected ManagedTypes getManagedTypes() { return managedTypes == null ? ManagedTypes.empty() : managedTypes; } @Override - public void applyTo(@NonNull GenerationContext generationContext, - @NonNull BeanRegistrationCode beanRegistrationCode) { + public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { List> types = getManagedTypes().toList(); diff --git a/src/main/java/org/springframework/data/aot/Predicates.java b/src/main/java/org/springframework/data/aot/Predicates.java index 868afb625..30b8cabc1 100644 --- a/src/main/java/org/springframework/data/aot/Predicates.java +++ b/src/main/java/org/springframework/data/aot/Predicates.java @@ -32,7 +32,11 @@ import java.util.function.Predicate; public abstract class Predicates { public static final Predicate IS_ENUM_MEMBER = member -> member.getDeclaringClass().isEnum(); - public static final Predicate IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); + public static final Predicate IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); // this + // should + // go + // into + // JPA public static final Predicate IS_OBJECT_MEMBER = member -> Object.class.equals(member.getDeclaringClass()); public static final Predicate IS_JAVA = member -> member.getDeclaringClass().getPackageName().startsWith("java."); public static final Predicate IS_NATIVE = member -> Modifier.isNative(member.getModifiers()); diff --git a/src/main/java/org/springframework/data/aot/RepositoryBeanDefinitionReader.java b/src/main/java/org/springframework/data/aot/RepositoryBeanDefinitionReader.java index 558af4e79..d6b1cf420 100644 --- a/src/main/java/org/springframework/data/aot/RepositoryBeanDefinitionReader.java +++ b/src/main/java/org/springframework/data/aot/RepositoryBeanDefinitionReader.java @@ -17,13 +17,14 @@ package org.springframework.data.aot; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.function.Supplier; import java.util.stream.Collectors; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.data.repository.config.RepositoryFragmentConfiguration; -import org.springframework.data.repository.config.RepositoryMetadata; +import org.springframework.data.repository.config.RepositoryConfiguration; +import org.springframework.data.repository.config.RepositoryFragmentConfigurationProvider; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFragment; @@ -31,19 +32,15 @@ import org.springframework.data.util.Lazy; import org.springframework.util.ClassUtils; /** - * Reader used to extract {@link RepositoryInformation} from {@link RepositoryMetadata}. + * Reader used to extract {@link RepositoryInformation} from {@link RepositoryConfiguration}. * * @author Christoph Strobl * @author John Blum - * @see org.springframework.data.repository.config.RepositoryFragmentConfiguration - * @see org.springframework.data.repository.config.RepositoryMetadata - * @see org.springframework.data.repository.core.RepositoryInformation - * @see org.springframework.data.repository.core.support.RepositoryFragment * @since 3.0.0 */ class RepositoryBeanDefinitionReader { - static RepositoryInformation readRepositoryInformation(RepositoryMetadata metadata, + static RepositoryInformation readRepositoryInformation(RepositoryConfiguration metadata, ConfigurableListableBeanFactory beanFactory) { return new AotRepositoryInformation(metadataSupplier(metadata, beanFactory), @@ -51,33 +48,33 @@ class RepositoryBeanDefinitionReader { } @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Supplier>> fragments(RepositoryMetadata metadata, + private static Supplier>> fragments(RepositoryConfiguration metadata, ConfigurableListableBeanFactory beanFactory) { - return Lazy.of(() -> (Collection>) metadata.getFragmentConfiguration().stream() - .flatMap(it -> { + if (metadata instanceof RepositoryFragmentConfigurationProvider provider) { - RepositoryFragmentConfiguration fragmentConfiguration = (RepositoryFragmentConfiguration) it; - List fragments = new ArrayList<>(2); + return Lazy.of(() -> { + return provider.getFragmentConfiguration().stream().flatMap(it -> { - if (fragmentConfiguration.getClassName() != null) { - fragments.add(RepositoryFragment.implemented(forName(fragmentConfiguration.getClassName(), beanFactory))); - } - if (fragmentConfiguration.getInterfaceName() != null) { - fragments.add(RepositoryFragment.structural(forName(fragmentConfiguration.getInterfaceName(), beanFactory))); - } + List> fragments = new ArrayList<>(1); - return fragments.stream(); - }) - .collect(Collectors.toList())); + // TODO: Implemented accepts an Object, not a class. + fragments.add(RepositoryFragment.implemented(forName(it.getClassName(), beanFactory))); + fragments.add(RepositoryFragment.structural(forName(it.getInterfaceName(), beanFactory))); + + return fragments.stream(); + }).collect(Collectors.toList()); + }); + } + + return Lazy.of(Collections::emptyList); } @SuppressWarnings({ "rawtypes", "unchecked" }) - private static Supplier> repositoryBaseClass(RepositoryMetadata metadata, + private static Supplier> repositoryBaseClass(RepositoryConfiguration metadata, ConfigurableListableBeanFactory beanFactory) { - return Lazy.of(() -> - (Class) metadata.getRepositoryBaseClassName().map(it -> forName(it.toString(), beanFactory)) + return Lazy.of(() -> (Class) metadata.getRepositoryBaseClassName().map(it -> forName(it.toString(), beanFactory)) .orElseGet(() -> { // TODO: retrieve the default without loading the actual RepositoryBeanFactory return Object.class; @@ -85,7 +82,7 @@ class RepositoryBeanDefinitionReader { } static Supplier metadataSupplier( - RepositoryMetadata metadata, ConfigurableListableBeanFactory beanFactory) { + RepositoryConfiguration metadata, ConfigurableListableBeanFactory beanFactory) { return Lazy.of(() -> new DefaultRepositoryMetadata(forName(metadata.getRepositoryInterface(), beanFactory))); } diff --git a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java index 6d6d7691d..1e8fc318d 100644 --- a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java +++ b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotContribution.java @@ -19,7 +19,6 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -27,7 +26,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Predicate; -import java.util.stream.Collectors; import org.springframework.aop.SpringProxy; import org.springframework.aop.framework.Advised; @@ -47,12 +45,11 @@ import org.springframework.core.annotation.MergedAnnotation; import org.springframework.data.projection.EntityProjectionIntrospector; import org.springframework.data.projection.TargetAware; import org.springframework.data.repository.Repository; +import org.springframework.data.repository.config.RepositoryConfiguration; import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport; -import org.springframework.data.repository.config.RepositoryMetadata; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFragment; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Component; import org.springframework.util.Assert; @@ -62,29 +59,24 @@ import org.springframework.util.ClassUtils; * {@link BeanRegistrationAotContribution} used to contribute repository registrations. * * @author John Blum - * @see org.springframework.aot.generate.GenerationContext - * @see org.springframework.beans.factory.aot.BeanRegistrationAotContribution - * @see org.springframework.beans.factory.support.RegisteredBean - * @see org.springframework.data.aot.RepositoryRegistrationAotProcessor - * @since 3.0.0 + * @since 3.0 */ public class RepositoryRegistrationAotContribution implements BeanRegistrationAotContribution { - private static final String KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME = - "org.springframework.data.repository.kotlin.CoroutineCrudRepository"; + private static final String KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME = "org.springframework.data.repository.kotlin.CoroutineCrudRepository"; /** * Factory method used to construct a new instance of {@link RepositoryRegistrationAotContribution} initialized with * the given, required {@link RepositoryRegistrationAotProcessor} from which this contribution was created. * - * @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from which this - * contribution was created. + * @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from + * which this contribution was created. * @return a new instance of {@link RepositoryRegistrationAotContribution}. * @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}. * @see org.springframework.data.aot.RepositoryRegistrationAotProcessor */ public static RepositoryRegistrationAotContribution fromProcessor( - @NonNull RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) { + RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) { return new RepositoryRegistrationAotContribution(repositoryRegistrationAotProcessor); } @@ -93,28 +85,25 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo private BiConsumer moduleContribution; - @NonNull private final RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor; /** - * Constructs a new instance of the {@link RepositoryRegistrationAotContribution} initialized with the given, - * required {@link RepositoryRegistrationAotProcessor} from which this contribution was created. + * Constructs a new instance of the {@link RepositoryRegistrationAotContribution} initialized with the given, required + * {@link RepositoryRegistrationAotProcessor} from which this contribution was created. * - * @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from which this - * contribution was created. + * @param repositoryRegistrationAotProcessor reference back to the {@link RepositoryRegistrationAotProcessor} from + * which this contribution was created. * @throws IllegalArgumentException if the {@link RepositoryRegistrationAotProcessor} is {@literal null}. * @see org.springframework.data.aot.RepositoryRegistrationAotProcessor */ protected RepositoryRegistrationAotContribution( - @NonNull RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) { + RepositoryRegistrationAotProcessor repositoryRegistrationAotProcessor) { - Assert.notNull(repositoryRegistrationAotProcessor, - "RepositoryRegistrationAotProcessor must not be null"); + Assert.notNull(repositoryRegistrationAotProcessor, "RepositoryRegistrationAotProcessor must not be null"); this.repositoryRegistrationAotProcessor = repositoryRegistrationAotProcessor; } - @NonNull protected ConfigurableListableBeanFactory getBeanFactory() { return getRepositoryRegistrationAotProcessor().getBeanFactory(); } @@ -123,16 +112,14 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo return Optional.ofNullable(this.moduleContribution); } - @NonNull protected AotRepositoryContext getRepositoryContext() { Assert.state(this.repositoryContext != null, - "The AOT RepositoryContext was not properly initialized; did you call the forBean(:RegisteredBean) method"); + "The AOT RepositoryContext was not properly initialized; did you call the forBean(:RegisteredBean) method"); return this.repositoryContext; } - @NonNull protected RepositoryRegistrationAotProcessor getRepositoryRegistrationAotProcessor() { return this.repositoryRegistrationAotProcessor; } @@ -146,20 +133,21 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo } /** - * Builds a {@link RepositoryRegistrationAotContribution} for given, required {@link RegisteredBean} - * representing the {@link Repository} registered in the bean registry. + * Builds a {@link RepositoryRegistrationAotContribution} for given, required {@link RegisteredBean} representing the + * {@link Repository} registered in the bean registry. * * @param repositoryBean {@link RegisteredBean} for the {@link Repository}; must not be {@literal null}. - * @return a {@link RepositoryRegistrationAotContribution} to contribute AOT metadata and code - * for the {@link Repository} {@link RegisteredBean}. + * @return a {@link RepositoryRegistrationAotContribution} to contribute AOT metadata and code for the + * {@link Repository} {@link RegisteredBean}. * @throws IllegalArgumentException if the {@link RegisteredBean} is {@literal null}. * @see org.springframework.beans.factory.support.RegisteredBean */ - public RepositoryRegistrationAotContribution forBean(@NonNull RegisteredBean repositoryBean) { + public RepositoryRegistrationAotContribution forBean(RegisteredBean repositoryBean) { Assert.notNull(repositoryBean, "The RegisteredBean for the repository must not be null"); - RepositoryMetadata repositoryMetadata = getRepositoryRegistrationAotProcessor().getRepositoryMetadata(repositoryBean); + RepositoryConfiguration repositoryMetadata = getRepositoryRegistrationAotProcessor() + .getRepositoryMetadata(repositoryBean); this.repositoryContext = buildAotRepositoryContext(repositoryBean, repositoryMetadata); @@ -168,14 +156,14 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo return this; } - protected DefaultAotRepositoryContext buildAotRepositoryContext(@NonNull RegisteredBean bean, - @NonNull RepositoryMetadata repositoryMetadata) { + protected DefaultAotRepositoryContext buildAotRepositoryContext(RegisteredBean bean, + RepositoryConfiguration repositoryMetadata) { RepositoryInformation repositoryInformation = resolveRepositoryInformation(repositoryMetadata); - DefaultAotRepositoryContext repositoryContext = new DefaultAotRepositoryContext(); + DefaultAotRepositoryContext repositoryContext = new DefaultAotRepositoryContext( + AotContext.from(this.getBeanFactory())); - repositoryContext.setAotContext(this::getBeanFactory); repositoryContext.setBeanName(bean.getBeanName()); repositoryContext.setBasePackages(repositoryMetadata.getBasePackages().toSet()); repositoryContext.setIdentifyingAnnotations(resolveIdentifyingAnnotations()); @@ -190,17 +178,17 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo try { // TODO: Getting all beans of type RepositoryConfigurationExtensionSupport will have the effect that - // if the user is currently operating in multi-store mode, then all identifying annotations from - // all stores will be included in the resulting Set. - // When using AOT, is multi-store mode allowed? I don't see why not, but does this work correctly - // with AOT, ATM? - Map repositoryConfigurationExtensionBeans = - getBeanFactory().getBeansOfType(RepositoryConfigurationExtensionSupport.class); - -// repositoryConfigurationExtensionBeans.values().stream() -// .map(RepositoryConfigurationExtensionSupport::getIdentifyingAnnotations) -// .flatMap(Collection::stream) -// .collect(Collectors.toCollection(() -> identifyingAnnotations)); + // if the user is currently operating in multi-store mode, then all identifying annotations from + // all stores will be included in the resulting Set. + // When using AOT, is multi-store mode allowed? I don't see why not, but does this work correctly + // with AOT, ATM? + Map repositoryConfigurationExtensionBeans = getBeanFactory() + .getBeansOfType(RepositoryConfigurationExtensionSupport.class); + + // repositoryConfigurationExtensionBeans.values().stream() + // .map(RepositoryConfigurationExtensionSupport::getIdentifyingAnnotations) + // .flatMap(Collection::stream) + // .collect(Collectors.toCollection(() -> identifyingAnnotations)); } catch (Throwable ignore) { // Possible BeansException because no bean exists of type RepositoryConfigurationExtension, @@ -210,26 +198,25 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo return identifyingAnnotations; } - private RepositoryInformation resolveRepositoryInformation(RepositoryMetadata repositoryMetadata) { + private RepositoryInformation resolveRepositoryInformation(RepositoryConfiguration repositoryMetadata) { return RepositoryBeanDefinitionReader.readRepositoryInformation(repositoryMetadata, getBeanFactory()); } /** - * Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the {@link FactoryBean}. - * - * We just need to set the target {@link Repository} {@link Class type} of the {@link RepositoryFactoryBeanSupport} - * while keeping the actual ID and DomainType set to {@link Object}. If the generic type signature does not match, - * then we do not try to resolve and remap the types, but rather set the {@literal factoryBeanObjectType} attribute - * on the {@link RootBeanDefinition}. + * Helps the AOT processing render the {@link FactoryBean} type correctly that is used to tell the outcome of the + * {@link FactoryBean}. We just need to set the target {@link Repository} {@link Class type} of the + * {@link RepositoryFactoryBeanSupport} while keeping the actual ID and DomainType set to {@link Object}. If the + * generic type signature does not match, then we do not try to resolve and remap the types, but rather set the + * {@literal factoryBeanObjectType} attribute on the {@link RootBeanDefinition}. */ - protected void enhanceRepositoryBeanDefinition(@NonNull RegisteredBean repositoryBean, - @NonNull RepositoryMetadata repositoryMetadata, @NonNull AotRepositoryContext repositoryContext) { + protected void enhanceRepositoryBeanDefinition(RegisteredBean repositoryBean, + RepositoryConfiguration repositoryMetadata, AotRepositoryContext repositoryContext) { logTrace(String.format("Enhancing repository factory bean definition [%s]", repositoryBean.getBeanName())); Class repositoryFactoryBeanType = repositoryContext - .resolveType(repositoryMetadata.getRepositoryFactoryBeanClassName()) - .orElse(RepositoryFactoryBeanSupport.class); + .introspectType(repositoryMetadata.getRepositoryFactoryBeanClassName()).resolveType() + .orElse(RepositoryFactoryBeanSupport.class); ResolvableType resolvedRepositoryFactoryBeanType = ResolvableType.forClass(repositoryFactoryBeanType); @@ -237,16 +224,16 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo if (isRepositoryWithTypeParameters(resolvedRepositoryFactoryBeanType)) { repositoryBeanDefinition.setTargetType(ResolvableType.forClassWithGenerics(repositoryFactoryBeanType, - repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class)); + repositoryContext.getRepositoryInformation().getRepositoryInterface(), Object.class, Object.class)); } else { repositoryBeanDefinition.setTargetType(resolvedRepositoryFactoryBeanType); repositoryBeanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, - repositoryContext.getRepositoryInformation().getRepositoryInterface()); + repositoryContext.getRepositoryInformation().getRepositoryInterface()); } } private boolean isRepositoryWithTypeParameters(ResolvableType type) { - return type != null && type.getGenerics().length == 3; + return type.getGenerics().length == 3; } /** @@ -255,7 +242,7 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo * @param moduleContribution {@link BiConsumer} used by data modules to submit contributions; can be {@literal null}. * @return this. */ - @NonNull + @SuppressWarnings("unused") public RepositoryRegistrationAotContribution withModuleContribution( @Nullable BiConsumer moduleContribution) { @@ -264,29 +251,27 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo } @Override - public void applyTo(@NonNull GenerationContext generationContext, - @NonNull BeanRegistrationCode beanRegistrationCode) { + public void applyTo(GenerationContext generationContext, BeanRegistrationCode beanRegistrationCode) { contributeRepositoryInfo(this.repositoryContext, generationContext); getModuleContribution().ifPresent(it -> it.accept(getRepositoryContext(), generationContext)); } - private void contributeRepositoryInfo(@NonNull AotRepositoryContext repositoryContext, - @NonNull GenerationContext contribution) { + private void contributeRepositoryInfo(AotRepositoryContext repositoryContext, GenerationContext contribution) { RepositoryInformation repositoryInformation = getRepositoryInformation(); - logTrace("Contributing repository information for [%s]", - repositoryInformation.getRepositoryInterface()); + logTrace("Contributing repository information for [%s]", repositoryInformation.getRepositoryInterface()); // TODO: is this the way? contribution.getRuntimeHints().reflection() - .registerType(repositoryInformation.getRepositoryInterface(), hint -> - hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)) - .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, MemberCategory.DECLARED_FIELDS)); + .registerType(repositoryInformation.getRepositoryInterface(), + hint -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_METHODS)) + .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, MemberCategory.DECLARED_FIELDS)); // Repository Fragments for (RepositoryFragment fragment : getRepositoryInformation().getFragments()) { @@ -308,26 +293,26 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo SpringProxy.class, Advised.class, DecoratingProxy.class); // Transactional Repository Proxy - //repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> { - - // TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy - // registration suffice? - // In other words, simply having a single JDK Proxy registration either with or without - // the additional Serializable TypeReference? - // NOTE: Using a single JDK Proxy registration causes the - // simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() test case method to fail. - List transactionalRepositoryProxyTypeReferences = - transactionalRepositoryProxyTypeReferences(repositoryInformation); - + // repositoryContext.ifTransactionManagerPresent(transactionManagerBeanNames -> { + + // TODO: Is the following double JDK Proxy registration above necessary or would a single JDK Proxy + // registration suffice? + // In other words, simply having a single JDK Proxy registration either with or without + // the additional Serializable TypeReference? + // NOTE: Using a single JDK Proxy registration causes the + // simpleRepositoryWithTxManagerNoKotlinNoReactiveButComponent() test case method to fail. + List transactionalRepositoryProxyTypeReferences = transactionalRepositoryProxyTypeReferences( + repositoryInformation); + + contribution.getRuntimeHints().proxies() + .registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0])); + + if (isComponentAnnotatedRepository(repositoryInformation)) { + transactionalRepositoryProxyTypeReferences.add(TypeReference.of(Serializable.class)); contribution.getRuntimeHints().proxies() .registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0])); - - if (isComponentAnnotatedRepository(repositoryInformation)) { - transactionalRepositoryProxyTypeReferences.add(TypeReference.of(Serializable.class)); - contribution.getRuntimeHints().proxies() - .registerJdkProxy(transactionalRepositoryProxyTypeReferences.toArray(new TypeReference[0])); - } - //}); + } + // }); // Reactive Repositories if (repositoryInformation.isReactiveRepository()) { @@ -342,12 +327,10 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo } // Repository query methods - repositoryInformation.getQueryMethods() - .map(repositoryInformation::getReturnedDomainClass) - .filter(Class::isInterface) - .forEach(type -> { - if (EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy() - .test(type, repositoryInformation.getDomainType())) { + repositoryInformation.getQueryMethods().map(repositoryInformation::getReturnedDomainClass) + .filter(Class::isInterface).forEach(type -> { + if (EntityProjectionIntrospector.ProjectionPredicate.typeHierarchy().test(type, + repositoryInformation.getDomainType())) { contributeProjection(type, contribution); } }); @@ -360,58 +343,52 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo private boolean isKotlinCoroutineRepository(AotRepositoryContext repositoryContext, RepositoryInformation repositoryInformation) { - return repositoryContext.resolveType(KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME) - .filter(it -> ClassUtils.isAssignable(it, repositoryInformation.getRepositoryInterface())) - .isPresent(); + return repositoryContext.introspectType(KOTLIN_COROUTINE_REPOSITORY_TYPE_NAME).resolveType() + .filter(it -> ClassUtils.isAssignable(it, repositoryInformation.getRepositoryInterface())).isPresent(); } private List kotlinRepositoryReflectionTypeReferences() { - return new ArrayList<>(Arrays.asList( - TypeReference.of("org.springframework.data.repository.kotlin.CoroutineCrudRepository"), - TypeReference.of(Repository.class), - TypeReference.of(Iterable.class), - TypeReference.of("kotlinx.coroutines.flow.Flow"), - TypeReference.of("kotlin.collections.Iterable"), - TypeReference.of("kotlin.Unit"), - TypeReference.of("kotlin.Long"), - TypeReference.of("kotlin.Boolean") - )); + return new ArrayList<>( + Arrays.asList(TypeReference.of("org.springframework.data.repository.kotlin.CoroutineCrudRepository"), + TypeReference.of(Repository.class), // + TypeReference.of(Iterable.class), // + TypeReference.of("kotlinx.coroutines.flow.Flow"), // + TypeReference.of("kotlin.collections.Iterable"), // + TypeReference.of("kotlin.Unit"), // + TypeReference.of("kotlin.Long"), // + TypeReference.of("kotlin.Boolean"))); } private List transactionalRepositoryProxyTypeReferences(RepositoryInformation repositoryInformation) { - return new ArrayList<>(Arrays.asList( - TypeReference.of(repositoryInformation.getRepositoryInterface()), - TypeReference.of(Repository.class), - TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"), - TypeReference.of("org.springframework.aop.framework.Advised"), - TypeReference.of(DecoratingProxy.class) - )); + return new ArrayList<>(Arrays.asList(TypeReference.of(repositoryInformation.getRepositoryInterface()), + TypeReference.of(Repository.class), // + TypeReference.of("org.springframework.transaction.interceptor.TransactionalProxy"), // + TypeReference.of("org.springframework.aop.framework.Advised"), // + TypeReference.of(DecoratingProxy.class))); } private void contributeProjection(Class type, GenerationContext generationContext) { - generationContext.getRuntimeHints().proxies() - .registerJdkProxy(type, TargetAware.class, SpringProxy.class, DecoratingProxy.class); + generationContext.getRuntimeHints().proxies().registerJdkProxy(type, TargetAware.class, SpringProxy.class, + DecoratingProxy.class); } static boolean isJavaOrPrimitiveType(Class type) { - - return TypeUtils.type(type).isPartOf("java") - || type.isPrimitive() - || ClassUtils.isPrimitiveArray(type); + return TypeUtils.type(type).isPartOf("java") // + || ClassUtils.isPrimitiveOrWrapper(type) // + || ClassUtils.isPrimitiveArray(type); // } - static boolean isSpringDataManagedAnnotation(MergedAnnotation annotation) { + static boolean isSpringDataManagedAnnotation(@Nullable MergedAnnotation annotation) { - return annotation != null - && (isInSpringDataNamespace(annotation.getType()) || annotation.getMetaTypes().stream() - .anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace)); + return annotation != null && (isInSpringDataNamespace(annotation.getType()) + || annotation.getMetaTypes().stream().anyMatch(RepositoryRegistrationAotContribution::isInSpringDataNamespace)); } static boolean isInSpringDataNamespace(Class type) { - return type != null && type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE); + return type.getPackage().getName().startsWith(TypeContributor.DATA_NAMESPACE); } static void contributeType(Class type, GenerationContext generationContext) { @@ -419,8 +396,8 @@ public class RepositoryRegistrationAotContribution implements BeanRegistrationAo } // TODO What was this meant to be used for? Was this type filter maybe meant to be used in - // the TypeContributor.contribute(:Class, :Predicate :GenerationContext) method - // used in the contributeType(..) method above? + // the TypeContributor.contribute(:Class, :Predicate :GenerationContext) method + // used in the contributeType(..) method above? public Predicate> typeFilter() { // like only document ones. // TODO: As in MongoDB? return it -> true; } diff --git a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java index 77d89034b..e549820a8 100644 --- a/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/RepositoryRegistrationAotProcessor.java @@ -35,10 +35,9 @@ 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.RepositoryConfiguration; import org.springframework.data.repository.config.RepositoryConfigurationExtension; -import org.springframework.data.repository.config.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -46,24 +45,22 @@ import org.springframework.util.StringUtils; /** * {@link BeanRegistrationAotProcessor} responsible processing and providing AOT configuration for repositories. *

- * Processes {@link RepositoryFactoryBeanSupport repository factory beans} to provide generic type information to - * the AOT tooling to allow deriving target type from the {@link RootBeanDefinition bean definition}. If generic types - * do not match due to customization of the factory bean by the user, at least the target repository type is provided - * via the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}. + * Processes {@link RepositoryFactoryBeanSupport repository factory beans} to provide generic type information to the + * AOT tooling to allow deriving target type from the {@link RootBeanDefinition bean definition}. If generic types do + * not match due to customization of the factory bean by the user, at least the target repository type is provided via + * the {@link FactoryBean#OBJECT_TYPE_ATTRIBUTE}. *

*

- * 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. + * 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. *

- * The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()} - * and gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}. + * The processor is typically configured via {@link RepositoryConfigurationExtension#getRepositoryAotProcessor()} and + * gets added by the {@link org.springframework.data.repository.config.RepositoryConfigurationDelegate}. * * @author Christoph Strobl * @author John Blum - * @see org.springframework.beans.factory.BeanFactoryAware - * @see org.springframework.beans.factory.aot.BeanRegistrationAotProcessor * @since 3.0.0 */ @SuppressWarnings("unused") @@ -73,45 +70,41 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr private final Log logger = LogFactory.getLog(getClass()); - private Map> configMap; + private Map> configMap; @Nullable @Override - public BeanRegistrationAotContribution processAheadOfTime(@NonNull RegisteredBean bean) { + public BeanRegistrationAotContribution processAheadOfTime(RegisteredBean bean) { - return isRepositoryBean(bean) - ? newRepositoryRegistrationAotContribution(bean) - : null; + return isRepositoryBean(bean) ? newRepositoryRegistrationAotContribution(bean) : null; } protected void contribute(AotRepositoryContext repositoryContext, GenerationContext generationContext) { repositoryContext.getResolvedTypes().stream() - .filter(it -> !RepositoryRegistrationAotContribution - .isJavaOrPrimitiveType(it)) + .filter(it -> !RepositoryRegistrationAotContribution.isJavaOrPrimitiveType(it)) .forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext)); repositoryContext.getResolvedAnnotations().stream() - .filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation) - .map(MergedAnnotation::getType) + .filter(RepositoryRegistrationAotContribution::isSpringDataManagedAnnotation).map(MergedAnnotation::getType) .forEach(it -> RepositoryRegistrationAotContribution.contributeType(it, generationContext)); } private boolean isRepositoryBean(RegisteredBean bean) { - return bean != null && getConfigMap().containsKey(bean.getBeanName()); + return getConfigMap().containsKey(bean.getBeanName()); } protected RepositoryRegistrationAotContribution newRepositoryRegistrationAotContribution( - @NonNull RegisteredBean repositoryBean) { + RegisteredBean repositoryBean) { - RepositoryRegistrationAotContribution contribution = - RepositoryRegistrationAotContribution.fromProcessor(this).forBean(repositoryBean); + RepositoryRegistrationAotContribution contribution = RepositoryRegistrationAotContribution.fromProcessor(this) + .forBean(repositoryBean); return contribution.withModuleContribution(this::contribute); } @Override - public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeansException { + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory, () -> "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory); @@ -119,44 +112,40 @@ public class RepositoryRegistrationAotProcessor implements BeanRegistrationAotPr this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } - @NonNull protected ConfigurableListableBeanFactory getBeanFactory() { return this.beanFactory; } - public void setConfigMap(@Nullable Map> configMap) { + public void setConfigMap(@Nullable Map> configMap) { this.configMap = configMap; } - @NonNull - public Map> getConfigMap() { + public Map> getConfigMap() { return nullSafeMap(this.configMap); } - @NonNull private Map nullSafeMap(@Nullable Map map) { return map != null ? map : Collections.emptyMap(); } @Nullable - protected RepositoryMetadata getRepositoryMetadata(@NonNull RegisteredBean bean) { + protected RepositoryConfiguration getRepositoryMetadata(RegisteredBean bean) { return getConfigMap().get(nullSafeBeanName(bean)); } - private String nullSafeBeanName(@Nullable RegisteredBean bean) { + private String nullSafeBeanName(RegisteredBean bean) { - String beanName = bean != null ? bean.getBeanName() : null; + String beanName = bean.getBeanName(); return StringUtils.hasText(beanName) ? beanName : ""; } - @NonNull protected Log getLogger() { return this.logger; } - private void logAt(Predicate logLevelPredicate, BiConsumer logOperation, - String message, Object... arguments) { + private void logAt(Predicate logLevelPredicate, BiConsumer logOperation, String message, + Object... arguments) { Log logger = getLogger(); diff --git a/src/main/java/org/springframework/data/aot/SpringDataBeanFactoryInitializationAotProcessor.java b/src/main/java/org/springframework/data/aot/SpringDataBeanFactoryInitializationAotProcessor.java index 9e5cba659..8227a4c75 100644 --- a/src/main/java/org/springframework/data/aot/SpringDataBeanFactoryInitializationAotProcessor.java +++ b/src/main/java/org/springframework/data/aot/SpringDataBeanFactoryInitializationAotProcessor.java @@ -15,12 +15,12 @@ */ package org.springframework.data.aot; -import java.util.Arrays; import java.util.Collections; import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution; import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor; import org.springframework.beans.factory.config.BeanDefinition; @@ -29,7 +29,6 @@ import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueH import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.data.domain.ManagedTypes; -import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; @@ -52,37 +51,36 @@ public class SpringDataBeanFactoryInitializationAotProcessor implements BeanFact @Nullable @Override - public BeanFactoryInitializationAotContribution processAheadOfTime( - @NonNull ConfigurableListableBeanFactory beanFactory) { + public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) { processManagedTypes(beanFactory); return null; } - private void processManagedTypes(@NonNull ConfigurableListableBeanFactory beanFactory) { + private void processManagedTypes(ConfigurableListableBeanFactory beanFactory) { if (beanFactory instanceof BeanDefinitionRegistry registry) { for (String beanName : beanFactory.getBeanNamesForType(ManagedTypes.class)) { BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName); - if(beanDefinition.getConstructorArgumentValues().isEmpty()) { + if (beanDefinition.getConstructorArgumentValues().isEmpty()) { return; } ValueHolder argumentValue = beanDefinition.getConstructorArgumentValues().getArgumentValue(0, null, null, null); - if (argumentValue.getValue() instanceof Supplier supplier) { + if (argumentValue.getValue()instanceof Supplier supplier) { if (logger.isDebugEnabled()) { logger.info(String.format("Replacing ManagedType bean definition %s.", beanName)); } Object value = supplier.get(); - if(ObjectUtils.isArray(value)) { + if (ObjectUtils.isArray(value)) { value = CollectionUtils.arrayToList(value); } - if(!(value instanceof Iterable)) { + if (!(value instanceof Iterable)) { value = Collections.singleton(value); } diff --git a/src/main/java/org/springframework/data/aot/TypeCollector.java b/src/main/java/org/springframework/data/aot/TypeCollector.java index 697280586..b4c0b255d 100644 --- a/src/main/java/org/springframework/data/aot/TypeCollector.java +++ b/src/main/java/org/springframework/data/aot/TypeCollector.java @@ -35,9 +35,9 @@ import java.util.function.Predicate; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.ResolvableType; import org.springframework.data.util.Lazy; -import org.springframework.lang.NonNull; import org.springframework.util.ObjectUtils; import org.springframework.util.ReflectionUtils; @@ -62,39 +62,39 @@ public class TypeCollector { private final Predicate methodFilter = method -> { - Predicate excludedDomainsPredicate = methodToTest -> - excludedDomainsFilter.test(methodToTest.getDeclaringClass()); + // TODO: Eagerly construct predicate objects to avoid object allocations during test(…) + Predicate excludedDomainsPredicate = methodToTest -> excludedDomainsFilter + .test(methodToTest.getDeclaringClass()); - Predicate excludedMethodsPredicate = Predicates.IS_BRIDGE_METHOD - .or(Predicates.IS_OBJECT_MEMBER) - .or(Predicates.IS_HIBERNATE_MEMBER) - .or(Predicates.IS_ENUM_MEMBER) - .or(Predicates.IS_NATIVE) - .or(Predicates.IS_PRIVATE) - .or(Predicates.IS_PROTECTED) - .or(Predicates.IS_SYNTHETIC) - .or(excludedDomainsPredicate.negate()); + Predicate excludedMethodsPredicate = Predicates.IS_BRIDGE_METHOD // + .or(Predicates.IS_OBJECT_MEMBER) // + .or(Predicates.IS_HIBERNATE_MEMBER) // + .or(Predicates.IS_ENUM_MEMBER) // + .or(Predicates.IS_NATIVE) // + .or(Predicates.IS_PRIVATE) // + .or(Predicates.IS_PROTECTED) // + .or(Predicates.IS_SYNTHETIC) // + .or(excludedDomainsPredicate.negate()); // return !excludedMethodsPredicate.test(method); }; private Predicate fieldFilter = field -> { - Predicate excludedFieldPredicate = Predicates.IS_HIBERNATE_MEMBER - .or(Predicates.IS_SYNTHETIC) - .or(Predicates.IS_JAVA); + // TODO: Eagerly construct predicate objects to avoid object allocations during test(…) + Predicate excludedFieldPredicate = Predicates.IS_HIBERNATE_MEMBER // + .or(Predicates.IS_SYNTHETIC) // + .or(Predicates.IS_JAVA); return !excludedFieldPredicate.test(field); }; - @NonNull - public TypeCollector filterFields(@NonNull Predicate filter) { + public TypeCollector filterFields(Predicate filter) { this.fieldFilter = filter.and(filter); return this; } - @NonNull - public TypeCollector filterTypes(@NonNull Predicate> filter) { + public TypeCollector filterTypes(Predicate> filter) { this.typeFilter = this.typeFilter.and(filter); return this; } @@ -105,12 +105,10 @@ public class TypeCollector { * @param types the types to inspect * @return a type model collector for the type */ - @NonNull public static ReachableTypes inspect(Class... types) { return inspect(Arrays.asList(types)); } - @NonNull public static ReachableTypes inspect(Collection> types) { return new ReachableTypes(new TypeCollector(), types); } @@ -137,9 +135,11 @@ public class TypeCollector { additionalTypes.addAll(visitConstructorsOfType(type)); additionalTypes.addAll(visitMethodsOfType(type)); additionalTypes.addAll(visitFieldsOfType(type)); + if (!ObjectUtils.isEmpty(type.toClass().getDeclaredClasses())) { additionalTypes.addAll(Arrays.asList(type.toClass().getDeclaredClasses())); } + for (Type discoveredType : additionalTypes) { processType(ResolvableType.forType(discoveredType, type), cache, callback); } @@ -204,7 +204,7 @@ public class TypeCollector { private final Lazy>> reachableTypes = Lazy.of(this::collect); private final TypeCollector typeCollector; - public ReachableTypes(@NonNull TypeCollector typeCollector, @NonNull Iterable> roots) { + public ReachableTypes(TypeCollector typeCollector, Iterable> roots) { this.typeCollector = typeCollector; this.roots = roots; @@ -229,7 +229,7 @@ public class TypeCollector { private final Map mutableCache = new LinkedHashMap<>(); - public void add(@NonNull ResolvableType resolvableType) { + public void add(ResolvableType resolvableType) { mutableCache.put(resolvableType.toString(), resolvableType); } @@ -237,7 +237,7 @@ public class TypeCollector { mutableCache.clear(); } - public boolean contains(@NonNull ResolvableType key) { + public boolean contains(ResolvableType key) { return mutableCache.containsKey(key.toString()); } diff --git a/src/main/java/org/springframework/data/aot/TypeUtils.java b/src/main/java/org/springframework/data/aot/TypeUtils.java index 53980effc..b99a84bea 100644 --- a/src/main/java/org/springframework/data/aot/TypeUtils.java +++ b/src/main/java/org/springframework/data/aot/TypeUtils.java @@ -21,9 +21,9 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Parameter; +import java.util.Collection; import java.util.LinkedHashSet; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; import org.springframework.core.MethodParameter; @@ -38,9 +38,12 @@ import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl */ -// TODO: Consider moving to the org.springframework.data.util package. +// TODO: Consider moving to the org.springframework.data.util package or make this package-private if not used somewhere +// else. public class TypeUtils { + public static String TRANSACTION_MANAGER_CLASS_NAME = "org.springframework.transaction.TransactionManager"; + /** * Resolve ALL annotations present for a given type. Will inspect type, constructors, parameters, methods, fields,... * @@ -50,25 +53,25 @@ public class TypeUtils { public static Set> resolveUsedAnnotations(Class type) { Set> annotations = new LinkedHashSet<>(); - annotations.addAll(TypeUtils.resolveAnnotationsFor(type).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(type).toList()); for (Constructor ctor : type.getDeclaredConstructors()) { - annotations.addAll(TypeUtils.resolveAnnotationsFor(ctor).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(ctor).toList()); for (Parameter parameter : ctor.getParameters()) { - annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList()); } } for (Field field : type.getDeclaredFields()) { - annotations.addAll(TypeUtils.resolveAnnotationsFor(field).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(field).toList()); } try { for (Method method : type.getDeclaredMethods()) { - annotations.addAll(TypeUtils.resolveAnnotationsFor(method).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(method).toList()); for (Parameter parameter : method.getParameters()) { - annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).collect(Collectors.toSet())); + annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList()); } } } catch (NoClassDefFoundError e) { - // ignore an move on + // ignore and move on } return annotations; } @@ -83,13 +86,14 @@ public class TypeUtils { .from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream(); } - public static Set> resolveAnnotationTypesFor(AnnotatedElement element, AnnotationFilter filter) { + public static Collection> resolveAnnotationTypesFor(AnnotatedElement element, + AnnotationFilter filter) { return MergedAnnotations .from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream() - .map(MergedAnnotation::getType).collect(Collectors.toSet()); + .map(MergedAnnotation::getType).toList(); } - public static Set> resolveAnnotationTypesFor(AnnotatedElement element) { + public static Collection> resolveAnnotationTypesFor(AnnotatedElement element) { return resolveAnnotationTypesFor(element, AnnotationFilter.PLAIN); } @@ -214,7 +218,7 @@ public class TypeUtils { private static class TypeOpsImpl implements TypeOps { - private Class type; + private final Class type; TypeOpsImpl(Class type) { this.type = type; diff --git a/src/main/java/org/springframework/data/aot/hint/AuditingHints.java b/src/main/java/org/springframework/data/aot/hint/AuditingHints.java index 36852ce4b..02c297c3c 100644 --- a/src/main/java/org/springframework/data/aot/hint/AuditingHints.java +++ b/src/main/java/org/springframework/data/aot/hint/AuditingHints.java @@ -26,8 +26,13 @@ import org.springframework.data.domain.ReactiveAuditorAware; import org.springframework.lang.Nullable; /** + * Runtime hint registrars for Spring Data modules that provide auditing functionality. + *

+ * Mainly for internal use within the framework. + * * @author Christoph Strobl * @since 3.0 + * @see RuntimeHintsRegistrar */ public class AuditingHints { diff --git a/src/main/java/org/springframework/data/aot/hint/package-info.java b/src/main/java/org/springframework/data/aot/hint/package-info.java new file mode 100644 index 000000000..932f06138 --- /dev/null +++ b/src/main/java/org/springframework/data/aot/hint/package-info.java @@ -0,0 +1,5 @@ +/** + * Predefined Runtime Hints. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.aot.hint; diff --git a/src/main/java/org/springframework/data/aot/package-info.java b/src/main/java/org/springframework/data/aot/package-info.java new file mode 100644 index 000000000..85b86ff76 --- /dev/null +++ b/src/main/java/org/springframework/data/aot/package-info.java @@ -0,0 +1,6 @@ +/** + * Support for registering the need for reflection, resources, java serialization and proxies at runtime for Ahead of + * Time compilation. + */ +@org.springframework.lang.NonNullApi +package org.springframework.data.aot; diff --git a/src/main/java/org/springframework/data/auditing/config/AuditingBeanDefinitionRegistrarSupport.java b/src/main/java/org/springframework/data/auditing/config/AuditingBeanDefinitionRegistrarSupport.java index d45b25303..e740553f5 100644 --- a/src/main/java/org/springframework/data/auditing/config/AuditingBeanDefinitionRegistrarSupport.java +++ b/src/main/java/org/springframework/data/auditing/config/AuditingBeanDefinitionRegistrarSupport.java @@ -56,28 +56,41 @@ public abstract class AuditingBeanDefinitionRegistrarSupport implements ImportBe Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null"); Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); - AbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(registry, getConfiguration(annotationMetadata)); + AbstractBeanDefinition ahbd = registerAuditHandlerBeanDefinition(getConfiguration(annotationMetadata), registry); registerAuditListenerBeanDefinition(ahbd, registry); } /** * Registers an appropriate BeanDefinition for an {@link AuditingHandler}. * - * @param registry must not be {@literal null}. * @param configuration must not be {@literal null}. + * @param registry must not be {@literal null}. * @return */ - private AbstractBeanDefinition registerAuditHandlerBeanDefinition(BeanDefinitionRegistry registry, - AuditingConfiguration configuration) { + protected AbstractBeanDefinition registerAuditHandlerBeanDefinition(AuditingConfiguration configuration, + BeanDefinitionRegistry registry) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); Assert.notNull(configuration, "AuditingConfiguration must not be null"); - AbstractBeanDefinition ahbd = getAuditHandlerBeanDefinitionBuilder(configuration).getBeanDefinition(); + BeanDefinitionBuilder builder = getAuditHandlerBeanDefinitionBuilder(configuration); + postProcess(builder, configuration, registry); + AbstractBeanDefinition ahbd = builder.getBeanDefinition(); registry.registerBeanDefinition(getAuditingHandlerBeanName(), ahbd); return ahbd; } + /** + * Customization hook to post-process the AuditHandler BeanDefinition. + * + * @param builder must not be {@literal null}. + * @param registry must not be {@literal null}. + * @param configuration must not be {@literal null}. + * @since 3.0 + */ + protected void postProcess(BeanDefinitionBuilder builder, AuditingConfiguration configuration, + BeanDefinitionRegistry registry) {} + /** * Creates a {@link BeanDefinitionBuilder} to ease the definition of store specific {@link AuditingHandler} * implementations. 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 bc1606eaa..80695add4 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryBeanDefinitionBuilder.java @@ -17,8 +17,8 @@ package org.springframework.data.repository.config; import static org.springframework.beans.factory.config.BeanDefinition.*; +import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -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; @@ -120,7 +121,8 @@ class RepositoryBeanDefinitionBuilder { extension.getDefaultNamedQueryLocation()); configuration.getNamedQueriesLocation().ifPresent(definitionBuilder::setLocations); - String namedQueriesBeanName = BeanDefinitionReaderUtils.uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry); + String namedQueriesBeanName = BeanDefinitionReaderUtils + .uniqueBeanName(extension.getModuleIdentifier() + ".named-queries", registry); BeanDefinition namedQueries = definitionBuilder.build(configuration.getSource()); registry.registerBeanDefinition(namedQueriesBeanName, namedQueries); @@ -138,41 +140,43 @@ class RepositoryBeanDefinitionBuilder { } // TODO: merge that with the one that creates the BD - RepositoryMetadata buildMetadata(RepositoryConfiguration configuration) { + RepositoryConfigurationAdapter buildMetadata(RepositoryConfiguration configuration) { ImplementationDetectionConfiguration config = configuration .toImplementationDetectionConfiguration(metadataReaderFactory); - List repositoryFragmentConfigurationStream = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()) // + List repositoryFragmentConfigurationStream = fragmentMetadata + .getFragmentInterfaces(configuration.getRepositoryInterface()) // .map(it -> detectRepositoryFragmentConfiguration(it, config)) // - .flatMap(Optionals::toStream) - .collect(Collectors.toList());// + .flatMap(Optionals::toStream).toList(); - if(repositoryFragmentConfigurationStream.isEmpty()) { + if (repositoryFragmentConfigurationStream.isEmpty()) { ImplementationLookupConfiguration lookup = configuration.toLookupConfiguration(metadataReaderFactory); Optional beanDefinition = implementationDetector.detectCustomImplementation(lookup); - if(beanDefinition.isPresent()) { + if (beanDefinition.isPresent()) { repositoryFragmentConfigurationStream = new ArrayList<>(1); - List interfaceNames = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()).collect(Collectors.toList()); + List interfaceNames = fragmentMetadata.getFragmentInterfaces(configuration.getRepositoryInterface()) + .toList(); String implClassName = beanDefinition.get().getBeanClassName(); try { - for (String iName : metadataReaderFactory.getMetadataReader(implClassName).getClassMetadata().getInterfaceNames()) { - if(interfaceNames.contains(iName)) { + for (String iName : metadataReaderFactory.getMetadataReader(implClassName).getClassMetadata() + .getInterfaceNames()) { + if (interfaceNames.contains(iName)) { repositoryFragmentConfigurationStream.add(new RepositoryFragmentConfiguration(iName, implClassName)); break; } } - } catch (Exception e) { - e.printStackTrace(); + } catch (IOException e) { + throw new IllegalStateException(e); } } } - return new RepositoryMetadata(configuration, repositoryFragmentConfigurationStream); + return new RepositoryConfigurationAdapter<>(configuration, repositoryFragmentConfigurationStream); } private Optional registerCustomImplementation(RepositoryConfiguration configuration) { diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryMetadata.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationAdapter.java similarity index 92% rename from src/main/java/org/springframework/data/repository/config/RepositoryMetadata.java rename to src/main/java/org/springframework/data/repository/config/RepositoryConfigurationAdapter.java index 8516ff437..f0501d100 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationAdapter.java @@ -27,12 +27,13 @@ import org.springframework.lang.Nullable; * @author Christoph Strobl * @since 3.0 */ -public class RepositoryMetadata implements RepositoryConfiguration { +class RepositoryConfigurationAdapter + implements RepositoryConfiguration, RepositoryFragmentConfigurationProvider { RepositoryConfiguration repositoryConfiguration; List fragmentConfiguration; - public RepositoryMetadata(RepositoryConfiguration repositoryConfiguration, + public RepositoryConfigurationAdapter(RepositoryConfiguration repositoryConfiguration, List fragmentConfiguration) { this.repositoryConfiguration = repositoryConfiguration; @@ -116,6 +117,7 @@ public class RepositoryMetadata impleme return repositoryConfiguration.getResourceDescription(); } + @Override public List getFragmentConfiguration() { return fragmentConfiguration; } 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 8fe79d2f2..e34474c69 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -21,13 +21,11 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.aot.AotDetector; + import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; @@ -48,7 +46,6 @@ import org.springframework.core.io.support.SpringFactoriesLoader; import org.springframework.core.log.LogMessage; import org.springframework.core.metrics.ApplicationStartup; import org.springframework.core.metrics.StartupStep; -import org.springframework.data.util.TypeScanner; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -71,8 +68,7 @@ public class RepositoryConfigurationDelegate { private static final String REPOSITORY_REGISTRATION = "Spring Data %s - Registering repository: %s - Interface: %s - Factory: %s"; private static final String MULTIPLE_MODULES = "Multiple Spring Data modules found, entering strict repository configuration mode"; private static final String NON_DEFAULT_AUTOWIRE_CANDIDATE_RESOLVER = "Non-default AutowireCandidateResolver (%s) detected. Skipping the registration of LazyRepositoryInjectionPointResolver. Lazy repository injection will not be working"; - - static final String FACTORY_BEAN_OBJECT_TYPE = FactoryBean.OBJECT_TYPE_ATTRIBUTE; // "factoryBeanObjectType"; + private static final String FACTORY_BEAN_OBJECT_TYPE = FactoryBean.OBJECT_TYPE_ATTRIBUTE; private static final Log logger = LogFactory.getLog(RepositoryConfigurationDelegate.class); @@ -169,7 +165,7 @@ public class RepositoryConfigurationDelegate { List definitions = new ArrayList<>(); Map> configurationsByRepositoryName = new HashMap<>(configurations.size()); - Map> metadataByRepositoryBeanName = new HashMap<>(configurations.size()); + Map> metadataByRepositoryBeanName = new HashMap<>(configurations.size()); for (RepositoryConfiguration configuration : configurations) { @@ -223,7 +219,7 @@ public class RepositoryConfigurationDelegate { } private void registerAotComponents(BeanDefinitionRegistry registry, RepositoryConfigurationExtension extension, - Map> metadataByRepositoryBeanName) { + Map> metadataByRepositoryBeanName) { // module-specific repository aot processor String repositoryAotProcessorBeanName = String.format("data-%s.repository-aot-processor" /* might be duplicate */, diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationProvider.java b/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationProvider.java new file mode 100644 index 000000000..401f94f7a --- /dev/null +++ b/src/main/java/org/springframework/data/repository/config/RepositoryFragmentConfigurationProvider.java @@ -0,0 +1,34 @@ +/* + * 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.repository.config; + +import java.util.List; + +/** + * Interface exposing {@link RepositoryFragmentConfiguration}. + * + * @author Mark Paluch + * @since 3.0 + */ +public interface RepositoryFragmentConfigurationProvider { + + /** + * Returns the fragment configuration. + * + * @return the fragment configuration. + */ + List getFragmentConfiguration(); +} diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java index c5e3876b4..fba3c4773 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java @@ -164,7 +164,6 @@ public abstract class RepositoryInformationSupport implements RepositoryInformat * @return */ protected boolean isQueryAnnotationPresentOn(Method method) { - return AnnotationUtils.findAnnotation(method, QueryAnnotation.class) != null; } diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java index 9788e65f7..e8f0edd0d 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryMetadata.java @@ -129,10 +129,8 @@ public interface RepositoryMetadata { boolean isReactiveRepository(); /** - * * @return * @since 3.0 - * */ Set> getFragments(); } diff --git a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java index 35d880abc..a7851a304 100644 --- a/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/DefaultRepositoryInformation.java @@ -25,8 +25,6 @@ import java.util.concurrent.ConcurrentHashMap; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryInformationSupport; import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.util.TypeInformation; -import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -102,13 +100,7 @@ class DefaultRepositoryInformation extends RepositoryInformationSupport implemen return baseComposition.getMethod(method) != null; } - /** - * - * @return - * @since 3.0 - * - */ - @Nullable + @Override public Set> getFragments() { return composition.getFragments().toSet(); } diff --git a/src/test/java/org/springframework/data/aot/CodeContributionAssert.java b/src/test/java/org/springframework/data/aot/CodeContributionAssert.java index 072b1f4fb..ade9ca520 100644 --- a/src/test/java/org/springframework/data/aot/CodeContributionAssert.java +++ b/src/test/java/org/springframework/data/aot/CodeContributionAssert.java @@ -15,7 +15,7 @@ */ package org.springframework.data.aot; -import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.*; import java.util.Arrays; import java.util.stream.Stream; @@ -25,19 +25,17 @@ import org.assertj.core.api.AbstractAssert; import org.springframework.aot.generate.GenerationContext; import org.springframework.aot.hint.ClassProxyHint; import org.springframework.aot.hint.JdkProxyHint; -import org.springframework.aot.hint.ProxyHintsPredicates; import org.springframework.aot.hint.RuntimeHintsPredicates; /** - * AssertJ {@link AbstractAssert Assertion} for code contributions originating from - * Spring Data Repository infrastructure AOT processing. + * AssertJ {@link AbstractAssert Assertion} for code contributions originating from Spring Data Repository + * infrastructure AOT processing. * * @author Christoph Strobl * @author John Blum - * @see org.assertj.core.api.AbstractAssert - * @see org.springframework.aot.generate.GenerationContext - * @since 3.0.0 + * @since 3.0 */ +@SuppressWarnings("UnusedReturnValue") public class CodeContributionAssert extends AbstractAssert { public CodeContributionAssert(GenerationContext contribution) { @@ -47,8 +45,7 @@ public class CodeContributionAssert extends AbstractAssert... types) { for (Class type : types) { - assertThat(this.actual.getRuntimeHints()) - .describedAs("No reflection entry found for [%s]", type) + assertThat(this.actual.getRuntimeHints()).describedAs("No reflection entry found for [%s]", type) .matches(RuntimeHintsPredicates.reflection().onType(type)); } @@ -58,8 +55,7 @@ public class CodeContributionAssert extends AbstractAssert... types) { for (Class type : types) { - assertThat(this.actual.getRuntimeHints()) - .describedAs("Reflection entry found for [%s]", type) + assertThat(this.actual.getRuntimeHints()).describedAs("Reflection entry found for [%s]", type) .matches(RuntimeHintsPredicates.reflection().onType(type).negate()); } @@ -68,8 +64,7 @@ public class CodeContributionAssert extends AbstractAssert entryPoint) { - assertThat(jdkProxiesFor(entryPoint).findFirst()) - .describedAs("No JDK proxy found for [%s]", entryPoint) + assertThat(jdkProxiesFor(entryPoint).findFirst()).describedAs("No JDK proxy found for [%s]", entryPoint) .isPresent(); return this; @@ -78,8 +73,7 @@ public class CodeContributionAssert extends AbstractAssert entryPoint) { assertThat(jdkProxiesFor(entryPoint).findFirst()) - .describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint) - .isNotPresent(); + .describedAs("Found JDK proxy matching [%s] though it should not be present", entryPoint).isNotPresent(); return this; } @@ -87,8 +81,8 @@ public class CodeContributionAssert extends AbstractAssert... proxyInterfaces) { assertThat(jdkProxiesFor(proxyInterfaces[0])) - .describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) - .anySatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); + .describedAs("Unable to find JDK proxy matching [%s]", Arrays.asList(proxyInterfaces)) + .anySatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); return this; } @@ -96,8 +90,7 @@ public class CodeContributionAssert extends AbstractAssert... proxyInterfaces) { assertThat(jdkProxiesFor(proxyInterfaces[0])) - .describedAs("Found JDK proxy matching [%s] though it should not be present", - Arrays.asList(proxyInterfaces)) + .describedAs("Found JDK proxy matching [%s] though it should not be present", Arrays.asList(proxyInterfaces)) .noneSatisfy(it -> new JdkProxyAssert(it).matches(proxyInterfaces)); return this; @@ -105,9 +98,8 @@ public class CodeContributionAssert extends AbstractAssert jdkProxiesFor(Class entryPoint) { - return this.actual.getRuntimeHints().proxies().jdkProxies() - .filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() - .equals(entryPoint.getCanonicalName())); + return this.actual.getRuntimeHints().proxies().jdkProxies().filter(jdkProxyHint -> jdkProxyHint + .getProxiedInterfaces().get(0).getCanonicalName().equals(entryPoint.getCanonicalName())); } public CodeContributionAssert contributesClassProxy(Class... proxyInterfaces) { @@ -121,8 +113,7 @@ public class CodeContributionAssert extends AbstractAssert classProxiesFor(Class entryPoint) { - return this.actual.getRuntimeHints().proxies().classProxies() - .filter(jdkProxyHint -> jdkProxyHint.getProxiedInterfaces().get(0).getCanonicalName() - .equals(entryPoint.getCanonicalName())); + return this.actual.getRuntimeHints().proxies().classProxies().filter(jdkProxyHint -> jdkProxyHint + .getProxiedInterfaces().get(0).getCanonicalName().equals(entryPoint.getCanonicalName())); } } diff --git a/src/test/java/org/springframework/data/aot/JdkProxyAssert.java b/src/test/java/org/springframework/data/aot/JdkProxyAssert.java index 1b4489fa2..91ebc2aa9 100644 --- a/src/test/java/org/springframework/data/aot/JdkProxyAssert.java +++ b/src/test/java/org/springframework/data/aot/JdkProxyAssert.java @@ -26,7 +26,6 @@ import org.springframework.aot.hint.TypeReference; /** * @author Christoph Strobl - * @since 2022/04 */ public class JdkProxyAssert extends AbstractAssert { diff --git a/src/test/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessorUnitTests.java b/src/test/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessorUnitTests.java index b0c7d7d95..084162738 100644 --- a/src/test/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessorUnitTests.java +++ b/src/test/java/org/springframework/data/aot/ManagedTypesBeanRegistrationAotProcessorUnitTests.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.aot.generate.ClassNameGenerator; import org.springframework.aot.generate.DefaultGenerationContext; +import org.springframework.aot.generate.GeneratedClasses; import org.springframework.aot.generate.InMemoryGeneratedFiles; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsPredicates; @@ -72,7 +73,8 @@ class ManagedTypesBeanRegistrationAotProcessorUnitTests { BeanRegistrationAotContribution contribution = createPostProcessor("commons") .processAheadOfTime(RegisteredBean.of(beanFactory, "commons.managed-types")); - DefaultGenerationContext generationContext = new DefaultGenerationContext(new ClassNameGenerator(), + DefaultGenerationContext generationContext = new DefaultGenerationContext( + new GeneratedClasses(new ClassNameGenerator(Object.class)), new InMemoryGeneratedFiles(), new RuntimeHints()); contribution.applyTo(generationContext, null); diff --git a/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotContributionAssert.java b/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotContributionAssert.java index dc09f71e9..ac4f30bb9 100644 --- a/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotContributionAssert.java +++ b/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotContributionAssert.java @@ -15,8 +15,8 @@ */ package org.springframework.data.aot; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; import java.util.LinkedHashSet; import java.util.Set; @@ -27,36 +27,41 @@ import org.assertj.core.api.AbstractAssert; import org.springframework.aot.generate.ClassNameGenerator; import org.springframework.aot.generate.DefaultGenerationContext; import org.springframework.aot.generate.InMemoryGeneratedFiles; -import org.springframework.aot.hint.RuntimeHints; import org.springframework.beans.factory.aot.BeanRegistrationCode; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryFragment; -import org.springframework.lang.NonNull; /** * AssertJ {@link AbstractAssert Assertion} for {@link RepositoryRegistrationAotContribution}. * * @author Christoph Strobl * @author John Blum - * @see org.mockito.Mockito - * @see org.assertj.core.api.AbstractAssert - * @see org.springframework.data.aot.RepositoryRegistrationAotContribution - * @since 3.0.0 + * @since 3.0 */ public class RepositoryRegistrationAotContributionAssert - extends AbstractAssert { + extends AbstractAssert { - @NonNull public static RepositoryRegistrationAotContributionAssert assertThatContribution( - @NonNull RepositoryRegistrationAotContribution actual) { + RepositoryRegistrationAotContribution actual) { return new RepositoryRegistrationAotContributionAssert(actual); } - public RepositoryRegistrationAotContributionAssert(@NonNull RepositoryRegistrationAotContribution actual) { + /** + * Create the assertion object. + * + * @param actual + */ + public RepositoryRegistrationAotContributionAssert(RepositoryRegistrationAotContribution actual) { super(actual, RepositoryRegistrationAotContributionAssert.class); } + /** + * Verifies that the actual repository type is equal to the given one. + * + * @param expected + * @return {@code this} assertion object. + */ public RepositoryRegistrationAotContributionAssert targetRepositoryTypeIs(Class expected) { assertThat(getRepositoryInformation().getRepositoryInterface()).isEqualTo(expected); @@ -64,6 +69,11 @@ public class RepositoryRegistrationAotContributionAssert return this.myself; } + /** + * Verifies that the actual repository has no repository fragments. + * + * @return {@code this} assertion object. + */ public RepositoryRegistrationAotContributionAssert hasNoFragments() { assertThat(getRepositoryInformation().getFragments()).isEmpty(); @@ -71,6 +81,11 @@ public class RepositoryRegistrationAotContributionAssert return this; } + /** + * Verifies that the actual repository has repository fragments. + * + * @return {@code this} assertion object. + */ public RepositoryRegistrationAotContributionAssert hasFragments() { assertThat(getRepositoryInformation().getFragments()).isNotEmpty(); @@ -78,10 +93,14 @@ public class RepositoryRegistrationAotContributionAssert return this; } + /** + * Verifies that the actual repository fragments satisfy the given {@link Consumer}. + * + * @return {@code this} assertion object. + */ public RepositoryRegistrationAotContributionAssert verifyFragments(Consumer>> consumer) { - assertThat(getRepositoryInformation().getFragments()) - .satisfies(it -> consumer.accept(new LinkedHashSet<>(it))); + assertThat(getRepositoryInformation().getFragments()).satisfies(it -> consumer.accept(new LinkedHashSet<>(it))); return this; } @@ -91,8 +110,8 @@ public class RepositoryRegistrationAotContributionAssert BeanRegistrationCode mockBeanRegistrationCode = mock(BeanRegistrationCode.class); - DefaultGenerationContext generationContext = - new DefaultGenerationContext(new ClassNameGenerator(), new InMemoryGeneratedFiles(), new RuntimeHints()); + DefaultGenerationContext generationContext = new DefaultGenerationContext(new ClassNameGenerator(Object.class), + new InMemoryGeneratedFiles()); this.actual.applyTo(generationContext, mockBeanRegistrationCode); @@ -103,13 +122,10 @@ public class RepositoryRegistrationAotContributionAssert private RepositoryInformation getRepositoryInformation() { - assertThat(this.actual) - .describedAs("No repository interface found on null bean contribution") - .isNotNull(); + assertThat(this.actual).describedAs("No repository interface found on null bean contribution").isNotNull(); assertThat(this.actual.getRepositoryInformation()) - .describedAs("No repository interface found on null repository information") - .isNotNull(); + .describedAs("No repository interface found on null repository information").isNotNull(); return this.actual.getRepositoryInformation(); } diff --git a/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotProcessorIntegrationTests.java b/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotProcessorIntegrationTests.java index edf296c7e..93bec97d0 100644 --- a/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotProcessorIntegrationTests.java +++ b/src/test/java/org/springframework/data/aot/RepositoryRegistrationAotProcessorIntegrationTests.java @@ -15,8 +15,8 @@ */ package org.springframework.data.aot; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.data.aot.RepositoryRegistrationAotContributionAssert.assertThatContribution; +import static org.assertj.core.api.Assertions.*; +import static org.springframework.data.aot.RepositoryRegistrationAotContributionAssert.*; import java.io.Serializable; @@ -51,9 +51,6 @@ import org.springframework.transaction.interceptor.TransactionalProxy; * Integration Tests for {@link RepositoryRegistrationAotProcessor}. * * @author Christoph Strobl - * @see org.junit.jupiter.api.Test - * @see org.springframework.data.aot.RepositoryRegistrationAotProcessor - * @see org.springframework.data.aot.RepositoryRegistrationAotContributionAssert * @author John Blum */ public class RepositoryRegistrationAotProcessorIntegrationTests { @@ -61,8 +58,8 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void simpleRepositoryNoTxManagerNoKotlinNoReactiveNoComponent() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithSimpleCrudRepository.class) - .forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithSimpleCrudRepository.class).forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); assertThatContribution(repositoryBeanContribution) // .targetRepositoryTypeIs(ConfigWithSimpleCrudRepository.MyRepo.class) // @@ -123,8 +120,7 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { // interface .contributesReflectionFor(PagingAndSortingRepository.class) // base repository .contributesReflectionFor( - ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.Person.class) // repository domain - // type + ConfigWithTransactionManagerPresentAndAtComponentAnnotatedRepoisoty.Person.class) // domain type // proxies .contributesJdkProxy( @@ -142,8 +138,8 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void contributesFragmentsCorrectly() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithFragments.class) - .forRepository(ConfigWithFragments.RepositoryWithFragments.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithFragments.class).forRepository(ConfigWithFragments.RepositoryWithFragments.class); assertThatContribution(repositoryBeanContribution) // .targetRepositoryTypeIs(ConfigWithFragments.RepositoryWithFragments.class) // @@ -175,8 +171,9 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void contributesCustomImplementationCorrectly() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithCustomImplementation.class) - .forRepository(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithCustomImplementation.class) + .forRepository(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class); assertThatContribution(repositoryBeanContribution) // .targetRepositoryTypeIs(ConfigWithCustomImplementation.RepositoryWithCustomImplementation.class) // @@ -197,12 +194,11 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void contributesDomainTypeAndReachableTypesCorrectly() { - RepositoryRegistrationAotContribution repositoryBeanContribution = - computeAotConfiguration(ConfigWithSimpleCrudRepository.class) - .forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithSimpleCrudRepository.class).forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); - assertThatContribution(repositoryBeanContribution).codeContributionSatisfies(contribution -> - contribution.contributesReflectionFor(ConfigWithSimpleCrudRepository.Person.class, + assertThatContribution(repositoryBeanContribution).codeContributionSatisfies( + contribution -> contribution.contributesReflectionFor(ConfigWithSimpleCrudRepository.Person.class, ConfigWithSimpleCrudRepository.Address.class)); } @@ -245,19 +241,18 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void contributesTypesFromQueryMethods() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class) - .forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithQueryMethods.class).forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); assertThatContribution(repositoryBeanContribution) - .codeContributionSatisfies(contribution -> - contribution.contributesReflectionFor(ProjectionInterface.class)); + .codeContributionSatisfies(contribution -> contribution.contributesReflectionFor(ProjectionInterface.class)); } @Test // GH-2593 void contributesProxiesForPotentialProjections() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class) - .forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithQueryMethods.class).forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); assertThatContribution(repositoryBeanContribution) // .codeContributionSatisfies(contribution -> { @@ -271,8 +266,8 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void contributesProxiesForDataAnnotations() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithQueryMethods.class) - .forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithQueryMethods.class).forRepository(ConfigWithQueryMethods.CustomerRepositoryWithQueryMethods.class); assertThatContribution(repositoryBeanContribution) // .codeContributionSatisfies(contribution -> { @@ -286,8 +281,8 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { @Test // GH-2593 void doesNotCareAboutNonDataAnnotations() { - RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration(ConfigWithSimpleCrudRepository.class) - .forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); + RepositoryRegistrationAotContribution repositoryBeanContribution = computeAotConfiguration( + ConfigWithSimpleCrudRepository.class).forRepository(ConfigWithSimpleCrudRepository.MyRepo.class); assertThatContribution(repositoryBeanContribution) // .codeContributionSatisfies(contribution -> { @@ -311,16 +306,15 @@ public class RepositoryRegistrationAotProcessorIntegrationTests { String[] repositoryBeanNames = applicationContext.getBeanNamesForType(repositoryType); assertThat(repositoryBeanNames) - .describedAs("Unable to find repository [%s] in configuration [%s]", - repositoryType, configuration) + .describedAs("Unable to find repository [%s] in configuration [%s]", repositoryType, configuration) .hasSize(1); String repositoryBeanName = repositoryBeanNames[0]; ConfigurableBeanFactory beanFactory = applicationContext.getDefaultListableBeanFactory(); - RepositoryRegistrationAotProcessor repositoryAotProcessor = - applicationContext.getBean(RepositoryRegistrationAotProcessor.class); + RepositoryRegistrationAotProcessor repositoryAotProcessor = applicationContext + .getBean(RepositoryRegistrationAotProcessor.class); repositoryAotProcessor.setBeanFactory(beanFactory); diff --git a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java index 3a9895bbf..32747be85 100644 --- a/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java +++ b/src/test/java/org/springframework/data/repository/config/RepositoryConfigurationDelegateUnitTests.java @@ -25,6 +25,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.springframework.aop.framework.Advised; +import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.ComponentScan; @@ -67,7 +68,7 @@ class RepositoryConfigurationDelegateUnitTests { var beanDefinition = definition.getBeanDefinition(); - assertThat(beanDefinition.getAttribute(RepositoryConfigurationDelegate.FACTORY_BEAN_OBJECT_TYPE).toString()) + assertThat(beanDefinition.getAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE).toString()) .endsWith("Repository"); } }